support undo path

This commit is contained in:
Ivan Kopeykin 2020-09-16 23:07:06 +03:00
parent 705fb23728
commit 49009486df
11 changed files with 120 additions and 25 deletions

View File

@ -23,6 +23,7 @@ const PublicPathRuntimeModule = require("./runtime/PublicPathRuntimeModule");
const SystemContextRuntimeModule = require("./runtime/SystemContextRuntimeModule");
const ShareRuntimeModule = require("./sharing/ShareRuntimeModule");
const StringXor = require("./util/StringXor");
const { getUndoPath } = require("./util/identifier");
/** @typedef {import("./Chunk")} Chunk */
/** @typedef {import("./Compiler")} Compiler */
@ -158,16 +159,24 @@ class RuntimePlugin {
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.publicPath)
.tap("RuntimePlugin", (chunk, runtimeRequirements) => {
const {
importFunctionName,
publicPath,
scriptType
} = compilation.outputOptions;
const module = new PublicPathRuntimeModule(
publicPath,
scriptType,
importFunctionName
);
const { outputOptions } = compilation;
const { publicPath } = outputOptions;
let undoPath;
if (publicPath === "auto") {
const chunkName = compilation.getPath(
JavascriptModulesPlugin.getChunkFilenameTemplate(
chunk,
outputOptions
),
{
chunk
}
);
undoPath = getUndoPath(chunkName, false);
}
const module = new PublicPathRuntimeModule(outputOptions, undoPath);
if (
typeof publicPath !== "string" ||

View File

@ -91,6 +91,11 @@ class RuntimeTemplate {
return this.outputOptions.environment.module;
}
supportTemplateLiteral() {
// TODO
return false;
}
returningFunction(returnValue, args = "") {
return this.supportsArrowFunction()
? `(${args}) => ${returnValue}`

View File

@ -8,9 +8,10 @@ const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const Template = require("../Template");
/** @typedef {import("../../declarations/WebpackOptions").PublicPath} PublicPath */
/** @typedef {import("../../declarations/WebpackOptions").ScriptType} ScriptType */
/** @typedef {import("../../declarations/WebpackOptions").Output} OutputOptions */
/** @typedef {import("../../declarations/WebpackOptions").PublicPath} PublicPathOptions */
/** @typedef {import("../Compilation")} Compilation */
/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
class PublicPathRuntimeModule extends RuntimeModule {
/**
@ -24,15 +25,16 @@ class PublicPathRuntimeModule extends RuntimeModule {
return [RuntimeGlobals.global];
}
/**
* @param {PublicPath} publicPath public path
* @param {ScriptType} scriptType script type
* @param {string} importName import name
* @param {OutputOptions} outputOptions output options
* @param {string=} undoPath undo path
*/
constructor(publicPath, scriptType, importName) {
constructor(outputOptions, undoPath) {
super("publicPath");
const { publicPath, scriptType, importFunctionName } = outputOptions;
this.publicPath = publicPath;
this.scriptType = scriptType;
this.importName = importName;
this.importName = importFunctionName;
this.undoPath = undoPath;
}
/**
@ -43,7 +45,10 @@ class PublicPathRuntimeModule extends RuntimeModule {
const { runtimeTemplate } = compilation;
if (publicPath === "auto") {
if (scriptType === "module") {
return `${RuntimeGlobals.publicPath} = ${importName}.meta.url.replace(/[^\\/]+$/, "")`;
return `${RuntimeGlobals.publicPath} = ${this.applyUndoPath(
`${importName}.meta.url.replace(/[^\\/]+$/, "")`,
runtimeTemplate
)};`;
}
return `${RuntimeGlobals.publicPath} = ${runtimeTemplate.iife(
@ -51,7 +56,10 @@ class PublicPathRuntimeModule extends RuntimeModule {
Template.indent([
`if ("document" in ${RuntimeGlobals.global} && "currentScript" in ${RuntimeGlobals.global}.document) `,
Template.indent(
`return ${RuntimeGlobals.global}.document.currentScript.src.replace(/[^\\/]+$/, "");`
`return ${this.applyUndoPath(
`${RuntimeGlobals.global}.document.currentScript.src.replace(/[^\\/]+$/, "")`,
runtimeTemplate
)};`
),
" else ",
Template.indent(`return ${this.definePath("")};`)
@ -63,7 +71,7 @@ class PublicPathRuntimeModule extends RuntimeModule {
}
/**
* @param {PublicPath} publicPath public path
* @param {PublicPathOptions} publicPath public path
* @returns {string} runtime code
*/
definePath(publicPath) {
@ -73,6 +81,22 @@ class PublicPathRuntimeModule extends RuntimeModule {
})
);
}
/**
* @param {string} code code
* @param {RuntimeTemplate} runtimeTemplate runtime template
* @returns {string} generated code
*/
applyUndoPath(code, runtimeTemplate) {
const { undoPath } = this;
if (!undoPath) return code;
if (runtimeTemplate.supportTemplateLiteral()) {
return `\`$\{${code}}${undoPath}\``;
}
return `${code} + "${undoPath}"`;
}
}
module.exports = PublicPathRuntimeModule;

View File

@ -9,6 +9,7 @@ const checkArrayExpectation = require("./checkArrayExpectation");
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
const deprecationTracking = require("./helpers/deprecationTracking");
const FakeDocument = require("./helpers/FakeDocument");
const CurrentScript = require("./helpers/CurrentScript");
const webpack = require("..");
const prepareOptions = require("./helpers/prepareOptions");
@ -222,12 +223,13 @@ const describeCases = config => {
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
if (bundlePath) {
filesCount++;
const document = new FakeDocument();
const globalContext = {
console: console,
expect: expect,
setTimeout: setTimeout,
clearTimeout: clearTimeout,
document: new FakeDocument(),
document,
location: {
href: "https://test.cases/path/index.html",
origin: "https://test.cases",
@ -243,6 +245,7 @@ const describeCases = config => {
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
let content;
let p;
let subPath = "";
if (Array.isArray(module)) {
p = path.join(currentDirectory, ".array-require.js");
content = `module.exports = (${module
@ -253,6 +256,26 @@ const describeCases = config => {
} else {
p = path.join(currentDirectory, module);
content = fs.readFileSync(p, "utf-8");
const lastSlash = module.lastIndexOf("/");
let firstSlash = module.indexOf("/");
if (lastSlash !== -1 && firstSlash !== lastSlash) {
if (firstSlash !== -1) {
let next = module.indexOf("/", firstSlash + 1);
let dir = module.slice(firstSlash + 1, next);
while (dir === ".") {
firstSlash = next;
next = module.indexOf("/", firstSlash + 1);
dir = module.slice(firstSlash + 1, next);
}
}
subPath = module.slice(
firstSlash + 1,
lastSlash + 1
);
}
}
if (p in requireCache) {
return requireCache[p].exports;
@ -262,6 +285,9 @@ const describeCases = config => {
};
requireCache[p] = m;
let runInNewContext = false;
let oldCurrentScript = document.currentScript;
document.currentScript = new CurrentScript(subPath);
const moduleScope = {
require: _require.bind(
null,
@ -357,6 +383,10 @@ const describeCases = config => {
? vm.runInNewContext(code, globalContext, p)
: vm.runInThisContext(code, p);
fn.call(m.exports, moduleScope);
//restore state
document.currentScript = oldCurrentScript;
return m.exports;
} else if (
testConfig.modules &&

View File

@ -0,0 +1,5 @@
import asset from "./asset.jpg";
it("should define public path", () => {
expect(asset).toBe("https://test.cases/path/inner1/inner2/../../asset.jpg");
});

View File

@ -0,0 +1,8 @@
module.exports = {
findBundle: function() {
return [
"./inner1/inner2/a.js",
"./b.js"
];
}
};

View File

@ -2,7 +2,16 @@
module.exports = {
mode: "none",
target: "web",
entry() {
return {
a: "./a",
b: "./b"
};
},
output: {
filename: data => {
return data.chunk.name === "a" ? `inner1/inner2/[name].js` : "[name].js";
},
assetModuleFilename: "[name][ext]"
},
module: {

View File

@ -0,0 +1,8 @@
class CurrentScript {
constructor(path = "", type = "text/javascript") {
this.src = `https://test.cases/path/${path}index.js`;
this.type = type;
}
}
module.exports = CurrentScript;

View File

@ -2,10 +2,6 @@ module.exports = class FakeDocument {
constructor() {
this.head = this.createElement("head");
this.baseURI = "https://test.cases/path/index.html";
this.currentScript = {
src: "https://test.cases/path/index.js",
type: "text/javascript"
};
this._elementsByTagName = new Map([["head", [this.head]]]);
}

1
types.d.ts vendored
View File

@ -7774,6 +7774,7 @@ declare abstract class RuntimeTemplate {
supportsBigIntLiteral(): boolean;
supportsDynamicImport(): boolean;
supportsEcmaScriptModuleSyntax(): boolean;
supportTemplateLiteral(): boolean;
returningFunction(returnValue?: any, args?: string): string;
basicFunction(args?: any, body?: any): string;
destructureArray(items?: any, value?: any): string;