diff --git a/docs/recipes/watch-mode.md b/docs/recipes/watch-mode.md index 710ce8996..830031220 100644 --- a/docs/recipes/watch-mode.md +++ b/docs/recipes/watch-mode.md @@ -4,17 +4,6 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs AVA comes with an intelligent watch mode. It watches for files to change and runs just those tests that are affected. -AVA 6 is introducing a new watch mode that relies on recurse file watching in Node.js. To use the old watch mode, set the implementation to `ava5+chokidar` and install [`chokidar`] alongside AVA: - -`ava.config.mjs`: -```js -export default { - watchMode: { - implementation: 'ava5+chokidar', - }, -} -``` - ## Running tests with watch mode enabled You can enable watch mode using the `--watch` or `-w` flags: @@ -29,8 +18,6 @@ Please note that integrated debugging and the TAP reporter are unavailable when AVA 5 uses [`chokidar`] as the file watcher. Note that even if you see warnings about optional dependencies failing during install, it will still work fine. Please refer to the *[Install Troubleshooting]* section of `chokidar` documentation for how to resolve the installation problems with chokidar. -The same applies with AVA 6 when using the `ava5+chokidar` watcher. However you'll need to install `chokidar` separately. - Otherwise, AVA 6 uses `fs.watch()`. Support for `recursive` mode is required. Note that this has only become available on Linux since Node.js 20. [Other caveats apply](https://nodejs.org/api/fs.html#caveats), for example this won't work well on network filesystems and Docker host mounts. ## Ignoring changes @@ -55,7 +42,7 @@ If your tests write to disk they may trigger the watcher to rerun your tests. Co AVA tracks which source files your test files depend on. If you change such a dependency only the test file that depends on it will be rerun. AVA will rerun all tests if it cannot determine which test file depends on the changed source file. -AVA 5 (and the `ava5+chokidar` watcher in AVA 6) spies on `require()` calls to track dependencies. Custom extensions and transpilers are supported, provided you [added them in your `package.json` or `ava.config.*` file][config], and not from inside your test file. +AVA 5 spies on `require()` calls to track dependencies. Custom extensions and transpilers are supported, provided you [added them in your `package.json` or `ava.config.*` file][config], and not from inside your test file. With AVA 6, dependency tracking works for `require()` and `import` syntax, as supported by [@vercel/nft](https://github.com/vercel/nft). `import()` is supported but dynamic paths such as `import(myVariable)` are not. diff --git a/lib/ava5-watcher.js b/lib/ava5-watcher.js deleted file mode 100644 index 643b11cf3..000000000 --- a/lib/ava5-watcher.js +++ /dev/null @@ -1,453 +0,0 @@ -import nodePath from 'node:path'; - -import chokidar from 'chokidar'; -import createDebug from 'debug'; - -import {chalk} from './chalk.js'; -import {applyTestFileFilter, classifyAva5Watcher as classify, getChokidarIgnorePatterns} from './globs.js'; - -const debug = createDebug('ava:watcher'); - -function rethrowAsync(error) { - // Don't swallow exceptions. Note that any - // expected error should already have been logged - setImmediate(() => { - throw error; - }); -} - -const MIN_DEBOUNCE_DELAY = 10; -const INITIAL_DEBOUNCE_DELAY = 100; -const END_MESSAGE = chalk.gray('Type `r` and press enter to rerun tests\nType `u` and press enter to update snapshots\n'); - -class Debouncer { - constructor(watcher) { - this.watcher = watcher; - this.timer = null; - this.repeat = false; - } - - debounce(delay) { - if (this.timer) { - this.again = true; - return; - } - - delay = delay ? Math.max(delay, MIN_DEBOUNCE_DELAY) : INITIAL_DEBOUNCE_DELAY; - - const timer = setTimeout(async () => { - await this.watcher.busy; - // Do nothing if debouncing was canceled while waiting for the busy - // promise to fulfil - if (this.timer !== timer) { - return; - } - - if (this.again) { - this.timer = null; - this.again = false; - this.debounce(delay / 2); - } else { - this.watcher.runAfterChanges(); - this.timer = null; - this.again = false; - } - }, delay); - - this.timer = timer; - } - - cancel() { - if (this.timer) { - clearTimeout(this.timer); - this.timer = null; - this.again = false; - } - } -} - -class TestDependency { - constructor(file, dependencies) { - this.file = file; - this.dependencies = dependencies; - } - - contains(dependency) { - return this.dependencies.includes(dependency); - } -} - -export default class Watcher { - constructor({api, filter = [], globs, projectDir, providers, reporter}) { - this.debouncer = new Debouncer(this); - - this.runVector = 0; - this.previousFiles = []; - this.globs = {cwd: projectDir, ...globs}; - - const patternFilters = filter.map(({pattern}) => pattern); - - this.providers = providers; - this.run = (specificFiles = [], updateSnapshots = false) => { - this.runVector++; - - let runOnlyExclusive = false; - if (specificFiles.length > 0) { - const exclusiveFiles = specificFiles.filter(file => this.filesWithExclusiveTests.includes(file)); - runOnlyExclusive = exclusiveFiles.length !== this.filesWithExclusiveTests.length; - if (runOnlyExclusive) { - // The test files that previously contained exclusive tests are always - // run, together with the remaining specific files. - const remainingFiles = specificFiles.filter(file => !exclusiveFiles.includes(file)); - specificFiles = [...this.filesWithExclusiveTests, ...remainingFiles]; - } - - if (filter.length > 0) { - specificFiles = applyTestFileFilter({ - cwd: projectDir, - expandDirectories: false, - filter: patternFilters, - testFiles: specificFiles, - treatFilterPatternsAsFiles: false, - }); - } - - this.pruneFailures(specificFiles); - } - - this.touchedFiles.clear(); - this.previousFiles = specificFiles; - this.busy = api.run({ - files: specificFiles, - filter, - runtimeOptions: { - previousFailures: this.sumPreviousFailures(this.runVector), - runOnlyExclusive, - firstRun: this.runVector === 1, - updateSnapshots: updateSnapshots === true, - }, - }) - .then(() => { - reporter.endRun(); - reporter.lineWriter.writeLine(END_MESSAGE); - }) - .catch(rethrowAsync); - }; - - this.testDependencies = []; - this.trackTestDependencies(api); - - this.temporaryFiles = new Set(); - this.touchedFiles = new Set(); - this.trackTouchedFiles(api); - - this.filesWithExclusiveTests = []; - this.trackExclusivity(api); - - this.filesWithFailures = []; - this.trackFailures(api); - - this.dirtyStates = {}; - this.watchFiles(); - this.rerunAll(); - } - - watchFiles() { - chokidar.watch(['**/*'], { - cwd: this.globs.cwd, - ignored: getChokidarIgnorePatterns(this.globs), - ignoreInitial: true, - }).on('all', (event, path) => { - if (event === 'add' || event === 'change' || event === 'unlink') { - debug('Detected %s of %s', event, path); - this.dirtyStates[nodePath.join(this.globs.cwd, path)] = event; - this.debouncer.debounce(); - } - }); - } - - trackTestDependencies(api) { - api.on('run', plan => { - plan.status.on('stateChange', evt => { - let dependencies; - if (evt.type === 'dependencies') { - dependencies = evt.dependencies; - } else if (evt.type === 'accessed-snapshots') { - dependencies = [evt.filename]; - } else { - return; - } - - dependencies = dependencies.filter(filePath => { - const {isIgnoredByWatcher} = classify(filePath, this.globs); - return !isIgnoredByWatcher; - }); - this.updateTestDependencies(evt.testFile, dependencies); - }); - }); - } - - updateTestDependencies(file, dependencies) { - // Ensure the rewritten test file path is included in the dependencies, - // since changes to non-rewritten paths are ignored. - for (const {main} of this.providers) { - const rewritten = main.resolveTestFile(file); - if (!dependencies.includes(rewritten)) { - dependencies = [rewritten, ...dependencies]; - } - } - - if (dependencies.length === 0) { - this.testDependencies = this.testDependencies.filter(dep => dep.file !== file); - return; - } - - const isUpdate = this.testDependencies.some(dep => { - if (dep.file !== file) { - return false; - } - - dep.dependencies = dependencies; - - return true; - }); - - if (!isUpdate) { - this.testDependencies.push(new TestDependency(file, dependencies)); - } - } - - trackTouchedFiles(api) { - api.on('run', plan => { - plan.status.on('stateChange', evt => { - if (evt.type !== 'touched-files') { - return; - } - - for (const file of evt.files.changedFiles) { - this.touchedFiles.add(file); - } - - for (const file of evt.files.temporaryFiles) { - this.temporaryFiles.add(file); - } - }); - }); - } - - trackExclusivity(api) { - api.on('run', plan => { - plan.status.on('stateChange', evt => { - if (evt.type !== 'worker-finished') { - return; - } - - const fileStats = plan.status.stats.byFile.get(evt.testFile); - const ranExclusiveTests = fileStats.selectedTests > 0 && fileStats.declaredTests > fileStats.selectedTests; - this.updateExclusivity(evt.testFile, ranExclusiveTests); - }); - }); - } - - updateExclusivity(file, hasExclusiveTests) { - const index = this.filesWithExclusiveTests.indexOf(file); - - if (hasExclusiveTests && index === -1) { - this.filesWithExclusiveTests.push(file); - } else if (!hasExclusiveTests && index !== -1) { - this.filesWithExclusiveTests.splice(index, 1); - } - } - - trackFailures(api) { - api.on('run', plan => { - this.pruneFailures(plan.files); - - const currentVector = this.runVector; - plan.status.on('stateChange', evt => { - if (!evt.testFile) { - return; - } - - switch (evt.type) { - case 'hook-failed': - case 'internal-error': - case 'process-exit': - case 'test-failed': - case 'uncaught-exception': - case 'unhandled-rejection': - case 'worker-failed': { - this.countFailure(evt.testFile, currentVector); - break; - } - - default: { - break; - } - } - }); - }); - } - - pruneFailures(files) { - const toPrune = new Set(files); - this.filesWithFailures = this.filesWithFailures.filter(state => !toPrune.has(state.file)); - } - - countFailure(file, vector) { - const isUpdate = this.filesWithFailures.some(state => { - if (state.file !== file) { - return false; - } - - state.count++; - return true; - }); - - if (!isUpdate) { - this.filesWithFailures.push({ - file, - vector, - count: 1, - }); - } - } - - sumPreviousFailures(beforeVector) { - let total = 0; - - for (const state of this.filesWithFailures) { - if (state.vector < beforeVector) { - total += state.count; - } - } - - return total; - } - - cleanUnlinkedTests(unlinkedTests) { - for (const testFile of unlinkedTests) { - this.updateTestDependencies(testFile, []); - this.updateExclusivity(testFile, false); - this.pruneFailures([testFile]); - } - } - - observeStdin(stdin) { - stdin.resume(); - stdin.setEncoding('utf8'); - - stdin.on('data', async data => { - data = data.trim().toLowerCase(); - if (data !== 'r' && data !== 'rs' && data !== 'u') { - return; - } - - // Cancel the debouncer, it might rerun specific tests whereas *all* tests - // need to be rerun - this.debouncer.cancel(); - await this.busy; - // Cancel the debouncer again, it might have restarted while waiting for - // the busy promise to fulfil - this.debouncer.cancel(); - if (data === 'u') { - this.updatePreviousSnapshots(); - } else { - this.rerunAll(); - } - }); - } - - rerunAll() { - this.dirtyStates = {}; - this.run(); - } - - updatePreviousSnapshots() { - this.dirtyStates = {}; - this.run(this.previousFiles, true); - } - - runAfterChanges() { - const {dirtyStates} = this; - this.dirtyStates = {}; - - let dirtyPaths = Object.keys(dirtyStates).filter(path => { - if (this.touchedFiles.has(path)) { - debug('Ignoring known touched file %s', path); - this.touchedFiles.delete(path); - return false; - } - - // Unlike touched files, temporary files are never cleared. We may see - // adds and unlinks detected separately, so we track the temporary files - // as long as AVA is running. - if (this.temporaryFiles.has(path)) { - debug('Ignoring known temporary file %s', path); - return false; - } - - return true; - }); - - for (const {main} of this.providers) { - dirtyPaths = dirtyPaths.filter(path => { - if (main.ignoreChange(path)) { - debug('Ignoring changed file %s', path); - return false; - } - - return true; - }); - } - - const dirtyHelpersAndSources = []; - const addedOrChangedTests = []; - const unlinkedTests = []; - for (const filePath of dirtyPaths) { - const {isIgnoredByWatcher, isTest} = classify(filePath, this.globs); - if (!isIgnoredByWatcher) { - if (isTest) { - if (dirtyStates[filePath] === 'unlink') { - unlinkedTests.push(filePath); - } else { - addedOrChangedTests.push(filePath); - } - } else { - dirtyHelpersAndSources.push(filePath); - } - } - } - - this.cleanUnlinkedTests(unlinkedTests); - - // No need to rerun tests if the only change is that tests were deleted - if (unlinkedTests.length === dirtyPaths.length) { - return; - } - - if (dirtyHelpersAndSources.length === 0) { - // Run any new or changed tests - this.run(addedOrChangedTests); - return; - } - - // Try to find tests that depend on the changed source files - const testsByHelpersOrSource = dirtyHelpersAndSources.map(path => this.testDependencies.filter(dep => dep.contains(path)).map(dep => { - debug('%s is a dependency of %s', path, dep.file); - return dep.file; - })).filter(tests => tests.length > 0); - - // Rerun all tests if source files were changed that could not be traced to - // specific tests - if (testsByHelpersOrSource.length !== dirtyHelpersAndSources.length) { - debug('Files remain that cannot be traced to specific tests: %O', dirtyHelpersAndSources); - debug('Rerunning all tests'); - this.run(); - return; - } - - // Run all affected tests - this.run([...new Set([addedOrChangedTests, testsByHelpersOrSource].flat(2))]); - } -} diff --git a/lib/cli.js b/lib/cli.js index 002a40b6a..309f45ba5 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -345,7 +345,7 @@ export default async function loadCli() { // eslint-disable-line complexity if (Object.hasOwn(conf, 'typescript')) { const {default: providerManager} = await import('./provider-manager.js'); try { - const {identifier: protocol, level, main} = await providerManager.typescript(projectDir, {fullConfig: conf}); + const {identifier: protocol, level, main} = await providerManager.typescript(projectDir); providers.push({ level, protocol, @@ -417,7 +417,6 @@ export default async function loadCli() { // eslint-disable-line complexity concurrency: combined.concurrency ?? 0, workerThreads: combined.workerThreads !== false, debug, - enableAva5DependencyTracking: argv.watch && conf.watchMode?.implementation === 'ava5+chokidar', environmentVariables, experiments, extensions, @@ -489,52 +488,35 @@ export default async function loadCli() { // eslint-disable-line complexity }); if (argv.watch) { - if (Object.hasOwn(conf, 'watchMode') && Object.hasOwn(conf.watchMode, 'implementation')) { - if (conf.watchMode.implementation === 'ava5+chokidar') { - const {default: Watcher} = await import('./ava5-watcher.js'); - const watcher = new Watcher({ - api, - filter, - globs, - projectDir, - providers, - reporter, - }); - watcher.observeStdin(process.stdin); - } else { - exit('The ’watchMode.implementation’ option must be set to “ava5+chokidar”'); - } - } else { - const {available, start} = await import('./watcher.js'); - if (!available(projectDir)) { - exit('Watch mode requires support for recursive fs.watch()'); - return; - } - - let abortController; - if (process.env.TEST_AVA) { - const {takeCoverage} = await import('node:v8'); - abortController = new AbortController(); - process.on('message', message => { - if (message === 'abort-watcher') { - abortController.abort(); - takeCoverage(); - } - }); - process.channel?.unref(); - } + const {available, start} = await import('./watcher.js'); + if (!available(projectDir)) { + exit('Watch mode requires support for recursive fs.watch()'); + return; + } - start({ - api, - filter, - globs, - projectDir, - providers, - reporter, - stdin: process.stdin, - signal: abortController.signal, + let abortController; + if (process.env.TEST_AVA) { + const {takeCoverage} = await import('node:v8'); + abortController = new AbortController(); + process.on('message', message => { + if (message === 'abort-watcher') { + abortController.abort(); + takeCoverage(); + } }); + process.channel?.unref(); } + + start({ + api, + filter, + globs, + projectDir, + providers, + reporter, + stdin: process.stdin, + signal: abortController.signal, + }); } else { let debugWithoutSpecificFile = false; api.on('run', plan => { diff --git a/lib/glob-helpers.cjs b/lib/glob-helpers.cjs index 5c869e5b9..2c10b589c 100644 --- a/lib/glob-helpers.cjs +++ b/lib/glob-helpers.cjs @@ -16,8 +16,6 @@ const defaultPicomatchIgnorePatterns = [ ...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`), ]; -const defaultMatchNoIgnore = picomatch(defaultPicomatchIgnorePatterns); - const matchingCache = new WeakMap(); const processMatchingPatterns = input => { let result = matchingCache.get(input); @@ -55,22 +53,6 @@ function classify(file, {cwd, extensions, filePatterns}) { exports.classify = classify; -const matchesIgnorePatterns = (file, patterns) => { - const {matchNoIgnore} = processMatchingPatterns(patterns); - return matchNoIgnore(file) || defaultMatchNoIgnore(file); -}; - -function classifyAva5Watcher(file, {cwd, extensions, filePatterns, ignoredByWatcherPatterns}) { - file = normalizeFileForMatching(cwd, file); - return { - isIgnoredByWatcher: matchesIgnorePatterns(file, ignoredByWatcherPatterns), - isTest: hasExtension(extensions, file) && !isHelperish(file) && filePatterns.length > 0 && matches(file, filePatterns), - }; -} - -// TODO: Delete along with ava5+chokidar watcher. -exports.classifyAva5Watcher = classifyAva5Watcher; - const hasExtension = (extensions, file) => extensions.includes(path.extname(file).slice(1)); exports.hasExtension = hasExtension; diff --git a/lib/globs.js b/lib/globs.js index c789dcc97..1c0d6c602 100644 --- a/lib/globs.js +++ b/lib/globs.js @@ -14,7 +14,6 @@ import { export { classify, - classifyAva5Watcher, isHelperish, matches, normalizePattern, @@ -128,14 +127,6 @@ export async function findTests({cwd, extensions, filePatterns}) { return files.filter(file => !path.basename(file).startsWith('_')); } -// TODO: Delete along with ava5+chokidar watcher. -export function getChokidarIgnorePatterns({ignoredByWatcherPatterns}) { - return [ - ...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`), - ...ignoredByWatcherPatterns.filter(pattern => !pattern.startsWith('!')), - ]; -} - export function buildIgnoreMatcher({ignoredByWatcherPatterns}) { const patterns = [ ...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`), diff --git a/lib/provider-manager.js b/lib/provider-manager.js index eaf4220a2..b63ea9e50 100644 --- a/lib/provider-manager.js +++ b/lib/provider-manager.js @@ -4,12 +4,10 @@ import pkg from './pkg.cjs'; export const levels = { // As the protocol changes, comparing levels by integer allows AVA to be // compatible with different versions. - ava3Stable: 1, - ava6: 2, + ava6: 1, }; const levelsByProtocol = Object.assign(Object.create(null), { - 'ava-3.2': levels.ava3Stable, 'ava-6': levels.ava6, }); @@ -51,15 +49,8 @@ async function load(providerModule, projectDir, selectProtocol = () => true) { } const providerManager = { - async typescript(projectDir, {fullConfig, protocol}) { - const legacy = fullConfig?.watchMode?.implementation === 'ava5+chokidar'; - return load('@ava/typescript', projectDir, identifier => { - if (protocol === undefined) { - return !legacy || identifier === 'ava-3.2'; - } - - return identifier === protocol; - }); + async typescript(projectDir, {protocol}) { + return load('@ava/typescript', projectDir, identifier => protocol === undefined || identifier === protocol); }, }; diff --git a/lib/worker/ava5-dependency-tracker.js b/lib/worker/ava5-dependency-tracker.js deleted file mode 100644 index f56ea10ad..000000000 --- a/lib/worker/ava5-dependency-tracker.js +++ /dev/null @@ -1,48 +0,0 @@ -import process from 'node:process'; - -import channel from './channel.cjs'; - -const seenDependencies = new Set(); -let newDependencies = []; - -function flush() { - if (newDependencies.length === 0) { - return; - } - - channel.send({type: 'dependencies', dependencies: newDependencies}); - newDependencies = []; -} - -function track(filename) { - if (seenDependencies.has(filename)) { - return; - } - - if (newDependencies.length === 0) { - process.nextTick(flush); - } - - seenDependencies.add(filename); - newDependencies.push(filename); -} - -const tracker = { - flush, - track, - install(extensions, testPath) { - for (const ext of Object.keys(extensions)) { - const wrappedHandler = extensions[ext]; - - extensions[ext] = (module, filename) => { - if (filename !== testPath) { - track(filename); - } - - wrappedHandler(module, filename); - }; - } - }, -}; - -export default tracker; diff --git a/lib/worker/base.js b/lib/worker/base.js index 576da720c..fd1fee372 100644 --- a/lib/worker/base.js +++ b/lib/worker/base.js @@ -14,8 +14,6 @@ import providerManager from '../provider-manager.js'; import Runner from '../runner.js'; import serializeError from '../serialize-error.js'; -// TODO: Delete along with ava5+chokidar watcher. -import dependencyTracking from './ava5-dependency-tracker.js'; import channel from './channel.cjs'; import lineNumberSelection from './line-numbers.js'; import {set as setOptions} from './options.cjs'; @@ -31,7 +29,6 @@ const {apply} = Reflect; const realExit = process.exit; async function exit(code, forceSync = false) { - dependencyTracking.flush(); const flushing = channel.flush(); if (!forceSync) { await flushing; @@ -100,10 +97,6 @@ const run = async options => { }); runner.on('accessed-snapshots', filename => channel.send({type: 'accessed-snapshots', filename})); - if (options.enableAva5DependencyTracking) { - runner.on('dependency', dependencyTracking.track); - } - runner.on('stateChange', state => channel.send(state)); runner.on('error', error => { @@ -233,12 +226,6 @@ const run = async options => { } } - if (options.enableAva5DependencyTracking) { - // Install dependency tracker after the require configuration has been evaluated - // to make sure we also track dependencies with custom require hooks - dependencyTracking.install(require.extensions, testPath); - } - if (options.debug?.port !== undefined && options.debug?.host !== undefined) { // If an inspector was active when the main process started, and is // already active for the worker process, do not open a new one. diff --git a/package-lock.json b/package-lock.json index 8318cc343..83cff46c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -74,15 +74,11 @@ "node": "^18.18 || ^20.8 || ^21" }, "peerDependencies": { - "@ava/typescript": "*", - "chokidar": "^3.5.3" + "@ava/typescript": "*" }, "peerDependenciesMeta": { "@ava/typescript": { "optional": true - }, - "chokidar": { - "optional": true } } }, @@ -2807,7 +2803,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -2820,7 +2816,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, @@ -3055,7 +3051,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8" } @@ -3445,7 +3441,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "individual", @@ -5649,6 +5645,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -6544,7 +6541,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -8559,7 +8556,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -9927,7 +9924,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -9939,7 +9936,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=8.6" }, diff --git a/package.json b/package.json index 7597c7628..806cb1652 100644 --- a/package.json +++ b/package.json @@ -139,15 +139,11 @@ "zen-observable": "^0.10.0" }, "peerDependencies": { - "@ava/typescript": "*", - "chokidar": "^3.5.3" + "@ava/typescript": "*" }, "peerDependenciesMeta": { "@ava/typescript": { "optional": true - }, - "chokidar": { - "optional": true } }, "volta": { diff --git a/test-tap/globs.js b/test-tap/globs.js index 486e7085d..e43123d35 100644 --- a/test-tap/globs.js +++ b/test-tap/globs.js @@ -201,101 +201,6 @@ test('isTest after provider modifications', t => { t.end(); }); -test('isIgnoredByWatcher with defaults', t => { - const options = { - ...globs.normalizeGlobs({extensions: ['js'], providers: []}), - cwd: fixture(), - }; - - function isIgnoredByWatcher(file) { - t.ok(globs.classifyAva5Watcher(fixture(file), options).isIgnoredByWatcher, `${file} should be ignored`); - } - - function notIgnored(file) { - t.notOk(globs.classifyAva5Watcher(fixture(file), options).isIgnoredByWatcher, `${file} should not be ignored`); - } - - notIgnored('foo-bar.js'); - notIgnored('foo.js'); - notIgnored('foo/blah.js'); - notIgnored('bar/foo.js'); - - notIgnored('_foo-bar.js'); - notIgnored('foo/_foo-bar.js'); - notIgnored('fixtures/foo.js'); - notIgnored('helpers/foo.js'); - - notIgnored('snapshots/foo.js.snap'); - isIgnoredByWatcher('snapshots/foo.js.snap.md'); - notIgnored('foo-bar.json'); - notIgnored('foo-bar.coffee'); - - notIgnored('bar.js'); - notIgnored('bar/bar.js'); - isIgnoredByWatcher('node_modules/foo.js'); - t.end(); -}); - -test('isIgnoredByWatcher with patterns', t => { - const options = { - ...globs.normalizeGlobs({ - files: ['**/foo*'], - ignoredByWatcher: ['**/bar*'], - extensions: ['js'], - providers: [], - }), - cwd: fixture(), - }; - - t.ok(globs.classifyAva5Watcher(fixture('node_modules/foo/foo.js'), options).isIgnoredByWatcher); - t.ok(globs.classifyAva5Watcher(fixture('bar.js'), options).isIgnoredByWatcher); - t.ok(globs.classifyAva5Watcher(fixture('foo/bar.js'), options).isIgnoredByWatcher); - t.end(); -}); - -test('isIgnoredByWatcher (pattern starts with directory)', t => { - const options = { - ...globs.normalizeGlobs({ - files: ['**/foo*'], - ignoredByWatcher: ['foo/**/*'], - extensions: ['js'], - providers: [], - }), - cwd: fixture(), - }; - - t.ok(globs.classifyAva5Watcher(fixture('node_modules/foo/foo.js'), options).isIgnoredByWatcher); - t.notOk(globs.classifyAva5Watcher(fixture('bar.js'), options).isIgnoredByWatcher); - t.ok(globs.classifyAva5Watcher(fixture('foo/bar.js'), options).isIgnoredByWatcher); - t.end(); -}); - -test('isIgnoredByWatcher after provider modifications', t => { - const options = { - ...globs.normalizeGlobs({ - extensions: ['js'], - providers: [{ - level: 2, - main: { - updateGlobs({filePatterns, ignoredByWatcherPatterns}) { - t.ok(filePatterns.length > 0); - t.ok(ignoredByWatcherPatterns.length > 0); - return { - filePatterns, - ignoredByWatcherPatterns: ['foo.js'], - }; - }, - }, - }], - }), - cwd: fixture(), - }; - - t.ok(globs.classifyAva5Watcher(fixture('foo.js'), options).isIgnoredByWatcher); - t.notOk(globs.classifyAva5Watcher(fixture('bar.js'), options).isIgnoredByWatcher); - t.end(); -}); - test('findFiles finds non-ignored files (just .cjs)', async t => { const fixtureDir = fixture('default-patterns'); process.chdir(fixtureDir);