aboutsummaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/NormalModuleFactory.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/webpack/lib/NormalModuleFactory.js')
-rw-r--r--node_modules/webpack/lib/NormalModuleFactory.js526
1 files changed, 526 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/NormalModuleFactory.js b/node_modules/webpack/lib/NormalModuleFactory.js
new file mode 100644
index 00000000..23ef0ae1
--- /dev/null
+++ b/node_modules/webpack/lib/NormalModuleFactory.js
@@ -0,0 +1,526 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+"use strict";
+
+const path = require("path");
+const asyncLib = require("neo-async");
+const {
+ Tapable,
+ AsyncSeriesWaterfallHook,
+ SyncWaterfallHook,
+ SyncBailHook,
+ SyncHook,
+ HookMap
+} = require("tapable");
+const NormalModule = require("./NormalModule");
+const RawModule = require("./RawModule");
+const RuleSet = require("./RuleSet");
+const cachedMerge = require("./util/cachedMerge");
+
+const EMPTY_RESOLVE_OPTIONS = {};
+
+const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
+
+const loaderToIdent = data => {
+ if (!data.options) {
+ return data.loader;
+ }
+ if (typeof data.options === "string") {
+ return data.loader + "?" + data.options;
+ }
+ if (typeof data.options !== "object") {
+ throw new Error("loader options must be string or object");
+ }
+ if (data.ident) {
+ return data.loader + "??" + data.ident;
+ }
+ return data.loader + "?" + JSON.stringify(data.options);
+};
+
+const identToLoaderRequest = resultString => {
+ const idx = resultString.indexOf("?");
+ if (idx >= 0) {
+ const loader = resultString.substr(0, idx);
+ const options = resultString.substr(idx + 1);
+ return {
+ loader,
+ options
+ };
+ } else {
+ return {
+ loader: resultString,
+ options: undefined
+ };
+ }
+};
+
+const dependencyCache = new WeakMap();
+
+class NormalModuleFactory extends Tapable {
+ constructor(context, resolverFactory, options) {
+ super();
+ this.hooks = {
+ resolver: new SyncWaterfallHook(["resolver"]),
+ factory: new SyncWaterfallHook(["factory"]),
+ beforeResolve: new AsyncSeriesWaterfallHook(["data"]),
+ afterResolve: new AsyncSeriesWaterfallHook(["data"]),
+ createModule: new SyncBailHook(["data"]),
+ module: new SyncWaterfallHook(["module", "data"]),
+ createParser: new HookMap(() => new SyncBailHook(["parserOptions"])),
+ parser: new HookMap(() => new SyncHook(["parser", "parserOptions"])),
+ createGenerator: new HookMap(
+ () => new SyncBailHook(["generatorOptions"])
+ ),
+ generator: new HookMap(
+ () => new SyncHook(["generator", "generatorOptions"])
+ )
+ };
+ this._pluginCompat.tap("NormalModuleFactory", options => {
+ switch (options.name) {
+ case "before-resolve":
+ case "after-resolve":
+ options.async = true;
+ break;
+ case "parser":
+ this.hooks.parser
+ .for("javascript/auto")
+ .tap(options.fn.name || "unnamed compat plugin", options.fn);
+ return true;
+ }
+ let match;
+ match = /^parser (.+)$/.exec(options.name);
+ if (match) {
+ this.hooks.parser
+ .for(match[1])
+ .tap(
+ options.fn.name || "unnamed compat plugin",
+ options.fn.bind(this)
+ );
+ return true;
+ }
+ match = /^create-parser (.+)$/.exec(options.name);
+ if (match) {
+ this.hooks.createParser
+ .for(match[1])
+ .tap(
+ options.fn.name || "unnamed compat plugin",
+ options.fn.bind(this)
+ );
+ return true;
+ }
+ });
+ this.resolverFactory = resolverFactory;
+ this.ruleSet = new RuleSet(options.defaultRules.concat(options.rules));
+ this.cachePredicate =
+ typeof options.unsafeCache === "function"
+ ? options.unsafeCache
+ : Boolean.bind(null, options.unsafeCache);
+ this.context = context || "";
+ this.parserCache = Object.create(null);
+ this.generatorCache = Object.create(null);
+ this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
+ let resolver = this.hooks.resolver.call(null);
+
+ // Ignored
+ if (!resolver) return callback();
+
+ resolver(result, (err, data) => {
+ if (err) return callback(err);
+
+ // Ignored
+ if (!data) return callback();
+
+ // direct module
+ if (typeof data.source === "function") return callback(null, data);
+
+ this.hooks.afterResolve.callAsync(data, (err, result) => {
+ if (err) return callback(err);
+
+ // Ignored
+ if (!result) return callback();
+
+ let createdModule = this.hooks.createModule.call(result);
+ if (!createdModule) {
+ if (!result.request) {
+ return callback(new Error("Empty dependency (no request)"));
+ }
+
+ createdModule = new NormalModule(result);
+ }
+
+ createdModule = this.hooks.module.call(createdModule, result);
+
+ return callback(null, createdModule);
+ });
+ });
+ });
+ this.hooks.resolver.tap("NormalModuleFactory", () => (data, callback) => {
+ const contextInfo = data.contextInfo;
+ const context = data.context;
+ const request = data.request;
+
+ const loaderResolver = this.getResolver("loader");
+ const normalResolver = this.getResolver("normal", data.resolveOptions);
+
+ let matchResource = undefined;
+ let requestWithoutMatchResource = request;
+ const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
+ if (matchResourceMatch) {
+ matchResource = matchResourceMatch[1];
+ if (/^\.\.?\//.test(matchResource)) {
+ matchResource = path.join(context, matchResource);
+ }
+ requestWithoutMatchResource = request.substr(
+ matchResourceMatch[0].length
+ );
+ }
+
+ const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!");
+ const noAutoLoaders =
+ noPreAutoLoaders || requestWithoutMatchResource.startsWith("!");
+ const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!");
+ let elements = requestWithoutMatchResource
+ .replace(/^-?!+/, "")
+ .replace(/!!+/g, "!")
+ .split("!");
+ let resource = elements.pop();
+ elements = elements.map(identToLoaderRequest);
+
+ asyncLib.parallel(
+ [
+ callback =>
+ this.resolveRequestArray(
+ contextInfo,
+ context,
+ elements,
+ loaderResolver,
+ callback
+ ),
+ callback => {
+ if (resource === "" || resource[0] === "?") {
+ return callback(null, {
+ resource
+ });
+ }
+
+ normalResolver.resolve(
+ contextInfo,
+ context,
+ resource,
+ {},
+ (err, resource, resourceResolveData) => {
+ if (err) return callback(err);
+ callback(null, {
+ resourceResolveData,
+ resource
+ });
+ }
+ );
+ }
+ ],
+ (err, results) => {
+ if (err) return callback(err);
+ let loaders = results[0];
+ const resourceResolveData = results[1].resourceResolveData;
+ resource = results[1].resource;
+
+ // translate option idents
+ try {
+ for (const item of loaders) {
+ if (typeof item.options === "string" && item.options[0] === "?") {
+ const ident = item.options.substr(1);
+ item.options = this.ruleSet.findOptionsByIdent(ident);
+ item.ident = ident;
+ }
+ }
+ } catch (e) {
+ return callback(e);
+ }
+
+ if (resource === false) {
+ // ignored
+ return callback(
+ null,
+ new RawModule(
+ "/* (ignored) */",
+ `ignored ${context} ${request}`,
+ `${request} (ignored)`
+ )
+ );
+ }
+
+ const userRequest =
+ (matchResource !== undefined ? `${matchResource}!=!` : "") +
+ loaders
+ .map(loaderToIdent)
+ .concat([resource])
+ .join("!");
+
+ let resourcePath =
+ matchResource !== undefined ? matchResource : resource;
+ let resourceQuery = "";
+ const queryIndex = resourcePath.indexOf("?");
+ if (queryIndex >= 0) {
+ resourceQuery = resourcePath.substr(queryIndex);
+ resourcePath = resourcePath.substr(0, queryIndex);
+ }
+
+ const result = this.ruleSet.exec({
+ resource: resourcePath,
+ realResource:
+ matchResource !== undefined
+ ? resource.replace(/\?.*/, "")
+ : resourcePath,
+ resourceQuery,
+ issuer: contextInfo.issuer,
+ compiler: contextInfo.compiler
+ });
+ const settings = {};
+ const useLoadersPost = [];
+ const useLoaders = [];
+ const useLoadersPre = [];
+ for (const r of result) {
+ if (r.type === "use") {
+ if (r.enforce === "post" && !noPrePostAutoLoaders) {
+ useLoadersPost.push(r.value);
+ } else if (
+ r.enforce === "pre" &&
+ !noPreAutoLoaders &&
+ !noPrePostAutoLoaders
+ ) {
+ useLoadersPre.push(r.value);
+ } else if (
+ !r.enforce &&
+ !noAutoLoaders &&
+ !noPrePostAutoLoaders
+ ) {
+ useLoaders.push(r.value);
+ }
+ } else if (
+ typeof r.value === "object" &&
+ r.value !== null &&
+ typeof settings[r.type] === "object" &&
+ settings[r.type] !== null
+ ) {
+ settings[r.type] = cachedMerge(settings[r.type], r.value);
+ } else {
+ settings[r.type] = r.value;
+ }
+ }
+ asyncLib.parallel(
+ [
+ this.resolveRequestArray.bind(
+ this,
+ contextInfo,
+ this.context,
+ useLoadersPost,
+ loaderResolver
+ ),
+ this.resolveRequestArray.bind(
+ this,
+ contextInfo,
+ this.context,
+ useLoaders,
+ loaderResolver
+ ),
+ this.resolveRequestArray.bind(
+ this,
+ contextInfo,
+ this.context,
+ useLoadersPre,
+ loaderResolver
+ )
+ ],
+ (err, results) => {
+ if (err) return callback(err);
+ loaders = results[0].concat(loaders, results[1], results[2]);
+ process.nextTick(() => {
+ const type = settings.type;
+ const resolveOptions = settings.resolve;
+ callback(null, {
+ context: context,
+ request: loaders
+ .map(loaderToIdent)
+ .concat([resource])
+ .join("!"),
+ dependencies: data.dependencies,
+ userRequest,
+ rawRequest: request,
+ loaders,
+ resource,
+ matchResource,
+ resourceResolveData,
+ settings,
+ type,
+ parser: this.getParser(type, settings.parser),
+ generator: this.getGenerator(type, settings.generator),
+ resolveOptions
+ });
+ });
+ }
+ );
+ }
+ );
+ });
+ }
+
+ create(data, callback) {
+ const dependencies = data.dependencies;
+ const cacheEntry = dependencyCache.get(dependencies[0]);
+ if (cacheEntry) return callback(null, cacheEntry);
+ const context = data.context || this.context;
+ const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS;
+ const request = dependencies[0].request;
+ const contextInfo = data.contextInfo || {};
+ this.hooks.beforeResolve.callAsync(
+ {
+ contextInfo,
+ resolveOptions,
+ context,
+ request,
+ dependencies
+ },
+ (err, result) => {
+ if (err) return callback(err);
+
+ // Ignored
+ if (!result) return callback();
+
+ const factory = this.hooks.factory.call(null);
+
+ // Ignored
+ if (!factory) return callback();
+
+ factory(result, (err, module) => {
+ if (err) return callback(err);
+
+ if (module && this.cachePredicate(module)) {
+ for (const d of dependencies) {
+ dependencyCache.set(d, module);
+ }
+ }
+
+ callback(null, module);
+ });
+ }
+ );
+ }
+
+ resolveRequestArray(contextInfo, context, array, resolver, callback) {
+ if (array.length === 0) return callback(null, []);
+ asyncLib.map(
+ array,
+ (item, callback) => {
+ resolver.resolve(
+ contextInfo,
+ context,
+ item.loader,
+ {},
+ (err, result) => {
+ if (
+ err &&
+ /^[^/]*$/.test(item.loader) &&
+ !/-loader$/.test(item.loader)
+ ) {
+ return resolver.resolve(
+ contextInfo,
+ context,
+ item.loader + "-loader",
+ {},
+ err2 => {
+ if (!err2) {
+ err.message =
+ err.message +
+ "\n" +
+ "BREAKING CHANGE: It's no longer allowed to omit the '-loader' suffix when using loaders.\n" +
+ ` You need to specify '${
+ item.loader
+ }-loader' instead of '${item.loader}',\n` +
+ " see https://webpack.js.org/migrate/3/#automatic-loader-module-name-extension-removed";
+ }
+ callback(err);
+ }
+ );
+ }
+ if (err) return callback(err);
+
+ const optionsOnly = item.options
+ ? {
+ options: item.options
+ }
+ : undefined;
+ return callback(
+ null,
+ Object.assign({}, item, identToLoaderRequest(result), optionsOnly)
+ );
+ }
+ );
+ },
+ callback
+ );
+ }
+
+ getParser(type, parserOptions) {
+ let ident = type;
+ if (parserOptions) {
+ if (parserOptions.ident) {
+ ident = `${type}|${parserOptions.ident}`;
+ } else {
+ ident = JSON.stringify([type, parserOptions]);
+ }
+ }
+ if (ident in this.parserCache) {
+ return this.parserCache[ident];
+ }
+ return (this.parserCache[ident] = this.createParser(type, parserOptions));
+ }
+
+ createParser(type, parserOptions = {}) {
+ const parser = this.hooks.createParser.for(type).call(parserOptions);
+ if (!parser) {
+ throw new Error(`No parser registered for ${type}`);
+ }
+ this.hooks.parser.for(type).call(parser, parserOptions);
+ return parser;
+ }
+
+ getGenerator(type, generatorOptions) {
+ let ident = type;
+ if (generatorOptions) {
+ if (generatorOptions.ident) {
+ ident = `${type}|${generatorOptions.ident}`;
+ } else {
+ ident = JSON.stringify([type, generatorOptions]);
+ }
+ }
+ if (ident in this.generatorCache) {
+ return this.generatorCache[ident];
+ }
+ return (this.generatorCache[ident] = this.createGenerator(
+ type,
+ generatorOptions
+ ));
+ }
+
+ createGenerator(type, generatorOptions = {}) {
+ const generator = this.hooks.createGenerator
+ .for(type)
+ .call(generatorOptions);
+ if (!generator) {
+ throw new Error(`No generator registered for ${type}`);
+ }
+ this.hooks.generator.for(type).call(generator, generatorOptions);
+ return generator;
+ }
+
+ getResolver(type, resolveOptions) {
+ return this.resolverFactory.get(
+ type,
+ resolveOptions || EMPTY_RESOLVE_OPTIONS
+ );
+ }
+}
+
+module.exports = NormalModuleFactory;