diff --git a/README.md b/README.md index 7af1fdb7..2b15b70b 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,15 @@ tsx watch ./file.ts All imported files are watched except from the following directories: `node_modules`, `bower_components`, `vendor`, `dist`, and `.*` (hidden directories). +#### Ignore files from being watched + +If you wish to exclude files from being watched, you can pass `--ignored` multiple times: +```sh +tsx watch --ignored ./ignore-me.js --ignored ./ignore-me-too.js ./file.ts +``` + +This flag is passed to [chokidar](https://github.com/paulmillr/chokidar) thus glob patterns also work here. + #### Tips - Press Return to manually rerun - Pass in `--clear-screen=false` to disable clearing the screen on rerun diff --git a/src/watch/index.ts b/src/watch/index.ts index aaba93ba..a19c6e87 100644 --- a/src/watch/index.ts +++ b/src/watch/index.ts @@ -27,7 +27,12 @@ const flags = { description: 'Clearing the screen on rerun', default: true, }, -}; + ignored: { + type: [String], + description: 'Defines files/paths to be ignored', + default: [], + }, +} as const; export const watchCommand = command({ name: 'watch', @@ -47,6 +52,7 @@ export const watchCommand = command({ noCache: argv.flags.noCache, tsconfigPath: argv.flags.tsconfig, clearScreen: argv.flags.clearScreen, + ignored: argv.flags.ignored, ipc: true, }; @@ -123,6 +129,8 @@ export const watchCommand = command({ // Distribution files '**/dist/**', + + ...options.ignored, ], ignorePermissionErrors: true, }, diff --git a/tests/specs/watch.ts b/tests/specs/watch.ts index 6d83f7d3..353832dc 100644 --- a/tests/specs/watch.ts +++ b/tests/specs/watch.ts @@ -141,5 +141,70 @@ export default testSuite(async ({ describe }, fixturePath: string) => { // await tsxProcess; // }, 2000); }); + + describe('ignore', ({ test }) => { + test('multiple files ignored', async () => { + // given + const initialValue = 'first round'; + const alteredValue = 'second round'; + const includedFilename = 'included.js'; + const ignoredFilenames = ['ignored-1.js', 'ignored-2.js']; + + const fixtures = await createFixture({ + [includedFilename]: ` + import { value as value1 } from './ignored-1'; + import { value as value2 } from './ignored-2'; + console.log(value1 + value2); + `.trim(), + [ignoredFilenames[0]]: `export const value = '${initialValue}';`, + [ignoredFilenames[1]]: `export const value = '${initialValue}';`, + }); + + const tsxProcess = tsx({ + args: [ + 'watch', + '--clear-screen=false', + `--ignored=${path.join(fixtures.path, ignoredFilenames[0])}`, + `--ignored=${path.join(fixtures.path, ignoredFilenames[1])}`, + path.join(fixtures.path, includedFilename), + ], + }); + + let aggregatedOutput = ''; + async function onStdOut(data: Buffer) { + const chunkString = data.toString(); + aggregatedOutput += chunkString; + + if (new RegExp(`${initialValue}\n`).test(chunkString)) { + await Promise.all(ignoredFilenames.map(ignoredFilename => fixtures.writeFile(ignoredFilename, `export const value = '${alteredValue}';`))); + // make sure to wait for chokidar to pick up changes + // in the ignored file before manually triggering a rerun + setTimeout(async () => { + await fixtures.writeFile(includedFilename, 'console.log(\'TERMINATE\');'); + }, 500); + } else if (chunkString.match('TERMINATE\n')) { + // cleanup + await fixtures.rm(); + tsxProcess.kill(); + } + } + tsxProcess.stdout?.on('data', onStdOut); + + let error = false; + tsxProcess.stderr?.on('data', () => { + error = true; + }); + + // when + await tsxProcess; + + // then + if (error) { + // manten does not come with a fail() utility. + expect('No error throwing').toEqual('but was thrown.'); + } + expect(aggregatedOutput).not.toMatch(alteredValue); + }); + }); }); });