From 530e1fb172be65c4a4107f4d48961c7a19cf2632 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 1 Jun 2018 14:54:54 +0200 Subject: [PATCH] Add matchResource feature (for loaders) Match rules with custom resource name Also use this name as rule.issuer or splitChunks test Show nicely in stats --- lib/NormalModule.js | 28 ++++++++--- lib/NormalModuleFactory.js | 49 ++++++++++++++----- lib/RequestShortener.js | 2 + lib/RuleSet.js | 12 +++++ test/NormalModule.unittest.js | 16 +++--- test/cases/wasm/two-files-loader/index.js | 11 +++++ test/cases/wasm/two-files-loader/src/wasm.dat | 8 +++ .../wasm/two-files-loader/test.filter.js | 5 ++ .../wasm/two-files-loader/wrapper-loader.js | 17 +++++++ .../wasm/two-files-loader/wrapper-loader2.js | 18 +++++++ 10 files changed, 140 insertions(+), 26 deletions(-) create mode 100644 test/cases/wasm/two-files-loader/index.js create mode 100644 test/cases/wasm/two-files-loader/src/wasm.dat create mode 100644 test/cases/wasm/two-files-loader/test.filter.js create mode 100644 test/cases/wasm/two-files-loader/wrapper-loader.js create mode 100644 test/cases/wasm/two-files-loader/wrapper-loader2.js diff --git a/lib/NormalModule.js b/lib/NormalModule.js index 26ed491f9..974e5be54 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -43,9 +43,18 @@ const contextify = (context, request) => { .split("!") .map(r => { const splitPath = r.split("?"); - splitPath[0] = path.relative(context, splitPath[0]); - if (path.sep === "\\") splitPath[0] = splitPath[0].replace(/\\/g, "/"); - if (splitPath[0].indexOf("../") !== 0) splitPath[0] = "./" + splitPath[0]; + if (/^[a-zA-Z]:\\/.test(splitPath[0])) { + splitPath[0] = path.win32.relative(context, splitPath[0]); + if (!/^[a-zA-Z]:\\/.test(splitPath[0])) { + splitPath[0] = splitPath[0].replace(/\\/g, "/"); + } + } + if (/^\//.test(splitPath[0])) { + splitPath[0] = path.posix.relative(context, splitPath[0]); + } + if (!/^(\.\.\/|\/|[a-zA-Z]:\\)/.test(splitPath[0])) { + splitPath[0] = "./" + splitPath[0]; + } return splitPath.join("?"); }) .join("!"); @@ -76,6 +85,7 @@ class NormalModule extends Module { rawRequest, loaders, resource, + matchResource, parser, generator, resolveOptions @@ -90,6 +100,7 @@ class NormalModule extends Module { this.parser = parser; this.generator = generator; this.resource = resource; + this.matchResource = matchResource; this.loaders = loaders; if (resolveOptions !== undefined) this.resolveOptions = resolveOptions; @@ -123,16 +134,21 @@ class NormalModule extends Module { } nameForCondition() { - const idx = this.resource.indexOf("?"); - if (idx >= 0) return this.resource.substr(0, idx); - return this.resource; + const resource = this.matchResource || this.resource; + const idx = resource.indexOf("?"); + if (idx >= 0) return resource.substr(0, idx); + return resource; } updateCacheModule(module) { + this.type = module.type; + this.request = module.request; this.userRequest = module.userRequest; + this.rawRequest = module.rawRequest; this.parser = module.parser; this.generator = module.generator; this.resource = module.resource; + this.matchResource = module.matchResource; this.loaders = module.loaders; this.resolveOptions = module.resolveOptions; } diff --git a/lib/NormalModuleFactory.js b/lib/NormalModuleFactory.js index 11dc42c75..23ef0ae1d 100644 --- a/lib/NormalModuleFactory.js +++ b/lib/NormalModuleFactory.js @@ -4,6 +4,7 @@ */ "use strict"; +const path = require("path"); const asyncLib = require("neo-async"); const { Tapable, @@ -20,6 +21,8 @@ const cachedMerge = require("./util/cachedMerge"); const EMPTY_RESOLVE_OPTIONS = {}; +const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/; + const loaderToIdent = data => { if (!data.options) { return data.loader; @@ -158,19 +161,33 @@ class NormalModuleFactory extends Tapable { const context = data.context; const request = data.request; - const noPreAutoLoaders = request.startsWith("-!"); - const noAutoLoaders = noPreAutoLoaders || request.startsWith("!"); - const noPrePostAutoLoaders = request.startsWith("!!"); - let elements = request + const loaderResolver = this.getResolver("loader"); + const normalResolver = this.getResolver("normal", data.resolveOptions); + + let matchResource = undefined; + let requestWithoutMatchResource = request; + const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request); + if (matchResourceMatch) { + matchResource = matchResourceMatch[1]; + if (/^\.\.?\//.test(matchResource)) { + matchResource = path.join(context, matchResource); + } + requestWithoutMatchResource = request.substr( + matchResourceMatch[0].length + ); + } + + const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!"); + const noAutoLoaders = + noPreAutoLoaders || requestWithoutMatchResource.startsWith("!"); + const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!"); + let elements = requestWithoutMatchResource .replace(/^-?!+/, "") .replace(/!!+/g, "!") .split("!"); let resource = elements.pop(); elements = elements.map(identToLoaderRequest); - const loaderResolver = this.getResolver("loader"); - const normalResolver = this.getResolver("normal", data.resolveOptions); - asyncLib.parallel( [ callback => @@ -234,12 +251,15 @@ class NormalModuleFactory extends Tapable { ); } - const userRequest = loaders - .map(loaderToIdent) - .concat([resource]) - .join("!"); + const userRequest = + (matchResource !== undefined ? `${matchResource}!=!` : "") + + loaders + .map(loaderToIdent) + .concat([resource]) + .join("!"); - let resourcePath = resource; + let resourcePath = + matchResource !== undefined ? matchResource : resource; let resourceQuery = ""; const queryIndex = resourcePath.indexOf("?"); if (queryIndex >= 0) { @@ -249,6 +269,10 @@ class NormalModuleFactory extends Tapable { const result = this.ruleSet.exec({ resource: resourcePath, + realResource: + matchResource !== undefined + ? resource.replace(/\?.*/, "") + : resourcePath, resourceQuery, issuer: contextInfo.issuer, compiler: contextInfo.compiler @@ -326,6 +350,7 @@ class NormalModuleFactory extends Tapable { rawRequest: request, loaders, resource, + matchResource, resourceResolveData, settings, type, diff --git a/lib/RequestShortener.js b/lib/RequestShortener.js index e3e080b9f..7b007816a 100644 --- a/lib/RequestShortener.js +++ b/lib/RequestShortener.js @@ -10,6 +10,7 @@ const PATH_CHARS_REGEXP = /[-[\]{}()*+?.,\\^$|#\s]/g; const SEPARATOR_REGEXP = /[/\\]$/; const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g; const INDEX_JS_REGEXP = /\/index.js(!|\?|\(query\))/g; +const MATCH_RESOURCE_REGEXP = /!=!/; const normalizeBackSlashDirection = request => { return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/"); @@ -73,6 +74,7 @@ class RequestShortener { } result = result.replace(INDEX_JS_REGEXP, "$1"); result = result.replace(FRONT_OR_BACK_BANG_REGEXP, ""); + result = result.replace(MATCH_RESOURCE_REGEXP, " = "); this.cache.set(request, result); return result; } diff --git a/lib/RuleSet.js b/lib/RuleSet.js index 2013a26d7..abc44c1a9 100644 --- a/lib/RuleSet.js +++ b/lib/RuleSet.js @@ -203,6 +203,14 @@ module.exports = class RuleSet { } } + if (rule.realResource) { + try { + newRule.realResource = RuleSet.normalizeCondition(rule.realResource); + } catch (error) { + throw new Error(RuleSet.buildErrorMessage(rule.realResource, error)); + } + } + if (rule.resourceQuery) { try { newRule.resourceQuery = RuleSet.normalizeCondition(rule.resourceQuery); @@ -477,10 +485,13 @@ module.exports = class RuleSet { _run(data, rule, result) { // test conditions if (rule.resource && !data.resource) return false; + if (rule.realResource && !data.realResource) return false; if (rule.resourceQuery && !data.resourceQuery) return false; if (rule.compiler && !data.compiler) return false; if (rule.issuer && !data.issuer) return false; if (rule.resource && !rule.resource(data.resource)) return false; + if (rule.realResource && !rule.realResource(data.realResource)) + return false; if (data.issuer && rule.issuer && !rule.issuer(data.issuer)) return false; if ( data.resourceQuery && @@ -497,6 +508,7 @@ module.exports = class RuleSet { const keys = Object.keys(rule).filter(key => { return ![ "resource", + "realResource", "resourceQuery", "compiler", "issuer", diff --git a/test/NormalModule.unittest.js b/test/NormalModule.unittest.js index a635b1c2d..329001bc2 100644 --- a/test/NormalModule.unittest.js +++ b/test/NormalModule.unittest.js @@ -16,11 +16,11 @@ describe("NormalModule", () => { let resource; let parser; beforeEach(() => { - request = "some/request"; - userRequest = "some/userRequest"; + request = "/some/request"; + userRequest = "/some/userRequest"; rawRequest = "some/rawRequest"; loaders = []; - resource = "some/resource"; + resource = "/some/resource"; parser = { parse() {} }; @@ -66,14 +66,14 @@ describe("NormalModule", () => { it("contextifies the userRequest of the module", () => { expect( normalModule.libIdent({ - context: "some/context" + context: "/some/context" }) ).toBe("../userRequest"); }); describe("given a userRequest containing loaders", () => { beforeEach(() => { userRequest = - "some/userRequest!some/other/userRequest!some/thing/is/off/here"; + "/some/userRequest!/some/other/userRequest!/some/thing/is/off/here"; normalModule = new NormalModule({ type: "javascript/auto", request, @@ -87,7 +87,7 @@ describe("NormalModule", () => { it("contextifies every path in the userRequest", () => { expect( normalModule.libIdent({ - context: "some/context" + context: "/some/context" }) ).toBe("../userRequest!../other/userRequest!../thing/is/off/here"); }); @@ -95,7 +95,7 @@ describe("NormalModule", () => { describe("given a userRequest containing query parameters", () => { it("ignores paths in query parameters", () => { userRequest = - "some/context/loader?query=foo\\bar&otherPath=testpath/other"; + "F:\\some\\context\\loader?query=foo\\bar&otherPath=testpath/other"; normalModule = new NormalModule({ type: "javascript/auto", request, @@ -107,7 +107,7 @@ describe("NormalModule", () => { }); expect( normalModule.libIdent({ - context: "some/context" + context: "F:\\some\\context" }) ).toBe("./loader?query=foo\\bar&otherPath=testpath/other"); }); diff --git a/test/cases/wasm/two-files-loader/index.js b/test/cases/wasm/two-files-loader/index.js new file mode 100644 index 000000000..36682a4ad --- /dev/null +++ b/test/cases/wasm/two-files-loader/index.js @@ -0,0 +1,11 @@ +it("should be able to create two modules from loader", function() { + return import("./wrapper-loader!./src/wasm.dat").then(function(wasm) { + expect(wasm.getString()).toEqual("Hello World"); + }); +}); + +it("should be able to create two modules from loader with remaining request", function() { + return import("./wrapper-loader2!./src/wasm.dat?2").then(function(wasm) { + expect(wasm.getString()).toEqual("Hello World"); + }); +}); diff --git a/test/cases/wasm/two-files-loader/src/wasm.dat b/test/cases/wasm/two-files-loader/src/wasm.dat new file mode 100644 index 000000000..50335ed49 --- /dev/null +++ b/test/cases/wasm/two-files-loader/src/wasm.dat @@ -0,0 +1,8 @@ +(module + (memory (export "memory") 1) + (data (i32.const 16) "Hello World\00") + (func (export "getString") (result i32) + (i32.const 16) + ) +) + diff --git a/test/cases/wasm/two-files-loader/test.filter.js b/test/cases/wasm/two-files-loader/test.filter.js new file mode 100644 index 000000000..231773496 --- /dev/null +++ b/test/cases/wasm/two-files-loader/test.filter.js @@ -0,0 +1,5 @@ +var supportsWebAssembly = require("../../../helpers/supportsWebAssembly"); + +module.exports = function(config) { + return supportsWebAssembly(); +}; diff --git a/test/cases/wasm/two-files-loader/wrapper-loader.js b/test/cases/wasm/two-files-loader/wrapper-loader.js new file mode 100644 index 000000000..544fc1a78 --- /dev/null +++ b/test/cases/wasm/two-files-loader/wrapper-loader.js @@ -0,0 +1,17 @@ +const stringifyRequest = require("loader-utils").stringifyRequest; + +module.exports.pitch = function(remainingRequest) { + return ` + import { getString as _getString, memory } from ${stringifyRequest(this, + `${this.resourcePath}.wat!=!${remainingRequest}` + )}; + + export function getString() { + const strBuf = new Uint8Array(memory.buffer, _getString()); + const idx = strBuf.indexOf(0); + const strBuf2 = strBuf.slice(0, idx); + const str = Buffer.from(strBuf2).toString("utf-8"); + return str; + }; + `; +}; diff --git a/test/cases/wasm/two-files-loader/wrapper-loader2.js b/test/cases/wasm/two-files-loader/wrapper-loader2.js new file mode 100644 index 000000000..6cd67a725 --- /dev/null +++ b/test/cases/wasm/two-files-loader/wrapper-loader2.js @@ -0,0 +1,18 @@ +const stringifyRequest = require("loader-utils").stringifyRequest; + +module.exports.pitch = function(remainingRequest) { + return ` + import { getString as _getString, memory } from ${stringifyRequest( + this, + `${this.resourcePath}.wasm!=!wast-loader!${remainingRequest}` + )}; + + export function getString() { + const strBuf = new Uint8Array(memory.buffer, _getString()); + const idx = strBuf.indexOf(0); + const strBuf2 = strBuf.slice(0, idx); + const str = Buffer.from(strBuf2).toString("utf-8"); + return str; + }; + `; +};