UNPKG

9.93 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.PreserveModuleNamePlugin = exports.preserveModuleName = void 0;
4const path = require("path");
5const logger_1 = require("./logger");
6exports.preserveModuleName = Symbol();
7const TAP_NAME = "Aurelia:PreserveModuleName";
8const logger = (0, logger_1.createLogger)('PreserveModuleNamePlugin');
9// This plugins preserves the module names of IncludeDependency and
10// AureliaDependency so that they can be dynamically requested by
11// aurelia-loader.
12// All other dependencies are handled by webpack itself and don't
13// need special treatment.
14class PreserveModuleNamePlugin {
15 constructor(isDll = false) {
16 this.isDll = isDll;
17 }
18 apply(compiler) {
19 compiler.hooks.compilation.tap(TAP_NAME, compilation => {
20 compilation.hooks.beforeModuleIds.tap(TAP_NAME, $modules => {
21 let modules = Array.from($modules);
22 let { modules: m, extensions: e, alias: a } = compilation.options.resolve;
23 let roots = m;
24 let extensions = e;
25 // if it's not an object, it's pretty hard to guess how to map to common usage of alias
26 // temporarily not handle anything that is not a record of aliases
27 let alias = a == null || a instanceof Array ? {} : a;
28 roots = roots.map(x => path.resolve(x));
29 const normalizers = extensions.map(x => new RegExp(x.replace(/\./g, "\\.") + "$", "i"));
30 // ModuleConcatenationPlugin merges modules into new ConcatenatedModule
31 let modulesBeforeConcat = modules.slice();
32 for (let i = 0; i < modulesBeforeConcat.length; i++) {
33 let m = modulesBeforeConcat[i];
34 // TODO: verify if this still works
35 // ==================================================
36 // We don't `import ConcatenatedModule` and then `m instanceof ConcatenatedModule`
37 // because it was introduced in Webpack 3.0 and we're still compatible with 2.x at the moment.
38 if (m.constructor.name === "ConcatenatedModule")
39 modulesBeforeConcat.splice(i--, 1, ...m["modules"]);
40 }
41 for (let module of getPreservedModules(modules, compilation)) {
42 // Even though it's imported by Aurelia, it's still possible that the module
43 // became the _root_ of a ConcatenatedModule.
44 // We use `constructor.name` rather than `instanceof` for compat. with Webpack 2.
45 let realModule = module;
46 if (module.constructor.name === "ConcatenatedModule")
47 realModule = module["rootModule"];
48 let preserve = realModule[exports.preserveModuleName];
49 let id = typeof preserve === "string" ? preserve : null;
50 // No absolute request to preserve, we try to normalize the module resource
51 if (!id && realModule.resource)
52 id = fixNodeModule(realModule, modulesBeforeConcat) ||
53 makeModuleRelative(roots, realModule.resource) ||
54 aliasRelative(alias, realModule.resource);
55 if (!id)
56 throw new Error(`Can't figure out a normalized module name for ${realModule.rawRequest}, please call PLATFORM.moduleName() somewhere to help.`);
57 // Remove default extensions
58 normalizers.forEach(n => id = id.replace(n, ""));
59 // Keep "async!" in front of code splits proxies, they are used by aurelia-loader
60 if (/^async[?!]/.test(realModule.rawRequest))
61 id = "async!" + id;
62 id = id.replace(/\\/g, "/");
63 if (module.buildMeta) // meta can be null if the module contains errors
64 module.buildMeta["aurelia-id"] = id;
65 if (!this.isDll) {
66 compilation.chunkGraph.setModuleId(module, id);
67 }
68 }
69 });
70 });
71 }
72}
73exports.PreserveModuleNamePlugin = PreserveModuleNamePlugin;
74;
75function getPreservedModules(modules, compilation) {
76 return new Set(modules.filter(m => {
77 var _a;
78 // Some modules might have [preserveModuleName] already set, see ConventionDependenciesPlugin.
79 let value = m[exports.preserveModuleName];
80 for (let connection of compilation.moduleGraph.getIncomingConnections(m)) {
81 // todo: verify against commented code below
82 if (!((_a = connection === null || connection === void 0 ? void 0 : connection.dependency) === null || _a === void 0 ? void 0 : _a[exports.preserveModuleName])) {
83 continue;
84 }
85 value = true;
86 let req = removeLoaders(connection.dependency.request);
87 // We try to find an absolute string and set that as the module [preserveModuleName], as it's the best id.
88 if (req && !req.startsWith(".")) {
89 m[exports.preserveModuleName] = req;
90 return true;
91 }
92 }
93 return !!value;
94 }));
95}
96function aliasRelative(aliases, resource) {
97 // We consider that aliases point to local folder modules.
98 // For example: `"my-lib": "../my-lib/src"`.
99 // Basically we try to make the resource relative to the alias target,
100 // and if it works we build the id from the alias name.
101 // So file `../my-lib/src/index.js` becomes `my-lib/index.js`.
102 // Note that this only works with aliases pointing to folders, not files.
103 // To have a "default" file in the folder, the following construct works:
104 // alias: { "mod$": "src/index.js", "mod": "src" }
105 if (!aliases)
106 return null;
107 for (let name in aliases) {
108 let target = aliases[name];
109 // TODO:
110 // not sure how to handle anything other than a simple mapping yet
111 // just ignore for now
112 if (typeof target !== 'string')
113 continue;
114 let root = path.resolve(target);
115 let relative = path.relative(root, resource);
116 if (relative.startsWith(".."))
117 continue;
118 name = name.replace(/\$$/, ""); // A trailing $ indicates an exact match in webpack
119 return relative ? name + "/" + relative : name;
120 }
121 return null;
122}
123function makeModuleRelative(roots, resource) {
124 for (let root of roots) {
125 let relative = path.relative(root, resource);
126 if (!relative.startsWith(".."))
127 return relative;
128 }
129 return null;
130}
131function fixNodeModule(module, allModules) {
132 if (!/\bnode_modules\b/i.test(module.resource))
133 return null;
134 // The problem with node_modules is that often the root of the module is not /node_modules/my-lib
135 // Webpack is going to look for `main` in `project.json` to find where the main file actually is.
136 // And this can of course be configured differently with `resolve.alias`, `resolve.mainFields` & co.
137 // Our best hope is that the file was not required as a relative path, then we can just preserve that.
138 // We just need to be careful with loaders (e.g. async!)
139 let request = removeLoaders(module.rawRequest); // we assume that Aurelia dependencies always have a rawRequest
140 if (!request.startsWith("."))
141 return request;
142 // Otherwise we need to build the relative path from the module root, which as explained above is hard to find.
143 // Ideally we could use webpack resolvers, but they are currently async-only, which can't be used in before-modules-id
144 // See https://github.com/webpack/webpack/issues/1634
145 // Instead, we'll look for the root library module, because it should have been requested somehow and work from there.
146 // Note that the negative lookahead (?!.*node_modules) ensures that we only match the last node_modules/ folder in the path,
147 // in case the package was located in a sub-node_modules (which can occur in special circumstances).
148 // We also need to take care of scoped modules. If the name starts with @ we must keep two parts,
149 // so @corp/bar is the proper module name.
150 let name = /\bnode_modules[\\/](?!.*\bnode_modules\b)((?:@[^\\/]+[\\/])?[^\\/]+)/i.exec(module.resource)[1];
151 if (!name) {
152 logger.log('issue while fixing node modules: not a node module', module.resource);
153 return;
154 }
155 name = name.replace("\\", "/"); // normalize \ to / for scoped modules
156 let entry = allModules.find(m => removeLoaders(m.rawRequest) === name);
157 if (entry)
158 return name + "/" + path.relative(path.dirname(entry.resource), module.resource);
159 // We could not find the raw module. Let's try to find another a more complex entry point.
160 for (let m of allModules) {
161 let req = removeLoaders(m.rawRequest);
162 if (!req || !req.startsWith(name) || !m.resource)
163 continue;
164 let i = m.resource.replace(/\\/g, "/").lastIndexOf(req.substr(name.length));
165 if (i < 0)
166 continue;
167 let root = m.resource.substr(0, i);
168 return name + "/" + path.relative(root, module.resource);
169 }
170 throw new Error("PreserveModuleNamePlugin: Unable to find root of module " + name);
171}
172function removeLoaders(request) {
173 // We have to be careful, as it seems that in the allModules.find() call above
174 // some modules might have m.rawRequst === undefined
175 if (!request)
176 return request;
177 let lastBang = request.lastIndexOf("!");
178 return lastBang < 0 ? request : request.substr(lastBang + 1);
179}