refactor scheme handling into plugins

This commit is contained in:
Tobias Koppers 2020-07-03 14:45:49 +02:00
parent a11922fea3
commit 7dad1c1c30
16 changed files with 384 additions and 216 deletions

View File

@ -5,14 +5,11 @@
"use strict";
const http = require("http");
const https = require("https");
const parseJson = require("json-parse-better-errors");
const { getContext, runLoaders } = require("loader-runner");
const querystring = require("querystring");
const validateOptions = require("schema-utils");
const { SyncHook } = require("tapable");
const { URL, fileURLToPath } = require("url");
const { HookMap, SyncHook, AsyncSeriesBailHook } = require("tapable");
const {
CachedSource,
OriginalSource,
@ -26,9 +23,8 @@ const ModuleError = require("./ModuleError");
const ModuleParseError = require("./ModuleParseError");
const ModuleWarning = require("./ModuleWarning");
const RuntimeGlobals = require("./RuntimeGlobals");
const UnsupportedSchemeError = require("./UnsupportedSchemeError");
const UnhandledSchemeError = require("./UnhandledSchemeError");
const WebpackError = require("./WebpackError");
const { decodeDataURI } = require("./util/DataURI");
const { getScheme } = require("./util/URLAbsoluteSpecifier");
const {
compareLocations,
@ -130,88 +126,6 @@ const asBuffer = input => {
return input;
};
/**
* @param {InputFileSystem} fs fs
* @returns {(resource: string, cb: (err: Error|null, data?: string|Buffer) => void) => void} read function
*/
const readResourceFn = fs => {
return (resource, callback) => {
const scheme = getScheme(resource);
if (scheme) {
switch (scheme) {
case "data":
return process.nextTick(() => {
callback(null, decodeDataURI(resource));
});
case "file":
return fs.readFile(fileURLToPath(new URL(resource)), callback);
case "http":
return http.get(new URL(resource), res => {
if (res.statusCode !== 200) {
res.destroy();
return callback(
new ModuleBuildError(resource, {
from: `http request status code = ${res.statusCode}`
})
);
}
const bufferArr = [];
res.on("data", chunk => {
bufferArr.push(chunk);
});
res.on("end", () => {
if (!res.complete) {
return callback(
new ModuleBuildError(resource, {
from: "http request was terminated"
})
);
}
callback(null, Buffer.concat(bufferArr));
});
});
case "https":
return https.get(new URL(resource), res => {
if (res.statusCode !== 200) {
res.destroy();
return callback(
new ModuleBuildError(resource, {
from: `https request status code = ${res.statusCode}`
})
);
}
const bufferArr = [];
res.on("data", chunk => {
bufferArr.push(chunk);
});
res.on("end", () => {
if (!res.complete) {
return callback(
new ModuleBuildError(resource, {
from: "https request was terminated"
})
);
}
callback(null, Buffer.concat(bufferArr));
});
});
default:
callback(new UnsupportedSchemeError(scheme, resource));
}
}
fs.readFile(resource, callback);
};
};
class NonErrorEmittedError extends WebpackError {
constructor(error) {
super();
@ -232,6 +146,7 @@ makeSerializable(
/**
* @typedef {Object} NormalModuleCompilationHooks
* @property {SyncHook<[object, NormalModule]>} loader
* @property {HookMap<AsyncSeriesBailHook<[string, NormalModule], string | Buffer>>} readResourceForScheme
*/
/** @type {WeakMap<Compilation, NormalModuleCompilationHooks>} */
@ -251,7 +166,10 @@ class NormalModule extends Module {
let hooks = compilationHooksMap.get(compilation);
if (hooks === undefined) {
hooks = {
loader: new SyncHook(["loaderContext", "module"])
loader: new SyncHook(["loaderContext", "module"]),
readResourceForScheme: new HookMap(
() => new AsyncSeriesBailHook(["resource", "module"])
)
};
compilationHooksMap.set(compilation, hooks);
}
@ -269,7 +187,6 @@ class NormalModule extends Module {
* @param {string | undefined} options.matchResource path + query of the matched resource (virtual)
* @param {Parser} options.parser the parser used
* @param {Generator} options.generator the generator used
* @param {string=} options.scheme scheme
* @param {Object} options.resolveOptions options used for resolving requests from this module
*/
constructor({
@ -282,8 +199,7 @@ class NormalModule extends Module {
matchResource,
parser,
generator,
resolveOptions,
scheme
resolveOptions
}) {
super(type, getContext(resource));
@ -304,8 +220,6 @@ class NormalModule extends Module {
this.resource = resource;
/** @type {string | undefined} */
this.matchResource = matchResource;
/** @type {string | undefined} */
this.scheme = scheme;
/** @type {LoaderItem[]} */
this.loaders = loaders;
if (resolveOptions !== undefined) {
@ -380,6 +294,7 @@ class NormalModule extends Module {
updateCacheModule(module) {
super.updateCacheModule(module);
const m = /** @type {NormalModule} */ (module);
this.binary = m.binary;
this.request = m.request;
this.userRequest = m.userRequest;
this.rawRequest = m.rawRequest;
@ -388,7 +303,6 @@ class NormalModule extends Module {
this.resource = m.resource;
this.matchResource = m.matchResource;
this.loaders = m.loaders;
this.scheme = m.scheme;
}
/**
@ -700,7 +614,22 @@ class NormalModule extends Module {
resource: this.resource,
loaders: this.loaders,
context: loaderContext,
readResource: readResourceFn(fs)
readResource: (resource, callback) => {
const scheme = getScheme(resource);
if (scheme) {
NormalModule.getCompilationHooks(compilation)
.readResourceForScheme.for(scheme)
.callAsync(resource, this, (err, result) => {
if (err) return callback(err);
if (typeof result !== "string" && !result) {
return callback(new UnhandledSchemeError(scheme, resource));
}
return callback(null, result);
});
} else {
fs.readFile(resource, callback);
}
}
},
(err, result) => {
if (!result) {
@ -994,9 +923,6 @@ class NormalModule extends Module {
// always try to build in case of an error
if (this.error) return callback(null, true);
// always build when module resource is an absolute url, except data url
if (this.scheme && this.scheme !== "data") return callback(null, true);
// always build when module is not cacheable
if (!this.buildInfo.cacheable) return callback(null, true);
@ -1039,9 +965,6 @@ class NormalModule extends Module {
serialize(context) {
const { write } = context;
// constructor
write(this.type);
write(this.resource);
// deserialize
write(this._source);
write(this._sourceSizes);
@ -1054,11 +977,10 @@ class NormalModule extends Module {
}
static deserialize(context) {
const { read } = context;
const obj = new NormalModule({
type: read(),
resource: read(),
// will be filled by updateCacheModule
type: "",
resource: "",
request: null,
userRequest: null,
rawRequest: null,

View File

@ -22,9 +22,10 @@ const BasicMatcherRulePlugin = require("./rules/BasicMatcherRulePlugin");
const RuleSetCompiler = require("./rules/RuleSetCompiler");
const UseEffectRulePlugin = require("./rules/UseEffectRulePlugin");
const LazySet = require("./util/LazySet");
const { getScheme, getMimetype } = require("./util/URLAbsoluteSpecifier");
const { getScheme } = require("./util/URLAbsoluteSpecifier");
const { cachedCleverMerge, cachedSetProperty } = require("./util/cleverMerge");
const { join } = require("./util/fs");
const { parsePathQueryFragment } = require("./util/identifier");
/** @typedef {import("../declarations/WebpackOptions").ModuleOptions} ModuleOptions */
/** @typedef {import("./Generator")} Generator */
@ -48,6 +49,16 @@ const { join } = require("./util/fs");
* @property {boolean} cacheable allow to use the unsafe cache
*/
/**
* @typedef {Object} ResourceData
* @property {string} resource
* @property {string} path
* @property {string} query
* @property {string} fragment
*/
/** @typedef {ResourceData & { data: Record<string, any> }} ResourceDataWithData */
const EMPTY_RESOLVE_OPTIONS = {};
const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
@ -148,6 +159,10 @@ class NormalModuleFactory extends ModuleFactory {
this.hooks = Object.freeze({
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
resolve: new AsyncSeriesBailHook(["resolveData"]),
/** @type {HookMap<AsyncSeriesBailHook<[ResourceDataWithData, ResolveData], true | void>>} */
resolveForScheme: new HookMap(
() => new AsyncSeriesBailHook(["resourceData", "resolveData"])
),
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
factorize: new AsyncSeriesBailHook(["resolveData"]),
/** @type {AsyncSeriesBailHook<[ResolveData], TODO>} */
@ -266,24 +281,14 @@ class NormalModuleFactory extends ModuleFactory {
} = data;
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver(
"normal",
dependencies.length > 0
? cachedSetProperty(
resolveOptions || EMPTY_RESOLVE_OPTIONS,
"dependencyType",
dependencies[0].category
)
: resolveOptions
);
/** @type {string} */
let matchResource = undefined;
/** @type {ResourceData | undefined} */
let matchResourceData = undefined;
/** @type {string} */
let requestWithoutMatchResource = request;
const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
if (matchResourceMatch) {
matchResource = matchResourceMatch[1];
let matchResource = matchResourceMatch[1];
if (matchResource.charCodeAt(0) === 46) {
// 46 === ".", 47 === "/"
const secondChar = matchResource.charCodeAt(1);
@ -295,6 +300,10 @@ class NormalModuleFactory extends ModuleFactory {
matchResource = join(this.fs, context, matchResource);
}
}
matchResourceData = {
resource: matchResource,
...parsePathQueryFragment(matchResource)
};
requestWithoutMatchResource = request.substr(
matchResourceMatch[0].length
);
@ -319,11 +328,11 @@ class NormalModuleFactory extends ModuleFactory {
contextDependencies
};
/** @type {string | false} */
let resource;
/** @type {ResourceDataWithData} */
let resourceData;
/** @type {string | undefined} */
const scheme = getScheme(unresolvedResource);
let resourceResolveData;
let loaders;
const continueCallback = needCalls(2, err => {
@ -354,7 +363,7 @@ class NormalModuleFactory extends ModuleFactory {
return callback(e);
}
if (resource === false) {
if (!resourceData) {
// ignored
return callback(
null,
@ -367,26 +376,18 @@ class NormalModuleFactory extends ModuleFactory {
}
const userRequest =
(matchResource !== undefined ? `${matchResource}!=!` : "") +
stringifyLoadersAndResource(loaders, resource);
let resourcePath =
matchResource !== undefined ? matchResource : resource;
let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?");
if (queryIndex >= 0) {
resourceQuery = resourcePath.substr(queryIndex);
resourcePath = resourcePath.substr(0, queryIndex);
}
(matchResourceData !== undefined
? `${matchResourceData.resource}!=!`
: "") +
stringifyLoadersAndResource(loaders, resourceData.resource);
const resourceDataForRules = matchResourceData || resourceData;
const result = this.ruleSet.exec({
resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery,
mimetype: scheme ? getMimetype(scheme, resource) : "",
resource: resourceDataForRules.path,
realResource: resourceData.path,
resourceQuery: resourceDataForRules.query,
resourceFragment: resourceDataForRules.fragment,
mimetype: matchResourceData ? "" : resourceData.data.mimetype || "",
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
});
@ -426,7 +427,7 @@ class NormalModuleFactory extends ModuleFactory {
return callback(err);
}
const allLoaders = postLoaders;
if (matchResource === undefined) {
if (matchResourceData === undefined) {
for (const loader of loaders) allLoaders.push(loader);
for (const loader of normalLoaders) allLoaders.push(loader);
} else {
@ -437,13 +438,18 @@ class NormalModuleFactory extends ModuleFactory {
const type = settings.type;
const resolveOptions = settings.resolve;
Object.assign(data.createData, {
request: stringifyLoadersAndResource(allLoaders, resource),
request: stringifyLoadersAndResource(
allLoaders,
resourceData.resource
),
userRequest,
rawRequest: request,
loaders: allLoaders,
resource,
matchResource,
resourceResolveData,
resource: resourceData.resource,
matchResource: matchResourceData
? matchResourceData.resource
: undefined,
resourceResolveData: resourceData.data,
settings,
type,
parser: this.getParser(type, settings.parser),
@ -501,19 +507,45 @@ class NormalModuleFactory extends ModuleFactory {
}
);
if (
unresolvedResource === "" ||
unresolvedResource.charCodeAt(0) === 63
) {
// 63 === "?"
resource = unresolvedResource;
return continueCallback();
// resource with scheme
if (scheme) {
resourceData = {
resource: unresolvedResource,
data: {},
path: undefined,
query: undefined,
fragment: undefined
};
this.hooks.resolveForScheme
.for(scheme)
.callAsync(resourceData, data, err => {
if (err) return continueCallback(err);
continueCallback();
});
}
if (scheme) {
resource = unresolvedResource;
// resource without scheme and without path
else if (/^($|\?|#)/.test(unresolvedResource)) {
resourceData = {
resource: unresolvedResource,
data: {},
...parsePathQueryFragment(unresolvedResource)
};
continueCallback();
} else {
}
// resource without scheme and with path
else {
const normalResolver = this.getResolver(
"normal",
dependencies.length > 0
? cachedSetProperty(
resolveOptions || EMPTY_RESOLVE_OPTIONS,
"dependencyType",
dependencies[0].category
)
: resolveOptions
);
normalResolver.resolve(
contextInfo,
context,
@ -521,8 +553,13 @@ class NormalModuleFactory extends ModuleFactory {
resolveContext,
(err, resolvedResource, resolvedResourceResolveData) => {
if (err) return continueCallback(err);
resource = resolvedResource;
resourceResolveData = resolvedResourceResolveData;
if (resolvedResource !== false) {
resourceData = {
resource: resolvedResource,
data: resolvedResourceResolveData,
...parsePathQueryFragment(resolvedResource)
};
}
continueCallback();
}
);

View File

@ -0,0 +1,33 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const WebpackError = require("./WebpackError");
const makeSerializable = require("./util/makeSerializable");
class UnhandledSchemeError extends WebpackError {
/**
* @param {string} scheme scheme
* @param {string} resource resource
*/
constructor(scheme, resource) {
super(
`Reading from "${resource}" is not handled by plugins (Unhandled scheme).` +
'\nWebpack supports "data:" and "file:" URIs by default.' +
`\nYou may need an additional plugin to handle "${scheme}:" URIs.`
);
this.file = resource;
this.name = "UnhandledSchemeError";
}
}
makeSerializable(
UnhandledSchemeError,
"webpack/lib/UnhandledSchemeError",
"UnhandledSchemeError"
);
module.exports = UnhandledSchemeError;

View File

@ -1,32 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Ivan Kopeykin @vankop
*/
"use strict";
const WebpackError = require("./WebpackError");
const makeSerializable = require("./util/makeSerializable");
class UnsupportedSchemeError extends WebpackError {
/**
* @param {string} scheme scheme
* @param {string} specifier specifier
*/
constructor(scheme, specifier) {
super(
`Unsupported scheme ${JSON.stringify(
scheme
)} provided, specifier: ${specifier}.`
);
this.name = "UnsupportedSchemeError";
}
}
makeSerializable(
UnsupportedSchemeError,
"webpack/lib/UnsupportedSchemeError",
"UnsupportedSchemeError"
);
module.exports = UnsupportedSchemeError;

View File

@ -26,6 +26,9 @@ const TemplatedPathPlugin = require("./TemplatedPathPlugin");
const UseStrictPlugin = require("./UseStrictPlugin");
const WarnCaseSensitiveModulesPlugin = require("./WarnCaseSensitiveModulesPlugin");
const DataUriPlugin = require("./schemes/DataUriPlugin");
const FileUriPlugin = require("./schemes/FileUriPlugin");
const ResolverCachePlugin = require("./cache/ResolverCachePlugin");
const CommonJsPlugin = require("./dependencies/CommonJsPlugin");
@ -325,6 +328,9 @@ class WebpackOptionsApply extends OptionsApply {
: true
}).apply(compiler);
new DataUriPlugin().apply(compiler);
new FileUriPlugin().apply(compiler);
new CompatibilityPlugin().apply(compiler);
new HarmonyModulesPlugin({
module: options.module,

View File

@ -426,5 +426,13 @@ module.exports = mergeExports(fn, {
get serialization() {
return require("./util/serialization");
}
},
experiments: {
schemes: {
get HttpUriPlugin() {
return require("./schemes/HttpUriPlugin");
}
}
}
});

View File

@ -0,0 +1,36 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const NormalModule = require("../NormalModule");
const { getMimetype, decodeDataURI } = require("../util/DataURI");
/** @typedef {import("../Compiler")} Compiler */
class DataUriPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"DataUriPlugin",
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.resolveForScheme
.for("data")
.tap("DataUriPlugin", resourceData => {
resourceData.data.mimetype = getMimetype(resourceData.resource);
});
NormalModule.getCompilationHooks(compilation)
.readResourceForScheme.for("data")
.tap("DataUriPlugin", resource => decodeDataURI(resource));
}
);
}
}
module.exports = DataUriPlugin;

View File

@ -0,0 +1,40 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { URL, fileURLToPath } = require("url");
/** @typedef {import("../Compiler")} Compiler */
class FileUriPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"FileUriPlugin",
(compilation, { normalModuleFactory }) => {
normalModuleFactory.hooks.resolveForScheme
.for("file")
.tap("FileUriPlugin", resourceData => {
const url = new URL(resourceData.resource);
const path = fileURLToPath(url);
const query = url.search;
const fragment = url.hash;
resourceData.path = path;
resourceData.query = query;
resourceData.fragment = fragment;
resourceData.resource = path + query + fragment;
return true;
});
}
);
}
}
module.exports = FileUriPlugin;

View File

@ -0,0 +1,89 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { URL } = require("url");
const NormalModule = require("../NormalModule");
const memorize = require("../util/memorize");
/** @typedef {import("../Compiler")} Compiler */
class HttpUriPlugin {
/**
* Apply the plugin
* @param {Compiler} compiler the compiler instance
* @returns {void}
*/
apply(compiler) {
compiler.hooks.compilation.tap(
"HttpUriPlugin",
(compilation, { normalModuleFactory }) => {
const resolveHandler = resourceData => {
const url = new URL(resourceData.resource);
resourceData.path = url.origin + url.pathname;
resourceData.query = url.search;
resourceData.fragment = url.hash;
return /** @type {true} */ (true);
};
const readHandler = (scheme, getBuiltin) => (
resource,
module,
callback
) => {
return getBuiltin().get(new URL(resource), res => {
if (res.statusCode !== 200) {
res.destroy();
return callback(
new Error(`${scheme} request status code = ${res.statusCode}`)
);
}
const bufferArr = [];
res.on("data", chunk => {
bufferArr.push(chunk);
});
res.on("end", () => {
if (!res.complete) {
return callback(new Error(`${scheme} request was terminated`));
}
callback(null, Buffer.concat(bufferArr));
});
});
};
normalModuleFactory.hooks.resolveForScheme
.for("http")
.tap("HttpUriPlugin", resolveHandler);
normalModuleFactory.hooks.resolveForScheme
.for("https")
.tap("HttpUriPlugin", resolveHandler);
NormalModule.getCompilationHooks(compilation)
.readResourceForScheme.for("http")
.tapAsync(
"HttpUriPlugin",
readHandler(
"http",
memorize(() => require("http"))
)
);
NormalModule.getCompilationHooks(compilation)
.readResourceForScheme.for("https")
.tapAsync(
"HttpUriPlugin",
readHandler(
"https",
memorize(() => require("https"))
)
);
}
);
}
}
module.exports = HttpUriPlugin;

View File

@ -5,8 +5,6 @@
"use strict";
const { getMimetype: getDataUrlMimetype } = require("./DataURI");
/** @typedef {import("./fs").InputFileSystem} InputFileSystem */
/** @typedef {(error: Error|null, result?: Buffer) => void} ErrorFirstCallback */
@ -85,20 +83,5 @@ function getProtocol(specifier) {
return scheme === undefined ? undefined : scheme + ":";
}
/**
* @param {string} scheme protocol
* @param {string} specifier specifier
* @returns {string} mimetype if exists
*/
function getMimetype(scheme, specifier) {
switch (scheme) {
case "data":
return getDataUrlMimetype(specifier);
default:
return "";
}
}
exports.getScheme = getScheme;
exports.getMimetype = getMimetype;
exports.getProtocol = getProtocol;

View File

@ -243,3 +243,19 @@ const _absolutify = (context, request) => {
const absolutify = makeCacheable(_absolutify);
exports.absolutify = absolutify;
const PATH_QUERY_FRAGMENT_REGEXP = /^([^?#]*)(\?[^#]*)?(#.*)?$/;
/**
* @param {string} str the path with query and fragment
* @returns {{ path: string, query: string, fragment: string }} parsed parts
*/
const parsePathQueryFragment = str => {
const match = PATH_QUERY_FRAGMENT_REGEXP.exec(str);
return {
path: match[1],
query: match[2] || "",
fragment: match[3] || ""
};
};
exports.parsePathQueryFragment = parsePathQueryFragment;

View File

@ -170,7 +170,7 @@ module.exports = {
require("../sharing/ProvideForSharedDependency"),
UnsupportedFeatureWarning: () => require("../UnsupportedFeatureWarning"),
"util/LazySet": () => require("../util/LazySet"),
UnsupportedSchemeError: () => require("../UnsupportedSchemeError"),
UnhandledSchemeError: () => require("../UnhandledSchemeError"),
WebpackError: () => require("../WebpackError"),
"util/registerExternalSerializer": () => {

View File

@ -1,4 +1,4 @@
import cssContent from "http://localhost:9990/index.css";
import cssContent from "http://localhost:9990/index.css?query#fragment";
it("http url request should be supported", () => {
expect(cssContent).toBe("a {}.webpack{}");

View File

@ -1,3 +1,4 @@
const { HttpUriPlugin } = require("../../../../").experiments.schemes;
const ServerPlugin = require("./server");
/** @type {import("../../../../").Configuration} */
@ -11,5 +12,5 @@ module.exports = {
}
]
},
plugins: [new ServerPlugin(9990)]
plugins: [new ServerPlugin(9990), new HttpUriPlugin()]
};

View File

@ -1,3 +1,5 @@
const { HttpUriPlugin } = require("../../../../").experiments.schemes;
/** @type {import("../../../../").Configuration} */
module.exports = {
mode: "development",
@ -8,5 +10,6 @@ module.exports = {
loader: "./loaders/md-loader"
}
]
}
},
plugins: [new HttpUriPlugin()]
};

36
types.d.ts vendored
View File

@ -2858,6 +2858,14 @@ declare class HotModuleReplacementPlugin {
apply(compiler: Compiler): void;
static getParserHooks(parser: JavascriptParser): HMRJavascriptParserHooks;
}
declare class HttpUriPlugin {
constructor();
/**
* Apply the plugin
*/
apply(compiler: Compiler): void;
}
declare class IgnorePlugin {
constructor(options: IgnorePluginOptions);
options: IgnorePluginOptions;
@ -4440,10 +4448,6 @@ declare class NormalModule extends Module {
* the generator used
*/
generator: Generator;
/**
* scheme
*/
scheme?: string;
/**
* options used for resolving requests from this module
*/
@ -4457,7 +4461,6 @@ declare class NormalModule extends Module {
generator: Generator;
resource: string;
matchResource: string;
scheme: string;
loaders: LoaderItem[];
error: WebpackError;
createSourceForAsset(
@ -4497,10 +4500,28 @@ declare class NormalModule extends Module {
}
declare interface NormalModuleCompilationHooks {
loader: SyncHook<[any, NormalModule], void>;
readResourceForScheme: HookMap<
AsyncSeriesBailHook<[string, NormalModule], string | Buffer>
>;
}
declare abstract class NormalModuleFactory extends ModuleFactory {
hooks: Readonly<{
resolve: AsyncSeriesBailHook<[ResolveData], any>;
resolveForScheme: HookMap<
AsyncSeriesBailHook<
[
{
resource: string;
path: string;
query: string;
fragment: string;
data: Record<string, any>;
},
ResolveData
],
true | void
>
>;
factorize: AsyncSeriesBailHook<[ResolveData], any>;
beforeResolve: AsyncSeriesBailHook<[ResolveData], any>;
afterResolve: AsyncSeriesBailHook<[ResolveData], any>;
@ -8617,6 +8638,11 @@ declare namespace exports {
export { MEASURE_START_OPERATION, MEASURE_END_OPERATION };
}
}
export namespace experiments {
export namespace schemes {
export { HttpUriPlugin };
}
}
export type WebpackPluginFunction = (
this: Compiler,
compiler: Compiler