Merge pull request #11252 from webpack/improve/cache-and-progress

do cache related tasks after calling handler in watching
This commit is contained in:
Tobias Koppers 2020-08-01 19:11:48 +02:00 committed by GitHub
commit fdfaa2efa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 326 additions and 145 deletions

View File

@ -1996,15 +1996,7 @@ Or do you want to use the entrypoints '${name}' and '${runtime}' independently o
this.unseal();
return this.seal(callback);
}
this.compiler.cache.storeBuildDependencies(
this.buildDependencies,
err => {
if (err) {
return callback(err);
}
return this.hooks.afterSeal.callAsync(callback);
}
);
return this.hooks.afterSeal.callAsync(callback);
});
};

View File

@ -226,6 +226,9 @@ class Compiler {
/** @type {boolean} */
this.running = false;
/** @type {boolean} */
this.idle = false;
/** @type {boolean} */
this.watchMode = false;
@ -353,7 +356,9 @@ class Compiler {
const finalCallback = (err, stats) => {
if (logger) logger.time("beginIdle");
this.idle = true;
this.cache.beginIdle();
this.idle = true;
if (logger) logger.timeEnd("beginIdle");
this.running = false;
if (err) {
@ -419,16 +424,20 @@ class Compiler {
this.hooks.done.callAsync(stats, err => {
logger.timeEnd("done hook");
if (err) return finalCallback(err);
return finalCallback(null, stats);
this.cache.storeBuildDependencies(
compilation.buildDependencies,
err => {
if (err) return finalCallback(err);
return finalCallback(null, stats);
}
);
});
});
});
});
};
this.cache.endIdle(err => {
if (err) return finalCallback(err);
const run = () => {
this.hooks.beforeRun.callAsync(this, err => {
if (err) return finalCallback(err);
@ -442,7 +451,18 @@ class Compiler {
});
});
});
});
};
if (this.idle) {
this.cache.endIdle(err => {
if (err) return finalCallback(err);
this.idle = false;
run();
});
} else {
run();
}
}
/**

View File

@ -27,9 +27,17 @@ class MultiWatching {
this.compiler = compiler;
}
invalidate() {
for (const watching of this.watchings) {
watching.invalidate();
invalidate(callback) {
if (callback) {
asyncLib.each(
this.watchings,
(watching, callback) => watching.invalidate(callback),
callback
);
} else {
for (const watching of this.watchings) {
watching.invalidate();
}
}
}

View File

@ -61,6 +61,7 @@ const makeSerializable = require("./util/makeSerializable");
* @property {string} loader
* @property {any} options
* @property {string?} ident
* @property {string?} type
*/
/**
@ -148,6 +149,7 @@ makeSerializable(
/**
* @typedef {Object} NormalModuleCompilationHooks
* @property {SyncHook<[object, NormalModule]>} loader
* @property {SyncHook<[LoaderItem[], object, NormalModule]>} beforeLoaders
* @property {HookMap<AsyncSeriesBailHook<[string, NormalModule], string | Buffer>>} readResourceForScheme
*/
@ -169,6 +171,7 @@ class NormalModule extends Module {
if (hooks === undefined) {
hooks = {
loader: new SyncHook(["loaderContext", "module"]),
beforeLoaders: new SyncHook(["loaders", "module", "loaderContext"]),
readResourceForScheme: new HookMap(
() => new AsyncSeriesBailHook(["resource", "module"])
)
@ -607,6 +610,9 @@ class NormalModule extends Module {
return callback();
};
const hooks = NormalModule.getCompilationHooks(compilation);
hooks.beforeLoaders.call(this.loaders, this, loaderContext);
runLoaders(
{
resource: this.resource,
@ -615,8 +621,8 @@ class NormalModule extends Module {
readResource: (resource, callback) => {
const scheme = getScheme(resource);
if (scheme) {
NormalModule.getCompilationHooks(compilation)
.readResourceForScheme.for(scheme)
hooks.readResourceForScheme
.for(scheme)
.callAsync(resource, this, (err, result) => {
if (err) return callback(err);
if (typeof result !== "string" && !result) {

View File

@ -9,6 +9,8 @@ const validateOptions = require("schema-utils");
const schema = require("../schemas/plugins/ProgressPlugin.json");
const Compiler = require("./Compiler");
const MultiCompiler = require("./MultiCompiler");
const NormalModule = require("./NormalModule");
const { contextify } = require("./util/identifier");
/** @typedef {import("../declarations/plugins/ProgressPlugin").HandlerFunction} HandlerFunction */
/** @typedef {import("../declarations/plugins/ProgressPlugin").ProgressPluginArgument} ProgressPluginArgument */
@ -19,7 +21,7 @@ const median3 = (a, b, c) => {
};
const createDefaultHandler = (profile, logger) => {
/** @type {{ value: string, time: number, alreadyReported: number }[]} */
/** @type {{ value: string, time: number }[]} */
const lastStateInfo = [];
const defaultHandler = (percentage, msg, ...args) => {
@ -28,62 +30,57 @@ const createDefaultHandler = (profile, logger) => {
lastStateInfo.length = 0;
}
const fullState = [msg, ...args];
const state = fullState.map((_, i) =>
fullState.slice(0, i + 1).join(" ")
);
const state = fullState.map(s => s.replace(/\d+\/\d+ /g, ""));
const now = Date.now();
const len = Math.max(state.length, lastStateInfo.length);
for (let i = 0; i < len; i++) {
for (let i = len; i >= 0; i--) {
const stateItem = i < state.length ? state[i] : undefined;
const lastStateItem =
i < lastStateInfo.length ? lastStateInfo[i] : undefined;
if (lastStateItem) {
if (stateItem !== lastStateItem.value) {
const alreadyReported = lastStateItem.alreadyReported;
const diff = now - lastStateItem.time;
const stateMsg = alreadyReported
? `${diff} ms (-${alreadyReported} ms) ${lastStateItem.value}`
: `${diff} ms ${lastStateItem.value}`;
const d = diff - alreadyReported;
// This depends on timing so we ignore it for coverage
/* istanbul ignore next */
{
if (d > 10000) {
logger.error(stateMsg);
} else if (d > 1000) {
logger.warn(stateMsg);
} else if (d > 10) {
logger.info(stateMsg);
} else if (d > 5) {
logger.log(stateMsg);
} else {
logger.debug(stateMsg);
if (lastStateItem.value) {
let reportState = lastStateItem.value;
if (i > 0) {
reportState = lastStateInfo[i - 1].value + " > " + reportState;
}
const stateMsg = `${" | ".repeat(i)}${diff} ms ${reportState}`;
const d = diff;
// This depends on timing so we ignore it for coverage
/* istanbul ignore next */
{
if (d > 10000) {
logger.error(stateMsg);
} else if (d > 1000) {
logger.warn(stateMsg);
} else if (d > 10) {
logger.info(stateMsg);
} else if (d > 5) {
logger.log(stateMsg);
} else {
logger.debug(stateMsg);
}
}
}
if (stateItem === undefined) {
lastStateInfo[i] = undefined;
lastStateInfo.length = i;
} else {
lastStateItem.value = stateItem;
lastStateItem.time = now;
lastStateItem.alreadyReported = 0;
lastStateInfo.length = i + 1;
}
lastStateInfo.length = i + 1;
for (let j = 0; j < i; j++) {
lastStateInfo[j].alreadyReported += diff;
}
break;
}
} else {
lastStateInfo[i] = {
value: stateItem,
time: now,
alreadyReported: 0
time: now
};
}
}
}
logger.status(`${Math.floor(percentage * 100)}%`, msg, ...args);
if (percentage === 1) logger.status();
if (percentage === 1 || (!msg && args.length === 0)) logger.status();
};
return defaultHandler;
@ -184,6 +181,7 @@ class ProgressPlugin {
const showDependencies = this.showDependencies;
const showActiveModules = this.showActiveModules;
let lastActiveModule = "";
let currentLoader = "";
let lastModulesCount = this.modulesCount;
let lastDependenciesCount = this.dependenciesCount;
let lastEntriesCount = 0;
@ -231,18 +229,36 @@ class ProgressPlugin {
const percentage = 0.1 + percentageFactor * 0.55;
if (showEntries) {
items.push(`${doneEntries}/${entriesCount} entries`);
}
if (showDependencies) {
items.push(`${doneDependencies}/${dependenciesCount} dependencies`);
}
if (showModules) {
items.push(`${doneModules}/${modulesCount} modules`);
}
if (showActiveModules) {
items.push(`${activeModules.size} active`);
items.push(lastActiveModule);
if (currentLoader) {
items.push(
`import loader ${contextify(
compiler.context,
currentLoader,
compiler.root
)}`
);
} else {
const statItems = [];
if (showEntries) {
statItems.push(`${doneEntries}/${entriesCount} entries`);
}
if (showDependencies) {
statItems.push(
`${doneDependencies}/${dependenciesCount} dependencies`
);
}
if (showModules) {
statItems.push(`${doneModules}/${modulesCount} modules`);
}
if (showActiveModules) {
statItems.push(`${activeModules.size} active`);
}
if (statItems.length > 0) {
items.push(statItems.join(" "));
}
if (showActiveModules) {
items.push(lastActiveModule);
}
}
handler(percentage, "building", ...items);
lastUpdate = Date.now();
@ -310,6 +326,7 @@ class ProgressPlugin {
compiler.hooks.beforeCompile.tapAsync(
"ProgressPlugin",
(params, callback) => {
handler(0.06, "setup", "waiting for cache");
cache.get((err, data) => {
if (err) {
return callback(err);
@ -362,9 +379,35 @@ class ProgressPlugin {
compilation.hooks.failedEntry.tap("ProgressPlugin", entryDone);
compilation.hooks.succeedEntry.tap("ProgressPlugin", entryDone);
// avoid dynamic require if bundled with webpack
// @ts-expect-error
if (typeof __webpack_require__ !== "function") {
const requiredLoaders = new Set();
NormalModule.getCompilationHooks(compilation).beforeLoaders.tap(
"ProgressPlugin",
loaders => {
for (const loader of loaders) {
if (
loader.type !== "module" &&
!requiredLoaders.has(loader.loader)
) {
requiredLoaders.add(loader.loader);
currentLoader = loader.loader;
update();
require(loader.loader);
}
}
if (currentLoader) {
currentLoader = "";
update();
}
}
);
}
const hooks = {
finishModules: "finish module graph",
seal: "sealing",
seal: "plugins",
optimizeDependencies: "dependencies optimization",
afterOptimizeDependencies: "after dependencies optimization",
beforeChunks: "chunk graph",
@ -410,24 +453,24 @@ class ProgressPlugin {
compilation.hooks[name].intercept({
name: "ProgressPlugin",
call() {
handler(percentage, title);
handler(percentage, "sealing", title);
},
done() {
handler(percentage, title);
handler(percentage, "sealing", title);
},
result() {
handler(percentage, title);
handler(percentage, "sealing", title);
},
error() {
handler(percentage, title);
handler(percentage, "sealing", title);
},
tap(tap) {
// p is percentage from 0 to 1
// args is any number of messages in a hierarchical matter
progressReporters.set(compilation.compiler, (p, ...args) => {
handler(percentage, title, tap.name, ...args);
handler(percentage, "sealing", title, tap.name, ...args);
});
handler(percentage, title, tap.name);
handler(percentage, "sealing", title, tap.name);
}
});
});
@ -441,53 +484,98 @@ class ProgressPlugin {
handler(0.65, "building");
}
});
const interceptHook = (hook, progress, name) => {
const interceptHook = (hook, progress, category, name) => {
hook.intercept({
name: "ProgressPlugin",
call() {
handler(progress, name);
handler(progress, category, name);
},
done() {
handler(progress, name);
handler(progress, category, name);
},
result() {
handler(progress, name);
handler(progress, category, name);
},
error() {
handler(progress, name);
handler(progress, category, name);
},
tap(tap) {
progressReporters.set(compiler, (p, ...args) => {
handler(progress, name, tap.name, ...args);
handler(progress, category, name, tap.name, ...args);
});
handler(progress, name, tap.name);
handler(progress, category, name, tap.name);
}
});
};
interceptHook(compiler.hooks.initialize, 0.0, "initialize");
interceptHook(compiler.hooks.beforeRun, 0.01, "before run");
interceptHook(compiler.hooks.run, 0.02, "run");
interceptHook(compiler.hooks.watchRun, 0.02, "watch run");
compiler.cache.hooks.endIdle.intercept({
name: "ProgressPlugin",
call() {
handler(0, "");
}
});
interceptHook(compiler.cache.hooks.endIdle, 0.01, "cache", "end idle");
compiler.hooks.initialize.intercept({
name: "ProgressPlugin",
call() {
handler(0, "");
}
});
interceptHook(compiler.hooks.initialize, 0.01, "setup", "initialize");
interceptHook(compiler.hooks.beforeRun, 0.02, "setup", "before run");
interceptHook(compiler.hooks.run, 0.03, "setup", "run");
interceptHook(compiler.hooks.watchRun, 0.03, "setup", "watch run");
interceptHook(
compiler.hooks.normalModuleFactory,
0.03,
0.04,
"setup",
"normal module factory"
);
interceptHook(
compiler.hooks.contextModuleFactory,
0.03,
0.05,
"setup",
"context module factory"
);
interceptHook(compiler.hooks.beforeCompile, 0.035, "before compile");
interceptHook(compiler.hooks.compile, 0.04, "compile");
interceptHook(compiler.hooks.thisCompilation, 0.05, "setup compilation");
interceptHook(compiler.hooks.compilation, 0.06, "setup compilation");
interceptHook(compiler.hooks.finishMake, 0.69, "finish building");
interceptHook(compiler.hooks.emit, 0.95, "emitting");
interceptHook(compiler.hooks.afterEmit, 0.98, "after emitting");
interceptHook(compiler.hooks.done, 0.99, "done");
interceptHook(compiler.hooks.watchClose, 0.99, "closing watch compilation");
interceptHook(
compiler.hooks.beforeCompile,
0.06,
"setup",
"before compile"
);
interceptHook(compiler.hooks.compile, 0.07, "setup", "compile");
interceptHook(compiler.hooks.thisCompilation, 0.08, "setup", "compilation");
interceptHook(compiler.hooks.compilation, 0.09, "setup", "compilation");
interceptHook(compiler.hooks.finishMake, 0.69, "building", "finish");
interceptHook(compiler.hooks.emit, 0.95, "emitting", "emit");
interceptHook(compiler.hooks.afterEmit, 0.98, "emitting", "after emit");
interceptHook(compiler.hooks.done, 0.99, "done", "plugins");
compiler.hooks.done.intercept({
name: "ProgressPlugin",
done() {
handler(0.99, "");
}
});
interceptHook(
compiler.cache.hooks.storeBuildDependencies,
0.99,
"cache",
"store build dependencies"
);
interceptHook(compiler.cache.hooks.shutdown, 0.99, "cache", "shutdown");
interceptHook(compiler.cache.hooks.beginIdle, 0.99, "cache", "begin idle");
interceptHook(
compiler.hooks.watchClose,
0.99,
"end",
"closing watch compilation"
);
compiler.cache.hooks.beginIdle.intercept({
name: "ProgressPlugin",
done() {
handler(1, "");
}
});
compiler.cache.hooks.shutdown.intercept({
name: "ProgressPlugin",
done() {
handler(1, "");

View File

@ -63,12 +63,11 @@ class Watching {
this.startTime = Date.now();
this.running = true;
this.invalid = false;
this.compiler.cache.endIdle(err => {
if (err) return this._done(err);
const run = () => {
this.compiler.hooks.watchRun.callAsync(this.compiler, err => {
if (err) return this._done(err);
const onCompiled = (err, compilation) => {
if (err) return this._done(err);
if (err) return this._done(err, compilation);
if (this.invalid) return this._done();
if (this.compiler.hooks.shouldEmit.call(compilation) === false) {
@ -80,13 +79,13 @@ class Watching {
logger.time("emitAssets");
this.compiler.emitAssets(compilation, err => {
logger.timeEnd("emitAssets");
if (err) return this._done(err);
if (this.invalid) return this._done();
if (err) return this._done(err, compilation);
if (this.invalid) return this._done(null, compilation);
logger.time("emitRecords");
this.compiler.emitRecords(err => {
logger.timeEnd("emitRecords");
if (err) return this._done(err);
if (err) return this._done(err, compilation);
if (compilation.hooks.needAdditionalPass.call()) {
compilation.needAdditionalPass = true;
@ -97,10 +96,10 @@ class Watching {
logger.time("done hook");
this.compiler.hooks.done.callAsync(stats, err => {
logger.timeEnd("done hook");
if (err) return this._done(err);
if (err) return this._done(err, compilation);
this.compiler.hooks.additionalPass.callAsync(err => {
if (err) return this._done(err);
if (err) return this._done(err, compilation);
this.compiler.compile(onCompiled);
});
});
@ -113,7 +112,17 @@ class Watching {
};
this.compiler.compile(onCompiled);
});
});
};
if (this.compiler.idle) {
this.compiler.cache.endIdle(err => {
if (err) return this._done(err);
this.compiler.idle = false;
run();
});
} else {
run();
}
}
/**
@ -134,36 +143,69 @@ class Watching {
*/
_done(err, compilation) {
this.running = false;
if (this.invalid) return this._go();
const stats = compilation ? this._getStats(compilation) : null;
if (err) {
const logger = compilation && compilation.getLogger("webpack.Watching");
let stats = null;
const handleError = err => {
this.compiler.hooks.failed.call(err);
this.compiler.cache.beginIdle();
this.compiler.idle = true;
this.handler(err, stats);
for (const cb of this.callbacks) cb();
this.callbacks.length = 0;
};
if (this.invalid) {
if (compilation) {
logger.time("storeBuildDependencies");
this.compiler.cache.storeBuildDependencies(
compilation.buildDependencies,
err => {
logger.timeEnd("storeBuildDependencies");
if (err) return handleError(err);
this._go();
}
);
} else {
this._go();
}
return;
}
const logger = compilation.getLogger("webpack.Watching");
stats = compilation ? this._getStats(compilation) : null;
if (err) return handleError(err);
logger.time("done hook");
this.compiler.hooks.done.callAsync(stats, () => {
this.compiler.hooks.done.callAsync(stats, err => {
logger.timeEnd("done hook");
logger.time("beginIdle");
this.compiler.cache.beginIdle();
logger.timeEnd("beginIdle");
if (err) return handleError(err);
this.handler(null, stats);
process.nextTick(() => {
if (!this.closed) {
this.watch(
compilation.fileDependencies,
compilation.contextDependencies,
compilation.missingDependencies
);
logger.time("storeBuildDependencies");
this.compiler.cache.storeBuildDependencies(
compilation.buildDependencies,
err => {
logger.timeEnd("storeBuildDependencies");
if (err) return handleError(err);
logger.time("beginIdle");
this.compiler.cache.beginIdle();
this.compiler.idle = true;
logger.timeEnd("beginIdle");
process.nextTick(() => {
if (!this.closed) {
this.watch(
compilation.fileDependencies,
compilation.contextDependencies,
compilation.missingDependencies
);
}
});
for (const cb of this.callbacks) cb();
this.callbacks.length = 0;
this.compiler.hooks.afterDone.call(stats);
}
});
for (const cb of this.callbacks) cb();
this.callbacks.length = 0;
this.compiler.hooks.afterDone.call(stats);
);
});
}
@ -212,7 +254,7 @@ class Watching {
}
/**
* @param {Callback<void>=} callback signals when the build is invalidated
* @param {Callback<void>=} callback signals when the build has completed again
* @returns {void}
*/
invalidate(callback) {
@ -266,7 +308,7 @@ class Watching {
}
return;
}
const finalCallback = () => {
const finalCallback = (err, compilation) => {
this.running = false;
this.compiler.running = false;
this.compiler.watchMode = false;
@ -274,12 +316,27 @@ class Watching {
this.compiler.removedFiles = undefined;
this.compiler.fileTimestamps = undefined;
this.compiler.contextTimestamps = undefined;
this.compiler.cache.shutdown(err => {
this.compiler.hooks.watchClose.call();
const closeCallbacks = this._closeCallbacks;
this._closeCallbacks = undefined;
for (const cb of closeCallbacks) cb(err);
});
const shutdown = () => {
this.compiler.cache.shutdown(err => {
this.compiler.hooks.watchClose.call();
const closeCallbacks = this._closeCallbacks;
this._closeCallbacks = undefined;
for (const cb of closeCallbacks) cb(err);
});
};
if (compilation) {
const logger = compilation.getLogger("webpack.Watching");
logger.time("storeBuildDependencies");
this.compiler.cache.storeBuildDependencies(
compilation.buildDependencies,
err => {
logger.timeEnd("storeBuildDependencies");
shutdown();
}
);
} else {
shutdown();
}
};
this.closed = true;

View File

@ -71,7 +71,7 @@ class IdleFileCachePlugin {
);
compiler.cache.hooks.storeBuildDependencies.tap(
{ name: "IdleFile", stage: Cache.STAGE_DISK },
{ name: "IdleFileCachePlugin", stage: Cache.STAGE_DISK },
dependencies => {
pendingIdleTasks.set(BUILD_DEPENDENCIES_KEY, () =>
strategy.storeBuildDependencies(dependencies)

View File

@ -40,14 +40,25 @@ describe("ProgressPlugin", function () {
expect(logs).toContainEqual(
expect.stringMatching(
/\[webpack\.Progress\] \d+ ms module ids DeterministicModuleIdsPlugin\n$/
/\[webpack\.Progress\] {2}| {2}| \d+ ms module ids > DeterministicModuleIdsPlugin\n$/
)
);
expect(logs).toContainEqual(
expect.stringMatching(
/\[webpack\.Progress\] \d+ ms(?: \(-\d+ ms\))? module ids\n$/
/\[webpack\.Progress\] {2}| \d+ ms building > \.\.\. entries \.\.\. dependencies \.\.\. modules\n$/
)
);
expect(logs).toContainEqual(
expect.stringMatching(/\[webpack\.Progress\] \d+ ms building\n$/)
);
expect(logs).toContainEqual(
expect.stringMatching(
/\[webpack\.Progress\] {2}| \d+ ms sealing > module ids\n$/
)
);
expect(logs).toContainEqual(
expect.stringMatching(/\[webpack\.Progress\] \d+ ms sealing\n$/)
);
});
});
@ -82,14 +93,10 @@ describe("ProgressPlugin", function () {
expect(logs.length).toBeGreaterThan(20);
logs.forEach(log => expect(log.length).toBeLessThanOrEqual(35));
expect(logs).toContain(
"75% ...optimization ...ChunksPlugin",
"75% sealing ...mization ...nsPlugin",
"trims each detail string equally"
);
expect(logs).toContain(
"10% ...ding ...ries ...cies ...ules",
"remove empty arguments and omit arguments when no space"
);
expect(logs).toContain("93% after asset optimization");
expect(logs).toContain("92% sealing asset processing");
expect(logs).toContain("100%");
});
});
@ -113,8 +120,8 @@ describe("ProgressPlugin", function () {
return RunCompilerAsync(compiler).then(() => {
const logs = getLogs(stderr.toString());
expect(logs).toContain("3% normal module factory");
expect(logs).toContain("3% context module factory");
expect(logs).toContain("4% setup normal module factory");
expect(logs).toContain("5% setup context module factory");
});
});

View File

@ -1,8 +1,8 @@
it("should contain the custom progress messages", function() {
it("should contain the custom progress messages", function () {
var data = require("data");
expect(data).toContain("optimizing");
expect(data).toContain("optimizing|CustomPlugin");
expect(data).toContain("sealing|optimizing");
expect(data).toContain("sealing|optimizing|CustomPlugin");
expect(data).toContain(
"optimizing|CustomPlugin|custom category|custom message"
"sealing|optimizing|CustomPlugin|custom category|custom message"
);
});

5
types.d.ts vendored
View File

@ -1498,6 +1498,7 @@ declare class Compiler {
cache: Cache;
compilerPath: string;
running: boolean;
idle: boolean;
watchMode: boolean;
getCache(name: string): CacheFacade;
getInfrastructureLogger(name: string | (() => string)): WebpackLogger;
@ -3922,6 +3923,7 @@ declare interface LoaderItem {
loader: string;
options: any;
ident: string;
type: string;
}
declare class LoaderOptionsPlugin {
constructor(options?: LoaderOptionsPluginOptions);
@ -4621,7 +4623,7 @@ declare abstract class MultiStats {
declare abstract class MultiWatching {
watchings: Watching[];
compiler: MultiCompiler;
invalidate(): void;
invalidate(callback?: any): void;
suspend(): void;
resume(): void;
close(callback: CallbackFunction<void>): void;
@ -4864,6 +4866,7 @@ declare class NormalModule extends Module {
}
declare interface NormalModuleCompilationHooks {
loader: SyncHook<[any, NormalModule], void>;
beforeLoaders: SyncHook<[LoaderItem[], any, NormalModule], void>;
readResourceForScheme: HookMap<
AsyncSeriesBailHook<[string, NormalModule], string | Buffer>
>;