Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm): allow WebAssembly module filenames to use more path templates (e.g. [file], [base]) #10202

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
25 changes: 25 additions & 0 deletions lib/ChunkGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,31 @@ class ChunkGraph {
};
}

/**
* @param {Chunk} chunk the chunk
* @param {ModuleFilterPredicate} filterFn function used to filter modules
* @param {boolean} includeAllChunks all chunks or only async chunks
* @returns {Record<string, string>} filename map
*/
getChunkFilenameMap(chunk, filterFn, includeAllChunks = false) {
const filenameMap = Object.create(null);
for (const asyncChunk of includeAllChunks
? chunk.getAllReferencedChunks()
: chunk.getAllAsyncChunks()) {
for (const module of this.getOrderedChunkModulesIterable(
asyncChunk,
compareModulesById(this)
)) {
if (filterFn(module)) {
const moduleId = this.getModuleId(module);
filenameMap[moduleId] = module.nameForCondition();
}
}
}

return filenameMap;
}

/**
* @param {Chunk} chunk the chunk
* @param {ChunkFilterPredicate} filterFn function used to filter chunks
Expand Down
7 changes: 7 additions & 0 deletions lib/Compilation.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ const { arrayToSetDeprecation } = require("./util/deprecation");
* @property {function(number): string=} hashWithLength
*/

/**
* @typedef {Object} PathDataFilenameMap
* @property {Record<string, string>} map
* @property {function(Record<string, string>): string} placeholderMapToStr
*/

/**
* @typedef {Object} PathData
* @property {ChunkGraph=} chunkGraph
Expand All @@ -177,6 +183,7 @@ const { arrayToSetDeprecation } = require("./util/deprecation");
* @property {(Chunk|ChunkPathData)=} chunk
* @property {(Module|ModulePathData)=} module
* @property {string=} filename
* @property {PathDataFilenameMap=} filenameMap
* @property {string=} basename
* @property {string=} query
* @property {string=} contentHashType
Expand Down
86 changes: 62 additions & 24 deletions lib/TemplatedPathPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,33 @@ const deprecated = (fn, message, code) => {
};
};

const getFilenameReplacementObj = filename => {
const idx = filename.indexOf("?");

let file, query;

if (idx >= 0) {
file = filename.substr(0, idx);
query = filename.substr(idx);
} else {
file = filename;
query = "";
}

const ext = extname(file);
const base = basename(file);
const name = base.slice(0, base.length - ext.length);
const path = file.slice(0, file.length - base.length);
return {
file,
query,
path,
base,
name,
ext
};
};

/**
* @param {string | function(PathData, AssetInfo=): string} path the raw path
* @param {PathData} data context data
Expand All @@ -100,36 +127,47 @@ const replacePathVariables = (path, data, assetInfo) => {
// [path] - /some/path/
// [name] - file
// [ext] - .js
if (data.filename) {
if (data.filename || data.filenameMap) {
let replacementObj;
if (typeof data.filename === "string") {
const idx = data.filename.indexOf("?");

let file, query;

if (idx >= 0) {
file = data.filename.substr(0, idx);
query = data.filename.substr(idx);
} else {
file = data.filename;
query = "";
}
replacementObj = getFilenameReplacementObj(data.filename);
} else if (data.filenameMap) {
// this map is of the form {moduleId: filename}
const map = data.filenameMap.map;
replacementObj = {};
// convert this map into a form where each filename placeholder (file, base, etc.),
// has an associated map of form {moduleId: strValue}
Object.keys(map).forEach(assetId => {
let filename = map[assetId];
let rep = getFilenameReplacementObj(filename);
Object.keys(rep).forEach(fileKey => {
if (!replacementObj[fileKey]) {
replacementObj[fileKey] = {};
}
replacementObj[fileKey][assetId] = rep[fileKey];
});
});

// convert these filename placeholder maps into strings
Object.keys(replacementObj).forEach(fileKey => {
replacementObj[fileKey] = data.filenameMap.placeholderMapToStr(
replacementObj[fileKey]
);
});
}

const ext = extname(file);
const base = basename(file);
const name = base.slice(0, base.length - ext.length);
const path = file.slice(0, file.length - base.length);

replacements.set("file", replacer(file));
replacements.set("query", replacer(query, true));
replacements.set("path", replacer(path, true));
replacements.set("base", replacer(base));
replacements.set("name", replacer(name));
replacements.set("ext", replacer(ext, true));
if (replacementObj) {
replacements.set("file", replacer(replacementObj.file));
replacements.set("query", replacer(replacementObj.query, true));
replacements.set("path", replacer(replacementObj.path, true));
replacements.set("base", replacer(replacementObj.base));
replacements.set("name", replacer(replacementObj.name));
replacements.set("ext", replacer(replacementObj.ext, true));
// Legacy
replacements.set(
"filebase",
deprecated(
replacer(base),
replacer(replacementObj.base),
"[filebase] is now [base]",
"DEP_WEBPACK_TEMPLATE_PATH_PLUGIN_REPLACE_PATH_VARIABLES_FILENAME"
)
Expand Down
18 changes: 16 additions & 2 deletions lib/wasm-async/AsyncWasmChunkLoadingRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,24 @@ class AsyncWasmChunkLoadingRuntimeModule extends RuntimeModule {
const { compilation, chunk } = this;
const { outputOptions } = compilation;
const fn = RuntimeGlobals.instantiateWasm;
const filterWasm = m => m.type.startsWith("webassembly");
const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps(
chunk,
m => m.type.startsWith("webassembly"),
filterWasm,
true
);
const filenameMap = this.compilation.chunkGraph.getChunkFilenameMap(
chunk,
filterWasm,
true
);

const wasmModuleSrcPath = compilation.getPath(
JSON.stringify(outputOptions.webassemblyModuleFilename),
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}}().slice(0, ${length}) + "`,
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
module: {
id: '" + wasmModuleId + "',
hash: `" + ${JSON.stringify(chunkModuleMaps.hash)}[wasmModuleId] + "`,
Expand All @@ -48,9 +55,16 @@ class AsyncWasmChunkLoadingRuntimeModule extends RuntimeModule {
}
return `" + ${JSON.stringify(shortChunkHashMap)}[wasmModuleId] + "`;
}
},
filenameMap: {
map: filenameMap,
placeholderMapToStr: placeholderMap => {
return `" + ${JSON.stringify(placeholderMap)}[wasmModuleId] + "`;
}
}
}
);

return Template.asString([
`${fn} = function(exports, wasmModuleId, importsObj) {`,
Template.indent([
Expand Down
1 change: 1 addition & 0 deletions lib/wasm-async/AsyncWebAssemblyJavascriptGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
runtimeRequirements.add(RuntimeGlobals.moduleId);
runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
runtimeRequirements.add(RuntimeGlobals.getFullHash);
/** @type {InitFragment[]} */
const initFragments = [];
/** @type {Map<Module, { request: string, importVar: string }>} */
Expand Down
6 changes: 4 additions & 2 deletions lib/wasm-async/AsyncWebAssemblyModulesPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class AsyncWebAssemblyModulesPlugin {
});

compilation.hooks.renderManifest.tap(
"WebAssemblyModulesPlugin",
"AsyncWebAssemblyModulesPlugin",
(result, options) => {
const { moduleGraph, chunkGraph, runtimeTemplate } = compilation;
const {
Expand All @@ -133,6 +133,7 @@ class AsyncWebAssemblyModulesPlugin {
if (module.type === "webassembly/async") {
const filenameTemplate =
outputOptions.webassemblyModuleFilename;
const filename = module.nameForCondition();

result.push({
render: () =>
Expand All @@ -151,7 +152,8 @@ class AsyncWebAssemblyModulesPlugin {
filenameTemplate,
pathOptions: {
module,
chunkGraph
chunkGraph,
filename
},
auxiliary: true,
identifier: `webassemblyAsyncModule${chunkGraph.getModuleId(
Expand Down
16 changes: 14 additions & 2 deletions lib/wasm/WasmChunkLoadingRuntimeModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,16 @@ class WasmChunkLoadingRuntimeModule extends RuntimeModule {
declarations
);
});
const filterWasm = m => m.type.startsWith("webassembly");
const chunkModuleMaps = this.compilation.chunkGraph.getChunkModuleMaps(
chunk,
m => m.type.startsWith("webassembly")
filterWasm
);
const filenameMap = this.compilation.chunkGraph.getChunkFilenameMap(
chunk,
filterWasm
);

const createImportObject = content =>
mangleImports
? `{ ${JSON.stringify(WebAssemblyUtils.MANGLED_MODULE)}: ${content} }`
Expand All @@ -213,7 +219,7 @@ class WasmChunkLoadingRuntimeModule extends RuntimeModule {
{
hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
hashWithLength: length =>
`" + ${RuntimeGlobals.getFullHash}}().slice(0, ${length}) + "`,
`" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
module: {
id: '" + wasmModuleId + "',
hash: `" + ${JSON.stringify(chunkModuleMaps.hash)}[wasmModuleId] + "`,
Expand All @@ -228,6 +234,12 @@ class WasmChunkLoadingRuntimeModule extends RuntimeModule {
}
return `" + ${JSON.stringify(shortChunkHashMap)}[wasmModuleId] + "`;
}
},
filenameMap: {
map: filenameMap,
placeholderMapToStr: placeholderMap => {
return `" + ${JSON.stringify(placeholderMap)}[wasmModuleId] + "`;
}
}
}
);
Expand Down
1 change: 1 addition & 0 deletions lib/wasm/WebAssemblyJavascriptGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
runtimeRequirements.add(RuntimeGlobals.module);
runtimeRequirements.add(RuntimeGlobals.moduleId);
runtimeRequirements.add(RuntimeGlobals.wasmInstances);
runtimeRequirements.add(RuntimeGlobals.getFullHash);
if (exportsInfo.otherExportsInfo.used !== UsageState.Unused) {
runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
runtimeRequirements.add(RuntimeGlobals.exports);
Expand Down
4 changes: 3 additions & 1 deletion lib/wasm/WebAssemblyModulesPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WebAssemblyModulesPlugin {
if (module.type === "webassembly/sync") {
const filenameTemplate =
outputOptions.webassemblyModuleFilename;
const filename = module.nameForCondition();

result.push({
render: () =>
Expand All @@ -91,7 +92,8 @@ class WebAssemblyModulesPlugin {
filenameTemplate,
pathOptions: {
module,
chunkGraph
chunkGraph,
filename
},
auxiliary: true,
identifier: `webassemblyModule${chunkGraph.getModuleId(
Expand Down
10 changes: 5 additions & 5 deletions test/__snapshots__/StatsTestCases.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3785,7 +3785,7 @@ WARNING in Terser Plugin: Dropping unused function someUnRemoteUsedFunction5 [we
`;

exports[`StatsTestCases should print correct stats for wasm-explorer-examples-sync 1`] = `
"Hash: 6447a9fca2b71320b7dd
"Hash: 9098bd6e65dca188d536
Time: Xms
Built at: 1970-04-20 12:42:42
Asset Size
Expand All @@ -3798,7 +3798,7 @@ Built at: 1970-04-20 12:42:42
780.bundle.js 526 bytes [emitted]
99.bundle.js 220 bytes [emitted]
a0e9dd97d7ced35a5b2c.module.wasm 154 bytes [emitted] [immutable]
bundle.js 11 KiB [emitted] [name: main-1df31ce3]
bundle.js 11.2 KiB [emitted] [name: main-1df31ce3]
d37b3336426771c2a6e2.module.wasm 531 bytes [emitted] [immutable]
ebd3f263522776d85971.module.wasm 156 bytes [emitted] [immutable]
Entrypoint main = bundle.js
Expand All @@ -3810,9 +3810,9 @@ chunk 325.bundle.js 1.54 KiB (javascript) 274 bytes (webassembly) [rendered]
./popcnt.wasm 50 bytes (javascript) 120 bytes (webassembly) [built]
./testFunction.wasm 50 bytes (javascript) 154 bytes (webassembly) [built]
./tests.js 1.44 KiB [built]
chunk bundle.js (main-1df31ce3) 586 bytes (javascript) 5.51 KiB (runtime) [entry] [rendered]
chunk bundle.js (main-1df31ce3) 586 bytes (javascript) 5.56 KiB (runtime) [entry] [rendered]
./index.js 586 bytes [built]
+ 8 hidden chunk modules
+ 9 hidden chunk modules
chunk 526.bundle.js (id hint: vendors) 34 bytes [rendered] split chunk (cache group: defaultVendors)
./node_modules/env.js 34 bytes [built]
chunk 780.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered]
Expand All @@ -3827,5 +3827,5 @@ chunk 780.bundle.js 110 bytes (javascript) 444 bytes (webassembly) [rendered]
./fast-math.wasm 60 bytes (javascript) 290 bytes (webassembly) [built]
./duff.wasm 50 bytes (javascript) 531 bytes (webassembly) [built]
./node_modules/env.js 34 bytes [built]
+ 8 hidden modules"
+ 9 hidden modules"
`;
21 changes: 21 additions & 0 deletions test/configCases/wasm/path-templates-async/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use strict";

const path = require('path');
const fs = require('fs');

it("should use path template for async wasm modules", function() {
return import("./module").then(function() {
const statsPath = path.join(
__dirname,
"stats.json"
);
const stats = JSON.parse(fs.readFileSync(statsPath));
let found = false;
stats.assets.forEach((asset) => {
found = found || !!asset.name.match(
/^\w{20}\.\w{20}\.\w{16}\.wasm\.wat\.wat\.wasm$/
);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        const found = stats.assets.every((asset) => {
            !!asset.name.match(
                /^\w{20}\.\w{20}\.\w{16}\.wasm\.wat\.wat\.wasm$/
            );
        });

What about this approach?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamesgeorge007 Good idea, I did this with the slight change that it should be .some rather than .every (I'm looking for only one outputted file that matches this reg exp).

expect(found).toEqual(true);
});
});
5 changes: 5 additions & 0 deletions test/configCases/wasm/path-templates-async/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import await { add, getNumber } from "./wasm.wat?1";

export function run() {
return add(getNumber(), 2);
}
5 changes: 5 additions & 0 deletions test/configCases/wasm/path-templates-async/test.filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");

module.exports = function(config) {
return supportsWebAssembly();
};
10 changes: 10 additions & 0 deletions test/configCases/wasm/path-templates-async/wasm.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func (result i32)))
(func $add (export "add") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
(i32.add
(get_local $p0)
(get_local $p1)))
(func $getNumber (export "getNumber") (type $t1) (result i32)
(i32.const 40)))