Skip to content

Commit 1559111

Browse files
authored
feat: Bundle iframe and vat worker via kernel-browser-runtime (#531)
#522 introduced the `kernel-browser-runtime` package, which bundled the kernel Web Worker. Further experiments indicated that it would be salutary for this package to bundle the vat worker, i.e. vat iframe, as well. To facilitate this, we replace `esbuild` with `vite` in `kernel-browser-runtime`, and extract all vat iframe-related functionality to this package. We also extract the extension's Vite plugins into a dedicated, private package so that they can be reused. Finally, during development, it was discovered that Vite duplicates the `@sqlite.org/sqlite.wasm` WASM binaries. This was a preexisting issue in the extension, when we bundled the kernel worker there. Thankfully, the duplicate files are extraneous and can be deleted, which is done using an inline plugin in `kernel-browser-runtime`. Many red herrings died in the creation of this PR.
1 parent 9ca5e30 commit 1559111

35 files changed

Lines changed: 535 additions & 139 deletions

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"build:clean": "yarn clean && yarn build",
1717
"build:docs": "yarn workspaces foreach --all --exclude @ocap/monorepo --exclude @ocap/extension --parallel --interlaced --verbose run build:docs",
1818
"build:source": "ts-bridge --project tsconfig.build.json --verbose",
19-
"build:special": "yarn workspace @metamask/kernel-shims run build && yarn workspace @metamask/kernel-browser-runtime run build:kernel-worker && yarn workspace @ocap/extension run build && yarn workspace @ocap/kernel-test run build:vats",
19+
"build:special": "yarn workspace @ocap/vite-plugins run build && yarn workspace @metamask/kernel-shims run build && yarn workspace @metamask/kernel-browser-runtime run build:vite && yarn workspace @ocap/extension run build && yarn workspace @ocap/kernel-test run build:vats",
2020
"bundle": "node ./scripts/bundle-vat.js",
2121
"changelog:update": "yarn workspaces foreach --all --no-private --parallel --interlaced --verbose run changelog:update",
2222
"changelog:validate": "yarn workspaces foreach --all --no-private --parallel --interlaced --verbose run changelog:validate",
@@ -109,15 +109,16 @@
109109
"lavamoat": {
110110
"allowScripts": {
111111
"$root$": true,
112+
"@ocap/cli>@metamask/logger>@metamask/streams": true,
112113
"@lavamoat/preinstall-always-fail": false,
114+
"eslint-import-resolver-typescript>unrs-resolver": false,
115+
"eslint-plugin-import-x>unrs-resolver": false,
113116
"simple-git-hooks": false,
114117
"vite>esbuild": false,
115118
"vite>sass>@parcel/watcher": false,
116119
"vitest>@vitest/browser>webdriverio>@wdio/utils>edgedriver": false,
117120
"vitest>@vitest/browser>webdriverio>@wdio/utils>geckodriver": false,
118-
"vitest>@vitest/mocker>msw": false,
119-
"@ocap/cli>@metamask/logger>@metamask/streams": true,
120-
"eslint-plugin-import-x>unrs-resolver": false
121+
"vitest>@vitest/mocker>msw": false
121122
}
122123
},
123124
"resolutions": {

packages/extension/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"@metamask/eslint-config-typescript": "^14.0.0",
6464
"@ocap/cli": "workspace:^",
6565
"@ocap/test-utils": "workspace:^",
66+
"@ocap/vite-plugins": "workspace:^",
6667
"@playwright/test": "^1.51.1",
6768
"@testing-library/dom": "^10.4.0",
6869
"@testing-library/jest-dom": "^6.6.3",
@@ -76,7 +77,6 @@
7677
"@typescript-eslint/utils": "^8.29.0",
7778
"@vitejs/plugin-react": "^4.3.4",
7879
"@vitest/eslint-plugin": "^1.1.44",
79-
"cheerio": "^1.0.0",
8080
"depcheck": "^1.4.7",
8181
"eslint": "^9.23.0",
8282
"eslint-config-prettier": "^10.1.1",
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
// @ts-check
2+
13
import path from 'path';
24

35
export const sourceDir = './src';
46
export const buildDir = path.resolve(sourceDir, '../dist');
57

8+
/**
9+
* @type {import('@ocap/vite-plugins').PreludeRecord}
10+
*/
611
export const trustedPreludes = {
7-
background: path.resolve(sourceDir, 'env/background-trusted-prelude.js'),
12+
background: {
13+
path: path.resolve(sourceDir, 'env/background-trusted-prelude.js'),
14+
},
815
};

packages/extension/src/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"permissions": ["offscreen", "unlimitedStorage"],
1414
"sandbox": {
15-
"pages": ["iframe.html"]
15+
"pages": ["browser-runtime/vat/iframe.html"]
1616
},
1717
"content_security_policy": {
1818
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'none'; frame-ancestors 'none';",

packages/extension/src/offscreen.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ async function makeKernelWorker(): Promise<{
5151
kernelStream: DuplexStream<JsonRpcResponse, JsonRpcCall>;
5252
vatWorkerService: VatWorkerServer;
5353
}> {
54-
const worker = new Worker('kernel-worker/index.mjs', { type: 'module' });
54+
const worker = new Worker('browser-runtime/kernel-worker/index.js', {
55+
type: 'module',
56+
});
5557

5658
const port = await initializeMessageChannel((message, transfer) =>
5759
worker.postMessage(message, transfer),
@@ -67,7 +69,7 @@ async function makeKernelWorker(): Promise<{
6769
(vatId) =>
6870
makeIframeVatWorker({
6971
id: vatId,
70-
iframeUri: 'iframe.html',
72+
iframeUri: 'browser-runtime/vat/iframe.html',
7173
getPort: initializeMessageChannel,
7274
logger: logger.subLogger({
7375
tags: ['iframe-vat-worker', vatId],

packages/extension/test/build/build-tests.mjs

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,35 @@ import {
77
trustedPreludes,
88
} from '../../scripts/build-constants.mjs';
99

10+
const { hasOwn } = Object;
11+
1012
const untransformedFiles = [
1113
{
1214
sourcePath: path.resolve('../kernel-shims/dist/endoify.js'),
13-
builtPath: path.resolve(buildDir, 'endoify.js'),
15+
buildPath: path.resolve(buildDir, 'endoify.js'),
1416
},
1517
{
1618
sourcePath: path.resolve(sourceDir, 'env/dev-console.js'),
17-
builtPath: path.resolve(buildDir, 'dev-console.js'),
19+
buildPath: path.resolve(buildDir, 'dev-console.js'),
1820
},
19-
...Object.values(trustedPreludes).map((preludePath) => ({
20-
sourcePath: preludePath,
21-
builtPath: path.join(buildDir, path.basename(preludePath)),
22-
})),
21+
...Object.values(trustedPreludes).map((prelude) => {
22+
if (hasOwn(prelude, 'path')) {
23+
return {
24+
sourcePath: prelude.path,
25+
buildPath: path.join(buildDir, path.basename(prelude.path)),
26+
};
27+
}
28+
29+
const preludePath = /^import ["']([^"']+)["']/iu.exec(prelude.content)[1];
30+
if (!preludePath) {
31+
throw new Error('No prelude path found in content');
32+
}
33+
34+
return {
35+
sourcePath: preludePath,
36+
buildPath: path.join(buildDir, path.basename(preludePath)),
37+
};
38+
}),
2339
];
2440

2541
await runTests();
@@ -42,16 +58,14 @@ async function runTests() {
4258
* Test that shims and preludes are packaged untransformed.
4359
*/
4460
async function checkUntransformed() {
45-
console.log('Checking if shims and preludes are packaged untransformed...');
46-
47-
for (const { builtPath, sourcePath } of untransformedFiles) {
61+
for (const { buildPath, sourcePath } of untransformedFiles) {
4862
const [originalContent, builtContent] = await Promise.all([
4963
fs.readFile(sourcePath, 'utf8'),
50-
fs.readFile(builtPath, 'utf8'),
64+
fs.readFile(buildPath, 'utf8'),
5165
]);
5266
if (originalContent.trim() !== builtContent.trim()) {
5367
throw new Error(
54-
`The ${builtPath} is transformed or differs from the original source.`,
68+
`"${buildPath}" is transformed or differs from the original source.`,
5569
);
5670
}
5771
}
@@ -61,15 +75,16 @@ async function checkUntransformed() {
6175
* Test that trusted preludes are loaded at the top of the file.
6276
*/
6377
async function checkTrustedPreludes() {
64-
console.log('Checking that trusted preludes are loaded at the top...');
78+
for (const [outputFileName, prelude] of Object.entries(trustedPreludes)) {
79+
const outputFilePath = path.join(buildDir, `${outputFileName}.js`);
80+
const outputFileContent = await fs.readFile(outputFilePath, 'utf8');
81+
const expectedImportStatement = hasOwn(prelude, 'path')
82+
? `import "./${path.basename(prelude.path)}";`
83+
: prelude.content;
6584

66-
for (const [preludeName, preludePath] of Object.entries(trustedPreludes)) {
67-
const expectedImport = path.basename(preludePath);
68-
const builtFilePath = path.join(buildDir, `${preludeName}.js`);
69-
const content = await fs.readFile(builtFilePath, 'utf8');
70-
if (!content.startsWith(`import "./${expectedImport}";`)) {
85+
if (!outputFileContent.startsWith(expectedImportStatement)) {
7186
throw new Error(
72-
`The trusted prelude ${expectedImport} is not imported in the first position in ${preludeName}.js`,
87+
`The trusted prelude \`${expectedImportStatement}\` is not imported in the first position in "${outputFileName}.js"`,
7388
);
7489
}
7590
}

packages/extension/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
{ "path": "../logger" },
2929
{ "path": "../ocap-kernel" },
3030
{ "path": "../streams" },
31-
{ "path": "../test-utils" }
31+
{ "path": "../test-utils" },
32+
{ "path": "../vite-plugins" }
3233
],
3334
"include": [
3435
"../../vitest.config.ts",

packages/extension/vite.config.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// eslint-disable-next-line spaced-comment
22
/// <reference types="vitest" />
33

4+
import {
5+
extensionDev,
6+
htmlTrustedPrelude,
7+
jsTrustedPrelude,
8+
} from '@ocap/vite-plugins';
49
import react from '@vitejs/plugin-react';
510
import path from 'path';
611
import sourcemaps from 'rollup-plugin-sourcemaps2';
@@ -15,9 +20,6 @@ import {
1520
buildDir,
1621
trustedPreludes,
1722
} from './scripts/build-constants.mjs';
18-
import { extensionDev } from './vite-plugins/extension-dev.ts';
19-
import { htmlTrustedPrelude } from './vite-plugins/html-trusted-prelude.ts';
20-
import { jsTrustedPrelude } from './vite-plugins/js-trusted-prelude.ts';
2123

2224
/**
2325
* Files that need to be statically copied to the destination directory.
@@ -28,14 +30,12 @@ const staticCopyTargets: readonly (string | Target)[] = [
2830
'manifest.json',
2931
// External modules
3032
'env/dev-console.js',
33+
'env/background-trusted-prelude.js',
3134
'../../kernel-shims/dist/endoify.js',
3235
{
33-
src: '../../kernel-browser-runtime/dist/kernel-worker/*',
34-
dest: './kernel-worker',
35-
// rename: 'kernel-worker.js',
36+
src: '../../kernel-browser-runtime/dist/static/*',
37+
dest: './browser-runtime',
3638
},
37-
// Trusted preludes
38-
...new Set(Object.values(trustedPreludes)),
3939
];
4040

4141
// https://vitejs.dev/config/
@@ -56,7 +56,6 @@ export default defineConfig(({ mode }) => {
5656
input: {
5757
background: path.resolve(sourceDir, 'background.ts'),
5858
offscreen: path.resolve(sourceDir, 'offscreen.html'),
59-
iframe: path.resolve(sourceDir, 'iframe.html'),
6059
popup: path.resolve(sourceDir, 'popup.html'),
6160
},
6261
output: {

packages/kernel-browser-runtime/package.json

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
"dist/"
4040
],
4141
"scripts": {
42-
"build": "yarn build:ts && yarn build:kernel-worker",
42+
"build": "yarn build:ts && yarn build:vite",
4343
"build:ts": "ts-bridge --project tsconfig.build.json --clean",
44-
"build:kernel-worker": "node scripts/build-kernel-worker.js",
44+
"build:vite": "vite build --config vite.config.ts && yarn test:build",
4545
"build:docs": "typedoc",
4646
"changelog:update": "../../scripts/update-changelog.sh @metamask/kernel-browser-runtime",
4747
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/kernel-browser-runtime",
@@ -53,6 +53,7 @@
5353
"lint:misc": "prettier --no-error-on-unmatched-pattern '**/*.json' '**/*.md' '**/*.html' '!**/CHANGELOG.old.md' '**/*.yml' '!.yarnrc.yml' '!merged-packages/**' --ignore-path ../../.gitignore",
5454
"publish:preview": "yarn npm publish --tag preview",
5555
"test": "vitest run --config vitest.config.ts",
56+
"test:build": "node ./test/build-tests.mjs",
5657
"test:clean": "yarn test --no-cache --coverage.clean",
5758
"test:dev": "yarn test --mode development",
5859
"test:verbose": "yarn test --reporter verbose",
@@ -82,14 +83,14 @@
8283
"@metamask/eslint-config-nodejs": "^14.0.0",
8384
"@metamask/eslint-config-typescript": "^14.0.0",
8485
"@ocap/test-utils": "workspace:^",
86+
"@ocap/vite-plugins": "workspace:^",
8587
"@ts-bridge/cli": "^0.6.3",
8688
"@ts-bridge/shims": "^0.1.1",
8789
"@typescript-eslint/eslint-plugin": "^8.29.0",
8890
"@typescript-eslint/parser": "^8.29.0",
8991
"@typescript-eslint/utils": "^8.29.0",
9092
"@vitest/eslint-plugin": "^1.1.44",
9193
"depcheck": "^1.4.7",
92-
"esbuild": "^0.25.3",
9394
"eslint": "^9.23.0",
9495
"eslint-config-prettier": "^10.1.1",
9596
"eslint-import-resolver-typescript": "^4.3.1",
@@ -98,13 +99,15 @@
9899
"eslint-plugin-n": "^17.17.0",
99100
"eslint-plugin-prettier": "^5.2.6",
100101
"eslint-plugin-promise": "^7.2.1",
101-
"has-flag": "^5.0.1",
102102
"prettier": "^3.5.3",
103103
"rimraf": "^6.0.1",
104+
"rollup-plugin-sourcemaps2": "^0.5.0",
104105
"typedoc": "^0.28.1",
105106
"typescript": "~5.8.2",
106107
"typescript-eslint": "^8.29.0",
107108
"vite": "^6.3.5",
109+
"vite-plugin-checker": "^0.9.1",
110+
"vite-plugin-static-copy": "^2.3.0",
108111
"vitest": "^3.1.3"
109112
},
110113
"engines": {

packages/kernel-browser-runtime/scripts/build-kernel-worker.js

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)