From 85dd7b71d22786b4a0c5989e520ed877f6a62125 Mon Sep 17 00:00:00 2001 From: Ivan Kopeykin Date: Wed, 6 Apr 2022 13:40:41 +0300 Subject: [PATCH] add [root] and [internal-path] placeholders - [root] prevents asset path leading outside of build context - [internal-path] is like [path] guarded by [root] (`[root][path]`) --- lib/TemplatedPathPlugin.js | 31 +++++++++++++++- lib/config/defaults.js | 2 +- test/Defaults.unittest.js | 32 ++++++++--------- .../asset-emitted/asset-path/index.js | 7 ++++ .../asset-emitted/asset-path/test.config.js | 5 +++ .../asset-path/webpack.config.js | 36 +++++++++++++++++++ test/fixtures/asset/1.txt | 1 + test/fixtures/asset/package.json | 4 +++ 8 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 test/configCases/asset-emitted/asset-path/index.js create mode 100644 test/configCases/asset-emitted/asset-path/test.config.js create mode 100644 test/configCases/asset-emitted/asset-path/webpack.config.js create mode 100644 test/fixtures/asset/1.txt create mode 100644 test/fixtures/asset/package.json diff --git a/lib/TemplatedPathPlugin.js b/lib/TemplatedPathPlugin.js index 34ac92308..217297163 100644 --- a/lib/TemplatedPathPlugin.js +++ b/lib/TemplatedPathPlugin.js @@ -6,7 +6,7 @@ "use strict"; const mime = require("mime-types"); -const { basename, extname } = require("path"); +const { basename, extname, posix } = require("path"); const util = require("util"); const Chunk = require("./Chunk"); const Module = require("./Module"); @@ -29,6 +29,8 @@ const prepareId = id => { return id.replace(/(^[.-]|[^a-zA-Z0-9_-])+/g, "_"); }; +const internalPath = path => + path.replace(/^(\.\.\/)+/, m => "_/".repeat(m.length / 3)); const hashLength = (replacer, handler, assetInfo, hashName) => { const fn = (match, arg, input) => { @@ -79,6 +81,18 @@ const replacer = (value, allowEmpty) => { return fn; }; +/** + * @param {string} input input + * @param {number} index match index + * @returns {string} replaced path + */ +const rootReplacer = (input, index) => { + const subPath = input.slice(index + 6 /* "[root]".length */); + const normalized = posix.normalize(subPath); + + return input.slice(0, index) + internalPath(normalized); +}; + const deprecationCache = new Map(); const deprecatedFunction = (() => () => {})(); const deprecated = (fn, message, code) => { @@ -152,6 +166,10 @@ const replacePathVariables = (path, data, assetInfo) => { replacements.set("query", replacer(query, true)); replacements.set("fragment", replacer(fragment, true)); replacements.set("path", replacer(path, true)); + replacements.set( + "internal-path", + replacer(() => internalPath(path), true) + ); replacements.set("base", replacer(base)); replacements.set("name", replacer(name)); replacements.set("ext", replacer(ext, true)); @@ -325,6 +343,17 @@ const replacePathVariables = (path, data, assetInfo) => { return match; }); + const regexp = /\[root\]/gi; + const matches = []; + let match; + + while ((match = regexp.exec(path))) matches.push(match.index); + if (matches.length) { + for (let i = matches.length - 1; i >= 0; i--) { + path = rootReplacer(path, matches[i]); + } + } + return path; }; diff --git a/lib/config/defaults.js b/lib/config/defaults.js index bd91b469d..d4ca20aa4 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -739,7 +739,7 @@ const applyOutputDefaults = ( }); F(output, "module", () => !!outputModule); - D(output, "filename", output.module ? "[name].mjs" : "[name].js"); + D(output, "filename", output.module ? "[root][name].mjs" : "[root][name].js"); F(output, "iife", () => !output.module); D(output, "importFunctionName", "import"); D(output, "importMetaName", "import.meta"); diff --git a/test/Defaults.unittest.js b/test/Defaults.unittest.js index b9fee53b7..ff2c7bfed 100644 --- a/test/Defaults.unittest.js +++ b/test/Defaults.unittest.js @@ -300,7 +300,7 @@ describe("snapshots", () => { "assetModuleFilename": "[hash][ext][query]", "asyncChunks": true, "charset": true, - "chunkFilename": "[name].js", + "chunkFilename": "[root][name].js", "chunkFormat": "array-push", "chunkLoadTimeout": 120000, "chunkLoading": "jsonp", @@ -308,8 +308,8 @@ describe("snapshots", () => { "clean": undefined, "compareBeforeEmit": true, "crossOriginLoading": false, - "cssChunkFilename": "[name].css", - "cssFilename": "[name].css", + "cssChunkFilename": "[root][name].css", + "cssFilename": "[root][name].css", "devtoolFallbackModuleFilenameTemplate": undefined, "devtoolModuleFilenameTemplate": undefined, "devtoolNamespace": "webpack", @@ -330,7 +330,7 @@ describe("snapshots", () => { "forOf": true, "module": undefined, }, - "filename": "[name].js", + "filename": "[root][name].js", "globalObject": "self", "hashDigest": "hex", "hashDigestLength": 20, @@ -879,8 +879,8 @@ describe("snapshots", () => { - "externalsType": "var", + "externalsType": "module", @@ ... @@ - - "chunkFilename": "[name].js", - + "chunkFilename": "[name].mjs", + - "chunkFilename": "[root][name].js", + + "chunkFilename": "[root][name].mjs", @@ ... @@ - "dynamicImport": undefined, + "dynamicImport": true, @@ -888,8 +888,8 @@ describe("snapshots", () => { - "module": undefined, + "module": true, @@ ... @@ - - "filename": "[name].js", - + "filename": "[name].mjs", + - "filename": "[root][name].js", + + "filename": "[root][name].mjs", @@ ... @@ - "hotUpdateChunkFilename": "[id].[fullhash].hot-update.js", + "hotUpdateChunkFilename": "[id].[fullhash].hot-update.mjs", @@ -994,15 +994,15 @@ describe("snapshots", () => { + Received @@ ... @@ - - "chunkFilename": "[name].js", + - "chunkFilename": "[root][name].js", + "chunkFilename": "[id].bundle.js", @@ ... @@ - - "cssChunkFilename": "[name].css", - - "cssFilename": "[name].css", + - "cssChunkFilename": "[root][name].css", + - "cssFilename": "[root][name].css", + "cssChunkFilename": "[id].bundle.css", + "cssFilename": "bundle.css", @@ ... @@ - - "filename": "[name].js", + - "filename": "[root][name].js", + "filename": "bundle.js", `) ); @@ -1012,15 +1012,15 @@ describe("snapshots", () => { + Received @@ ... @@ - - "chunkFilename": "[name].js", + - "chunkFilename": "[root][name].js", + "chunkFilename": "[id].js", @@ ... @@ - - "cssChunkFilename": "[name].css", - - "cssFilename": "[name].css", + - "cssChunkFilename": "[root][name].css", + - "cssFilename": "[root][name].css", + "cssChunkFilename": "[id].css", + "cssFilename": "[id].css", @@ ... @@ - - "filename": "[name].js", + - "filename": "[root][name].js", + "filename": [Function filename], `) ); diff --git a/test/configCases/asset-emitted/asset-path/index.js b/test/configCases/asset-emitted/asset-path/index.js new file mode 100644 index 000000000..075cff49f --- /dev/null +++ b/test/configCases/asset-emitted/asset-path/index.js @@ -0,0 +1,7 @@ +import fs from "fs"; +import path from "path"; +import txt from "asset/1.txt"; + +it("should compile and run", () => { + expect(fs.readFileSync(path.resolve(__dirname, "..", txt)).toString()).toMatch("text"); +}); diff --git a/test/configCases/asset-emitted/asset-path/test.config.js b/test/configCases/asset-emitted/asset-path/test.config.js new file mode 100644 index 000000000..fe5b75888 --- /dev/null +++ b/test/configCases/asset-emitted/asset-path/test.config.js @@ -0,0 +1,5 @@ +module.exports = { + findBundle(i) { + return `./_/bundle${i}.js`; + } +}; diff --git a/test/configCases/asset-emitted/asset-path/webpack.config.js b/test/configCases/asset-emitted/asset-path/webpack.config.js new file mode 100644 index 000000000..8fdeb5401 --- /dev/null +++ b/test/configCases/asset-emitted/asset-path/webpack.config.js @@ -0,0 +1,36 @@ +const path = require("path"); + +const base = { + mode: "production", + target: "node", + module: { + rules: [ + { + test: /\.txt$/, + type: "asset/resource" + } + ] + }, + output: { + filename: "[root]../bundle0.js", + assetModuleFilename: "[root][path][name]-[hash][ext]" + }, + resolve: { + modules: [path.resolve(__dirname, "..", "..", "..", "fixtures")] + }, + stats: { + errorDetails: true + } +}; + +/** @type {import("../../../../types").Configuration[]} */ +module.exports = [ + base, + { + ...base, + output: { + filename: "[root]../bundle1.js", + assetModuleFilename: "some/dir/[root][path][name]-[hash][ext]" + } + } +]; diff --git a/test/fixtures/asset/1.txt b/test/fixtures/asset/1.txt new file mode 100644 index 000000000..8e27be7d6 --- /dev/null +++ b/test/fixtures/asset/1.txt @@ -0,0 +1 @@ +text diff --git a/test/fixtures/asset/package.json b/test/fixtures/asset/package.json new file mode 100644 index 000000000..05cddf15f --- /dev/null +++ b/test/fixtures/asset/package.json @@ -0,0 +1,4 @@ +{ + "name": "asset", + "version": "1.0.0" +}