mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			249 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
| 	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 { 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 {
 | |
| 	constructor(resolverFactory) {
 | |
| 		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.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);
 | |
| 	}
 | |
| };
 |