From d173389472acb5333d11f9a7791e1c753d83200d Mon Sep 17 00:00:00 2001 From: Mark Wubben Date: Mon, 6 Nov 2023 21:32:40 +0100 Subject: [PATCH] Remove selection of AVA 5 watcher When I first retained the AVA 5 logic, it was under the assumption AVA 6 would be released in the summer with another breaking release later in the year. It's now autumn, Node.js 16 is end of life, and for now it's just not worth having this logic around. Depending on how well the watcher works I'm not opposed to having it as an experimental "select your watch mode" feature though. --- docs/recipes/watch-mode.md | 15 +- lib/ava5-watcher.js | 453 -------------------------- lib/cli.js | 72 ++-- lib/glob-helpers.cjs | 18 - lib/globs.js | 9 - lib/provider-manager.js | 15 +- lib/worker/ava5-dependency-tracker.js | 48 --- lib/worker/base.js | 13 - package-lock.json | 23 +- package.json | 6 +- test-tap/globs.js | 95 ------ 11 files changed, 42 insertions(+), 725 deletions(-) delete mode 100644 lib/ava5-watcher.js delete mode 100644 lib/worker/ava5-dependency-tracker.js 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);