diff --git a/lib/ContextModule.js b/lib/ContextModule.js index e7170d44b53..297b1de3c43 100644 --- a/lib/ContextModule.js +++ b/lib/ContextModule.js @@ -61,7 +61,7 @@ const makeSerializable = require("./util/makeSerializable"); /** * @typedef {Object} ContextModuleOptionsExtras - * @property {string} resource + * @property {string|string[]} resource * @property {string=} resourceQuery * @property {string=} resourceFragment * @property {TODO} resolveOptions @@ -92,23 +92,36 @@ class ContextModule extends Module { * @param {ContextModuleOptions} options options object */ constructor(resolveDependencies, options) { - const parsed = parseResource(options ? options.resource : ""); - const resource = parsed.path; - const resourceQuery = (options && options.resourceQuery) || parsed.query; - const resourceFragment = - (options && options.resourceFragment) || parsed.fragment; - - super("javascript/dynamic", resource); + if (!options || typeof options.resource === "string") { + const parsed = parseResource( + options ? /** @type {string} */ (options.resource) : "" + ); + const resource = parsed.path; + const resourceQuery = (options && options.resourceQuery) || parsed.query; + const resourceFragment = + (options && options.resourceFragment) || parsed.fragment; + + super("javascript/dynamic", resource); + /** @type {ContextModuleOptions} */ + this.options = { + ...options, + resource, + resourceQuery, + resourceFragment + }; + } else { + super("javascript/dynamic"); + /** @type {ContextModuleOptions} */ + this.options = { + ...options, + resource: options.resource, + resourceQuery: options.resourceQuery || "", + resourceFragment: options.resourceFragment || "" + }; + } // Info from Factory this.resolveDependencies = resolveDependencies; - /** @type {ContextModuleOptions} */ - this.options = { - ...options, - resource, - resourceQuery, - resourceFragment - }; if (options && options.resolveOptions !== undefined) { this.resolveOptions = options.resolveOptions; } @@ -155,7 +168,11 @@ class ContextModule extends Module { } _createIdentifier() { - let identifier = this.context; + let identifier = + this.context || + (typeof this.options.resource === "string" + ? this.options.resource + : this.options.resource.join("|")); if (this.options.resourceQuery) { identifier += `|${this.options.resourceQuery}`; } @@ -220,7 +237,16 @@ class ContextModule extends Module { * @returns {string} a user readable identifier of the module */ readableIdentifier(requestShortener) { - let identifier = requestShortener.shorten(this.context) + "/"; + let identifier; + if (this.context) { + identifier = requestShortener.shorten(this.context) + "/"; + } else if (typeof this.options.resource === "string") { + identifier = requestShortener.shorten(this.options.resource) + "/"; + } else { + identifier = this.options.resource + .map(r => requestShortener.shorten(r) + "/") + .join(" "); + } if (this.options.resourceQuery) { identifier += ` ${this.options.resourceQuery}`; } @@ -270,11 +296,30 @@ class ContextModule extends Module { * @returns {string | null} an identifier for library inclusion */ libIdent(options) { - let identifier = contextify( - options.context, - this.context, - options.associatedObjectForCache - ); + let identifier; + + if (this.context) { + identifier = contextify( + options.context, + this.context, + options.associatedObjectForCache + ); + } else if (typeof this.options.resource === "string") { + identifier = contextify( + options.context, + this.options.resource, + options.associatedObjectForCache + ); + } else { + const arr = []; + for (const res of this.options.resource) { + arr.push( + contextify(options.context, res, options.associatedObjectForCache) + ); + } + identifier = arr.join(" "); + } + if (this.layer) identifier = `(${this.layer})/${identifier}`; if (this.options.mode) { identifier += ` ${this.options.mode}`; @@ -442,7 +487,11 @@ class ContextModule extends Module { compilation.fileSystemInfo.createSnapshot( startTime, null, - [this.context], + this.context + ? [this.context] + : typeof this.options.resource === "string" + ? [this.options.resource] + : this.options.resource, null, SNAPSHOT_OPTIONS, (err, snapshot) => { @@ -466,7 +515,13 @@ class ContextModule extends Module { missingDependencies, buildDependencies ) { - contextDependencies.add(this.context); + if (this.context) { + contextDependencies.add(this.context); + } else if (typeof this.options.resource === "string") { + contextDependencies.add(this.options.resource); + } else { + for (const res of this.options.resource) contextDependencies.add(res); + } } /** diff --git a/lib/ContextModuleFactory.js b/lib/ContextModuleFactory.js index f667e11f87f..31b75b4ba7a 100644 --- a/lib/ContextModuleFactory.js +++ b/lib/ContextModuleFactory.js @@ -167,6 +167,9 @@ module.exports = class ContextModuleFactory extends ModuleFactory { asyncLib.parallel( [ callback => { + const results = []; + const yield_ = obj => results.push(obj); + contextResolver.resolve( {}, context, @@ -174,11 +177,12 @@ module.exports = class ContextModuleFactory extends ModuleFactory { { fileDependencies, missingDependencies, - contextDependencies + contextDependencies, + yield: yield_ }, - (err, result) => { + err => { if (err) return callback(err); - callback(null, result); + callback(null, results); } ); }, @@ -213,15 +217,20 @@ module.exports = class ContextModuleFactory extends ModuleFactory { contextDependencies }); } - + const [contextResult, loaderResult] = result; this.hooks.afterResolve.callAsync( { addon: loadersPrefix + - result[1].join("!") + - (result[1].length > 0 ? "!" : ""), - resource: result[0], + loaderResult.join("!") + + (loaderResult.length > 0 ? "!" : ""), + resource: + contextResult.length > 1 + ? contextResult.map(r => r.path) + : contextResult[0].path, resolveDependencies: this.resolveDependencies.bind(this), + resourceQuery: contextResult[0].query, + resourceFragment: contextResult[0].fragment, ...beforeResolveResult }, (err, result) => { @@ -278,26 +287,28 @@ module.exports = class ContextModuleFactory extends ModuleFactory { } = options; if (!regExp || !resource) return callback(null, []); - const addDirectoryChecked = (directory, visited, callback) => { + let severalContexts = false; + const addDirectoryChecked = (ctx, directory, visited, callback) => { fs.realpath(directory, (err, realPath) => { if (err) return callback(err); if (visited.has(realPath)) return callback(null, []); let recursionStack; addDirectory( + ctx, directory, - (dir, callback) => { + (_, dir, callback) => { if (recursionStack === undefined) { recursionStack = new Set(visited); recursionStack.add(realPath); } - addDirectoryChecked(dir, recursionStack, callback); + addDirectoryChecked(ctx, dir, recursionStack, callback); }, callback ); }); }; - const addDirectory = (directory, addSubDirectory, callback) => { + const addDirectory = (ctx, directory, addSubDirectory, callback) => { fs.readdir(directory, (err, files) => { if (err) return callback(err); const processedFiles = cmf.hooks.contextModuleFiles.call( @@ -324,16 +335,15 @@ module.exports = class ContextModuleFactory extends ModuleFactory { if (stat.isDirectory()) { if (!recursive) return callback(); - addSubDirectory(subResource, callback); + addSubDirectory(ctx, subResource, callback); } else if ( stat.isFile() && (!include || subResource.match(include)) ) { const obj = { - context: resource, + context: ctx, request: - "." + - subResource.substr(resource.length).replace(/\\/g, "/") + "." + subResource.substr(ctx.length).replace(/\\/g, "/") }; this.hooks.alternativeRequests.callAsync( @@ -344,8 +354,11 @@ module.exports = class ContextModuleFactory extends ModuleFactory { alternatives = alternatives .filter(obj => regExp.test(obj.request)) .map(obj => { + const request = severalContexts + ? join(fs, obj.context, obj.request) + : obj.request; const dep = new ContextElementDependency( - obj.request + resourceQuery + resourceFragment, + request + resourceQuery + resourceFragment, obj.request, typePrefix, category, @@ -382,12 +395,38 @@ module.exports = class ContextModuleFactory extends ModuleFactory { }); }; - if (typeof fs.realpath === "function") { - addDirectoryChecked(resource, new Set(), callback); + const addSubDirectory = (ctx, dir, callback) => + addDirectory(ctx, dir, addSubDirectory, callback); + + const visitResource = (resource, callback) => { + if (typeof fs.realpath === "function") { + addDirectoryChecked(resource, resource, new Set(), callback); + } else { + addDirectory(resource, resource, addSubDirectory, callback); + } + }; + + if (typeof resource === "string") { + visitResource(resource, callback); } else { - const addSubDirectory = (dir, callback) => - addDirectory(dir, addSubDirectory, callback); - addDirectory(resource, addSubDirectory, callback); + severalContexts = true; + asyncLib.map(resource, visitResource, (err, result) => { + if (err) return callback(err); + + // result dependencies should have unique userRequest + // ordered by resolve result + const temp = new Set(); + const res = []; + for (let i = 0; i < result.length; i++) { + const inner = result[i]; + for (const el of inner) { + if (temp.has(el.userRequest)) continue; + res.push(el); + temp.add(el.userRequest); + } + } + callback(null, res); + }); } } }; diff --git a/lib/cache/ResolverCachePlugin.js b/lib/cache/ResolverCachePlugin.js index a0c0bbccbdb..7af5e848f0c 100644 --- a/lib/cache/ResolverCachePlugin.js +++ b/lib/cache/ResolverCachePlugin.js @@ -128,6 +128,11 @@ class ResolverCachePlugin { fileDependencies: new LazySet(), contextDependencies: new LazySet() }; + let yieldResult; + if (typeof newResolveContext.yield === "function") { + yieldResult = []; + newResolveContext.yield = obj => yieldResult.push(obj); + } const propagate = key => { if (resolveContext[key]) { addAllToSet(resolveContext[key], newResolveContext[key]); @@ -155,15 +160,19 @@ class ResolverCachePlugin { snapshotOptions, (err, snapshot) => { if (err) return callback(err); + const resolveResult = result || yieldResult; if (!snapshot) { - if (result) return callback(null, result); + if (resolveResult) return callback(null, resolveResult); return callback(); } - itemCache.store(new CacheEntry(result, snapshot), storeErr => { - if (storeErr) return callback(storeErr); - if (result) return callback(null, result); - callback(); - }); + itemCache.store( + new CacheEntry(resolveResult, snapshot), + storeErr => { + if (storeErr) return callback(storeErr); + if (resolveResult) return callback(null, resolveResult); + callback(); + } + ); } ); } @@ -173,6 +182,8 @@ class ResolverCachePlugin { factory(type, hook) { /** @type {Map} */ const activeRequests = new Map(); + /** @type {Map} */ + const activeRequestsWithYield = new Map(); hook.tap( "ResolverCachePlugin", /** @@ -197,29 +208,63 @@ class ResolverCachePlugin { if (request._ResolverCachePluginCacheMiss || !fileSystemInfo) { return callback(); } - const identifier = `${type}${optionsIdent}${objectToString( - request, - !cacheWithContext - )}`; - const activeRequest = activeRequests.get(identifier); - if (activeRequest) { - activeRequest.push(callback); - return; + const withYield = typeof resolveContext.yield === "function"; + const identifier = `${type}${ + withYield ? "|yield" : "|default" + }${optionsIdent}${objectToString(request, !cacheWithContext)}`; + + if (withYield) { + const activeRequest = activeRequestsWithYield.get(identifier); + if (activeRequest) { + activeRequest[0].push(callback); + activeRequest[1].push(resolveContext.yield); + return; + } + } else { + const activeRequest = activeRequests.get(identifier); + if (activeRequest) { + activeRequest.push(callback); + return; + } } const itemCache = cache.getItemCache(identifier, null); - let callbacks; - const done = (err, result) => { - if (callbacks === undefined) { - callback(err, result); - callbacks = false; - } else { - for (const callback of callbacks) { - callback(err, result); - } - activeRequests.delete(identifier); - callbacks = false; - } - }; + let callbacks, yields; + const done = withYield + ? (err, result) => { + if (callbacks === undefined) { + if (err) { + callback(err); + } else { + if (result) + for (const r of result) resolveContext.yield(r); + callback(null, null); + } + yields = undefined; + callbacks = false; + } else { + for (let i = 0; i < callbacks.length; i++) { + const cb = callbacks[i]; + const yield_ = yields[i]; + if (result) for (const r of result) yield_(r); + cb(null, null); + } + activeRequestsWithYield.delete(identifier); + yields = undefined; + callbacks = false; + } + } + : (err, result) => { + if (callbacks === undefined) { + callback(err, result); + callbacks = false; + } else { + for (const callback of callbacks) { + callback(err, result); + } + activeRequests.delete(identifier); + callbacks = false; + } + }; /** * @param {Error=} err error if any * @param {CacheEntry=} cacheEntry cache entry @@ -276,7 +321,14 @@ class ResolverCachePlugin { } }; itemCache.get(processCacheResult); - if (callbacks === undefined) { + if (withYield && callbacks === undefined) { + callbacks = [callback]; + yields = [resolveContext.yield]; + activeRequestsWithYield.set( + identifier, + /** @type {[any, any]} */ ([callbacks, yields]) + ); + } else if (callbacks === undefined) { callbacks = [callback]; activeRequests.set(identifier, callbacks); } diff --git a/package.json b/package.json index 9729347d102..55619e4453a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.8.3", + "enhanced-resolve": "^5.9.0", "es-module-lexer": "^0.9.0", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/test/ContextModule.unittest.js b/test/ContextModule.unittest.js index 4d3e691b111..ae3ab350377 100644 --- a/test/ContextModule.unittest.js +++ b/test/ContextModule.unittest.js @@ -13,10 +13,13 @@ describe("contextModule", () => { contextModule = new ContextModule(() => {}, { type: "javascript/auto", request, + resource: "a", mode: "lazy", regExp: /a|b/ }); - expect(contextModule.identifier()).toContain("/a%7Cb/"); + expect(contextModule.identifier()).toEqual( + expect.stringContaining("/a%7Cb/") + ); }); }); }); diff --git a/test/ContextModuleFactory.unittest.js b/test/ContextModuleFactory.unittest.js index 5ecc9bab59f..1f89e611d4f 100644 --- a/test/ContextModuleFactory.unittest.js +++ b/test/ContextModuleFactory.unittest.js @@ -119,5 +119,42 @@ describe("ContextModuleFactory", () => { } ); }); + + it("should resolve correctly several resources", done => { + memfs.readdir = (dir, callback) => { + if (dir === "/a") setTimeout(() => callback(null, ["/B"])); + if (dir === "/b") setTimeout(() => callback(null, ["/A"])); + if (dir === "/a/B") setTimeout(() => callback(null, ["a"])); + if (dir === "/b/A") setTimeout(() => callback(null, ["b"])); + }; + memfs.stat = (file, callback) => { + const resolvedValue = { + isDirectory: () => file !== "/a/B/a" && file !== "/b/A/b", + isFile: () => file === "/a/B/a" || file === "/b/A/b" + }; + setTimeout(() => callback(null, resolvedValue)); + }; + memfs.realpath = undefined; + factory.resolveDependencies( + memfs, + { + resource: ["/a", "/b"], + resourceFragment: "#hash", + resourceQuery: "?query", + recursive: true, + regExp: /.*/ + }, + (err, res) => { + expect(res).not.toStrictEqual([]); + expect(Array.isArray(res)).toBe(true); + expect(res.map(r => r.request)).toEqual([ + "/a/B/a?query#hash", + "/b/A/b?query#hash" + ]); + expect(res.map(r => r.userRequest)).toEqual(["./B/a", "./A/b"]); + done(); + } + ); + }); }); }); diff --git a/test/__snapshots__/StatsTestCases.basictest.js.snap b/test/__snapshots__/StatsTestCases.basictest.js.snap index 1edf8736b5c..740c357a5cb 100644 --- a/test/__snapshots__/StatsTestCases.basictest.js.snap +++ b/test/__snapshots__/StatsTestCases.basictest.js.snap @@ -737,62 +737,92 @@ exports[`StatsTestCases should print correct stats for concat-and-sideeffects 1` `; exports[`StatsTestCases should print correct stats for context-independence 1`] = ` -"asset main-9eb37fb0850a854f8074.js 10.4 KiB [emitted] [immutable] (name: main) - sourceMap main-9eb37fb0850a854f8074.js.map 9.26 KiB [emitted] [dev] (auxiliary name: main) +"asset main-1aad2f42f93e93c4e0b4.js 12.7 KiB [emitted] [immutable] (name: main) + sourceMap main-1aad2f42f93e93c4e0b4.js.map 11.1 KiB [emitted] [dev] (auxiliary name: main) asset 695-4dd37417c69a0af66bac.js 455 bytes [emitted] [immutable] sourceMap 695-4dd37417c69a0af66bac.js.map 342 bytes [emitted] [dev] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./a/index.js (in Xdir/context-independence/a) 40 bytes [built] [code generated] - ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./a/c/ ./a/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./a/c/a.js 18 bytes [optional] [built] [code generated] + ./a/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/a) 266 bytes + ./a/index.js (in Xdir/context-independence/a) 200 bytes [built] [code generated] + ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms -asset main-9eb37fb0850a854f8074.js 10.4 KiB [emitted] [immutable] (name: main) - sourceMap main-9eb37fb0850a854f8074.js.map 9.26 KiB [emitted] [dev] (auxiliary name: main) +asset main-1aad2f42f93e93c4e0b4.js 12.7 KiB [emitted] [immutable] (name: main) + sourceMap main-1aad2f42f93e93c4e0b4.js.map 11.1 KiB [emitted] [dev] (auxiliary name: main) asset 695-4dd37417c69a0af66bac.js 455 bytes [emitted] [immutable] sourceMap 695-4dd37417c69a0af66bac.js.map 342 bytes [emitted] [dev] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./b/index.js (in Xdir/context-independence/b) 40 bytes [built] [code generated] - ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./b/c/ ./b/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./b/c/a.js 18 bytes [optional] [built] [code generated] + ./b/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/b) 266 bytes + ./b/index.js (in Xdir/context-independence/b) 200 bytes [built] [code generated] + ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms -asset main-180aaac92930f4aab865.js 11.6 KiB [emitted] [immutable] (name: main) +asset main-488feb13e36da3e337fa.js 14.9 KiB [emitted] [immutable] (name: main) asset 695-828eb5c7418e1b8270bb.js 1.5 KiB [emitted] [immutable] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./a/index.js (in Xdir/context-independence/a) 40 bytes [built] [code generated] - ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./a/c/ ./a/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./a/c/a.js 18 bytes [optional] [built] [code generated] + ./a/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/a) 266 bytes + ./a/index.js (in Xdir/context-independence/a) 200 bytes [built] [code generated] + ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms -asset main-180aaac92930f4aab865.js 11.6 KiB [emitted] [immutable] (name: main) +asset main-488feb13e36da3e337fa.js 14.9 KiB [emitted] [immutable] (name: main) asset 695-828eb5c7418e1b8270bb.js 1.5 KiB [emitted] [immutable] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./b/index.js (in Xdir/context-independence/b) 40 bytes [built] [code generated] - ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./b/c/ ./b/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./b/c/a.js 18 bytes [optional] [built] [code generated] + ./b/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/b) 266 bytes + ./b/index.js (in Xdir/context-independence/b) 200 bytes [built] [code generated] + ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms -asset main-633b38bd6be14d7e5f1e.js 11.3 KiB [emitted] [immutable] (name: main) +asset main-c96ffcbdb3eefd9ed7c6.js 13.7 KiB [emitted] [immutable] (name: main) asset 695-ace208366ce0ce2556ef.js 1.01 KiB [emitted] [immutable] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./a/index.js (in Xdir/context-independence/a) 40 bytes [built] [code generated] - ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./a/c/ ./a/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./a/c/a.js 18 bytes [optional] [built] [code generated] + ./a/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/a) 266 bytes + ./a/index.js (in Xdir/context-independence/a) 200 bytes [built] [code generated] + ./a/chunk.js + 1 modules (in Xdir/context-independence/a) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms -asset main-633b38bd6be14d7e5f1e.js 11.3 KiB [emitted] [immutable] (name: main) +asset main-c96ffcbdb3eefd9ed7c6.js 13.7 KiB [emitted] [immutable] (name: main) asset 695-ace208366ce0ce2556ef.js 1.01 KiB [emitted] [immutable] -runtime modules 6.29 KiB 8 modules +runtime modules 6.6 KiB 9 modules orphan modules 19 bytes [orphan] 1 module -cacheable modules 106 bytes - ./b/index.js (in Xdir/context-independence/b) 40 bytes [built] [code generated] - ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] +built modules 500 bytes [built] + modules by layer 234 bytes + ./b/c/ ./b/cc/ eager ^\\\\.\\\\/.*$ namespace object 198 bytes [built] [code generated] + ./b/c/a.js 18 bytes [optional] [built] [code generated] + ./b/cc/b.js 18 bytes [optional] [built] [code generated] + modules by layer (in Xdir/context-independence/b) 266 bytes + ./b/index.js (in Xdir/context-independence/b) 200 bytes [built] [code generated] + ./b/chunk.js + 1 modules (in Xdir/context-independence/b) 66 bytes [built] [code generated] webpack x.x.x compiled successfully in X ms" `; diff --git a/test/configCases/resolve/issue-11335-context-module/index.js b/test/configCases/resolve/issue-11335-context-module/index.js new file mode 100644 index 00000000000..d4784570ea4 --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/index.js @@ -0,0 +1,18 @@ +import a from "app/widgets/a"; +import b from "app/widgets/b"; +import c from "app/widgets/c"; + + +it("static imports order", () => { + expect(a).toBe("main/widgets/a"); + expect(b).toBe("main/widgets/b"); + expect(c).toBe("foo/widgets/c"); +}); + +const load = id => import(/* webpackMode: "eager" */ `app/widgets/${id}?query#hash`); + +it("dynamic imports order", async () => { + expect((await load("a")).default).toBe("main/widgets/a"); + expect((await load("b")).default).toBe("main/widgets/b"); + expect((await load("c")).default).toBe("foo/widgets/c"); +}); diff --git a/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/b.js b/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/b.js new file mode 100644 index 00000000000..9b6f2974934 --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/b.js @@ -0,0 +1 @@ +export default "foo/widgets/b"; diff --git a/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/c.js b/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/c.js new file mode 100644 index 00000000000..0de4d4fb702 --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/src/foo/widgets/c.js @@ -0,0 +1 @@ +export default "foo/widgets/c"; diff --git a/test/configCases/resolve/issue-11335-context-module/src/main/widgets/a.js b/test/configCases/resolve/issue-11335-context-module/src/main/widgets/a.js new file mode 100644 index 00000000000..b6d0dbb4492 --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/src/main/widgets/a.js @@ -0,0 +1 @@ +export default "main/widgets/a"; diff --git a/test/configCases/resolve/issue-11335-context-module/src/main/widgets/b.js b/test/configCases/resolve/issue-11335-context-module/src/main/widgets/b.js new file mode 100644 index 00000000000..0b8fa8212af --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/src/main/widgets/b.js @@ -0,0 +1 @@ +export default "main/widgets/b"; diff --git a/test/configCases/resolve/issue-11335-context-module/webpack.config.js b/test/configCases/resolve/issue-11335-context-module/webpack.config.js new file mode 100644 index 00000000000..d1c50dcaac7 --- /dev/null +++ b/test/configCases/resolve/issue-11335-context-module/webpack.config.js @@ -0,0 +1,10 @@ +const path = require("path"); + +/** @type {import("../../../../").Configuration} */ +module.exports = { + resolve: { + alias: { + app: [path.join(__dirname, "src/main"), path.join(__dirname, "src/foo")] + } + } +}; diff --git a/test/statsCases/context-independence/a/c/a.js b/test/statsCases/context-independence/a/c/a.js new file mode 100644 index 00000000000..842e368a0a2 --- /dev/null +++ b/test/statsCases/context-independence/a/c/a.js @@ -0,0 +1 @@ +export default 2; diff --git a/test/statsCases/context-independence/a/cc/b.js b/test/statsCases/context-independence/a/cc/b.js new file mode 100644 index 00000000000..aef22247d75 --- /dev/null +++ b/test/statsCases/context-independence/a/cc/b.js @@ -0,0 +1 @@ +export default 1; diff --git a/test/statsCases/context-independence/a/index.js b/test/statsCases/context-independence/a/index.js index 8881119dd17..82c137c9a63 100644 --- a/test/statsCases/context-independence/a/index.js +++ b/test/statsCases/context-independence/a/index.js @@ -1,2 +1,4 @@ console.log("test"); import("./chunk"); +const module = Math.round(Math.random() * 100) % 2 === 0 ? "a" : "b"; +import(/* webpackMode: "eager" */`c/${module}`).then(({ default: d }) => console.log(d)); diff --git a/test/statsCases/context-independence/b/c/a.js b/test/statsCases/context-independence/b/c/a.js new file mode 100644 index 00000000000..842e368a0a2 --- /dev/null +++ b/test/statsCases/context-independence/b/c/a.js @@ -0,0 +1 @@ +export default 2; diff --git a/test/statsCases/context-independence/b/cc/b.js b/test/statsCases/context-independence/b/cc/b.js new file mode 100644 index 00000000000..aef22247d75 --- /dev/null +++ b/test/statsCases/context-independence/b/cc/b.js @@ -0,0 +1 @@ +export default 1; diff --git a/test/statsCases/context-independence/b/index.js b/test/statsCases/context-independence/b/index.js index 8881119dd17..82c137c9a63 100644 --- a/test/statsCases/context-independence/b/index.js +++ b/test/statsCases/context-independence/b/index.js @@ -1,2 +1,4 @@ console.log("test"); import("./chunk"); +const module = Math.round(Math.random() * 100) % 2 === 0 ? "a" : "b"; +import(/* webpackMode: "eager" */`c/${module}`).then(({ default: d }) => console.log(d)); diff --git a/test/statsCases/context-independence/webpack.config.js b/test/statsCases/context-independence/webpack.config.js index a28c4df55ec..4d1b9a68b6d 100644 --- a/test/statsCases/context-independence/webpack.config.js +++ b/test/statsCases/context-independence/webpack.config.js @@ -1,8 +1,13 @@ const path = require("path"); -const base = { +/** + * @param {string} name name + * @param {string} devtool devtool + * @returns {import("../../../").Configuration} configuration + */ +const base = (name, devtool) => ({ mode: "production", - devtool: "source-map", + devtool, module: { rules: [ { @@ -17,115 +22,37 @@ const base = { }, experiments: { layers: true - } -}; - -const base2 = { - ...base, - devtool: "eval-source-map" -}; - -const base3 = { - ...base, - devtool: "eval" -}; - -/** @type {import("../../../").Configuration[]} */ -module.exports = [ - { - ...base, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "a") - } - }, - context: path.resolve(__dirname, "a"), - output: { - path: path.resolve(__dirname, "../../js/stats/context-independence/a"), - filename: "[name]-[chunkhash].js" - } }, - { - ...base, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "b") - } - }, - context: path.resolve(__dirname, "b"), - output: { - path: path.resolve(__dirname, "../../js/stats/context-independence/b"), - filename: "[name]-[chunkhash].js" + entry: { + main: { + import: "./index", + layer: path.resolve(__dirname, name) } }, - { - ...base2, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "a") - } - }, - context: path.resolve(__dirname, "a"), - output: { - path: path.resolve( - __dirname, - "../../js/stats/context-independence/eval-source-map-a" - ), - filename: "[name]-[chunkhash].js" - } + context: path.resolve(__dirname, name), + output: { + path: path.resolve( + __dirname, + `../../js/stats/context-independence/${devtool}-${name}` + ), + filename: "[name]-[chunkhash].js" }, - { - ...base2, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "b") - } - }, - context: path.resolve(__dirname, "b"), - output: { - path: path.resolve( - __dirname, - "../../js/stats/context-independence/eval-source-map-b" - ), - filename: "[name]-[chunkhash].js" - } - }, - { - ...base3, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "a") - } - }, - context: path.resolve(__dirname, "a"), - output: { - path: path.resolve( - __dirname, - "../../js/stats/context-independence/eval-a" - ), - filename: "[name]-[chunkhash].js" - } - }, - { - ...base3, - entry: { - main: { - import: "./index", - layer: path.resolve(__dirname, "b") - } - }, - context: path.resolve(__dirname, "b"), - output: { - path: path.resolve( - __dirname, - "../../js/stats/context-independence/eval-b" - ), - filename: "[name]-[chunkhash].js" + resolve: { + alias: { + c: [ + path.resolve(__dirname, name, "c"), + path.resolve(__dirname, name, "cc") + ] } } +}); + +/** @type {import("../../../").Configuration[]} */ +module.exports = [ + base("a", "source-map"), + base("b", "source-map"), + base("a", "eval-source-map"), + base("b", "eval-source-map"), + base("a", "eval"), + base("b", "eval") ]; diff --git a/types.d.ts b/types.d.ts index 09495399187..932c52035e7 100644 --- a/types.d.ts +++ b/types.d.ts @@ -2526,7 +2526,7 @@ declare interface ContextModuleOptions { * exports referenced from modules (won't be mangled) */ referencedExports?: string[][]; - resource: string; + resource: string | string[]; resourceQuery?: string; resourceFragment?: string; resolveOptions: any; @@ -9489,6 +9489,11 @@ declare interface ResolveContext { * log function */ log?: (arg0: string) => void; + + /** + * yield result, if provided plugins can return several results + */ + yield?: (arg0: ResolveRequest) => void; } declare interface ResolveData { contextInfo: ModuleFactoryCreateDataContextInfo; @@ -11937,7 +11942,7 @@ declare interface UserResolveOptions { restrictions?: (string | RegExp)[]; /** - * Use only the sync constiants of the file system calls + * Use only the sync constraints of the file system calls */ useSyncFileSystemCalls?: boolean; diff --git a/yarn.lock b/yarn.lock index eeb0c5ee7bd..1657eea582a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2259,10 +2259,10 @@ enhanced-resolve@^4.0.0: memory-fs "^0.5.0" tapable "^1.0.0" -enhanced-resolve@^5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" - integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== +enhanced-resolve@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.0.tgz#49ac24953ac8452ed8fed2ef1340fc8e043667ee" + integrity sha512-weDYmzbBygL7HzGGS26M3hGQx68vehdEg6VUmqSOaFzXExFqlnKuSvsEJCVGQHScS8CQMbrAqftT+AzzHNt/YA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0"