123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
-
- const asyncLib = require("async");
- const Tapable = require("tapable");
- const NormalModule = require("./NormalModule");
- const RawModule = require("./RawModule");
- const Parser = require("./Parser");
- const RuleSet = require("./RuleSet");
-
- function 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);
- }
-
- function identToLoaderRequest(resultString) {
- const idx = resultString.indexOf("?");
- let options;
-
- if(idx >= 0) {
- options = resultString.substr(idx + 1);
- resultString = resultString.substr(0, idx);
-
- return {
- loader: resultString,
- options
- };
- } else {
- return {
- loader: resultString
- };
- }
- }
-
- class NormalModuleFactory extends Tapable {
- constructor(context, resolvers, options) {
- super();
- this.resolvers = resolvers;
- this.ruleSet = new RuleSet(options.rules || options.loaders);
- this.cachePredicate = typeof options.unsafeCache === "function" ? options.unsafeCache : Boolean.bind(null, options.unsafeCache);
- this.context = context || "";
- this.parserCache = {};
- this.plugin("factory", () => (result, callback) => {
- let resolver = this.applyPluginsWaterfall0("resolver", 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.applyPluginsAsyncWaterfall("after-resolve", data, (err, result) => {
- if(err) return callback(err);
-
- // Ignored
- if(!result) return callback();
-
- let createdModule = this.applyPluginsBailResult("create-module", result);
- if(!createdModule) {
-
- if(!result.request) {
- return callback(new Error("Empty dependency (no request)"));
- }
-
- createdModule = new NormalModule(
- result.request,
- result.userRequest,
- result.rawRequest,
- result.loaders,
- result.resource,
- result.parser
- );
- }
-
- createdModule = this.applyPluginsWaterfall0("module", createdModule);
-
- return callback(null, createdModule);
- });
- });
- });
- this.plugin("resolver", () => (data, callback) => {
- const contextInfo = data.contextInfo;
- const context = data.context;
- const request = data.request;
-
- const noAutoLoaders = /^-?!/.test(request);
- const noPrePostAutoLoaders = /^!!/.test(request);
- const noPostAutoLoaders = /^-!/.test(request);
- let elements = request.replace(/^-?!+/, "").replace(/!!+/g, "!").split("!");
- let resource = elements.pop();
- elements = elements.map(identToLoaderRequest);
-
- asyncLib.parallel([
- callback => this.resolveRequestArray(contextInfo, context, elements, this.resolvers.loader, callback),
- callback => {
- if(resource === "" || resource[0] === "?")
- return callback(null, {
- resource
- });
-
- this.resolvers.normal.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 {
- loaders.forEach(item => {
- if(typeof item.options === "string" && /^\?/.test(item.options)) {
- item.options = this.ruleSet.findOptionsByIdent(item.options.substr(1));
- }
- });
- } catch(e) {
- return callback(e);
- }
-
- if(resource === false) {
- // ignored
- return callback(null,
- new RawModule(
- "/* (ignored) */",
- `ignored ${context} ${request}`,
- `${request} (ignored)`
- )
- );
- }
-
- const userRequest = loaders.map(loaderToIdent).concat([resource]).join("!");
-
- let resourcePath = 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,
- resourceQuery,
- issuer: contextInfo.issuer,
- compiler: contextInfo.compiler
- });
- const settings = {};
- const useLoadersPost = [];
- const useLoaders = [];
- const useLoadersPre = [];
- result.forEach(r => {
- if(r.type === "use") {
- if(r.enforce === "post" && !noPostAutoLoaders && !noPrePostAutoLoaders)
- useLoadersPost.push(r.value);
- else if(r.enforce === "pre" && !noPrePostAutoLoaders)
- useLoadersPre.push(r.value);
- else if(!r.enforce && !noAutoLoaders && !noPrePostAutoLoaders)
- useLoaders.push(r.value);
- } else {
- settings[r.type] = r.value;
- }
- });
- asyncLib.parallel([
- this.resolveRequestArray.bind(this, contextInfo, this.context, useLoadersPost, this.resolvers.loader),
- this.resolveRequestArray.bind(this, contextInfo, this.context, useLoaders, this.resolvers.loader),
- this.resolveRequestArray.bind(this, contextInfo, this.context, useLoadersPre, this.resolvers.loader)
- ], (err, results) => {
- if(err) return callback(err);
- loaders = results[0].concat(loaders, results[1], results[2]);
- process.nextTick(() => {
- callback(null, {
- context: context,
- request: loaders.map(loaderToIdent).concat([resource]).join("!"),
- dependencies: data.dependencies,
- userRequest,
- rawRequest: request,
- loaders,
- resource,
- resourceResolveData,
- parser: this.getParser(settings.parser)
- });
- });
- });
- });
- });
- }
-
- create(data, callback) {
- const dependencies = data.dependencies;
- const cacheEntry = dependencies[0].__NormalModuleFactoryCache;
- if(cacheEntry) return callback(null, cacheEntry);
- const context = data.context || this.context;
- const request = dependencies[0].request;
- const contextInfo = data.contextInfo || {};
- this.applyPluginsAsyncWaterfall("before-resolve", {
- contextInfo,
- context,
- request,
- dependencies
- }, (err, result) => {
- if(err) return callback(err);
-
- // Ignored
- if(!result) return callback();
-
- const factory = this.applyPluginsWaterfall0("factory", null);
-
- // Ignored
- if(!factory) return callback();
-
- factory(result, (err, module) => {
- if(err) return callback(err);
-
- if(module && this.cachePredicate(module)) {
- dependencies.forEach(d => d.__NormalModuleFactoryCache = 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/guides/migrating/#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(parserOptions) {
- let ident = "null";
- if(parserOptions) {
- if(parserOptions.ident)
- ident = parserOptions.ident;
- else
- ident = JSON.stringify(parserOptions);
- }
- const parser = this.parserCache[ident];
- if(parser)
- return parser;
- return this.parserCache[ident] = this.createParser(parserOptions);
- }
-
- createParser(parserOptions) {
- const parser = new Parser();
- this.applyPlugins2("parser", parser, parserOptions || {});
- return parser;
- }
- }
-
- module.exports = NormalModuleFactory;
|