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
This commit is contained in:
Tobias Koppers 2018-06-01 14:54:54 +02:00
parent 3415a98330
commit 530e1fb172
10 changed files with 140 additions and 26 deletions

View File

@ -43,9 +43,18 @@ const contextify = (context, request) => {
.split("!") .split("!")
.map(r => { .map(r => {
const splitPath = r.split("?"); const splitPath = r.split("?");
splitPath[0] = path.relative(context, splitPath[0]); if (/^[a-zA-Z]:\\/.test(splitPath[0])) {
if (path.sep === "\\") splitPath[0] = splitPath[0].replace(/\\/g, "/"); splitPath[0] = path.win32.relative(context, splitPath[0]);
if (splitPath[0].indexOf("../") !== 0) splitPath[0] = "./" + 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("?"); return splitPath.join("?");
}) })
.join("!"); .join("!");
@ -76,6 +85,7 @@ class NormalModule extends Module {
rawRequest, rawRequest,
loaders, loaders,
resource, resource,
matchResource,
parser, parser,
generator, generator,
resolveOptions resolveOptions
@ -90,6 +100,7 @@ class NormalModule extends Module {
this.parser = parser; this.parser = parser;
this.generator = generator; this.generator = generator;
this.resource = resource; this.resource = resource;
this.matchResource = matchResource;
this.loaders = loaders; this.loaders = loaders;
if (resolveOptions !== undefined) this.resolveOptions = resolveOptions; if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;
@ -123,16 +134,21 @@ class NormalModule extends Module {
} }
nameForCondition() { nameForCondition() {
const idx = this.resource.indexOf("?"); const resource = this.matchResource || this.resource;
if (idx >= 0) return this.resource.substr(0, idx); const idx = resource.indexOf("?");
return this.resource; if (idx >= 0) return resource.substr(0, idx);
return resource;
} }
updateCacheModule(module) { updateCacheModule(module) {
this.type = module.type;
this.request = module.request;
this.userRequest = module.userRequest; this.userRequest = module.userRequest;
this.rawRequest = module.rawRequest;
this.parser = module.parser; this.parser = module.parser;
this.generator = module.generator; this.generator = module.generator;
this.resource = module.resource; this.resource = module.resource;
this.matchResource = module.matchResource;
this.loaders = module.loaders; this.loaders = module.loaders;
this.resolveOptions = module.resolveOptions; this.resolveOptions = module.resolveOptions;
} }

View File

@ -4,6 +4,7 @@
*/ */
"use strict"; "use strict";
const path = require("path");
const asyncLib = require("neo-async"); const asyncLib = require("neo-async");
const { const {
Tapable, Tapable,
@ -20,6 +21,8 @@ const cachedMerge = require("./util/cachedMerge");
const EMPTY_RESOLVE_OPTIONS = {}; const EMPTY_RESOLVE_OPTIONS = {};
const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
const loaderToIdent = data => { const loaderToIdent = data => {
if (!data.options) { if (!data.options) {
return data.loader; return data.loader;
@ -158,19 +161,33 @@ class NormalModuleFactory extends Tapable {
const context = data.context; const context = data.context;
const request = data.request; const request = data.request;
const noPreAutoLoaders = request.startsWith("-!"); const loaderResolver = this.getResolver("loader");
const noAutoLoaders = noPreAutoLoaders || request.startsWith("!"); const normalResolver = this.getResolver("normal", data.resolveOptions);
const noPrePostAutoLoaders = request.startsWith("!!");
let elements = request 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(/^-?!+/, "")
.replace(/!!+/g, "!") .replace(/!!+/g, "!")
.split("!"); .split("!");
let resource = elements.pop(); let resource = elements.pop();
elements = elements.map(identToLoaderRequest); elements = elements.map(identToLoaderRequest);
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
asyncLib.parallel( asyncLib.parallel(
[ [
callback => callback =>
@ -234,12 +251,15 @@ class NormalModuleFactory extends Tapable {
); );
} }
const userRequest = loaders const userRequest =
.map(loaderToIdent) (matchResource !== undefined ? `${matchResource}!=!` : "") +
.concat([resource]) loaders
.join("!"); .map(loaderToIdent)
.concat([resource])
.join("!");
let resourcePath = resource; let resourcePath =
matchResource !== undefined ? matchResource : resource;
let resourceQuery = ""; let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?"); const queryIndex = resourcePath.indexOf("?");
if (queryIndex >= 0) { if (queryIndex >= 0) {
@ -249,6 +269,10 @@ class NormalModuleFactory extends Tapable {
const result = this.ruleSet.exec({ const result = this.ruleSet.exec({
resource: resourcePath, resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery, resourceQuery,
issuer: contextInfo.issuer, issuer: contextInfo.issuer,
compiler: contextInfo.compiler compiler: contextInfo.compiler
@ -326,6 +350,7 @@ class NormalModuleFactory extends Tapable {
rawRequest: request, rawRequest: request,
loaders, loaders,
resource, resource,
matchResource,
resourceResolveData, resourceResolveData,
settings, settings,
type, type,

View File

@ -10,6 +10,7 @@ const PATH_CHARS_REGEXP = /[-[\]{}()*+?.,\\^$|#\s]/g;
const SEPARATOR_REGEXP = /[/\\]$/; const SEPARATOR_REGEXP = /[/\\]$/;
const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g; const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g;
const INDEX_JS_REGEXP = /\/index.js(!|\?|\(query\))/g; const INDEX_JS_REGEXP = /\/index.js(!|\?|\(query\))/g;
const MATCH_RESOURCE_REGEXP = /!=!/;
const normalizeBackSlashDirection = request => { const normalizeBackSlashDirection = request => {
return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/"); return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/");
@ -73,6 +74,7 @@ class RequestShortener {
} }
result = result.replace(INDEX_JS_REGEXP, "$1"); result = result.replace(INDEX_JS_REGEXP, "$1");
result = result.replace(FRONT_OR_BACK_BANG_REGEXP, ""); result = result.replace(FRONT_OR_BACK_BANG_REGEXP, "");
result = result.replace(MATCH_RESOURCE_REGEXP, " = ");
this.cache.set(request, result); this.cache.set(request, result);
return result; return result;
} }

View File

@ -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) { if (rule.resourceQuery) {
try { try {
newRule.resourceQuery = RuleSet.normalizeCondition(rule.resourceQuery); newRule.resourceQuery = RuleSet.normalizeCondition(rule.resourceQuery);
@ -477,10 +485,13 @@ module.exports = class RuleSet {
_run(data, rule, result) { _run(data, rule, result) {
// test conditions // test conditions
if (rule.resource && !data.resource) return false; if (rule.resource && !data.resource) return false;
if (rule.realResource && !data.realResource) return false;
if (rule.resourceQuery && !data.resourceQuery) return false; if (rule.resourceQuery && !data.resourceQuery) return false;
if (rule.compiler && !data.compiler) return false; if (rule.compiler && !data.compiler) return false;
if (rule.issuer && !data.issuer) return false; if (rule.issuer && !data.issuer) return false;
if (rule.resource && !rule.resource(data.resource)) 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.issuer && rule.issuer && !rule.issuer(data.issuer)) return false;
if ( if (
data.resourceQuery && data.resourceQuery &&
@ -497,6 +508,7 @@ module.exports = class RuleSet {
const keys = Object.keys(rule).filter(key => { const keys = Object.keys(rule).filter(key => {
return ![ return ![
"resource", "resource",
"realResource",
"resourceQuery", "resourceQuery",
"compiler", "compiler",
"issuer", "issuer",

View File

@ -16,11 +16,11 @@ describe("NormalModule", () => {
let resource; let resource;
let parser; let parser;
beforeEach(() => { beforeEach(() => {
request = "some/request"; request = "/some/request";
userRequest = "some/userRequest"; userRequest = "/some/userRequest";
rawRequest = "some/rawRequest"; rawRequest = "some/rawRequest";
loaders = []; loaders = [];
resource = "some/resource"; resource = "/some/resource";
parser = { parser = {
parse() {} parse() {}
}; };
@ -66,14 +66,14 @@ describe("NormalModule", () => {
it("contextifies the userRequest of the module", () => { it("contextifies the userRequest of the module", () => {
expect( expect(
normalModule.libIdent({ normalModule.libIdent({
context: "some/context" context: "/some/context"
}) })
).toBe("../userRequest"); ).toBe("../userRequest");
}); });
describe("given a userRequest containing loaders", () => { describe("given a userRequest containing loaders", () => {
beforeEach(() => { beforeEach(() => {
userRequest = userRequest =
"some/userRequest!some/other/userRequest!some/thing/is/off/here"; "/some/userRequest!/some/other/userRequest!/some/thing/is/off/here";
normalModule = new NormalModule({ normalModule = new NormalModule({
type: "javascript/auto", type: "javascript/auto",
request, request,
@ -87,7 +87,7 @@ describe("NormalModule", () => {
it("contextifies every path in the userRequest", () => { it("contextifies every path in the userRequest", () => {
expect( expect(
normalModule.libIdent({ normalModule.libIdent({
context: "some/context" context: "/some/context"
}) })
).toBe("../userRequest!../other/userRequest!../thing/is/off/here"); ).toBe("../userRequest!../other/userRequest!../thing/is/off/here");
}); });
@ -95,7 +95,7 @@ describe("NormalModule", () => {
describe("given a userRequest containing query parameters", () => { describe("given a userRequest containing query parameters", () => {
it("ignores paths in query parameters", () => { it("ignores paths in query parameters", () => {
userRequest = userRequest =
"some/context/loader?query=foo\\bar&otherPath=testpath/other"; "F:\\some\\context\\loader?query=foo\\bar&otherPath=testpath/other";
normalModule = new NormalModule({ normalModule = new NormalModule({
type: "javascript/auto", type: "javascript/auto",
request, request,
@ -107,7 +107,7 @@ describe("NormalModule", () => {
}); });
expect( expect(
normalModule.libIdent({ normalModule.libIdent({
context: "some/context" context: "F:\\some\\context"
}) })
).toBe("./loader?query=foo\\bar&otherPath=testpath/other"); ).toBe("./loader?query=foo\\bar&otherPath=testpath/other");
}); });

View File

@ -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");
});
});

View File

@ -0,0 +1,8 @@
(module
(memory (export "memory") 1)
(data (i32.const 16) "Hello World\00")
(func (export "getString") (result i32)
(i32.const 16)
)
)

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

View File

@ -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;
};
`;
};

View File

@ -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;
};
`;
};