From 3740d80976d92f9cd58ff741d02146ed2326385d Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Fri, 18 Sep 2020 03:29:26 -0400 Subject: [PATCH] refactor(@ngtools/webpack): adjust virtual file watching for Webpack 5 The Webpack 5 filesystem watch function has a different return object type than Webpack 4. This change adjusts the behavior based on the used Webpack version. --- .../src/virtual_file_system_decorator.ts | 266 ++++++++++++------ 1 file changed, 174 insertions(+), 92 deletions(-) diff --git a/packages/ngtools/webpack/src/virtual_file_system_decorator.ts b/packages/ngtools/webpack/src/virtual_file_system_decorator.ts index 54428d8ed5..484060f5e0 100644 --- a/packages/ngtools/webpack/src/virtual_file_system_decorator.ts +++ b/packages/ngtools/webpack/src/virtual_file_system_decorator.ts @@ -10,6 +10,7 @@ import { Stats } from 'fs'; import { InputFileSystem } from 'webpack'; import { WebpackCompilerHost } from './compiler_host'; import { NodeWatchFileSystemInterface } from './webpack'; +import { isWebpackFiveOrHigher } from './webpack-version'; export const NodeWatchFileSystem: NodeWatchFileSystemInterface = require( 'webpack/lib/node/NodeWatchFileSystem'); @@ -112,106 +113,187 @@ export class VirtualWatchFileSystemDecorator extends NodeWatchFileSystem { super(_virtualInputFileSystem); } - watch = ( - files: Iterable, - dirs: Iterable, - missing: Iterable, - startTime: number, - options: {}, - callback: Parameters[5], - callbackUndelayed: (filename: string, timestamp: number) => void, - ): ReturnType => { - const reverseReplacements = new Map(); - const reverseTimestamps = (map: Map) => { - for (const entry of Array.from(map.entries())) { - const original = reverseReplacements.get(entry[0]); - if (original) { - map.set(original, entry[1]); - map.delete(entry[0]); + mapReplacements( + original: Iterable, + reverseReplacements: Map, + ): Iterable { + if (!this._replacements) { + return original; + } + const replacements = this._replacements; + + return [...original].map(file => { + if (typeof replacements === 'function') { + const replacement = getSystemPath(replacements(normalize(file))); + if (replacement !== file) { + reverseReplacements.set(replacement, file); } - } - return map; - }; - - const newCallbackUndelayed = (filename: string, timestamp: number) => { - const original = reverseReplacements.get(filename); - if (original) { - this._virtualInputFileSystem.purge(original); - callbackUndelayed(original, timestamp); + return replacement; } else { - callbackUndelayed(filename, timestamp); - } - }; + const replacement = replacements.get(normalize(file)); + if (replacement) { + const fullReplacement = getSystemPath(replacement); + reverseReplacements.set(fullReplacement, file); - const newCallback: Parameters[5] = ( - err: Error | null, - filesModified: string[], - contextModified: string[], - missingModified: string[], - fileTimestamps: Map, - contextTimestamps: Map, - ) => { - // Update fileTimestamps with timestamps from virtual files. - const virtualFilesStats = this._virtualInputFileSystem.getVirtualFilesPaths() - .map((fileName) => ({ - path: fileName, - mtime: +this._virtualInputFileSystem.statSync(fileName).mtime, - })); - virtualFilesStats.forEach(stats => fileTimestamps.set(stats.path, +stats.mtime)); - callback( - err, - filesModified.map(value => reverseReplacements.get(value) || value), - contextModified.map(value => reverseReplacements.get(value) || value), - missingModified.map(value => reverseReplacements.get(value) || value), - reverseTimestamps(fileTimestamps), - reverseTimestamps(contextTimestamps), - ); - }; - - const mapReplacements = (original: Iterable): Iterable => { - if (!this._replacements) { - return original; - } - const replacements = this._replacements; - - return [...original].map(file => { - if (typeof replacements === 'function') { - const replacement = getSystemPath(replacements(normalize(file))); - if (replacement !== file) { - reverseReplacements.set(replacement, file); - } - - return replacement; + return fullReplacement; } else { - const replacement = replacements.get(normalize(file)); - if (replacement) { - const fullReplacement = getSystemPath(replacement); - reverseReplacements.set(fullReplacement, file); - - return fullReplacement; - } else { - return file; - } + return file; } - }); - }; + } + }); + } - const watcher = super.watch( - mapReplacements(files), - mapReplacements(dirs), - mapReplacements(missing), - startTime, - options, - newCallback, - newCallbackUndelayed, - ); + reverseTimestamps( + map: Map, + reverseReplacements: Map, + ): Map { + for (const entry of Array.from(map.entries())) { + const original = reverseReplacements.get(entry[0]); + if (original) { + map.set(original, entry[1]); + map.delete(entry[0]); + } + } - return { - close: () => watcher.close(), - pause: () => watcher.pause(), - getFileTimestamps: () => reverseTimestamps(watcher.getFileTimestamps()), - getContextTimestamps: () => reverseTimestamps(watcher.getContextTimestamps()), + return map; + } + + createWebpack4Watch() { + return ( + files: Iterable, + dirs: Iterable, + missing: Iterable, + startTime: number, + options: {}, + callback: Parameters[5], + callbackUndelayed: (filename: string, timestamp: number) => void, + ): ReturnType => { + const reverseReplacements = new Map(); + + const newCallbackUndelayed = (filename: string, timestamp: number) => { + const original = reverseReplacements.get(filename); + if (original) { + this._virtualInputFileSystem.purge(original); + callbackUndelayed(original, timestamp); + } else { + callbackUndelayed(filename, timestamp); + } + }; + + const newCallback: Parameters[5] = ( + err: Error | null, + filesModified: string[], + contextModified: string[], + missingModified: string[], + fileTimestamps: Map, + contextTimestamps: Map, + ) => { + // Update fileTimestamps with timestamps from virtual files. + const virtualFilesStats = this._virtualInputFileSystem.getVirtualFilesPaths() + .map((fileName) => ({ + path: fileName, + mtime: +this._virtualInputFileSystem.statSync(fileName).mtime, + })); + virtualFilesStats.forEach(stats => fileTimestamps.set(stats.path, +stats.mtime)); + callback( + err, + filesModified.map(value => reverseReplacements.get(value) || value), + contextModified.map(value => reverseReplacements.get(value) || value), + missingModified.map(value => reverseReplacements.get(value) || value), + this.reverseTimestamps(fileTimestamps, reverseReplacements), + this.reverseTimestamps(contextTimestamps, reverseReplacements), + ); + }; + + const watcher = super.watch( + this.mapReplacements(files, reverseReplacements), + this.mapReplacements(dirs, reverseReplacements), + this.mapReplacements(missing, reverseReplacements), + startTime, + options, + newCallback, + newCallbackUndelayed, + ); + + return { + close: () => watcher.close(), + pause: () => watcher.pause(), + getFileTimestamps: () => + this.reverseTimestamps(watcher.getFileTimestamps(), reverseReplacements), + getContextTimestamps: () => + this.reverseTimestamps(watcher.getContextTimestamps(), reverseReplacements), + }; }; } + + createWebpack5Watch() { + return ( + files: Iterable, + dirs: Iterable, + missing: Iterable, + startTime: number, + options: {}, + callback: Parameters[5], + callbackUndelayed: (filename: string, timestamp: number) => void, + ): ReturnType => { + const reverseReplacements = new Map(); + + const newCallbackUndelayed = (filename: string, timestamp: number) => { + const original = reverseReplacements.get(filename); + if (original) { + this._virtualInputFileSystem.purge(original); + callbackUndelayed(original, timestamp); + } else { + callbackUndelayed(filename, timestamp); + } + }; + + const newCallback = ( + err: Error, + // tslint:disable-next-line: no-any + fileTimeInfoEntries: Map, + // tslint:disable-next-line: no-any + contextTimeInfoEntries: Map, + missing: Set, + removals: Set, + ) => { + // Update fileTimestamps with timestamps from virtual files. + const virtualFilesStats = this._virtualInputFileSystem.getVirtualFilesPaths() + .map((fileName) => ({ + path: fileName, + mtime: +this._virtualInputFileSystem.statSync(fileName).mtime, + })); + virtualFilesStats.forEach(stats => fileTimeInfoEntries.set(stats.path, +stats.mtime)); + callback( + err, + this.reverseTimestamps(fileTimeInfoEntries, reverseReplacements), + this.reverseTimestamps(contextTimeInfoEntries, reverseReplacements), + new Set([...missing].map(value => reverseReplacements.get(value) || value)), + new Set([...removals].map(value => reverseReplacements.get(value) || value)), + ); + }; + + const watcher = super.watch( + this.mapReplacements(files, reverseReplacements), + this.mapReplacements(dirs, reverseReplacements), + this.mapReplacements(missing, reverseReplacements), + startTime, + options, + newCallback, + newCallbackUndelayed, + ); + + return { + close: () => watcher.close(), + pause: () => watcher.pause(), + getFileTimeInfoEntries: () => + this.reverseTimestamps(watcher.getFileTimeInfoEntries(), reverseReplacements), + getContextTimeInfoEntries: () => + this.reverseTimestamps(watcher.getContextTimeInfoEntries(), reverseReplacements), + }; + }; + } + + watch = isWebpackFiveOrHigher() ? this.createWebpack5Watch : this.createWebpack4Watch(); }