diff --git a/.changeset/pretty-tomatoes-stare.md b/.changeset/pretty-tomatoes-stare.md new file mode 100644 index 0000000..4ac48b0 --- /dev/null +++ b/.changeset/pretty-tomatoes-stare.md @@ -0,0 +1,5 @@ +--- +'xstate-codegen': patch +--- + +Added the --cwd option to change the working directory that the CLI tool operates in. This can be useful for altering which node_modules folder you'd like to target. diff --git a/packages/xstate-compiled/readme.md b/packages/xstate-compiled/readme.md index faa070d..4fbcd11 100644 --- a/packages/xstate-compiled/readme.md +++ b/packages/xstate-compiled/readme.md @@ -1,6 +1,8 @@ ## Type Safe State Machines -`xstate-codegen` gives you 100% type-safe usage of XState in Typescript. You get type safety on: +`xstate-codegen` gives you 100% type-safe usage of XState in Typescript. [Try it out at this codesandbox!](https://codesandbox.io/s/xstate-codegen-example-7etw2?file=/src/demo.machine.ts) + +You get type safety on: - Transition targets: `on: {EVENT: 'deep.nested.state'}` - Services @@ -46,6 +48,28 @@ type Event = { type: 'DUMMY_TYPE' }; const machine = Machine({}); ``` +### Usage with React + +```ts +import { useMachine } from '@xstate/compiled/react'; +import { machine } from './myMachine.machine'; + +const [state, dispatch] = useMachine(machine, { + // all options in here will be type checked +}); +``` + +### Usage with Interpret + +```ts +import { interpret } from '@xstate/compiled'; +import { machine } from './myMachine.machine'; + +const service = interpret(machine).withConfig({ + // all options in here will be type checked +}); +``` + ## Options ### Once @@ -61,3 +85,9 @@ By default, the CLI watches for changes in your files. Running `--once` runs the By default, the CLI adds the required declaration files inside node_modules at `node_modules/@xstate/compiled`. This writes the declaration files to a specified directory. > Note, this only writes the declaration files to the directory. The `.js` files still get written to `node_modules/@xstate/compiled`. + +### cwd + +`xstate-codegen "src/**/**.machine.ts" --cwd ../` + +Change the working directory that xstate-codegen operates in. This can sometimes be useful for altering which node_modules folder you'd like to target. diff --git a/packages/xstate-compiled/src/extractMachines.ts b/packages/xstate-compiled/src/extractMachines.ts index 1e633a2..10a7309 100644 --- a/packages/xstate-compiled/src/extractMachines.ts +++ b/packages/xstate-compiled/src/extractMachines.ts @@ -38,7 +38,6 @@ type ReferencePathsByImportName = Record< Array> >; -const cwd = process.cwd(); const extensions = ['.tsx', '.ts', '.jsx', '.js']; const getImports = ( @@ -241,9 +240,13 @@ const getCreatedExports = ( } }; -export const extractMachines = async ( - filePath: string, -): Promise => { +export const extractMachines = async ({ + cwd, + filePath, +}: { + filePath: string; + cwd: string; +}): Promise => { const resolvedFilePath = path.resolve(cwd, filePath); const build = await rollup({ diff --git a/packages/xstate-compiled/src/index.ts b/packages/xstate-compiled/src/index.ts index 4346f90..29e43f0 100755 --- a/packages/xstate-compiled/src/index.ts +++ b/packages/xstate-compiled/src/index.ts @@ -31,7 +31,9 @@ if (!pattern) { process.exit(1); } -const toRelative = (filePath: string) => path.relative(process.cwd(), filePath); +const cwd = objectArgs.cwd || process.cwd(); + +const toRelative = (filePath: string) => path.relative(cwd, filePath); const typedSuffix = /\.typed\.(js|ts|tsx|jsx)$/; @@ -42,7 +44,13 @@ let fileCache: Record< ReturnType & { id: string } > = {}; -gaze(pattern, {}, async function(err, watcher) { +type GazeOptions = Parameters[1]; + +const gazeOptions: GazeOptions = { + cwd, +}; + +gaze(pattern, gazeOptions, async function(err, watcher) { if (err) { console.log(err); process.exit(1); @@ -61,7 +69,7 @@ gaze(pattern, {}, async function(err, watcher) { process.exit(1); } - printJsFiles(); + printJsFiles(cwd); console.clear(); const addToCache = async (filePath: string) => { @@ -76,7 +84,7 @@ gaze(pattern, {}, async function(err, watcher) { if (!code.includes('@xstate/compiled')) { return; } - const machines = await extractMachines(filePath); + const machines = await extractMachines({ cwd, filePath }); if (machines.length === 0) { return; } @@ -100,7 +108,7 @@ gaze(pattern, {}, async function(err, watcher) { } }, Promise.resolve()); - printToFile(fileCache, objectArgs.outDir); + printToFile({ cache: fileCache, cwd, outDir: objectArgs.outDir }); if (objectArgs.once) { console.log('Completed!'.green.bold); @@ -113,20 +121,20 @@ gaze(pattern, {}, async function(err, watcher) { this.on('changed', async (filePath) => { console.log(`File Changed: `.cyan.bold + toRelative(filePath).gray); await addToCache(filePath); - printToFile(fileCache, objectArgs.outDir); + printToFile({ cache: fileCache, cwd, outDir: objectArgs.outDir }); }); // @ts-ignore this.on('added', async (filePath) => { console.log(`File Added: `.green.bold + toRelative(filePath).gray); await addToCache(filePath); - printToFile(fileCache, objectArgs.outDir); + printToFile({ cache: fileCache, cwd, outDir: objectArgs.outDir }); }); // @ts-ignore this.on('deleted', async (filePath) => { console.log(`File Deleted: `.red.bold + toRelative(filePath).gray); delete fileCache[filePath]; - printToFile(fileCache, objectArgs.outDir); + printToFile({ cache: fileCache, cwd, outDir: objectArgs.outDir }); }); console.log(`Watching for file changes in: `.cyan.bold + pattern.gray); diff --git a/packages/xstate-compiled/src/printToFile.ts b/packages/xstate-compiled/src/printToFile.ts index bacf03b..5ef7852 100644 --- a/packages/xstate-compiled/src/printToFile.ts +++ b/packages/xstate-compiled/src/printToFile.ts @@ -69,8 +69,10 @@ export const getDeclarationFileTexts = ( }; }; -export const getNodeModulesDir = () => { - const packageJson = pkgUp.sync(); +export const getNodeModulesDir = (cwd: string) => { + const packageJson = pkgUp.sync({ + cwd, + }); if (!packageJson) { throw new Error( @@ -83,29 +85,34 @@ export const getNodeModulesDir = () => { return targetDir; }; -export const printToFile = ( - cache: Record & { id: string }>, - outDir?: string, -) => { +export const printToFile = ({ + cache, + outDir, + cwd, +}: { + cache: Record & { id: string }>; + outDir?: string; + cwd: string; +}) => { const files = getDeclarationFileTexts(cache); - const nodeModulesDir = getNodeModulesDir(); + const nodeModulesDir = getNodeModulesDir(cwd); const targetDir = path.resolve(nodeModulesDir, '@xstate/compiled'); /** Delete @xstate/compiled directory so that it triggers VSCode to re-check it */ rimraf.sync(path.join(targetDir, '*.d.ts')); - printJsFiles(); + printJsFiles(cwd); ensureMultipleFoldersExist(nodeModulesDir, ['@xstate', 'compiled']); fs.writeFileSync( outDir - ? path.resolve(process.cwd(), outDir, 'index.d.ts') + ? path.resolve(cwd, outDir, 'index.d.ts') : path.join(targetDir, 'index.d.ts'), files['index.d.ts'], ); fs.writeFileSync( outDir - ? path.resolve(process.cwd(), outDir, 'react.d.ts') + ? path.resolve(cwd, outDir, 'react.d.ts') : path.join(targetDir, 'react.d.ts'), files['react.d.ts'], ); @@ -123,8 +130,8 @@ export const printToFile = ( * of rollup looking at the code to ensure there is a module for rollup * to statically analyse */ -export const printJsFiles = () => { - const nodeModulesDir = getNodeModulesDir(); +export const printJsFiles = (cwd: string) => { + const nodeModulesDir = getNodeModulesDir(cwd); const targetDir = path.resolve(nodeModulesDir, '@xstate/compiled'); ensureMultipleFoldersExist(nodeModulesDir, ['@xstate', 'compiled']); diff --git a/readme.md b/readme.md index 56849e3..4fbcd11 100644 --- a/readme.md +++ b/readme.md @@ -52,22 +52,22 @@ const machine = Machine({}); ```ts import { useMachine } from '@xstate/compiled/react'; -import { machine } from './myMachine.machine' +import { machine } from './myMachine.machine'; const [state, dispatch] = useMachine(machine, { // all options in here will be type checked -}) +}); ``` ### Usage with Interpret ```ts import { interpret } from '@xstate/compiled'; -import { machine } from './myMachine.machine' +import { machine } from './myMachine.machine'; -const service = interpret(machine, { +const service = interpret(machine).withConfig({ // all options in here will be type checked -}) +}); ``` ## Options @@ -85,3 +85,9 @@ By default, the CLI watches for changes in your files. Running `--once` runs the By default, the CLI adds the required declaration files inside node_modules at `node_modules/@xstate/compiled`. This writes the declaration files to a specified directory. > Note, this only writes the declaration files to the directory. The `.js` files still get written to `node_modules/@xstate/compiled`. + +### cwd + +`xstate-codegen "src/**/**.machine.ts" --cwd ../` + +Change the working directory that xstate-codegen operates in. This can sometimes be useful for altering which node_modules folder you'd like to target.