11import path from 'path' ;
2- import semver from 'semver' ;
32import chalk from 'chalk' ;
4- import findUp from 'find-up' ;
5- import inquirer from 'inquirer' ;
63import { PluginManager , PluginManagerOptions } from 'live-plugin-manager' ;
74
85import * as core from '@hypermod/core' ;
9- import {
10- type ModuleLoader as MdlLoader ,
11- fetchConfigAtPath ,
12- } from '@hypermod/fetcher' ;
6+ import { type ModuleLoader as MdlLoader } from '@hypermod/fetcher' ;
137
148import { InvalidUserInputError } from './errors' ;
15- import { fetchPackages } from './utils/fetch-package' ;
16- import { mergeConfigs } from './utils/merge-configs' ;
17- import { fetchConfigsForWorkspaces , getPackageJson } from './utils/file-system' ;
189import ModuleLoader from './utils/module-loader' ;
19- import { getConfigPrompt , getMultiConfigPrompt } from './prompt' ;
10+ import { resolveLocalTransforms } from './resolvers/local' ;
11+ import { resolveNpmTransforms } from './resolvers/npm' ;
12+ import { resolveAppTransforms } from './resolvers/app' ;
13+
14+ const CLI_DIR = path . join ( __dirname , '..' , 'node_modules' ) ;
2015
2116export default async function main (
2217 paths : string [ ] ,
@@ -29,7 +24,7 @@ export default async function main(
2924 }
3025
3126 const pluginManagerConfig : Partial < PluginManagerOptions > = {
32- pluginsPath : path . join ( __dirname , '..' , 'node_modules' ) ,
27+ pluginsPath : CLI_DIR ,
3328 } ;
3429
3530 // If a registry is provided in the CLI flags, use it for the pluginManagers configuration.
@@ -53,122 +48,22 @@ export default async function main(
5348
5449 let transforms : string [ ] = [ ] ;
5550
51+ /**
52+ * If no transforms are provided, attempt to find codemods in the local hypermod.config file
53+ * or in the local package.json file.
54+ */
5655 if ( ! flags . transform && ! flags . packages ) {
5756 console . log (
5857 chalk . green (
5958 'No transforms specified, attempting to find local hypermod.config file(s)' ,
6059 ) ,
6160 ) ;
6261
63- /**
64- * Attempt to locate a root package.json with a workspaces config.
65- * If found, show a prompt with all available codemods
66- */
67- const localPackageJson = await getPackageJson ( ) ;
68-
69- if ( localPackageJson && localPackageJson . workspaces ) {
70- const configs = await fetchConfigsForWorkspaces (
71- localPackageJson . workspaces ,
72- ) ;
73- const answers = await inquirer . prompt ( [ getMultiConfigPrompt ( configs ) ] ) ;
74- const selectedConfig = configs . find (
75- ( { filePath } ) => answers . codemod . filePath === filePath ,
76- ) ;
77-
78- if ( ! selectedConfig ) {
79- throw new Error (
80- `Unable to locate config at: ${ answers . codemod . filePath } ` ,
81- ) ;
82- }
83-
84- if (
85- selectedConfig . config . transforms &&
86- selectedConfig . config . transforms [ answers . codemod . selection ]
87- ) {
88- if ( flags . sequence ) {
89- Object . entries (
90- selectedConfig . config . transforms as Record < string , string > ,
91- )
92- . filter ( ( [ key ] ) =>
93- semver . satisfies ( key , `>=${ answers . codemod . selection } ` ) ,
94- )
95- . forEach ( ( [ , path ] ) => transforms . push ( path ) ) ;
96- } else {
97- transforms . push (
98- selectedConfig . config . transforms [ answers . codemod . selection ] ,
99- ) ;
100- }
101- } else if (
102- selectedConfig . config . presets &&
103- selectedConfig . config . presets [ answers . codemod . selection ]
104- ) {
105- transforms . push (
106- selectedConfig . config . presets [ answers . codemod . selection ] ,
107- ) ;
108- }
109- } else {
110- /**
111- * Otherwise, locate any config files in parent directories
112- */
113- const configFilePath = await findUp ( [
114- 'hypermod.config.js' ,
115- 'hypermod.config.mjs' ,
116- 'hypermod.config.cjs' ,
117- 'hypermod.config.ts' ,
118- 'hypermod.config.tsx' ,
119- 'src/hypermod.config.js' ,
120- 'src/hypermod.config.mjs' ,
121- 'src/hypermod.config.cjs' ,
122- 'src/hypermod.config.ts' ,
123- 'src/hypermod.config.tsx' ,
124- 'codemods/hypermod.config.js' ,
125- 'codemods/hypermod.config.mjs' ,
126- 'codemods/hypermod.config.cjs' ,
127- 'codemods/hypermod.config.ts' ,
128- 'codemods/hypermod.config.tsx' ,
129- 'codeshift.config.js' ,
130- 'codeshift.config.mjs' ,
131- 'codeshift.config.cjs' ,
132- 'codeshift.config.ts' ,
133- 'codeshift.config.tsx' ,
134- 'src/codeshift.config.js' ,
135- 'src/codeshift.config.mjs' ,
136- 'src/codeshift.config.cjs' ,
137- 'src/codeshift.config.ts' ,
138- 'src/codeshift.config.tsx' ,
139- 'codemods/codeshift.config.js' ,
140- 'codemods/codeshift.config.mjs' ,
141- 'codemods/codeshift.config.cjs' ,
142- 'codemods/codeshift.config.ts' ,
143- 'codemods/codeshift.config.tsx' ,
144- ] ) ;
145-
146- if ( ! configFilePath ) {
147- throw new InvalidUserInputError (
148- 'No transform provided, please specify a transform with either the --transform or --packages flags' ,
149- ) ;
150- }
151-
152- console . log (
153- chalk . green ( 'Found local hypermod.config file at:' ) ,
154- configFilePath ,
155- ) ;
156-
157- const config = await fetchConfigAtPath ( configFilePath ) ;
158- const answers = await inquirer . prompt ( [ getConfigPrompt ( config ) ] ) ;
159-
160- if ( config . transforms && config . transforms [ answers . codemod ] ) {
161- Object . entries ( config . transforms )
162- . filter ( ( [ key ] ) => semver . satisfies ( key , `>=${ answers . codemod } ` ) )
163- . forEach ( ( [ , codemod ] ) =>
164- transforms . push ( `${ configFilePath } @${ codemod } ` ) ,
165- ) ;
166- } else if ( config . presets && config . presets [ answers . codemod ] ) {
167- transforms . push ( `${ configFilePath } #${ answers . codemod } ` ) ;
168- }
169- }
62+ const localTransforms = await resolveLocalTransforms ( flags ) ;
63+ transforms . push ( ...localTransforms ) ;
17064 }
17165
66+ // If a direct path to a transform is provided, use it
17267 if ( flags . transform ) {
17368 if ( flags . transform . includes ( ',' ) ) {
17469 flags . transform . split ( ',' ) . forEach ( t => transforms . push ( t . trim ( ) ) ) ;
@@ -177,91 +72,32 @@ export default async function main(
17772 }
17873 }
17974
75+ // If a package name is provided, fetch the community and remote configs
76+ // and merge them to get the transforms
18077 if ( flags . packages ) {
181- const pkgs = flags . packages . split ( ',' ) . filter ( pkg => ! ! pkg ) ;
78+ const pkgs = flags . packages ! . split ( ',' ) . filter ( pkg => ! ! pkg ) ;
18279
18380 for ( const pkg of pkgs ) {
184- const shouldPrependAtSymbol = pkg . startsWith ( '@' ) ? '@' : '' ;
185- const pkgName =
186- shouldPrependAtSymbol + pkg . split ( / [ @ # ] / ) . filter ( str => ! ! str ) [ 0 ] ;
187-
188- const rawTransformIds = pkg . split ( / (? = [ @ # ] ) / ) . filter ( str => ! ! str ) ;
189- rawTransformIds . shift ( ) ;
190-
191- const transformIds = rawTransformIds
192- . filter ( id => id . startsWith ( '@' ) )
193- . map ( id => id . substring ( 1 ) )
194- . sort ( ( idA , idB ) => {
195- if ( semver . lt ( idA , idB ) ) return - 1 ;
196- if ( semver . gt ( idA , idB ) ) return 1 ;
197- return 0 ;
198- } ) ;
199-
200- const presetIds = rawTransformIds
201- . filter ( id => id . startsWith ( '#' ) )
202- . map ( id => id . substring ( 1 ) ) ;
81+ /**
82+ * If the package name starts with "hm-", it is a Hypermod transform.
83+ * We need to fetch the transform from the Hypermod API and add it to the transforms array.
84+ */
85+ if ( pkg . startsWith ( 'hm-' ) ) {
86+ const hmTransform = await resolveAppTransforms ( pkg , CLI_DIR ) ;
87+ transforms . push ( hmTransform ) ;
88+ continue ;
89+ }
20390
204- const { community, remote } = await fetchPackages (
205- pkgName ,
91+ /**
92+ * We need to fetch the transform from the npm registry and add it to the transforms array.
93+ */
94+ const npmTransforms = await resolveNpmTransforms (
95+ flags ,
96+ pkg ,
20697 packageManager ,
20798 ) ;
20899
209- const config = mergeConfigs ( community , remote ) ;
210-
211- // Validate transforms/presets
212- transformIds . forEach ( id => {
213- if ( ! semver . valid ( semver . coerce ( id . substring ( 1 ) ) ) ) {
214- throw new InvalidUserInputError (
215- `Invalid version provided to the --packages flag. Unable to resolve version "${ id } " for package "${ pkgName } ". Please try: "[scope]/[package]@[version]" for example @mylib/[email protected] ` , 216- ) ;
217- }
218-
219- if ( ! config . transforms || ! config . transforms [ id ] ) {
220- throw new InvalidUserInputError (
221- `Invalid version provided to the --packages flag. Unable to resolve version "${ id } " for package "${ pkgName } "` ,
222- ) ;
223- }
224- } ) ;
225-
226- presetIds . forEach ( id => {
227- if ( ! config . presets || ! config . presets [ id ] ) {
228- throw new InvalidUserInputError (
229- `Invalid preset provided to the --packages flag. Unable to resolve preset "${ id } " for package "${ pkgName } "` ,
230- ) ;
231- }
232- } ) ;
233-
234- if ( presetIds . length === 0 && transformIds . length === 0 ) {
235- const res : { codemod : string } = await inquirer . prompt ( [
236- getConfigPrompt ( config ) ,
237- ] ) ;
238-
239- if ( semver . valid ( semver . coerce ( res . codemod ) ) ) {
240- transformIds . push ( res . codemod ) ;
241- } else {
242- presetIds . push ( res . codemod ) ;
243- }
244- }
245-
246- // Get transform file paths
247- if ( config . transforms ) {
248- if ( flags . sequence ) {
249- Object . entries ( config . transforms )
250- . filter ( ( [ key ] ) => semver . satisfies ( key , `>=${ transformIds [ 0 ] } ` ) )
251- . forEach ( ( [ , path ] ) => transforms . push ( path ) ) ;
252- } else {
253- Object . entries ( config . transforms )
254- . filter ( ( [ id ] ) => transformIds . includes ( id ) )
255- . forEach ( ( [ , path ] ) => transforms . push ( path ) ) ;
256- }
257- }
258-
259- // Get preset file paths
260- if ( config . presets ) {
261- Object . entries ( config . presets )
262- . filter ( ( [ id ] ) => presetIds . includes ( id ) )
263- . forEach ( ( [ , path ] ) => transforms . push ( path ) ) ;
264- }
100+ transforms . push ( ...npmTransforms ) ;
265101 }
266102 }
267103
0 commit comments