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("!")
.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;
}

View File

@ -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,

View File

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

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) {
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",

View File

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

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