Merge pull request #9933 from webpack/bugfix/missing-managed-items

fix problem with snapshotting managed items
This commit is contained in:
Tobias Koppers 2019-11-06 01:06:27 +01:00 committed by GitHub
commit 408a4ae77e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 382 additions and 112 deletions

View File

@ -692,57 +692,112 @@ class Compilation {
}
/** @type {LogEntry[] | undefined} */
let logEntries;
return new Logger((type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
let trace;
switch (type) {
case LogType.warn:
case LogType.error:
case LogType.trace:
trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
.split("\n")
.slice(3);
break;
}
/** @type {LogEntry} */
const logEntry = {
time: Date.now(),
type,
args,
trace
};
if (this.hooks.log.call(name, logEntry) === undefined) {
if (logEntry.type === LogType.profileEnd) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profileEnd === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profileEnd(`[${name}] ${logEntry.args[0]}`);
return new Logger(
(type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
if (logEntries === undefined) {
logEntries = this.logging.get(name);
let trace;
switch (type) {
case LogType.warn:
case LogType.error:
case LogType.trace:
trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
.split("\n")
.slice(3);
break;
}
/** @type {LogEntry} */
const logEntry = {
time: Date.now(),
type,
args,
trace
};
if (this.hooks.log.call(name, logEntry) === undefined) {
if (logEntry.type === LogType.profileEnd) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profileEnd === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profileEnd(`[${name}] ${logEntry.args[0]}`);
}
}
if (logEntries === undefined) {
logEntries = [];
this.logging.set(name, logEntries);
logEntries = this.logging.get(name);
if (logEntries === undefined) {
logEntries = [];
this.logging.set(name, logEntries);
}
}
logEntries.push(logEntry);
if (logEntry.type === LogType.profile) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profile === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profile(`[${name}] ${logEntry.args[0]}`);
}
}
}
logEntries.push(logEntry);
if (logEntry.type === LogType.profile) {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
if (typeof console.profile === "function") {
// eslint-disable-next-line node/no-unsupported-features/node-builtins
console.profile(`[${name}] ${logEntry.args[0]}`);
},
childName => {
if (typeof name === "function") {
if (typeof childName === "function") {
return this.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
} else {
return this.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
} else {
if (typeof childName === "function") {
return this.getLogger(() => {
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
} else {
return this.getLogger(`${name}/${childName}`);
}
}
}
});
);
}
/**

View File

@ -232,21 +232,76 @@ class Compiler {
"Compiler.getInfrastructureLogger(name) called without a name"
);
}
return new Logger((type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called with a function not returning a name"
);
return new Logger(
(type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called with a function not returning a name"
);
}
}
if (this.hooks.infrastructureLog.call(name, type, args) === undefined) {
if (this.infrastructureLogger !== undefined) {
this.infrastructureLogger(name, type, args);
}
}
},
childName => {
if (typeof name === "function") {
if (typeof childName === "function") {
return this.getInfrastructureLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called with a function not returning a name"
);
}
}
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
} else {
return this.getInfrastructureLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
} else {
if (typeof childName === "function") {
return this.getInfrastructureLogger(() => {
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
} else {
return this.getInfrastructureLogger(`${name}/${childName}`);
}
}
}
if (this.hooks.infrastructureLog.call(name, type, args) === undefined) {
if (this.infrastructureLogger !== undefined) {
this.infrastructureLogger(name, type, args);
}
}
});
);
}
/**

View File

@ -21,6 +21,8 @@ const resolveContext = resolve.create({
let FS_ACCURACY = 2000;
const EMPTY_SET = new Set();
const RBDT_RESOLVE = 0;
const RBDT_RESOLVE_DIRECTORY = 1;
const RBDT_RESOLVE_FILE = 2;
@ -41,11 +43,11 @@ const INVALID = Symbol("invalid");
/**
* @typedef {Object} Snapshot
* @property {number=} startTime
* @property {Map<string, FileSystemInfoEntry | "error">=} fileTimestamps
* @property {Map<string, FileSystemInfoEntry | "ignore" | "error">=} fileTimestamps
* @property {Map<string, string | "error">=} fileHashes
* @property {Map<string, FileSystemInfoEntry | "error">=} contextTimestamps
* @property {Map<string, FileSystemInfoEntry | "ignore" | "error">=} contextTimestamps
* @property {Map<string, string | "error">=} contextHashes
* @property {Map<string, FileSystemInfoEntry | "error">=} missingTimestamps
* @property {Map<string, FileSystemInfoEntry | "ignore" | "error">=} missingTimestamps
* @property {Map<string, string | "error">=} managedItemInfo
*/
@ -143,6 +145,7 @@ const getManagedItem = (managedPath, path) => {
if (slashes !== 0) return null;
// if (path.slice(i + 1, i + 13) === "node_modules")
if (
path.length >= i + 13 &&
path.charCodeAt(i + 1) === 110 &&
path.charCodeAt(i + 2) === 111 &&
path.charCodeAt(i + 3) === 100 &&
@ -156,6 +159,12 @@ const getManagedItem = (managedPath, path) => {
path.charCodeAt(i + 11) === 101 &&
path.charCodeAt(i + 12) === 115
) {
// if this is the end of the path
if (path.length === i + 13) {
// return the node_modules directory
// it's special
return path;
}
const c = path.charCodeAt(i + 13);
// if next symbol is slash or backslash
if (c === 47 || c === 92) {
@ -184,11 +193,11 @@ class FileSystemInfo {
this._loggedPaths = logger ? new Set() : undefined;
/** @type {WeakMap<Snapshot, boolean | (function(WebpackError=, boolean=): void)[]>} */
this._snapshotCache = new WeakMap();
/** @type {Map<string, FileSystemInfoEntry | null>} */
/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */
this._fileTimestamps = new Map();
/** @type {Map<string, string>} */
this._fileHashes = new Map();
/** @type {Map<string, FileSystemInfoEntry | null>} */
/** @type {Map<string, FileSystemInfoEntry | "ignore" | null>} */
this._contextTimestamps = new Map();
/** @type {Map<string, string>} */
this._contextHashes = new Map();
@ -219,6 +228,11 @@ class FileSystemInfo {
parallelism: 10,
processor: this._getManagedItemInfo.bind(this)
});
this.managedItemDirectoryQueue = new AsyncQueue({
name: "managed item directory info",
parallelism: 10,
processor: this._getManagedItemDirectoryInfo.bind(this)
});
this.managedPaths = Array.from(managedPaths);
this.managedPathsWithSlash = this.managedPaths.map(p =>
join(fs, p, "_").slice(0, -1)
@ -241,7 +255,7 @@ class FileSystemInfo {
}
/**
* @param {Map<string, FileSystemInfoEntry | null>} map timestamps
* @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps
* @returns {void}
*/
addFileTimestamps(map) {
@ -251,7 +265,7 @@ class FileSystemInfo {
}
/**
* @param {Map<string, FileSystemInfoEntry | null>} map timestamps
* @param {Map<string, FileSystemInfoEntry | "ignore" | null>} map timestamps
* @returns {void}
*/
addContextTimestamps(map) {
@ -262,7 +276,7 @@ class FileSystemInfo {
/**
* @param {string} path file path
* @param {function(WebpackError=, FileSystemInfoEntry=): void} callback callback function
* @param {function(WebpackError=, (FileSystemInfoEntry | "ignore")=): void} callback callback function
* @returns {void}
*/
getFileTimestamp(path, callback) {
@ -273,7 +287,7 @@ class FileSystemInfo {
/**
* @param {string} path context path
* @param {function(WebpackError=, FileSystemInfoEntry=): void} callback callback function
* @param {function(WebpackError=, (FileSystemInfoEntry | "ignore")=): void} callback callback function
* @returns {void}
*/
getContextTimestamp(path, callback) {
@ -643,15 +657,15 @@ class FileSystemInfo {
* @returns {void}
*/
createSnapshot(startTime, files, directories, missing, options, callback) {
/** @type {Map<string, FileSystemInfoEntry | "error">} */
/** @type {Map<string, FileSystemInfoEntry | "ignore" | "error">} */
const fileTimestamps = new Map();
/** @type {Map<string, string | "error">} */
const fileHashes = new Map();
/** @type {Map<string, FileSystemInfoEntry | "error">} */
/** @type {Map<string, FileSystemInfoEntry | "ignore" | "error">} */
const contextTimestamps = new Map();
/** @type {Map<string, string | "error">} */
const contextHashes = new Map();
/** @type {Map<string, FileSystemInfoEntry | "error">} */
/** @type {Map<string, FileSystemInfoEntry | "ignore" | "error">} */
const missingTimestamps = new Map();
/** @type {Map<string, string | "error">} */
const managedItemInfo = new Map();
@ -699,6 +713,11 @@ class FileSystemInfo {
jobs++;
this.fileHashQueue.add(path, (err, entry) => {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting file hash of ${path}: ${err}`
);
}
fileHashes.set(path, "error");
} else {
fileHashes.set(path, entry);
@ -730,6 +749,11 @@ class FileSystemInfo {
jobs++;
this.fileTimestampQueue.add(path, (err, entry) => {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting file timestamp of ${path}: ${err}`
);
}
fileTimestamps.set(path, "error");
} else {
fileTimestamps.set(path, entry);
@ -764,6 +788,11 @@ class FileSystemInfo {
jobs++;
this.contextHashQueue.add(path, (err, entry) => {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting context hash of ${path}: ${err}`
);
}
contextHashes.set(path, "error");
} else {
contextHashes.set(path, entry);
@ -795,6 +824,11 @@ class FileSystemInfo {
jobs++;
this.contextTimestampQueue.add(path, (err, entry) => {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting context timestamp of ${path}: ${err}`
);
}
contextTimestamps.set(path, "error");
} else {
contextTimestamps.set(path, entry);
@ -828,6 +862,11 @@ class FileSystemInfo {
jobs++;
this.fileTimestampQueue.add(path, (err, entry) => {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting missing timestamp of ${path}: ${err}`
);
}
missingTimestamps.set(path, "error");
} else {
missingTimestamps.set(path, entry);
@ -840,11 +879,16 @@ class FileSystemInfo {
for (const path of managedItems) {
const cache = this._managedItems.get(path);
if (cache !== undefined) {
managedItemInfo.set(path, cache || "error");
managedItemInfo.set(path, cache);
} else {
jobs++;
this.managedItemQueue.add(path, (err, entry) => {
if (err || !entry) {
if (err) {
if (this.logger) {
this.logger.debug(
`Error snapshotting managed item ${path}: ${err}`
);
}
managedItemInfo.set(path, "error");
} else {
managedItemInfo.set(path, entry);
@ -964,7 +1008,7 @@ class FileSystemInfo {
// If there was an error while snapshotting (i. e. EBUSY)
// we can't compare further data and assume it's invalid
if (this._remainingLogs > 0) {
this._log(path, `there was an error during snapshotting`);
this._log(path, `there was an error during snapshotting hash`);
}
return false;
}
@ -979,8 +1023,8 @@ class FileSystemInfo {
};
/**
* @param {string} path file path
* @param {FileSystemInfoEntry} current current entry
* @param {FileSystemInfoEntry | "error"} snap entry from snapshot
* @param {FileSystemInfoEntry | "ignore"} current current entry
* @param {FileSystemInfoEntry | "ignore" | "error"} snap entry from snapshot
* @returns {boolean} true, if ok
*/
const checkExistance = (path, current, snap) => {
@ -988,10 +1032,12 @@ class FileSystemInfo {
// If there was an error while snapshotting (i. e. EBUSY)
// we can't compare further data and assume it's invalid
if (this._remainingLogs > 0) {
this._log(path, `there was an error during snapshotting`);
this._log(path, `there was an error during snapshotting existance`);
}
return false;
}
if (current === "ignore") return true;
if (snap === "ignore") return true;
if (!current !== !snap) {
// If existance of item differs
// it's invalid
@ -1007,8 +1053,8 @@ class FileSystemInfo {
};
/**
* @param {string} path file path
* @param {FileSystemInfoEntry} current current entry
* @param {FileSystemInfoEntry | "error"} snap entry from snapshot
* @param {FileSystemInfoEntry | "ignore"} current current entry
* @param {FileSystemInfoEntry | "ignore" | "error"} snap entry from snapshot
* @returns {boolean} true, if ok
*/
const checkFile = (path, current, snap) => {
@ -1016,10 +1062,15 @@ class FileSystemInfo {
// If there was an error while snapshotting (i. e. EBUSY)
// we can't compare further data and assume it's invalid
if (this._remainingLogs > 0) {
this._log(path, `there was an error during snapshotting`);
this._log(
path,
`there was an error during snapshotting file timestamp`
);
}
return false;
}
if (current === "ignore") return true;
if (snap === "ignore") return true;
if (current && current.safeTime > startTime) {
// If a change happened after starting reading the item
// this may no longer be valid
@ -1292,7 +1343,7 @@ class FileSystemInfo {
const managedItem = getManagedItem(managedPath, child);
if (managedItem) {
// construct timestampHash from managed info
return this.managedItemQueue.add(child, (err, info) => {
return this.managedItemQueue.add(managedItem, (err, info) => {
if (err) return callback(err);
return callback(null, {
safeTime: 0,
@ -1386,7 +1437,7 @@ class FileSystemInfo {
const managedItem = getManagedItem(managedPath, child);
if (managedItem) {
// construct hash from managed info
return this.managedItemQueue.add(child, (err, info) => {
return this.managedItemQueue.add(managedItem, (err, info) => {
if (err) return callback(err);
callback(null, info || "");
});
@ -1427,33 +1478,74 @@ class FileSystemInfo {
});
}
_getManagedItemInfo(path, callback) {
const packageJsonPath = join(this.fs, path, "package.json");
this.fs.readFile(packageJsonPath, (err, content) => {
_getManagedItemDirectoryInfo(path, callback) {
this.fs.readdir(path, (err, elements) => {
if (err) {
if (err.code === "ENOENT" || err.code === "ENOTDIR") {
// no package.json or path is not a directory
this._managedItems.set(path, null);
return callback(null, null);
return callback(null, EMPTY_SET);
}
return callback(err);
}
let data;
try {
data = JSON.parse(content.toString("utf-8"));
} catch (e) {
return callback(e);
const set = new Set(
elements.map(element => join(this.fs, path, element))
);
callback(null, set);
});
}
_getManagedItemInfo(path, callback) {
const dir = dirname(this.fs, path);
this.managedItemDirectoryQueue.add(dir, (err, elements) => {
if (err) {
return callback(err);
}
const info = `${data.name || ""}@${data.version || ""}`;
this._managedItems.set(path, info);
callback(null, info);
if (!elements.has(path)) {
// file or directory doesn't exist
this._managedItems.set(path, "missing");
return callback(null, "missing");
}
// something exists
// it may be a file or directory
if (
path.endsWith("node_modules") &&
(path.endsWith("/node_modules") || path.endsWith("\\node_modules"))
) {
// we are only interested in existance of this special directory
this._managedItems.set(path, "exists");
return callback(null, "exists");
}
// we assume it's a directory, as files shouldn't occur in managed paths
const packageJsonPath = join(this.fs, path, "package.json");
this.fs.readFile(packageJsonPath, (err, content) => {
if (err) {
if (err.code === "ENOENT" || err.code === "ENOTDIR") {
// no package.json or path is not a directory
this.logger.warn(
`Managed item ${path} isn't a directory or doesn't contain a package.json`
);
this._managedItems.set(path, "error");
return callback(null, "error");
}
return callback(err);
}
let data;
try {
data = JSON.parse(content.toString("utf-8"));
} catch (e) {
return callback(e);
}
const info = `${data.name || ""}@${data.version || ""}`;
this._managedItems.set(path, info);
callback(null, info);
});
});
}
getDeprecatedFileTimestamps() {
const map = new Map();
for (const [path, info] of this._fileTimestamps) {
if (info) map.set(path, info.safeTime);
if (info) map.set(path, typeof info === "object" ? info.safeTime : null);
}
return map;
}
@ -1461,7 +1553,7 @@ class FileSystemInfo {
getDeprecatedContextTimestamps() {
const map = new Map();
for (const [path, info] of this._contextTimestamps) {
if (info) map.set(path, info.safeTime);
if (info) map.set(path, typeof info === "object" ? info.safeTime : null);
}
return map;
}

View File

@ -11,6 +11,8 @@ const schema = require("../schemas/plugins/WatchIgnorePlugin.json");
/** @typedef {import("../declarations/plugins/WatchIgnorePlugin").WatchIgnorePluginOptions} WatchIgnorePluginOptions */
/** @typedef {import("./Compiler")} Compiler */
const IGNORE_TIME_ENTRY = "ignore";
class IgnoringWatchFileSystem {
constructor(wfs, paths) {
this.wfs = wfs;
@ -37,11 +39,11 @@ class IgnoringWatchFileSystem {
(err, fileTimestamps, dirTimestamps, removedFiles) => {
if (err) return callback(err);
for (const path of ignoredFiles) {
fileTimestamps.set(path, 1);
fileTimestamps.set(path, IGNORE_TIME_ENTRY);
}
for (const path of ignoredDirs) {
dirTimestamps.set(path, 1);
dirTimestamps.set(path, IGNORE_TIME_ENTRY);
}
callback(err, fileTimestamps, dirTimestamps, removedFiles);
@ -55,14 +57,14 @@ class IgnoringWatchFileSystem {
getContextInfoEntries: () => {
const dirTimestamps = watcher.getContextInfoEntries();
for (const path of ignoredDirs) {
dirTimestamps.set(path, 1);
dirTimestamps.set(path, IGNORE_TIME_ENTRY);
}
return dirTimestamps;
},
getFileTimeInfoEntries: () => {
const fileTimestamps = watcher.getFileTimeInfoEntries();
for (const path of ignoredFiles) {
fileTimestamps.set(path, 1);
fileTimestamps.set(path, IGNORE_TIME_ENTRY);
}
return fileTimestamps;
}

View File

@ -288,7 +288,8 @@ class PackFileCacheStrategy {
this.fileSerializer = createFileSerializer(fs);
this.fileSystemInfo = new FileSystemInfo(fs, {
managedPaths,
immutablePaths
immutablePaths,
logger: logger.getChildLogger("webpack.FileSystemInfo")
});
this.context = context;
this.cacheLocation = cacheLocation;

View File

@ -40,9 +40,11 @@ const TIMERS_SYMBOL = Symbol("webpack logger times");
class WebpackLogger {
/**
* @param {function(LogTypeEnum, any[]=): void} log log function
* @param {function(string | function(): string): WebpackLogger} getChildLogger function to create child logger
*/
constructor(log) {
constructor(log, getChildLogger) {
this[LOG_SYMBOL] = log;
this.getChildLogger = getChildLogger;
}
error(...args) {

View File

@ -22,11 +22,14 @@ let currentDefaultLogger = createConsoleLogger(currentDefaultLoggerOptions);
* @returns {Logger} a logger
*/
exports.getLogger = name => {
return new Logger((type, args) => {
if (exports.hooks.log.call(name, type, args) === undefined) {
currentDefaultLogger(name, type, args);
}
});
return new Logger(
(type, args) => {
if (exports.hooks.log.call(name, type, args) === undefined) {
currentDefaultLogger(name, type, args);
}
},
childName => exports.getLogger(`${name}/${childName}`)
);
};
/**

View File

@ -11,7 +11,7 @@
"@webassemblyjs/wasm-parser": "1.8.5",
"acorn": "^7.0.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "5.0.0-beta.3",
"enhanced-resolve": "5.0.0-beta.4",
"eslint-scope": "^5.0.0",
"events": "^3.0.0",
"glob-to-regexp": "^0.4.1",

View File

@ -6,7 +6,6 @@ describe("TestCases", () => {
name: "cache pack",
cache: {
type: "filesystem",
store: "pack",
managedPaths: [path.resolve(__dirname, "../node_modules")]
},
optimization: {

View File

@ -24,6 +24,8 @@ function copyDiff(src, dest, initial) {
var content = fs.readFileSync(srcFile);
if (/^DELETE\s*$/.test(content.toString("utf-8"))) {
fs.unlinkSync(destFile);
} else if (/^DELETE_DIRECTORY\s*$/.test(content.toString("utf-8"))) {
rimraf.sync(destFile);
} else {
fs.writeFileSync(destFile, content);
if (initial) {

View File

@ -0,0 +1,6 @@
import { foo, bar } from "foo";
it("should have the correct values", () => {
expect(foo).toBe("foo");
expect(bar).toBe("bar");
});

View File

@ -0,0 +1 @@
export const bar = "bar";

View File

@ -0,0 +1,4 @@
{
"name": "bar",
"version": "0"
}

View File

@ -0,0 +1,2 @@
export const foo = "foo";
export { bar } from "bar";

View File

@ -0,0 +1,4 @@
{
"name": "foo",
"version": "0"
}

View File

@ -0,0 +1,6 @@
import { foo, bar } from "foo";
it("should invalidate resolving if inner node_modules appears", () => {
expect(foo).toBe("foo");
expect(bar).toBe("inner-bar");
});

View File

@ -0,0 +1 @@
export const bar = "inner-bar";

View File

@ -0,0 +1,4 @@
{
"name": "bar",
"version": "1"
}

View File

@ -0,0 +1,6 @@
import { foo, bar } from "foo";
it("should not invalidate managed item if package version stays equal", () => {
expect(foo).toBe("foo");
expect(bar).toBe("inner-bar");
});

View File

@ -0,0 +1 @@
export const bar = "undetected-bar";

View File

@ -0,0 +1,6 @@
import { foo, bar } from "foo";
it("should invalidate when directory gets deleted", () => {
expect(foo).toBe("foo");
expect(bar).toBe("bar");
});

View File

@ -0,0 +1 @@
DELETE_DIRECTORY

View File

@ -0,0 +1,17 @@
const path = require("path");
module.exports = {
mode: "development",
cache: {
type: "memory",
managedPaths: [
path.resolve(
__dirname,
"../../../js/watch-src/cache/managedPath/node_modules"
)
]
},
module: {
unsafeCache: false
}
};

View File

@ -1767,10 +1767,10 @@ end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
enhanced-resolve@5.0.0-beta.3:
version "5.0.0-beta.3"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.0.0-beta.3.tgz#1f5b24d223db90a2e86235c365e337fcbf28a68b"
integrity sha512-SMjrP/h0qau9mj6L+dY0I350vTnHUDNm3hjV2QRzqhfaa6UYAkDoLfuRWPqAErKDejveFPyGLiQx1A8a5uR5hQ==
enhanced-resolve@5.0.0-beta.4:
version "5.0.0-beta.4"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.0.0-beta.4.tgz#a14a799c098c2c43ec2cd0b718b14a2eadec7301"
integrity sha512-5/C1ZLbPbiFKM9WnF2LKvwresdLoKb0Py6r9XAt9gojZ6wnJb1ay2OzLY+T0DX5KSrimvTufAGISysArUlRpdQ==
dependencies:
graceful-fs "^4.2.0"
tapable "^2.0.0-beta.8"