| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const asyncLib = require("neo-async");const path = require("path");const {	Tapable,	AsyncSeriesWaterfallHook,	SyncWaterfallHook} = require("tapable");const ContextModule = require("./ContextModule");const ContextElementDependency = require("./dependencies/ContextElementDependency");/** @typedef {import("./Module")} Module */const EMPTY_RESOLVE_OPTIONS = {};module.exports = class ContextModuleFactory extends Tapable {	constructor(resolverFactory) {		super();		this.hooks = {			/** @type {AsyncSeriesWaterfallHook<TODO>} */			beforeResolve: new AsyncSeriesWaterfallHook(["data"]),			/** @type {AsyncSeriesWaterfallHook<TODO>} */			afterResolve: new AsyncSeriesWaterfallHook(["data"]),			/** @type {SyncWaterfallHook<string[]>} */			contextModuleFiles: new SyncWaterfallHook(["files"]),			/** @type {SyncWaterfallHook<TODO[]>} */			alternatives: new AsyncSeriesWaterfallHook(["modules"])		};		this._pluginCompat.tap("ContextModuleFactory", options => {			switch (options.name) {				case "before-resolve":				case "after-resolve":				case "alternatives":					options.async = true;					break;			}		});		this.resolverFactory = resolverFactory;	}	create(data, callback) {		const context = data.context;		const dependencies = data.dependencies;		const resolveOptions = data.resolveOptions;		const dependency = dependencies[0];		this.hooks.beforeResolve.callAsync(			Object.assign(				{					context: context,					dependencies: dependencies,					resolveOptions				},				dependency.options			),			(err, beforeResolveResult) => {				if (err) return callback(err);				// Ignored				if (!beforeResolveResult) return callback();				const context = beforeResolveResult.context;				const request = beforeResolveResult.request;				const resolveOptions = beforeResolveResult.resolveOptions;				let loaders,					resource,					loadersPrefix = "";				const idx = request.lastIndexOf("!");				if (idx >= 0) {					let loadersRequest = request.substr(0, idx + 1);					let i;					for (						i = 0;						i < loadersRequest.length && loadersRequest[i] === "!";						i++					) {						loadersPrefix += "!";					}					loadersRequest = loadersRequest						.substr(i)						.replace(/!+$/, "")						.replace(/!!+/g, "!");					if (loadersRequest === "") {						loaders = [];					} else {						loaders = loadersRequest.split("!");					}					resource = request.substr(idx + 1);				} else {					loaders = [];					resource = request;				}				const contextResolver = this.resolverFactory.get(					"context",					resolveOptions || EMPTY_RESOLVE_OPTIONS				);				const loaderResolver = this.resolverFactory.get(					"loader",					EMPTY_RESOLVE_OPTIONS				);				asyncLib.parallel(					[						callback => {							contextResolver.resolve(								{},								context,								resource,								{},								(err, result) => {									if (err) return callback(err);									callback(null, result);								}							);						},						callback => {							asyncLib.map(								loaders,								(loader, callback) => {									loaderResolver.resolve(										{},										context,										loader,										{},										(err, result) => {											if (err) return callback(err);											callback(null, result);										}									);								},								callback							);						}					],					(err, result) => {						if (err) return callback(err);						this.hooks.afterResolve.callAsync(							Object.assign(								{									addon:										loadersPrefix +										result[1].join("!") +										(result[1].length > 0 ? "!" : ""),									resource: result[0],									resolveDependencies: this.resolveDependencies.bind(this)								},								beforeResolveResult							),							(err, result) => {								if (err) return callback(err);								// Ignored								if (!result) return callback();								return callback(									null,									new ContextModule(result.resolveDependencies, result)								);							}						);					}				);			}		);	}	resolveDependencies(fs, options, callback) {		const cmf = this;		let resource = options.resource;		let resourceQuery = options.resourceQuery;		let recursive = options.recursive;		let regExp = options.regExp;		let include = options.include;		let exclude = options.exclude;		if (!regExp || !resource) return callback(null, []);		const addDirectory = (directory, callback) => {			fs.readdir(directory, (err, files) => {				if (err) return callback(err);				files = cmf.hooks.contextModuleFiles.call(files);				if (!files || files.length === 0) return callback(null, []);				asyncLib.map(					files.filter(p => p.indexOf(".") !== 0),					(segment, callback) => {						const subResource = path.join(directory, segment);						if (!exclude || !subResource.match(exclude)) {							fs.stat(subResource, (err, stat) => {								if (err) {									if (err.code === "ENOENT") {										// ENOENT is ok here because the file may have been deleted between										// the readdir and stat calls.										return callback();									} else {										return callback(err);									}								}								if (stat.isDirectory()) {									if (!recursive) return callback();									addDirectory.call(this, subResource, callback);								} else if (									stat.isFile() &&									(!include || subResource.match(include))								) {									const obj = {										context: resource,										request:											"." +											subResource.substr(resource.length).replace(/\\/g, "/")									};									this.hooks.alternatives.callAsync(										[obj],										(err, alternatives) => {											if (err) return callback(err);											alternatives = alternatives												.filter(obj => regExp.test(obj.request))												.map(obj => {													const dep = new ContextElementDependency(														obj.request + resourceQuery,														obj.request													);													dep.optional = true;													return dep;												});											callback(null, alternatives);										}									);								} else {									callback();								}							});						} else {							callback();						}					},					(err, result) => {						if (err) return callback(err);						if (!result) return callback(null, []);						callback(							null,							result.filter(Boolean).reduce((a, i) => a.concat(i), [])						);					}				);			});		};		addDirectory(resource, callback);	}};
 |