From 8d87d658c42840b3695981edf3b927a234ec6f49 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 5 Dec 2020 14:17:13 +0100 Subject: [PATCH] fix: vendor node crawler from sane --- .eslintrc.js | 2 +- CHANGELOG.md | 3 +- jest.config.js | 1 + package.json | 3 +- packages/jest-haste-map/package.json | 2 - .../src/__tests__/index.test.js | 10 +- packages/jest-haste-map/src/index.ts | 41 +- .../src/{lib => watchers}/FSEventsWatcher.ts | 0 .../src/watchers/NodeWatcher.js | 378 ++++++++++++++++++ .../src/watchers/RecrawlWarning.js | 53 +++ .../src/{lib => watchers}/WatchmanWatcher.js | 10 +- .../jest-haste-map/src/watchers/common.js | 112 ++++++ patches/fsevents.patch | 13 - scripts/checkCopyrightHeaders.js | 3 + yarn.lock | 48 +-- 15 files changed, 581 insertions(+), 98 deletions(-) rename packages/jest-haste-map/src/{lib => watchers}/FSEventsWatcher.ts (100%) create mode 100644 packages/jest-haste-map/src/watchers/NodeWatcher.js create mode 100644 packages/jest-haste-map/src/watchers/RecrawlWarning.js rename packages/jest-haste-map/src/{lib => watchers}/WatchmanWatcher.js (96%) create mode 100644 packages/jest-haste-map/src/watchers/common.js delete mode 100644 patches/fsevents.patch diff --git a/.eslintrc.js b/.eslintrc.js index d76c514ed404..be7d6014b453 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -100,7 +100,7 @@ module.exports = { 'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts', 'packages/jest-fake-timers/src/legacyFakeTimers.ts', 'packages/jest-haste-map/src/index.ts', - 'packages/jest-haste-map/src/lib/FSEventsWatcher.ts', + 'packages/jest-haste-map/src/watchers/FSEventsWatcher.ts', 'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts', 'packages/jest-jasmine2/src/jasmine/Suite.ts', 'packages/jest-leak-detector/src/index.ts', diff --git a/CHANGELOG.md b/CHANGELOG.md index 9162645f879c..e173a72ae2e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - `[jest-circus]` Fix `testLocation` on Windows when using `test.each` ([#10871](https://github.com/facebook/jest/pull/10871)) - `[jest-console]` `console.dir` now respects the second argument correctly ([#10638](https://github.com/facebook/jest/pull/10638)) - `[jest-environment-jsdom]` Use inner realm’s `ArrayBuffer` constructor ([#10885](https://github.com/facebook/jest/pull/10885)) +- `[jest-haste-map]` Vendor `NodeWatcher` from `sane` ([#10919](https://github.com/facebook/jest/pull/10919)) - `[jest-jasmine2]` Fixed the issue of beforeAll & afterAll hooks getting executed even if it is inside a skipped `describe` block when it has child `tests` marked as either `only` or `todo` [#10451](https://github.com/facebook/jest/issues/10451) - `[jest-jasmine2]` Fixed the issues of child `tests` marked with `only` or `todo` getting executed even if it is inside a skipped parent `describe` block [#10451](https://github.com/facebook/jest/issues/10451) - `[jest-reporter]` Handle empty files when reporting code coverage with V8 ([#10819](https://github.com/facebook/jest/pull/10819)) @@ -29,7 +30,7 @@ - `[jest-runtime]` Fix stack overflow and promise deadlock when importing mutual dependant ES module ([#10892](https://github.com/facebook/jest/pull/10892)) - `[jest-transform]` Show enhanced `SyntaxError` message for all `SyntaxError`s ([#10749](https://github.com/facebook/jest/pull/10749)) - `[jest-transform]` [**BREAKING**] Refactor API to pass an options bag around rather than multiple boolean options ([#10753](https://github.com/facebook/jest/pull/10753)) -- `[jest-transform]` [**BREAKING**] Refactor API of transformers to pass an options bag rather than separate `config` and other options +- `[jest-transform]` [**BREAKING**] Refactor API of transformers to pass an options bag rather than separate `config` and other options ([#10834](https://github.com/facebook/jest/pull/10834)) - `[pretty-format]` [**BREAKING**] Convert to ES Modules ([#10515](https://github.com/facebook/jest/pull/10515)) ### Chore & Maintenance diff --git a/jest.config.js b/jest.config.js index 1aeed82b2d47..4ef88e4708fb 100644 --- a/jest.config.js +++ b/jest.config.js @@ -23,6 +23,7 @@ module.exports = { modulePathIgnorePatterns: [ 'examples/.*', 'packages/.*/build', + 'packages/.*/tsconfig.*', 'packages/jest-runtime/src/__tests__/test_root.*', 'website/.*', 'e2e/runtime-internal-module-registry/__mocks__', diff --git a/package.json b/package.json index a599162cc958..981c12b6dec1 100644 --- a/package.json +++ b/package.json @@ -148,7 +148,6 @@ "@testing-library/dom/pretty-format": "26.6.1", "@types/jest/jest-diff": "^25.1.0", "@types/jest/pretty-format": "^25.1.0", - "fbjs-scripts": "patch:fbjs-scripts@^1.1.0#./patches/fbjs-scripts.patch", - "jest-haste-map/fsevents": "patch:fsevents@^2.1.2#./patches/fsevents.patch" + "fbjs-scripts": "patch:fbjs-scripts@^1.1.0#./patches/fbjs-scripts.patch" } } diff --git a/packages/jest-haste-map/package.json b/packages/jest-haste-map/package.json index b22e18f50ef8..b1737c013cb7 100644 --- a/packages/jest-haste-map/package.json +++ b/packages/jest-haste-map/package.json @@ -25,7 +25,6 @@ "jest-util": "^26.6.2", "jest-worker": "^26.6.2", "micromatch": "^4.0.2", - "sane": "^4.0.3", "walker": "^1.0.7" }, "devDependencies": { @@ -33,7 +32,6 @@ "@types/anymatch": "^1.3.1", "@types/fb-watchman": "^2.0.0", "@types/micromatch": "^4.0.0", - "@types/sane": "^2.0.0", "jest-snapshot-serializer-raw": "^1.1.0", "slash": "^3.0.0" }, diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 19702655fdc3..4805292f7992 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -72,17 +72,13 @@ jest.mock('../crawlers/watchman', () => const mockWatcherConstructor = jest.fn(root => { const EventEmitter = require('events').EventEmitter; mockEmitters[root] = new EventEmitter(); - mockEmitters[root].close = jest.fn(callback => callback()); + mockEmitters[root].close = jest.fn(); setTimeout(() => mockEmitters[root].emit('ready'), 0); return mockEmitters[root]; }); -jest.mock('sane', () => ({ - NodeWatcher: mockWatcherConstructor, - WatchmanWatcher: mockWatcherConstructor, -})); - -jest.mock('../lib/WatchmanWatcher', () => mockWatcherConstructor); +jest.mock('../watchers/NodeWatcher', () => mockWatcherConstructor); +jest.mock('../watchers/WatchmanWatcher', () => mockWatcherConstructor); let mockChangedFiles; let mockFs; diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 6f5acf9582d0..c6def19cb48c 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -13,7 +13,6 @@ import {EventEmitter} from 'events'; import {tmpdir} from 'os'; import * as path from 'path'; import type {Stats} from 'graceful-fs'; -import {NodeWatcher, Watcher as SaneWatcher} from 'sane'; import type {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; import serializer from 'jest-serializer'; @@ -24,9 +23,6 @@ import H from './constants'; import nodeCrawl = require('./crawlers/node'); import watchmanCrawl = require('./crawlers/watchman'); import getMockName from './getMockName'; -import FSEventsWatcher = require('./lib/FSEventsWatcher'); -// @ts-expect-error: not converted to TypeScript - it's a fork: https://github.com/facebook/jest/pull/5387 -import WatchmanWatcher from './lib/WatchmanWatcher'; import * as fastPath from './lib/fast_path'; import getPlatformExtension from './lib/getPlatformExtension'; import normalizePathSep from './lib/normalizePathSep'; @@ -44,6 +40,11 @@ import type { ModuleMetaData, WorkerMetadata, } from './types'; +import FSEventsWatcher = require('./watchers/FSEventsWatcher'); +// @ts-expect-error: not converted to TypeScript - it's a fork: https://github.com/facebook/jest/pull/10919 +import NodeWatcher from './watchers/NodeWatcher'; +// @ts-expect-error: not converted to TypeScript - it's a fork: https://github.com/facebook/jest/pull/5387 +import WatchmanWatcher from './watchers/WatchmanWatcher'; import {getSha1, worker} from './worker'; // TypeScript doesn't like us importing from outside `rootDir`, but it doesn't // understand `require`. @@ -97,7 +98,7 @@ type InternalOptions = { }; type Watcher = { - close(callback: () => void): void; + close(): Promise; }; type WorkerInterface = {worker: typeof worker; getSha1: typeof getSha1}; @@ -210,7 +211,7 @@ function invariant(condition: unknown, message?: string): asserts condition { export default class HasteMap extends EventEmitter { private _buildPromise: Promise | null; private _cachePath: Config.Path; - private _changeInterval?: NodeJS.Timeout; + private _changeInterval?: ReturnType; private _console: Console; private _options: InternalOptions; private _watchers: Array; @@ -781,7 +782,7 @@ export default class HasteMap extends EventEmitter { this._options.retainAllFiles = true; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher - const Watcher: SaneWatcher = + const Watcher = canUseWatchman && this._options.useWatchman ? WatchmanWatcher : FSEventsWatcher.isSupported() @@ -798,7 +799,6 @@ export default class HasteMap extends EventEmitter { let mustCopy = true; const createWatcher = (root: Config.Path): Promise => { - // @ts-expect-error: TODO how? "Cannot use 'new' with an expression whose type lacks a call or construct signature." const watcher = new Watcher(root, { dot: true, glob: extensions.map(extension => '**/*.' + extension), @@ -824,10 +824,7 @@ export default class HasteMap extends EventEmitter { mustCopy = true; const changeEvent: ChangeEvent = { eventsQueue, - hasteFS: new HasteFS({ - files: hasteMap.files, - rootDir, - }), + hasteFS: new HasteFS({files: hasteMap.files, rootDir}), moduleMap: new HasteModuleMap({ duplicates: hasteMap.duplicates, map: hasteMap.map, @@ -1043,20 +1040,18 @@ export default class HasteMap extends EventEmitter { } } - end(): Promise { - // @ts-expect-error: TODO TS cannot decide if `setInterval` and `clearInterval` comes from NodeJS or the DOM - clearInterval(this._changeInterval); + async end(): Promise { + if (this._changeInterval) { + clearInterval(this._changeInterval); + } + if (!this._watchers.length) { - return Promise.resolve(); + return; } - return Promise.all( - this._watchers.map( - watcher => new Promise(resolve => watcher.close(resolve)), - ), - ).then(() => { - this._watchers = []; - }); + await Promise.all(this._watchers.map(watcher => watcher.close())); + + this._watchers = []; } /** diff --git a/packages/jest-haste-map/src/lib/FSEventsWatcher.ts b/packages/jest-haste-map/src/watchers/FSEventsWatcher.ts similarity index 100% rename from packages/jest-haste-map/src/lib/FSEventsWatcher.ts rename to packages/jest-haste-map/src/watchers/FSEventsWatcher.ts diff --git a/packages/jest-haste-map/src/watchers/NodeWatcher.js b/packages/jest-haste-map/src/watchers/NodeWatcher.js new file mode 100644 index 000000000000..9b74ee4175f0 --- /dev/null +++ b/packages/jest-haste-map/src/watchers/NodeWatcher.js @@ -0,0 +1,378 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/node_watcher.js + +'use strict'; + +const EventEmitter = require('events').EventEmitter; +const fs = require('fs'); +const platform = require('os').platform(); +const path = require('path'); +const common = require('./common'); + +/** + * Constants + */ + +const DEFAULT_DELAY = common.DEFAULT_DELAY; +const CHANGE_EVENT = common.CHANGE_EVENT; +const DELETE_EVENT = common.DELETE_EVENT; +const ADD_EVENT = common.ADD_EVENT; +const ALL_EVENT = common.ALL_EVENT; + +/** + * Export `NodeWatcher` class. + * Watches `dir`. + * + * @class NodeWatcher + * @param {String} dir + * @param {Object} opts + * @public + */ + +module.exports = class NodeWatcher extends EventEmitter { + constructor(dir, opts) { + super(); + + common.assignOptions(this, opts); + + this.watched = Object.create(null); + this.changeTimers = Object.create(null); + this.dirRegistery = Object.create(null); + this.root = path.resolve(dir); + this.watchdir = this.watchdir.bind(this); + this.register = this.register.bind(this); + this.checkedEmitError = this.checkedEmitError.bind(this); + + this.watchdir(this.root); + common.recReaddir( + this.root, + this.watchdir, + this.register, + this.emit.bind(this, 'ready'), + this.checkedEmitError, + this.ignored, + ); + } + + /** + * Register files that matches our globs to know what to type of event to + * emit in the future. + * + * Registery looks like the following: + * + * dirRegister => Map { + * dirpath => Map { + * filename => true + * } + * } + * + * @param {string} filepath + * @return {boolean} whether or not we have registered the file. + * @private + */ + + register(filepath) { + const relativePath = path.relative(this.root, filepath); + if ( + !common.isFileIncluded(this.globs, this.dot, this.doIgnore, relativePath) + ) { + return false; + } + + const dir = path.dirname(filepath); + if (!this.dirRegistery[dir]) { + this.dirRegistery[dir] = Object.create(null); + } + + const filename = path.basename(filepath); + this.dirRegistery[dir][filename] = true; + + return true; + } + + /** + * Removes a file from the registery. + * + * @param {string} filepath + * @private + */ + + unregister(filepath) { + const dir = path.dirname(filepath); + if (this.dirRegistery[dir]) { + const filename = path.basename(filepath); + delete this.dirRegistery[dir][filename]; + } + } + + /** + * Removes a dir from the registery. + * + * @param {string} dirpath + * @private + */ + + unregisterDir(dirpath) { + if (this.dirRegistery[dirpath]) { + delete this.dirRegistery[dirpath]; + } + } + + /** + * Checks if a file or directory exists in the registery. + * + * @param {string} fullpath + * @return {boolean} + * @private + */ + + registered(fullpath) { + const dir = path.dirname(fullpath); + return ( + this.dirRegistery[fullpath] || + (this.dirRegistery[dir] && + this.dirRegistery[dir][path.basename(fullpath)]) + ); + } + + /** + * Emit "error" event if it's not an ignorable event + * + * @param error + * @private + */ + checkedEmitError(error) { + if (!isIgnorableFileError(error)) { + this.emit('error', error); + } + } + + /** + * Watch a directory. + * + * @param {string} dir + * @private + */ + + watchdir(dir) { + if (this.watched[dir]) { + return; + } + + const watcher = fs.watch( + dir, + {persistent: true}, + this.normalizeChange.bind(this, dir), + ); + this.watched[dir] = watcher; + + watcher.on('error', this.checkedEmitError); + + if (this.root !== dir) { + this.register(dir); + } + } + + /** + * Stop watching a directory. + * + * @param {string} dir + * @private + */ + + stopWatching(dir) { + if (this.watched[dir]) { + this.watched[dir].close(); + delete this.watched[dir]; + } + } + + /** + * End watching. + * + * @public + */ + + close() { + Object.keys(this.watched).forEach(this.stopWatching, this); + this.removeAllListeners(); + + return Promise.resolve(); + } + + /** + * On some platforms, as pointed out on the fs docs (most likely just win32) + * the file argument might be missing from the fs event. Try to detect what + * change by detecting if something was deleted or the most recent file change. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + detectChangedFile(dir, event, callback) { + if (!this.dirRegistery[dir]) { + return; + } + + let found = false; + let closest = {mtime: 0}; + let c = 0; + Object.keys(this.dirRegistery[dir]).forEach(function (file, i, arr) { + fs.lstat(path.join(dir, file), (error, stat) => { + if (found) { + return; + } + + if (error) { + if (isIgnorableFileError(error)) { + found = true; + callback(file); + } else { + this.emit('error', error); + } + } else { + if (stat.mtime > closest.mtime) { + stat.file = file; + closest = stat; + } + if (arr.length === ++c) { + callback(closest.file); + } + } + }); + }, this); + } + + /** + * Normalize fs events and pass it on to be processed. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + normalizeChange(dir, event, file) { + if (!file) { + this.detectChangedFile(dir, event, actualFile => { + if (actualFile) { + this.processChange(dir, event, actualFile); + } + }); + } else { + this.processChange(dir, event, path.normalize(file)); + } + } + + /** + * Process changes. + * + * @param {string} dir + * @param {string} event + * @param {string} file + * @public + */ + + processChange(dir, event, file) { + const fullPath = path.join(dir, file); + const relativePath = path.join(path.relative(this.root, dir), file); + + fs.lstat(fullPath, (error, stat) => { + if (error && error.code !== 'ENOENT') { + this.emit('error', error); + } else if (!error && stat.isDirectory()) { + // win32 emits usless change events on dirs. + if (event !== 'change') { + this.watchdir(fullPath); + if ( + common.isFileIncluded( + this.globs, + this.dot, + this.doIgnore, + relativePath, + ) + ) { + this.emitEvent(ADD_EVENT, relativePath, stat); + } + } + } else { + const registered = this.registered(fullPath); + if (error && error.code === 'ENOENT') { + this.unregister(fullPath); + this.stopWatching(fullPath); + this.unregisterDir(fullPath); + if (registered) { + this.emitEvent(DELETE_EVENT, relativePath); + } + } else if (registered) { + this.emitEvent(CHANGE_EVENT, relativePath, stat); + } else { + if (this.register(fullPath)) { + this.emitEvent(ADD_EVENT, relativePath, stat); + } + } + } + }); + } + + /** + * Triggers a 'change' event after debounding it to take care of duplicate + * events on os x. + * + * @private + */ + + emitEvent(type, file, stat) { + const key = type + '-' + file; + const addKey = ADD_EVENT + '-' + file; + if (type === CHANGE_EVENT && this.changeTimers[addKey]) { + // Ignore the change event that is immediately fired after an add event. + // (This happens on Linux). + return; + } + clearTimeout(this.changeTimers[key]); + this.changeTimers[key] = setTimeout(() => { + delete this.changeTimers[key]; + if (type === ADD_EVENT && stat.isDirectory()) { + // Recursively emit add events and watch for sub-files/folders + common.recReaddir( + path.resolve(this.root, file), + function emitAddDir(dir, stats) { + this.watchdir(dir); + this.rawEmitEvent(ADD_EVENT, path.relative(this.root, dir), stats); + }.bind(this), + function emitAddFile(file, stats) { + this.register(file); + this.rawEmitEvent(ADD_EVENT, path.relative(this.root, file), stats); + }.bind(this), + function endCallback() {}, + this.checkedEmitError, + this.ignored, + ); + } else { + this.rawEmitEvent(type, file, stat); + } + }, DEFAULT_DELAY); + } + + /** + * Actually emit the events + */ + rawEmitEvent(type, file, stat) { + this.emit(type, file, this.root, stat); + this.emit(ALL_EVENT, type, file, this.root, stat); + } +}; +/** + * Determine if a given FS error can be ignored + * + * @private + */ +function isIgnorableFileError(error) { + return ( + error.code === 'ENOENT' || + // Workaround Windows node issue #4337. + (error.code === 'EPERM' && platform === 'win32') + ); +} diff --git a/packages/jest-haste-map/src/watchers/RecrawlWarning.js b/packages/jest-haste-map/src/watchers/RecrawlWarning.js new file mode 100644 index 000000000000..c1b9c753aa0d --- /dev/null +++ b/packages/jest-haste-map/src/watchers/RecrawlWarning.js @@ -0,0 +1,53 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/utils/recrawl-warning-dedupe.js + +'use strict'; + +class RecrawlWarning { + constructor(root, count) { + this.root = root; + this.count = count; + } + + static findByRoot(root) { + for (let i = 0; i < this.RECRAWL_WARNINGS.length; i++) { + const warning = this.RECRAWL_WARNINGS[i]; + if (warning.root === root) { + return warning; + } + } + } + + static isRecrawlWarningDupe(warningMessage) { + if (typeof warningMessage !== 'string') { + return false; + } + const match = warningMessage.match(this.REGEXP); + if (!match) { + return false; + } + const count = Number(match[1]); + const root = match[2]; + + const warning = this.findByRoot(root); + + if (warning) { + // only keep the highest count, assume count to either stay the same or + // increase. + if (warning.count >= count) { + return true; + } else { + // update the existing warning to the latest (highest) count + warning.count = count; + return false; + } + } else { + this.RECRAWL_WARNINGS.push(new RecrawlWarning(root, count)); + return false; + } + } +} + +RecrawlWarning.RECRAWL_WARNINGS = []; +RecrawlWarning.REGEXP = /Recrawled this watch (\d+) times, most recently because:\n([^:]+)/; + +module.exports = RecrawlWarning; diff --git a/packages/jest-haste-map/src/lib/WatchmanWatcher.js b/packages/jest-haste-map/src/watchers/WatchmanWatcher.js similarity index 96% rename from packages/jest-haste-map/src/lib/WatchmanWatcher.js rename to packages/jest-haste-map/src/watchers/WatchmanWatcher.js index 4471c0ec34ae..c7f8791bf34b 100644 --- a/packages/jest-haste-map/src/lib/WatchmanWatcher.js +++ b/packages/jest-haste-map/src/watchers/WatchmanWatcher.js @@ -10,8 +10,8 @@ import {EventEmitter} from 'events'; import path from 'path'; import watchman from 'fb-watchman'; import * as fs from 'graceful-fs'; -import common from 'sane/src/common'; -import RecrawlWarning from 'sane/src/utils/recrawl-warning-dedupe'; +import RecrawlWarning from './RecrawlWarning'; +import common from './common'; const CHANGE_EVENT = common.CHANGE_EVENT; const DELETE_EVENT = common.DELETE_EVENT; @@ -278,14 +278,12 @@ WatchmanWatcher.prototype.emitEvent = function ( /** * Closes the watcher. * - * @param {function} callback - * @private */ -WatchmanWatcher.prototype.close = function (callback) { +WatchmanWatcher.prototype.close = function () { this.client.removeAllListeners(); this.client.end(); - callback && callback(null, true); + return Promise.resolve(); }; /** diff --git a/packages/jest-haste-map/src/watchers/common.js b/packages/jest-haste-map/src/watchers/common.js new file mode 100644 index 000000000000..d2c27cbc134f --- /dev/null +++ b/packages/jest-haste-map/src/watchers/common.js @@ -0,0 +1,112 @@ +// vendored from https://github.com/amasad/sane/blob/64ff3a870c42e84f744086884bf55a4f9c22d376/src/common.js + +'use strict'; + +const platform = require('os').platform(); +const path = require('path'); +const anymatch = require('anymatch'); +const micromatch = require('micromatch'); +const walker = require('walker'); + +/** + * Constants + */ + +exports.DEFAULT_DELAY = 100; +exports.CHANGE_EVENT = 'change'; +exports.DELETE_EVENT = 'delete'; +exports.ADD_EVENT = 'add'; +exports.ALL_EVENT = 'all'; + +/** + * Assigns options to the watcher. + * + * @param {NodeWatcher|PollWatcher|WatchmanWatcher} watcher + * @param {?object} opts + * @return {boolean} + * @public + */ + +exports.assignOptions = function(watcher, opts) { + opts = opts || {}; + watcher.globs = opts.glob || []; + watcher.dot = opts.dot || false; + watcher.ignored = opts.ignored || false; + + if (!Array.isArray(watcher.globs)) { + watcher.globs = [watcher.globs]; + } + watcher.hasIgnore = + Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0); + watcher.doIgnore = opts.ignored ? anymatch(opts.ignored) : () => false; + + if (opts.watchman && opts.watchmanPath) { + watcher.watchmanPath = opts.watchmanPath; + } + + return opts; +}; + +/** + * Checks a file relative path against the globs array. + * + * @param {array} globs + * @param {string} relativePath + * @return {boolean} + * @public + */ + +exports.isFileIncluded = function(globs, dot, doIgnore, relativePath) { + if (doIgnore(relativePath)) { + return false; + } + return globs.length + ? micromatch.some(relativePath, globs, { dot }) + : dot || micromatch.some(relativePath, '**/*'); +}; + +/** + * Traverse a directory recursively calling `callback` on every directory. + * + * @param {string} dir + * @param {function} dirCallback + * @param {function} fileCallback + * @param {function} endCallback + * @param {*} ignored + * @public + */ + +exports.recReaddir = function( + dir, + dirCallback, + fileCallback, + endCallback, + errorCallback, + ignored +) { + walker(dir) + .filterDir(currentDir => !anymatch(ignored, currentDir)) + .on('dir', normalizeProxy(dirCallback)) + .on('file', normalizeProxy(fileCallback)) + .on('error', errorCallback) + .on('end', () => { + if (platform === 'win32') { + setTimeout(endCallback, 1000); + } else { + endCallback(); + } + }); +}; + +/** + * Returns a callback that when called will normalize a path and call the + * original callback + * + * @param {function} callback + * @return {function} + * @private + */ + +function normalizeProxy(callback) { + return (filepath, stats) => callback(path.normalize(filepath), stats); +} diff --git a/patches/fsevents.patch b/patches/fsevents.patch deleted file mode 100644 index b3cebe67de53..000000000000 --- a/patches/fsevents.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/fsevents.d.ts b/fsevents.d.ts -index 3979ab9e5b060e3a6c10449762ba872c5244f29a..2723c048a80df296ecc13eff5d96549f572a7383 100644 ---- a/fsevents.d.ts -+++ b/fsevents.d.ts -@@ -15,7 +15,7 @@ declare type Info = { - }; - declare type WatchHandler = (path: string, flags: number, id: string) => void; - export declare function watch(path: string, handler: WatchHandler): () => Promise; --export declare function watch(path: string, since: number, handler: WatchHandler); -+export declare function watch(path: string, since: number, handler: WatchHandler): () => Promise; - export declare function getInfo(path: string, flags: number): Info; - export declare const constants: { - None: 0x00000000; diff --git a/scripts/checkCopyrightHeaders.js b/scripts/checkCopyrightHeaders.js index 503c6c8fe5f0..49cd0f2e913c 100755 --- a/scripts/checkCopyrightHeaders.js +++ b/scripts/checkCopyrightHeaders.js @@ -103,6 +103,9 @@ const CUSTOM_IGNORED_PATTERNS = [ '^packages/expect/src/jasmineUtils\\.ts$', '^packages/jest-config/src/vendor/jsonlint\\.js$', '^packages/jest-diff/src/cleanupSemantic\\.ts$', + '^packages/jest-haste-map/src/watchers/common\\.js$', + '^packages/jest-haste-map/src/watchers/NodeWatcher\\.js$', + '^packages/jest-haste-map/src/watchers/RecrawlWarning\\.js$', '^website/static/css/code-block-buttons\\.css$', '^website/static/js/code-block-buttons\\.js', ].map(createRegExp); diff --git a/yarn.lock b/yarn.lock index d12f5d46319d..363699de02e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3840,15 +3840,6 @@ __metadata: languageName: node linkType: hard -"@types/sane@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/sane@npm:2.0.0" - dependencies: - "@types/node": "*" - checksum: 8e50935136662acd7d71fc5ccdff70c1821ceb175ad8fbab0fb43b44b1cc2a4d57c0d5916408c46444361b310d70e6b22a75dd55fc06241fc123a2fd23e29e72 - languageName: node - linkType: hard - "@types/semver@npm:^7.1.0": version: 7.3.4 resolution: "@types/semver@npm:7.3.4" @@ -9364,12 +9355,12 @@ fsevents@^1.2.7: languageName: node linkType: hard -fsevents@^2.1.2: - version: 2.2.1 - resolution: "fsevents@npm:2.2.1" +"fsevents@^2.1.2, fsevents@~2.1.2": + version: 2.1.3 + resolution: "fsevents@npm:2.1.3" dependencies: node-gyp: latest - checksum: 7cc4cdc3b027f7ff6f6dee69492d460948db719393865cd29f2e6cfbcc64fecde964ef10a614f762be4212403097a2fc4088686b1b70b41a9fc117b2cc4bfecd + checksum: 8977781884d06c5bcb97b5f909efdce9683c925f2a0ce7e098d2cdffe2e0a0a50b1868547bb94dca75428c06535a4a70517a7bb3bb5a974d93bf9ffc067291eb languageName: node linkType: hard @@ -9383,25 +9374,7 @@ fsevents@^2.1.2: languageName: node linkType: hard -"fsevents@patch:fsevents@^2.1.2#./patches/fsevents.patch::locator=%40jest%2Fmonorepo%40workspace%3A.": - version: 2.2.1 - resolution: "fsevents@patch:fsevents@npm%3A2.2.1#./patches/fsevents.patch::version=2.2.1&hash=ee4332&locator=%40jest%2Fmonorepo%40workspace%3A." - dependencies: - node-gyp: latest - checksum: af204dab4aa0332571dd19aafcccf3e597dbf21fa975f92cb64af2151e3f65ad26a7d6fc0b6d8d9c800285996afb738f8f9db7005c6b497d9256c04910ad1416 - languageName: node - linkType: hard - -"fsevents@patch:fsevents@patch%3Afsevents@^2.1.2%23./patches/fsevents.patch%3A%3Alocator=%2540jest%252Fmonorepo%2540workspace%253A.#builtin": - version: 2.2.1 - resolution: "fsevents@patch:fsevents@patch%3Afsevents@npm%253A2.2.1%23./patches/fsevents.patch%3A%3Aversion=2.2.1&hash=ee4332&locator=%2540jest%252Fmonorepo%2540workspace%253A.#builtin::version=2.2.1&hash=11e9ea" - dependencies: - node-gyp: latest - checksum: 8e9dbb7a60968fd6758148e4d246e1c55f6aae4d258132ae3440712453c28a1c555d9c8393087ec4f1d3e90576715ba395e0cda20905ec8eae6be61d20bf4d19 - languageName: node - linkType: hard - -"fsevents@patch:fsevents@~2.1.2#builtin": +"fsevents@patch:fsevents@^2.1.2#builtin, fsevents@patch:fsevents@~2.1.2#builtin": version: 2.1.3 resolution: "fsevents@patch:fsevents@npm%3A2.1.3#builtin::version=2.1.3&hash=11e9ea" dependencies: @@ -9410,15 +9383,6 @@ fsevents@^2.1.2: languageName: node linkType: hard -fsevents@~2.1.2: - version: 2.1.3 - resolution: "fsevents@npm:2.1.3" - dependencies: - node-gyp: latest - checksum: 8977781884d06c5bcb97b5f909efdce9683c925f2a0ce7e098d2cdffe2e0a0a50b1868547bb94dca75428c06535a4a70517a7bb3bb5a974d93bf9ffc067291eb - languageName: node - linkType: hard - "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -11746,7 +11710,6 @@ fsevents@~2.1.2: "@types/graceful-fs": ^4.1.2 "@types/micromatch": ^4.0.0 "@types/node": "*" - "@types/sane": ^2.0.0 anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.1.2 @@ -11757,7 +11720,6 @@ fsevents@~2.1.2: jest-util: ^26.6.2 jest-worker: ^26.6.2 micromatch: ^4.0.2 - sane: ^4.0.3 slash: ^3.0.0 walker: ^1.0.7 dependenciesMeta: