Skip to content

Commit

Permalink
Wasm packager (#9009)
Browse files Browse the repository at this point in the history
  • Loading branch information
mischnic authored Oct 3, 2023
1 parent f744d1b commit 12f4809
Show file tree
Hide file tree
Showing 18 changed files with 356 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"*.svg": "@parcel/packager-svg",
"*.{xml,rss,atom}": "@parcel/packager-xml",
"*.ts": "@parcel/packager-ts",
"*.wasm": "@parcel/packager-wasm",
"*.{jsonld,svg,webmanifest}": "@parcel/packager-raw-url",
"*": "@parcel/packager-raw"
},
Expand Down
1 change: 1 addition & 0 deletions packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@parcel/packager-js": "2.9.3",
"@parcel/packager-raw": "2.9.3",
"@parcel/packager-svg": "2.9.3",
"@parcel/packager-wasm": "2.9.3",
"@parcel/reporter-dev-server": "2.9.3",
"@parcel/resolver-default": "2.9.3",
"@parcel/runtime-browser-hmr": "2.9.3",
Expand Down
3 changes: 2 additions & 1 deletion packages/core/core/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import {version} from '../package.json';

export const PARCEL_VERSION = version;
export const HASH_REF_PREFIX = 'HASH_REF_';
export const HASH_REF_HASH_LEN = 16;
export const HASH_REF_REGEX: RegExp = new RegExp(
`${HASH_REF_PREFIX}\\w{16}`,
`${HASH_REF_PREFIX}\\w{${HASH_REF_HASH_LEN}}`,
'g',
);

Expand Down
48 changes: 39 additions & 9 deletions packages/core/core/src/requests/WriteBundleRequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {ConfigAndCachePath} from './ParcelConfigRequest';
import type {LoadedPlugin} from '../ParcelConfig';
import type {ProjectPath} from '../projectPath';

import {HASH_REF_PREFIX, HASH_REF_REGEX} from '../constants';
import {HASH_REF_HASH_LEN, HASH_REF_PREFIX} from '../constants';
import nullthrows from 'nullthrows';
import path from 'path';
import {NamedBundle} from '../public/Bundle';
Expand Down Expand Up @@ -40,6 +40,7 @@ import ParcelConfig from '../ParcelConfig';
import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
import {PluginTracer, tracer} from '@parcel/profiler';

const HASH_REF_PREFIX_LEN = HASH_REF_PREFIX.length;
const BOUNDARY_LENGTH = HASH_REF_PREFIX.length + 32 - 1;

type WriteBundleRequestInput = {|
Expand Down Expand Up @@ -295,17 +296,46 @@ async function runCompressor(
}

function replaceStream(hashRefToNameHash) {
let boundaryStr = '';
let boundaryStr = Buffer.alloc(0);
let replaced = Buffer.alloc(0);
return new Transform({
transform(chunk, encoding, cb) {
let str = boundaryStr + chunk.toString();
let replaced = str.replace(HASH_REF_REGEX, match => {
return hashRefToNameHash.get(match) || match;
});
boundaryStr = replaced.slice(replaced.length - BOUNDARY_LENGTH);
let strUpToBoundary = replaced.slice(
let str = Buffer.concat([boundaryStr, Buffer.from(chunk)]);
let lastMatchI = 0;
if (replaced.length < str.byteLength) {
replaced = Buffer.alloc(str.byteLength);
}
let replacedLength = 0;

while (lastMatchI < str.byteLength) {
let matchI = str.indexOf(HASH_REF_PREFIX, lastMatchI);
if (matchI === -1) {
replaced.set(
str.subarray(lastMatchI, str.byteLength),
replacedLength,
);
replacedLength += str.byteLength - lastMatchI;
break;
} else {
let match = str
.subarray(matchI, matchI + HASH_REF_PREFIX_LEN + HASH_REF_HASH_LEN)
.toString();
let replacement = Buffer.from(hashRefToNameHash.get(match) ?? match);
replaced.set(str.subarray(lastMatchI, matchI), replacedLength);
replacedLength += matchI - lastMatchI;
replaced.set(replacement, replacedLength);
replacedLength += replacement.byteLength;
lastMatchI = matchI + HASH_REF_PREFIX_LEN + HASH_REF_HASH_LEN;
}
}

boundaryStr = replaced.subarray(
replacedLength - BOUNDARY_LENGTH,
replacedLength,
);
let strUpToBoundary = replaced.subarray(
0,
replaced.length - BOUNDARY_LENGTH,
replacedLength - BOUNDARY_LENGTH,
);
cb(null, strUpToBoundary);
},
Expand Down
1 change: 1 addition & 0 deletions packages/core/integration-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"sugarss": "^3.0.3",
"tailwindcss": "^3.0.2",
"tempy": "^0.3.0",
"wasm-sourcemap": "^1.0.0",
"ws": "^7.0.0"
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "@parcel/config-default",
"transformers": {
"*.wasm": ["parcel-transformer-test"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log(new URL("index.wasm", import.meta.url))
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
19 changes: 19 additions & 0 deletions packages/core/integration-tests/test/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
overlayFS,
run,
} from '@parcel/test-utils';
import * as wasmmap from 'wasm-sourcemap';
import {relativePath} from '@parcel/utils';

describe('plugin', function () {
it("continue transformer pipeline on type change that doesn't change the pipeline", async function () {
Expand Down Expand Up @@ -310,4 +312,21 @@ parcel-transformer-b`,
await assertAsset('const replaced = 1;');
await assertAsset('const replaced = 2;');
});

it('should output sourcemaps when packaging Wasm', async () => {
let b = await bundle(
path.join(__dirname, '/integration/wasm-sourcemap-transformer/index.js'),
);
let wasmPath = nullthrows(
b.getBundles().find(b => b.type === 'wasm'),
).filePath;
let mapPath = wasmPath + '.map';
assert(await fs.exists(mapPath));

let wasm = await fs.readFile(wasmPath);
assert.equal(
wasmmap.GetSourceMapURL(wasm),
relativePath(distDir, mapPath, false),
);
});
});
25 changes: 25 additions & 0 deletions packages/packagers/wasm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@parcel/packager-wasm",
"version": "2.9.3",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "lib/WasmPackager.js",
"source": "src/WasmPackager.js",
"engines": {
"node": ">=12.0.0",
"parcel": "^2.9.3"
},
"dependencies": {
"@parcel/plugin": "2.9.3"
}
}
39 changes: 39 additions & 0 deletions packages/packagers/wasm/src/WasmPackager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @flow strict-local

import assert from 'assert';
import {Packager} from '@parcel/plugin';
import * as wasmmap from './wasm-sourcemap';

export default (new Packager({
async package({bundle, getSourceMapReference}) {
let assets = [];
bundle.traverseAssets(asset => {
assets.push(asset);
});

assert.equal(assets.length, 1, 'Wasm bundles must only contain one asset');

let [contents, map] = await Promise.all([
assets[0].getBuffer(),
assets[0].getMap(),
]);
let sourcemapReference = await getSourceMapReference(map);
if (sourcemapReference != null) {
return {
contents: Buffer.from(
wasmmap.SetSourceMapURL(
contents,
sourcemapReference,
sourcemapReference.includes('HASH_REF_')
? // HASH_REF_\w{16} -> \w{8}
sourcemapReference.length - (9 + 16 - 8)
: undefined,
),
),
map,
};
} else {
return {contents, map};
}
},
}): Packager);
Loading

0 comments on commit 12f4809

Please sign in to comment.