@swiftbrowser/web 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node/dist/node/config.d.ts.map +1 -1
- package/node/dist/node/config.js +12 -5
- package/node/dist/node/config.js.map +1 -1
- package/node/dist/plugins/playground.d.ts +49 -16
- package/node/dist/plugins/playground.d.ts.map +1 -1
- package/node/dist/plugins/playground.js +168 -22
- package/node/dist/plugins/playground.js.map +1 -1
- package/node/dist/src/playground/files.d.ts +67 -9
- package/node/dist/src/playground/files.d.ts.map +1 -1
- package/node/dist/src/playground/files.js +29 -6
- package/node/dist/src/playground/files.js.map +1 -1
- package/package.json +2 -1
- package/plugins/playground.ts +167 -27
- package/src/playground/assets.ts +151 -50
- package/src/playground/files.ts +83 -14
- package/src/playground/main.ts +14 -2
- package/src/playground/playground.css +1 -0
- package/src/playground/worker.ts +6 -4
- package/src/vite-env.d.ts +2 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,OAAO,EAAmD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAChI,OAAO,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,OAAO,EAAmD,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAChI,OAAO,EAAE,KAAK,iBAAiB,EAAyC,MAAM,0BAA0B,CAAC;AAEzG,OAAO,EAAmC,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sGAAsG;IACtG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAAC;CAChC;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC,GAAG,UAAU,CAG9F;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oHAAoH;IACpH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAOD;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAoBhC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,EAAE,CAWnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAmEjE"}
|
package/node/dist/node/config.js
CHANGED
|
@@ -20,7 +20,7 @@ import { assetCatalogs } from '../plugins/asset-catalogs.js';
|
|
|
20
20
|
import { bundleResources } from '../plugins/bundle-resources.js';
|
|
21
21
|
import { includedSites, readSiteApps } from '../plugins/included-sites.js';
|
|
22
22
|
import { ROOT_START_URL, siteLayoutPlugin } from '../plugins/site-layout.js';
|
|
23
|
-
import {
|
|
23
|
+
import { playgroundAliases, playgroundPackages } from '../plugins/playground.js';
|
|
24
24
|
import { swiftWasm } from '../plugins/swift-wasm.js';
|
|
25
25
|
import { displayNameOf, validateAppSpecs } from './apps.js';
|
|
26
26
|
/** The layout `options` resolve to (see `SiteOptions.layout`). */
|
|
@@ -122,9 +122,17 @@ export function createViteConfig(options) {
|
|
|
122
122
|
bundleResources(apps),
|
|
123
123
|
includedSites(landing, sites),
|
|
124
124
|
siteLayoutPlugin(site, main),
|
|
125
|
-
...(options.playground ? [
|
|
125
|
+
...(options.playground ? [playgroundPackages(path.resolve(root, '..'), options.playground)] : []),
|
|
126
126
|
],
|
|
127
|
-
...(options.playground
|
|
127
|
+
...(options.playground
|
|
128
|
+
? {
|
|
129
|
+
resolve: { alias: playgroundAliases(path.resolve(root, '..')) },
|
|
130
|
+
worker: { format: 'es' },
|
|
131
|
+
// Found only once the worker loads it, it would make the dev server
|
|
132
|
+
// reload the page mid-download.
|
|
133
|
+
optimizeDeps: { include: ['brotli-dec-wasm/web'] },
|
|
134
|
+
}
|
|
135
|
+
: {}),
|
|
128
136
|
// The landing page at `/` and the app at `/app/` (or the app alone). `mpa`
|
|
129
137
|
// keeps the dev server from answering unknown URLs with a page.
|
|
130
138
|
appType: 'mpa',
|
|
@@ -135,8 +143,7 @@ export function createViteConfig(options) {
|
|
|
135
143
|
__SB_COMMIT__: JSON.stringify(options.commit ?? 'dev'),
|
|
136
144
|
__SB_BRANCH__: JSON.stringify(options.branch ?? ''),
|
|
137
145
|
__SB_BUILT_AT__: JSON.stringify(new Date().toISOString()),
|
|
138
|
-
|
|
139
|
-
__SB_PLAYGROUND_FILES__: JSON.stringify(options.playground ? playgroundFiles(options.playground) : null),
|
|
146
|
+
__SB_PLAYGROUND__: JSON.stringify(options.playground ?? null),
|
|
140
147
|
},
|
|
141
148
|
server: {
|
|
142
149
|
port: 5173,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAqD,MAAM,2BAA2B,CAAC;AAChI,OAAO,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,24 +3,57 @@
|
|
|
3
3
|
* by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
|
|
4
4
|
* Only this repository's site has it: its code imports
|
|
5
5
|
* @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
|
|
6
|
-
* (the aliases below).
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* (the aliases below). The page fetches the npm packages it reads
|
|
7
|
+
* (src/playground/files.ts) from a CDN that serves them file by file, at one
|
|
8
|
+
* version, except those the site serves itself (`localPackages`): the kit
|
|
9
|
+
* and @swiftbrowser/playground made from the same commit as the site's
|
|
10
|
+
* renderer, so the two cannot drift, and any package the CDN does not have
|
|
11
|
+
* at that version. The dev server serves this repository's packages at
|
|
12
|
+
* /npm/, laid out as the CDN serves them, and a built site carries the
|
|
13
|
+
* files the page reads of its local packages there.
|
|
11
14
|
*/
|
|
12
15
|
import type { Plugin } from 'vite';
|
|
13
|
-
import { type
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
import { type PlaygroundManifest, type PlaygroundSource } from '../src/playground/files.js';
|
|
17
|
+
export type PlaygroundOptions = PlaygroundSource;
|
|
18
|
+
/** Where the dev server serves the workspaces' packages, and a built site its local ones. */
|
|
19
|
+
export declare const PLAYGROUND_NPM_PATH = "/npm/";
|
|
20
|
+
/** The kit, which must come from the same commit as the renderer. */
|
|
21
|
+
export declare const KIT_PACKAGE = "@swiftbrowser/kit";
|
|
22
|
+
/**
|
|
23
|
+
* Files a site stores brotli-compressed past this size: Cloudflare Pages
|
|
24
|
+
* serves files of up to 25 MiB.
|
|
25
|
+
*/
|
|
26
|
+
export declare const SITE_FILE_LIMIT: number;
|
|
27
|
+
/**
|
|
28
|
+
* Files a site stores brotli-compressed from this size, when compressing
|
|
29
|
+
* saves a tenth: a static host may not compress what it serves in transit
|
|
30
|
+
* (Cloudflare Pages leaves application/octet-stream as it is), and the
|
|
31
|
+
* archives and modules compress to about a fifth.
|
|
32
|
+
*/
|
|
33
|
+
export declare const SITE_COMPRESS_FROM: number;
|
|
23
34
|
/** The source aliases of the playground's imports, from the repository root. */
|
|
24
35
|
export declare function playgroundAliases(repoRoot: string): Record<string, string>;
|
|
25
|
-
|
|
36
|
+
/** The workspaces' package directories by package name. */
|
|
37
|
+
export declare function workspacePackages(repoRoot: string): Map<string, string>;
|
|
38
|
+
/** `@scope/name@version/file` → [name, file], whatever the version. */
|
|
39
|
+
export declare function packageRequest(pathname: string): [string, string] | null;
|
|
40
|
+
/** Every file of `manifest` by package, as the package holds it. */
|
|
41
|
+
export declare function manifestFiles(manifest: PlaygroundManifest): Map<string, string[]>;
|
|
42
|
+
/**
|
|
43
|
+
* The packages the site serves itself: @swiftbrowser/playground and the kit
|
|
44
|
+
* always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
|
|
45
|
+
* what the workspaces hold so a browser's cache keeps no stale file.
|
|
46
|
+
*/
|
|
47
|
+
export declare function localPackages(repoRoot: string, others?: readonly string[]): Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* Writes the files the page reads of each local package under `outDir`, at
|
|
50
|
+
* the paths `packages` names: brotli-compressed as `<file>.brotli` from
|
|
51
|
+
* SITE_COMPRESS_FROM (and always over the size limit), and playground.json
|
|
52
|
+
* naming those (`brotli`).
|
|
53
|
+
*/
|
|
54
|
+
export declare function writeLocalPackages(repoRoot: string, packages: Record<string, string>, outDir: string, limit?: number, compressFrom?: number): {
|
|
55
|
+
files: number;
|
|
56
|
+
bytes: number;
|
|
57
|
+
};
|
|
58
|
+
export declare function playgroundPackages(repoRoot: string, options: PlaygroundOptions): Plugin;
|
|
26
59
|
//# sourceMappingURL=playground.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAQL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,qEAAqE;AACrE,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAmB,CAAC;AAIhD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAE5C,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1E;AAED,2DAA2D;AAC3D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQvE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAGxE;AAED,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,kBAAkB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQjF;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,MAAM,EAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CActG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,SAAkB,EAAE,YAAY,SAAqB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAkDnM;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA0BvF"}
|
|
@@ -3,23 +3,38 @@
|
|
|
3
3
|
* by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
|
|
4
4
|
* Only this repository's site has it: its code imports
|
|
5
5
|
* @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
|
|
6
|
-
* (the aliases below).
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* (the aliases below). The page fetches the npm packages it reads
|
|
7
|
+
* (src/playground/files.ts) from a CDN that serves them file by file, at one
|
|
8
|
+
* version, except those the site serves itself (`localPackages`): the kit
|
|
9
|
+
* and @swiftbrowser/playground made from the same commit as the site's
|
|
10
|
+
* renderer, so the two cannot drift, and any package the CDN does not have
|
|
11
|
+
* at that version. The dev server serves this repository's packages at
|
|
12
|
+
* /npm/, laid out as the CDN serves them, and a built site carries the
|
|
13
|
+
* files the page reads of its local packages there.
|
|
11
14
|
*/
|
|
12
|
-
import {
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
13
17
|
import path from 'node:path';
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
import { brotliCompressSync, constants } from 'node:zlib';
|
|
19
|
+
import { PLAYGROUND_ICU, PLAYGROUND_MANIFEST, PLAYGROUND_MODULE_CACHE, PLAYGROUND_PACKAGE, SITE_BROTLI, isBrotli, } from '../src/playground/files.js';
|
|
20
|
+
/** Where the dev server serves the workspaces' packages, and a built site its local ones. */
|
|
21
|
+
export const PLAYGROUND_NPM_PATH = '/npm/';
|
|
22
|
+
/** The kit, which must come from the same commit as the renderer. */
|
|
23
|
+
export const KIT_PACKAGE = '@swiftbrowser/kit';
|
|
24
|
+
/**
|
|
25
|
+
* Files a site stores brotli-compressed past this size: Cloudflare Pages
|
|
26
|
+
* serves files of up to 25 MiB.
|
|
27
|
+
*/
|
|
28
|
+
export const SITE_FILE_LIMIT = 24 * 1024 * 1024;
|
|
29
|
+
/** Bumped whenever writeLocalPackages writes other files for the same packages. */
|
|
30
|
+
const SITE_LAYOUT = 2;
|
|
31
|
+
/**
|
|
32
|
+
* Files a site stores brotli-compressed from this size, when compressing
|
|
33
|
+
* saves a tenth: a static host may not compress what it serves in transit
|
|
34
|
+
* (Cloudflare Pages leaves application/octet-stream as it is), and the
|
|
35
|
+
* archives and modules compress to about a fifth.
|
|
36
|
+
*/
|
|
37
|
+
export const SITE_COMPRESS_FROM = 64 * 1024;
|
|
23
38
|
/** The source aliases of the playground's imports, from the repository root. */
|
|
24
39
|
export function playgroundAliases(repoRoot) {
|
|
25
40
|
return {
|
|
@@ -27,20 +42,151 @@ export function playgroundAliases(repoRoot) {
|
|
|
27
42
|
'@swiftbrowser/cli/browser': path.join(repoRoot, 'packages/cli/src/browser.ts'),
|
|
28
43
|
};
|
|
29
44
|
}
|
|
30
|
-
|
|
45
|
+
/** The workspaces' package directories by package name. */
|
|
46
|
+
export function workspacePackages(repoRoot) {
|
|
47
|
+
const dirs = new Map();
|
|
48
|
+
const packagesDir = path.join(repoRoot, 'packages');
|
|
49
|
+
for (const name of readdirSync(packagesDir)) {
|
|
50
|
+
const manifest = path.join(packagesDir, name, 'package.json');
|
|
51
|
+
if (existsSync(manifest))
|
|
52
|
+
dirs.set(JSON.parse(readFileSync(manifest, 'utf8')).name, path.join(packagesDir, name));
|
|
53
|
+
}
|
|
54
|
+
return dirs;
|
|
55
|
+
}
|
|
56
|
+
/** `@scope/name@version/file` → [name, file], whatever the version. */
|
|
57
|
+
export function packageRequest(pathname) {
|
|
58
|
+
const match = /^(@[^/]+\/[^/@]+)@[^/]+\/(.+)$/.exec(pathname);
|
|
59
|
+
return match ? [match[1], match[2]] : null;
|
|
60
|
+
}
|
|
61
|
+
/** Every file of `manifest` by package, as the package holds it. */
|
|
62
|
+
export function manifestFiles(manifest) {
|
|
63
|
+
const files = new Map();
|
|
64
|
+
const add = ({ package: name, file }) => void (files.get(name) ?? files.set(name, []).get(name)).push(file);
|
|
65
|
+
for (const file of [manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)])
|
|
66
|
+
add(file);
|
|
67
|
+
for (const [name, list] of Object.entries(manifest.sdk))
|
|
68
|
+
for (const file of list)
|
|
69
|
+
add({ package: name, file: `wasm32-unknown-wasip1/${file}` });
|
|
70
|
+
for (const file of [...manifest.kit, 'kit.json'])
|
|
71
|
+
add({ package: KIT_PACKAGE, file });
|
|
72
|
+
for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE])
|
|
73
|
+
add({ package: PLAYGROUND_PACKAGE, file });
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The packages the site serves itself: @swiftbrowser/playground and the kit
|
|
78
|
+
* always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
|
|
79
|
+
* what the workspaces hold so a browser's cache keeps no stale file.
|
|
80
|
+
*/
|
|
81
|
+
export function localPackages(repoRoot, others = []) {
|
|
82
|
+
const dirs = workspacePackages(repoRoot);
|
|
83
|
+
const own = dirs.get(PLAYGROUND_PACKAGE);
|
|
84
|
+
const manifestFile = path.join(own, PLAYGROUND_MANIFEST);
|
|
85
|
+
if (!existsSync(manifestFile))
|
|
86
|
+
throw new Error(`${manifestFile} is missing: run \`npm run playground:assets --workspace @swiftbrowser/web\``);
|
|
87
|
+
const hash = createHash('sha256');
|
|
88
|
+
for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE])
|
|
89
|
+
hash.update(readFileSync(path.join(own, file)));
|
|
90
|
+
hash.update(readFileSync(path.join(dirs.get(KIT_PACKAGE), 'kit.json')));
|
|
91
|
+
const names = [...new Set([PLAYGROUND_PACKAGE, KIT_PACKAGE, ...others])].sort();
|
|
92
|
+
for (const name of names)
|
|
93
|
+
hash.update(`${name}\n`);
|
|
94
|
+
// And how writeLocalPackages lays them out: another layout is other files.
|
|
95
|
+
hash.update(`layout ${SITE_LAYOUT} ${SITE_FILE_LIMIT} ${SITE_COMPRESS_FROM}\n`);
|
|
96
|
+
const version = `site-${hash.digest('hex').slice(0, 12)}`;
|
|
97
|
+
return Object.fromEntries(names.map((name) => [name, `${PLAYGROUND_NPM_PATH}${name}@${version}/`]));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Writes the files the page reads of each local package under `outDir`, at
|
|
101
|
+
* the paths `packages` names: brotli-compressed as `<file>.brotli` from
|
|
102
|
+
* SITE_COMPRESS_FROM (and always over the size limit), and playground.json
|
|
103
|
+
* naming those (`brotli`).
|
|
104
|
+
*/
|
|
105
|
+
export function writeLocalPackages(repoRoot, packages, outDir, limit = SITE_FILE_LIMIT, compressFrom = SITE_COMPRESS_FROM) {
|
|
106
|
+
const dirs = workspacePackages(repoRoot);
|
|
107
|
+
const manifest = JSON.parse(readFileSync(path.join(dirs.get(PLAYGROUND_PACKAGE), PLAYGROUND_MANIFEST), 'utf8'));
|
|
108
|
+
const brotli = new Set();
|
|
109
|
+
const rename = (file) => (brotli.has(`${file.package}/${file.file}`) ? { ...file, file: `${file.file}${SITE_BROTLI}` } : file);
|
|
110
|
+
const written = { files: 0, bytes: 0 };
|
|
111
|
+
for (const [name, files] of manifestFiles(manifest)) {
|
|
112
|
+
const base = packages[name];
|
|
113
|
+
if (!base)
|
|
114
|
+
continue;
|
|
115
|
+
const dir = dirs.get(name);
|
|
116
|
+
if (!dir)
|
|
117
|
+
throw new Error(`the site serves ${name}, which is not a package of this repository`);
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
if (name === PLAYGROUND_PACKAGE && file === PLAYGROUND_MANIFEST)
|
|
120
|
+
continue;
|
|
121
|
+
let data = readFileSync(path.join(dir, file));
|
|
122
|
+
let target = file;
|
|
123
|
+
if (isBrotli(file)) {
|
|
124
|
+
if (data.length > limit)
|
|
125
|
+
throw new Error(`${name}/${file} is over ${limit} bytes compressed: the site cannot serve it`);
|
|
126
|
+
}
|
|
127
|
+
else if (data.length >= compressFrom || data.length > limit) {
|
|
128
|
+
const compressed = brotliCompressSync(data, { params: { [constants.BROTLI_PARAM_QUALITY]: 5, [constants.BROTLI_PARAM_LGWIN]: 24, [constants.BROTLI_PARAM_SIZE_HINT]: data.length } });
|
|
129
|
+
if (compressed.length > limit)
|
|
130
|
+
throw new Error(`${name}/${file} is over ${limit} bytes even compressed`);
|
|
131
|
+
if (data.length > limit || compressed.length < data.length * 0.9) {
|
|
132
|
+
data = compressed;
|
|
133
|
+
brotli.add(`${name}/${file}`);
|
|
134
|
+
target = `${file}${SITE_BROTLI}`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const out = path.join(outDir, base, target);
|
|
138
|
+
mkdirSync(path.dirname(out), { recursive: true });
|
|
139
|
+
writeFileSync(out, data);
|
|
140
|
+
written.files += 1;
|
|
141
|
+
written.bytes += data.length;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (packages[PLAYGROUND_PACKAGE]) {
|
|
145
|
+
// A tool or plugin is named by its compressed file; the rest by the list.
|
|
146
|
+
const tools = new Set([manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)].map((file) => `${file.package}/${file.file}`));
|
|
147
|
+
const listed = [...brotli].filter((entry) => !tools.has(entry)).sort();
|
|
148
|
+
const site = {
|
|
149
|
+
...manifest,
|
|
150
|
+
frontend: rename(manifest.frontend),
|
|
151
|
+
linker: rename(manifest.linker),
|
|
152
|
+
plugins: Object.fromEntries(Object.entries(manifest.plugins).map(([macro, file]) => [macro, rename(file)])),
|
|
153
|
+
...(listed.length > 0 ? { brotli: listed } : {}),
|
|
154
|
+
};
|
|
155
|
+
const out = path.join(outDir, packages[PLAYGROUND_PACKAGE], PLAYGROUND_MANIFEST);
|
|
156
|
+
mkdirSync(path.dirname(out), { recursive: true });
|
|
157
|
+
writeFileSync(out, `${JSON.stringify(site)}\n`);
|
|
158
|
+
written.files += 1;
|
|
159
|
+
}
|
|
160
|
+
return written;
|
|
161
|
+
}
|
|
162
|
+
export function playgroundPackages(repoRoot, options) {
|
|
163
|
+
let outDir = '';
|
|
31
164
|
return {
|
|
32
|
-
name: 'swiftbrowser-playground-
|
|
165
|
+
name: 'swiftbrowser-playground-packages',
|
|
166
|
+
configResolved(config) {
|
|
167
|
+
outDir = path.resolve(config.root, config.build.outDir);
|
|
168
|
+
},
|
|
33
169
|
configureServer(server) {
|
|
34
|
-
const
|
|
35
|
-
server.middlewares.use(
|
|
36
|
-
const
|
|
37
|
-
|
|
170
|
+
const packages = workspacePackages(repoRoot);
|
|
171
|
+
server.middlewares.use(PLAYGROUND_NPM_PATH, (req, res, next) => {
|
|
172
|
+
const request = packageRequest(decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname).replace(/^\//, ''));
|
|
173
|
+
const dir = request && packages.get(request[0]);
|
|
174
|
+
if (!request || !dir)
|
|
38
175
|
return next();
|
|
39
|
-
|
|
176
|
+
const file = path.join(dir, request[1]);
|
|
177
|
+
if (!existsSync(file) || statSync(file).isDirectory() || !realpathSync(file).startsWith(realpathSync(dir)))
|
|
178
|
+
return next();
|
|
179
|
+
res.setHeader('Content-Type', file.endsWith('.wasm') ? 'application/wasm' : file.endsWith('.json') ? 'application/json' : 'application/octet-stream');
|
|
40
180
|
res.setHeader('Content-Length', statSync(file).size);
|
|
41
181
|
createReadStream(file).pipe(res);
|
|
42
182
|
});
|
|
43
183
|
},
|
|
184
|
+
writeBundle() {
|
|
185
|
+
if (!options.packages)
|
|
186
|
+
return;
|
|
187
|
+
const { files, bytes } = writeLocalPackages(repoRoot, options.packages, outDir);
|
|
188
|
+
this.info(`the playground's local packages: ${files} files, ${(bytes / 1e6).toFixed(1)} MB`);
|
|
189
|
+
},
|
|
44
190
|
};
|
|
45
191
|
}
|
|
46
192
|
//# sourceMappingURL=playground.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"playground.js","sourceRoot":"","sources":["../../../plugins/playground.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,70 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
export declare const PLAYGROUND_PACKAGE = "@swiftbrowser/playground";
|
|
2
|
+
export declare const PLAYGROUND_MANIFEST = "playground.json";
|
|
3
|
+
/** The trimmed ICU data, in @swiftbrowser/playground. */
|
|
4
|
+
export declare const PLAYGROUND_ICU = "icu.dat";
|
|
5
|
+
/** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
|
|
6
|
+
export declare const PLAYGROUND_MODULE_CACHE = "module-cache.pack";
|
|
7
|
+
/** Where the npm packages are fetched from by default. */
|
|
8
|
+
export declare const JSDELIVR = "https://cdn.jsdelivr.net/npm/";
|
|
9
|
+
/** A file of one of the packages. */
|
|
10
|
+
export interface PackageFile {
|
|
11
|
+
package: string;
|
|
12
|
+
/** The path in the package; `.br` files are brotli-compressed. */
|
|
13
|
+
file: string;
|
|
14
|
+
}
|
|
15
|
+
/** @swiftbrowser/playground's playground.json. */
|
|
16
|
+
export interface PlaygroundManifest {
|
|
17
|
+
/** The symbol Foundation's ICU reads its data through (`icudtNN_dat`). */
|
|
18
|
+
icuSymbol: string;
|
|
19
|
+
/** The apps whose compiles recorded the files. */
|
|
20
|
+
apps: string[];
|
|
21
|
+
frontend: PackageFile;
|
|
22
|
+
linker: PackageFile;
|
|
8
23
|
/** The macro plugins by the module whose macros they expand. */
|
|
9
|
-
plugins: Record<string,
|
|
24
|
+
plugins: Record<string, PackageFile>;
|
|
25
|
+
/**
|
|
26
|
+
* The Swift SDK's files a compile reads, by package, each relative to the
|
|
27
|
+
* SDK's `wasm32-unknown-wasip1` directory (the package's own
|
|
28
|
+
* `wasm32-unknown-wasip1/` holds it there).
|
|
29
|
+
*/
|
|
30
|
+
sdk: Record<string, string[]>;
|
|
31
|
+
/** The kit's files a compile reads, relative to @swiftbrowser/kit (`wasm32-unknown-wasip1/...`). */
|
|
32
|
+
kit: string[];
|
|
33
|
+
/**
|
|
34
|
+
* The SwiftBrowser sources the kit was built from (@swiftbrowser/swift's
|
|
35
|
+
* `sourcesHash`), when build-assets.ts could tell: the page compares it
|
|
36
|
+
* with the sources its own renderer was built from.
|
|
37
|
+
*/
|
|
38
|
+
shimSources?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Files, as `<package>/<path>`, that the site serving them stores
|
|
41
|
+
* brotli-compressed (`<path>.brotli`, SITE_BROTLI): an SDK or kit file,
|
|
42
|
+
* the ICU data or the module cache (plugins/playground.ts). A tool or
|
|
43
|
+
* plugin is named by its compressed file instead.
|
|
44
|
+
*/
|
|
45
|
+
brotli?: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The suffix of a file a site stores brotli-compressed. Not `.br`: a static
|
|
49
|
+
* server may send a `.br` file as `Content-Encoding: br`, which the browser
|
|
50
|
+
* unpacks before the page could.
|
|
51
|
+
*/
|
|
52
|
+
export declare const SITE_BROTLI = ".brotli";
|
|
53
|
+
/** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
|
|
54
|
+
export declare const isBrotli: (file: string) => boolean;
|
|
55
|
+
/** Where the packages come from. */
|
|
56
|
+
export interface PlaygroundSource {
|
|
57
|
+
/** A CDN's npm base URL, which serves every package at `version`... */
|
|
58
|
+
npm: string;
|
|
59
|
+
version: string;
|
|
60
|
+
/**
|
|
61
|
+
* ...but those named here, each served from its own base URL: the site's
|
|
62
|
+
* copy, made from the same commit as its renderer (plugins/playground.ts).
|
|
63
|
+
*/
|
|
64
|
+
packages?: Record<string, string>;
|
|
65
|
+
/** The SwiftBrowser sources the site's renderer was built from (`sourcesHash`). */
|
|
66
|
+
shimSources?: string;
|
|
10
67
|
}
|
|
11
|
-
|
|
68
|
+
/** The URL of a package's file. */
|
|
69
|
+
export declare function packageUrl(source: PlaygroundSource, name: string, file: string): string;
|
|
12
70
|
//# sourceMappingURL=files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAC7D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AACrD,yDAAyD;AACzD,eAAO,MAAM,cAAc,YAAY,CAAC;AACxC,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,sBAAsB,CAAC;AAC3D,0DAA0D;AAC1D,eAAO,MAAM,QAAQ,kCAAkC,CAAC;AAExD,qCAAqC;AACrC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,WAAW,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9B,oGAAoG;IACpG,GAAG,EAAE,MAAM,EAAE,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,4FAA4F;AAC5F,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,OAA6D,CAAC;AAEtG,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGvF"}
|
|
@@ -1,7 +1,30 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
1
|
+
// What the playground downloads, all from npm packages at one version (the
|
|
2
|
+
// release's lockstep version), through a CDN that serves npm packages file by
|
|
3
|
+
// file (jsDelivr): the compiler, the linker and the toolchain's macro plugins
|
|
4
|
+
// from @swiftbrowser/wasm-tools, the Swift SDK's files from the
|
|
5
|
+
// @swiftbrowser/wasm-sdk packages, the kit's from @swiftbrowser/kit, and what
|
|
6
|
+
// only the playground needs from @swiftbrowser/playground: its manifest
|
|
7
|
+
// (which files of those packages a compile reads), the trimmed ICU data and
|
|
8
|
+
// the module cache. playground/build-assets.ts makes that package.
|
|
9
|
+
export const PLAYGROUND_PACKAGE = '@swiftbrowser/playground';
|
|
10
|
+
export const PLAYGROUND_MANIFEST = 'playground.json';
|
|
11
|
+
/** The trimmed ICU data, in @swiftbrowser/playground. */
|
|
12
|
+
export const PLAYGROUND_ICU = 'icu.dat';
|
|
13
|
+
/** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
|
|
14
|
+
export const PLAYGROUND_MODULE_CACHE = 'module-cache.pack';
|
|
15
|
+
/** Where the npm packages are fetched from by default. */
|
|
16
|
+
export const JSDELIVR = 'https://cdn.jsdelivr.net/npm/';
|
|
17
|
+
/**
|
|
18
|
+
* The suffix of a file a site stores brotli-compressed. Not `.br`: a static
|
|
19
|
+
* server may send a `.br` file as `Content-Encoding: br`, which the browser
|
|
20
|
+
* unpacks before the page could.
|
|
21
|
+
*/
|
|
22
|
+
export const SITE_BROTLI = '.brotli';
|
|
23
|
+
/** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
|
|
24
|
+
export const isBrotli = (file) => file.endsWith('.br') || file.endsWith(SITE_BROTLI);
|
|
25
|
+
/** The URL of a package's file. */
|
|
26
|
+
export function packageUrl(source, name, file) {
|
|
27
|
+
const base = source.packages?.[name];
|
|
28
|
+
return base ? new URL(file, base).href : new URL(`${name}@${source.version}/${file}`, source.npm).href;
|
|
29
|
+
}
|
|
7
30
|
//# sourceMappingURL=files.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../../../src/playground/files.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,8EAA8E;AAC9E,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAC9E,wEAAwE;AACxE,4EAA4E;AAC5E,mEAAmE;AAEnE,MAAM,CAAC,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AACrD,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AACxC,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAC3D,0DAA0D;AAC1D,MAAM,CAAC,MAAM,QAAQ,GAAG,+BAA+B,CAAC;AA0CxD;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;AAErC,4FAA4F;AAC5F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AAgBtG,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,MAAwB,EAAE,IAAY,EAAE,IAAY;IAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;AACzG,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swiftbrowser/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "SwiftBrowser web renderer and dev server: applies render ops from a Swift/Wasm app to the DOM inside an iPhone frame.",
|
|
6
6
|
"repository": {
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"@playwright/test": "^1.56.0",
|
|
57
57
|
"@types/node": "^26.6.4",
|
|
58
58
|
"@types/qrcode": "^1.5.6",
|
|
59
|
+
"brotli-dec-wasm": "^2.3.2",
|
|
59
60
|
"typescript": "^5.9.2",
|
|
60
61
|
"vitest": "^5.0.3"
|
|
61
62
|
}
|
package/plugins/playground.ts
CHANGED
|
@@ -3,33 +3,54 @@
|
|
|
3
3
|
* by the WebAssembly compiler (docs/in-browser-compiler.md, Milestone 3).
|
|
4
4
|
* Only this repository's site has it: its code imports
|
|
5
5
|
* @swiftbrowser/wasm-compiler and the CLI's browser entry from their sources
|
|
6
|
-
* (the aliases below).
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* (the aliases below). The page fetches the npm packages it reads
|
|
7
|
+
* (src/playground/files.ts) from a CDN that serves them file by file, at one
|
|
8
|
+
* version, except those the site serves itself (`localPackages`): the kit
|
|
9
|
+
* and @swiftbrowser/playground made from the same commit as the site's
|
|
10
|
+
* renderer, so the two cannot drift, and any package the CDN does not have
|
|
11
|
+
* at that version. The dev server serves this repository's packages at
|
|
12
|
+
* /npm/, laid out as the CDN serves them, and a built site carries the
|
|
13
|
+
* files the page reads of its local packages there.
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
|
-
import {
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
import { createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
14
18
|
import path from 'node:path';
|
|
19
|
+
import { brotliCompressSync, constants } from 'node:zlib';
|
|
15
20
|
import type { Plugin } from 'vite';
|
|
16
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
PLAYGROUND_ICU,
|
|
23
|
+
PLAYGROUND_MANIFEST,
|
|
24
|
+
PLAYGROUND_MODULE_CACHE,
|
|
25
|
+
PLAYGROUND_PACKAGE,
|
|
26
|
+
SITE_BROTLI,
|
|
27
|
+
isBrotli,
|
|
28
|
+
type PackageFile,
|
|
29
|
+
type PlaygroundManifest,
|
|
30
|
+
type PlaygroundSource,
|
|
31
|
+
} from '../src/playground/files.js';
|
|
17
32
|
|
|
18
|
-
export
|
|
19
|
-
/** The directory build-assets wrote: its manifest, and the files the dev server serves at /playground-assets/. */
|
|
20
|
-
assetsDir: string;
|
|
21
|
-
/** Where the page fetches the assets from; default /playground-assets/. */
|
|
22
|
-
assetsUrl?: string;
|
|
23
|
-
}
|
|
33
|
+
export type PlaygroundOptions = PlaygroundSource;
|
|
24
34
|
|
|
25
|
-
/**
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
/** Where the dev server serves the workspaces' packages, and a built site its local ones. */
|
|
36
|
+
export const PLAYGROUND_NPM_PATH = '/npm/';
|
|
37
|
+
/** The kit, which must come from the same commit as the renderer. */
|
|
38
|
+
export const KIT_PACKAGE = '@swiftbrowser/kit';
|
|
39
|
+
/**
|
|
40
|
+
* Files a site stores brotli-compressed past this size: Cloudflare Pages
|
|
41
|
+
* serves files of up to 25 MiB.
|
|
42
|
+
*/
|
|
43
|
+
export const SITE_FILE_LIMIT = 24 * 1024 * 1024;
|
|
44
|
+
/** Bumped whenever writeLocalPackages writes other files for the same packages. */
|
|
45
|
+
const SITE_LAYOUT = 2;
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Files a site stores brotli-compressed from this size, when compressing
|
|
49
|
+
* saves a tenth: a static host may not compress what it serves in transit
|
|
50
|
+
* (Cloudflare Pages leaves application/octet-stream as it is), and the
|
|
51
|
+
* archives and modules compress to about a fifth.
|
|
52
|
+
*/
|
|
53
|
+
export const SITE_COMPRESS_FROM = 64 * 1024;
|
|
33
54
|
|
|
34
55
|
/** The source aliases of the playground's imports, from the repository root. */
|
|
35
56
|
export function playgroundAliases(repoRoot: string): Record<string, string> {
|
|
@@ -39,18 +60,137 @@ export function playgroundAliases(repoRoot: string): Record<string, string> {
|
|
|
39
60
|
};
|
|
40
61
|
}
|
|
41
62
|
|
|
42
|
-
|
|
63
|
+
/** The workspaces' package directories by package name. */
|
|
64
|
+
export function workspacePackages(repoRoot: string): Map<string, string> {
|
|
65
|
+
const dirs = new Map<string, string>();
|
|
66
|
+
const packagesDir = path.join(repoRoot, 'packages');
|
|
67
|
+
for (const name of readdirSync(packagesDir)) {
|
|
68
|
+
const manifest = path.join(packagesDir, name, 'package.json');
|
|
69
|
+
if (existsSync(manifest)) dirs.set((JSON.parse(readFileSync(manifest, 'utf8')) as { name: string }).name, path.join(packagesDir, name));
|
|
70
|
+
}
|
|
71
|
+
return dirs;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** `@scope/name@version/file` → [name, file], whatever the version. */
|
|
75
|
+
export function packageRequest(pathname: string): [string, string] | null {
|
|
76
|
+
const match = /^(@[^/]+\/[^/@]+)@[^/]+\/(.+)$/.exec(pathname);
|
|
77
|
+
return match ? [match[1]!, match[2]!] : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Every file of `manifest` by package, as the package holds it. */
|
|
81
|
+
export function manifestFiles(manifest: PlaygroundManifest): Map<string, string[]> {
|
|
82
|
+
const files = new Map<string, string[]>();
|
|
83
|
+
const add = ({ package: name, file }: PackageFile): void => void (files.get(name) ?? files.set(name, []).get(name)!).push(file);
|
|
84
|
+
for (const file of [manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)]) add(file);
|
|
85
|
+
for (const [name, list] of Object.entries(manifest.sdk)) for (const file of list) add({ package: name, file: `wasm32-unknown-wasip1/${file}` });
|
|
86
|
+
for (const file of [...manifest.kit, 'kit.json']) add({ package: KIT_PACKAGE, file });
|
|
87
|
+
for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE]) add({ package: PLAYGROUND_PACKAGE, file });
|
|
88
|
+
return files;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The packages the site serves itself: @swiftbrowser/playground and the kit
|
|
93
|
+
* always, and `others`, each at /npm/<name>@site-<hash>/, the hash naming
|
|
94
|
+
* what the workspaces hold so a browser's cache keeps no stale file.
|
|
95
|
+
*/
|
|
96
|
+
export function localPackages(repoRoot: string, others: readonly string[] = []): Record<string, string> {
|
|
97
|
+
const dirs = workspacePackages(repoRoot);
|
|
98
|
+
const own = dirs.get(PLAYGROUND_PACKAGE)!;
|
|
99
|
+
const manifestFile = path.join(own, PLAYGROUND_MANIFEST);
|
|
100
|
+
if (!existsSync(manifestFile)) throw new Error(`${manifestFile} is missing: run \`npm run playground:assets --workspace @swiftbrowser/web\``);
|
|
101
|
+
const hash = createHash('sha256');
|
|
102
|
+
for (const file of [PLAYGROUND_MANIFEST, PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE]) hash.update(readFileSync(path.join(own, file)));
|
|
103
|
+
hash.update(readFileSync(path.join(dirs.get(KIT_PACKAGE)!, 'kit.json')));
|
|
104
|
+
const names = [...new Set([PLAYGROUND_PACKAGE, KIT_PACKAGE, ...others])].sort();
|
|
105
|
+
for (const name of names) hash.update(`${name}\n`);
|
|
106
|
+
// And how writeLocalPackages lays them out: another layout is other files.
|
|
107
|
+
hash.update(`layout ${SITE_LAYOUT} ${SITE_FILE_LIMIT} ${SITE_COMPRESS_FROM}\n`);
|
|
108
|
+
const version = `site-${hash.digest('hex').slice(0, 12)}`;
|
|
109
|
+
return Object.fromEntries(names.map((name) => [name, `${PLAYGROUND_NPM_PATH}${name}@${version}/`]));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Writes the files the page reads of each local package under `outDir`, at
|
|
114
|
+
* the paths `packages` names: brotli-compressed as `<file>.brotli` from
|
|
115
|
+
* SITE_COMPRESS_FROM (and always over the size limit), and playground.json
|
|
116
|
+
* naming those (`brotli`).
|
|
117
|
+
*/
|
|
118
|
+
export function writeLocalPackages(repoRoot: string, packages: Record<string, string>, outDir: string, limit = SITE_FILE_LIMIT, compressFrom = SITE_COMPRESS_FROM): { files: number; bytes: number } {
|
|
119
|
+
const dirs = workspacePackages(repoRoot);
|
|
120
|
+
const manifest = JSON.parse(readFileSync(path.join(dirs.get(PLAYGROUND_PACKAGE)!, PLAYGROUND_MANIFEST), 'utf8')) as PlaygroundManifest;
|
|
121
|
+
const brotli = new Set<string>();
|
|
122
|
+
const rename = (file: PackageFile): PackageFile => (brotli.has(`${file.package}/${file.file}`) ? { ...file, file: `${file.file}${SITE_BROTLI}` } : file);
|
|
123
|
+
const written = { files: 0, bytes: 0 };
|
|
124
|
+
for (const [name, files] of manifestFiles(manifest)) {
|
|
125
|
+
const base = packages[name];
|
|
126
|
+
if (!base) continue;
|
|
127
|
+
const dir = dirs.get(name);
|
|
128
|
+
if (!dir) throw new Error(`the site serves ${name}, which is not a package of this repository`);
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
if (name === PLAYGROUND_PACKAGE && file === PLAYGROUND_MANIFEST) continue;
|
|
131
|
+
let data: Uint8Array = readFileSync(path.join(dir, file));
|
|
132
|
+
let target = file;
|
|
133
|
+
if (isBrotli(file)) {
|
|
134
|
+
if (data.length > limit) throw new Error(`${name}/${file} is over ${limit} bytes compressed: the site cannot serve it`);
|
|
135
|
+
} else if (data.length >= compressFrom || data.length > limit) {
|
|
136
|
+
const compressed = brotliCompressSync(data, { params: { [constants.BROTLI_PARAM_QUALITY]: 5, [constants.BROTLI_PARAM_LGWIN]: 24, [constants.BROTLI_PARAM_SIZE_HINT]: data.length } });
|
|
137
|
+
if (compressed.length > limit) throw new Error(`${name}/${file} is over ${limit} bytes even compressed`);
|
|
138
|
+
if (data.length > limit || compressed.length < data.length * 0.9) {
|
|
139
|
+
data = compressed;
|
|
140
|
+
brotli.add(`${name}/${file}`);
|
|
141
|
+
target = `${file}${SITE_BROTLI}`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const out = path.join(outDir, base, target);
|
|
145
|
+
mkdirSync(path.dirname(out), { recursive: true });
|
|
146
|
+
writeFileSync(out, data);
|
|
147
|
+
written.files += 1;
|
|
148
|
+
written.bytes += data.length;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (packages[PLAYGROUND_PACKAGE]) {
|
|
152
|
+
// A tool or plugin is named by its compressed file; the rest by the list.
|
|
153
|
+
const tools = new Set([manifest.frontend, manifest.linker, ...Object.values(manifest.plugins)].map((file) => `${file.package}/${file.file}`));
|
|
154
|
+
const listed = [...brotli].filter((entry) => !tools.has(entry)).sort();
|
|
155
|
+
const site: PlaygroundManifest = {
|
|
156
|
+
...manifest,
|
|
157
|
+
frontend: rename(manifest.frontend),
|
|
158
|
+
linker: rename(manifest.linker),
|
|
159
|
+
plugins: Object.fromEntries(Object.entries(manifest.plugins).map(([macro, file]) => [macro, rename(file)])),
|
|
160
|
+
...(listed.length > 0 ? { brotli: listed } : {}),
|
|
161
|
+
};
|
|
162
|
+
const out = path.join(outDir, packages[PLAYGROUND_PACKAGE], PLAYGROUND_MANIFEST);
|
|
163
|
+
mkdirSync(path.dirname(out), { recursive: true });
|
|
164
|
+
writeFileSync(out, `${JSON.stringify(site)}\n`);
|
|
165
|
+
written.files += 1;
|
|
166
|
+
}
|
|
167
|
+
return written;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function playgroundPackages(repoRoot: string, options: PlaygroundOptions): Plugin {
|
|
171
|
+
let outDir = '';
|
|
43
172
|
return {
|
|
44
|
-
name: 'swiftbrowser-playground-
|
|
173
|
+
name: 'swiftbrowser-playground-packages',
|
|
174
|
+
configResolved(config) {
|
|
175
|
+
outDir = path.resolve(config.root, config.build.outDir);
|
|
176
|
+
},
|
|
45
177
|
configureServer(server) {
|
|
46
|
-
const
|
|
47
|
-
server.middlewares.use(
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
178
|
+
const packages = workspacePackages(repoRoot);
|
|
179
|
+
server.middlewares.use(PLAYGROUND_NPM_PATH, (req, res, next) => {
|
|
180
|
+
const request = packageRequest(decodeURIComponent(new URL(req.url ?? '/', 'http://x').pathname).replace(/^\//, ''));
|
|
181
|
+
const dir = request && packages.get(request[0]);
|
|
182
|
+
if (!request || !dir) return next();
|
|
183
|
+
const file = path.join(dir, request[1]);
|
|
184
|
+
if (!existsSync(file) || statSync(file).isDirectory() || !realpathSync(file).startsWith(realpathSync(dir))) return next();
|
|
185
|
+
res.setHeader('Content-Type', file.endsWith('.wasm') ? 'application/wasm' : file.endsWith('.json') ? 'application/json' : 'application/octet-stream');
|
|
51
186
|
res.setHeader('Content-Length', statSync(file).size);
|
|
52
187
|
createReadStream(file).pipe(res);
|
|
53
188
|
});
|
|
54
189
|
},
|
|
190
|
+
writeBundle() {
|
|
191
|
+
if (!options.packages) return;
|
|
192
|
+
const { files, bytes } = writeLocalPackages(repoRoot, options.packages, outDir);
|
|
193
|
+
this.info(`the playground's local packages: ${files} files, ${(bytes / 1e6).toFixed(1)} MB`);
|
|
194
|
+
},
|
|
55
195
|
};
|
|
56
196
|
}
|
package/src/playground/assets.ts
CHANGED
|
@@ -1,53 +1,112 @@
|
|
|
1
|
-
// The playground's downloads (
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
1
|
+
// The playground's downloads (files.ts): its manifest from
|
|
2
|
+
// @swiftbrowser/playground, then the tools, the SDK's and the kit's files it
|
|
3
|
+
// names, the ICU data and the module cache, every one a file of an npm
|
|
4
|
+
// package, from the CDN at the source's version or from the site itself.
|
|
5
|
+
// Each is kept in the Cache API under its URL, which a version makes
|
|
6
|
+
// immutable, so a compile needs no request after the first visit. The tools
|
|
7
|
+
// come brotli-compressed, as does a file too big for the site, and no
|
|
8
|
+
// browser's DecompressionStream reads brotli: a small WebAssembly decoder
|
|
9
|
+
// unpacks them as they arrive, and the cache keeps them unpacked. Other files
|
|
10
|
+
// the server compresses in transit.
|
|
8
11
|
|
|
9
12
|
import { memoryDirectory } from '@swiftbrowser/wasm-compiler';
|
|
10
13
|
import type { Toolchain } from './build';
|
|
11
|
-
import
|
|
14
|
+
import {
|
|
15
|
+
PLAYGROUND_ICU,
|
|
16
|
+
PLAYGROUND_MANIFEST,
|
|
17
|
+
PLAYGROUND_MODULE_CACHE,
|
|
18
|
+
PLAYGROUND_PACKAGE,
|
|
19
|
+
SITE_BROTLI,
|
|
20
|
+
isBrotli,
|
|
21
|
+
type PackageFile,
|
|
22
|
+
type PlaygroundManifest,
|
|
23
|
+
type PlaygroundSource,
|
|
24
|
+
packageUrl,
|
|
25
|
+
} from './files';
|
|
12
26
|
import { readPack } from './pack';
|
|
13
27
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
const CACHE = 'swiftbrowser-playground';
|
|
29
|
+
/** Requests in flight at once for the SDK's and kit's files. */
|
|
30
|
+
const CONCURRENCY = 24;
|
|
31
|
+
|
|
32
|
+
type Brotli = typeof import('brotli-dec-wasm/web');
|
|
33
|
+
let brotli: Promise<Brotli> | null = null;
|
|
34
|
+
|
|
35
|
+
/** The brotli decoder, loaded on first use. */
|
|
36
|
+
function brotliDecoder(): Promise<Brotli> {
|
|
37
|
+
brotli ??= Promise.all([import('brotli-dec-wasm/web'), import('brotli-dec-wasm/web/bg.wasm?url')]).then(async ([decoder, wasm]) => {
|
|
38
|
+
await decoder.default({ module_or_path: wasm.default });
|
|
39
|
+
return decoder;
|
|
40
|
+
});
|
|
41
|
+
return brotli;
|
|
20
42
|
}
|
|
21
43
|
|
|
22
|
-
|
|
44
|
+
/** A stream that unpacks brotli as its input arrives. */
|
|
45
|
+
function brotliStream(decoder: Brotli): TransformStream<Uint8Array, Uint8Array> {
|
|
46
|
+
const stream = new decoder.BrotliDecStream();
|
|
47
|
+
let code: number = decoder.BrotliStreamResultCode.NeedsMoreInput;
|
|
48
|
+
return new TransformStream({
|
|
49
|
+
transform(chunk, controller) {
|
|
50
|
+
let input = chunk;
|
|
51
|
+
do {
|
|
52
|
+
const result = stream.dec(input, 1 << 22);
|
|
53
|
+
if (result.buf.length > 0) controller.enqueue(result.buf);
|
|
54
|
+
input = input.subarray(result.input_offset);
|
|
55
|
+
code = result.code;
|
|
56
|
+
} while (code === decoder.BrotliStreamResultCode.NeedsMoreOutput);
|
|
57
|
+
},
|
|
58
|
+
flush() {
|
|
59
|
+
stream.free();
|
|
60
|
+
if (code !== decoder.BrotliStreamResultCode.ResultSuccess) throw new Error('a brotli file ended early');
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const openCache = async (): Promise<Cache | null> => (typeof caches === 'undefined' ? null : caches.open(CACHE).catch(() => null));
|
|
23
66
|
|
|
24
|
-
/**
|
|
25
|
-
async function
|
|
26
|
-
const cache = typeof caches === 'undefined' ? null : await caches.open(CACHE).catch(() => null);
|
|
67
|
+
/** The file at `url`, unpacked when it is brotli (`isBrotli`), from the cache when it has it. */
|
|
68
|
+
async function download(cache: Cache | null, url: string): Promise<Response> {
|
|
27
69
|
const hit = await cache?.match(url);
|
|
28
70
|
if (hit) return hit;
|
|
29
71
|
const response = await fetch(url);
|
|
30
|
-
if (!response.ok) throw new Error(`${url}: HTTP ${response.status}`);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
72
|
+
if (!response.ok || !response.body) throw new Error(`${url}: HTTP ${response.status}`);
|
|
73
|
+
let file = response;
|
|
74
|
+
if (isBrotli(url)) {
|
|
75
|
+
const decoder = await brotliDecoder();
|
|
76
|
+
const type = /\.wasm\.(br|brotli)$/.test(url) ? 'application/wasm' : 'application/octet-stream';
|
|
77
|
+
file = new Response(response.body.pipeThrough(brotliStream(decoder)), { headers: { 'content-type': type } });
|
|
78
|
+
}
|
|
79
|
+
if (cache) {
|
|
80
|
+
// The page reads one copy as the cache takes the other, unpacked: of the
|
|
81
|
+
// headers only the type goes with it (compileStreaming wants it).
|
|
82
|
+
const headers = { 'content-type': file.headers.get('content-type') ?? 'application/octet-stream' };
|
|
83
|
+
const [kept, used] = file.body!.tee();
|
|
84
|
+
void cache.put(url, new Response(kept, { headers })).catch(() => {});
|
|
85
|
+
return new Response(used, { headers });
|
|
86
|
+
}
|
|
87
|
+
return file;
|
|
41
88
|
}
|
|
42
89
|
|
|
43
|
-
async function compileModule(
|
|
44
|
-
const response = await body(url);
|
|
90
|
+
async function compileModule(response: Response): Promise<WebAssembly.Module> {
|
|
45
91
|
if (typeof WebAssembly.compileStreaming === 'function' && (response.headers.get('content-type') ?? '').includes('application/wasm')) {
|
|
46
92
|
return WebAssembly.compileStreaming(response);
|
|
47
93
|
}
|
|
48
94
|
return WebAssembly.compile(await response.arrayBuffer());
|
|
49
95
|
}
|
|
50
96
|
|
|
97
|
+
/** `work` over `items`, at most `limit` at once, in order. */
|
|
98
|
+
async function pooled<T, R>(items: readonly T[], limit: number, work: (item: T) => Promise<R>): Promise<R[]> {
|
|
99
|
+
const results: R[] = new Array(items.length);
|
|
100
|
+
let next = 0;
|
|
101
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
102
|
+
while (next < items.length) {
|
|
103
|
+
const index = next++;
|
|
104
|
+
results[index] = await work(items[index]!);
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
|
|
51
110
|
export interface LoadedToolchain {
|
|
52
111
|
/** The tools and files, without macro plugins until `plugins` settles. */
|
|
53
112
|
toolchain: Toolchain;
|
|
@@ -58,39 +117,81 @@ export interface LoadedToolchain {
|
|
|
58
117
|
plugins: Promise<Toolchain['plugins']>;
|
|
59
118
|
/** The module cache built with the playground's flags. */
|
|
60
119
|
moduleCache: [string, Uint8Array][];
|
|
61
|
-
|
|
120
|
+
manifest: PlaygroundManifest;
|
|
121
|
+
/** Why the kit may not suit this page's renderer, when it may not. */
|
|
122
|
+
warning?: string;
|
|
62
123
|
}
|
|
63
124
|
|
|
64
|
-
|
|
65
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Whether the kit the manifest names was built from the sources the page's
|
|
127
|
+
* renderer was: the two speak one protocol, which either may change.
|
|
128
|
+
*/
|
|
129
|
+
export function kitWarning(manifest: Pick<PlaygroundManifest, 'shimSources'>, source: Pick<PlaygroundSource, 'shimSources'>): string | undefined {
|
|
130
|
+
if (!manifest.shimSources || !source.shimSources || manifest.shimSources === source.shimSources) return undefined;
|
|
131
|
+
return `The kit was built from other SwiftBrowser sources (${manifest.shimSources}) than this page's renderer (${source.shimSources}): an app may not run as it should.`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function loadToolchain(source: PlaygroundSource, onProgress: (text: string) => void = () => {}): Promise<LoadedToolchain> {
|
|
135
|
+
const cache = await openCache();
|
|
136
|
+
const url = ({ package: name, file }: PackageFile): string => packageUrl(source, name, file);
|
|
137
|
+
const bytes = async (file: PackageFile): Promise<Uint8Array<ArrayBuffer>> => new Uint8Array(await (await download(cache, url(file))).arrayBuffer());
|
|
66
138
|
const started = performance.now();
|
|
67
139
|
const lap = (text: string): void => onProgress(`${text} (${((performance.now() - started) / 1000).toFixed(1)} s)`);
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
140
|
+
const own = (file: string): PackageFile => ({ package: PLAYGROUND_PACKAGE, file });
|
|
141
|
+
const manifest = JSON.parse(new TextDecoder().decode(await bytes(own(PLAYGROUND_MANIFEST)))) as PlaygroundManifest;
|
|
142
|
+
|
|
143
|
+
const kitPackage = '@swiftbrowser/kit';
|
|
144
|
+
// What a site stores brotli-compressed it serves under another name.
|
|
145
|
+
const brotli = new Set(manifest.brotli ?? []);
|
|
146
|
+
const served = (name: string, file: string): PackageFile => ({ package: name, file: brotli.has(`${name}/${file}`) ? `${file}${SITE_BROTLI}` : file });
|
|
147
|
+
const sdkFiles = Object.entries(manifest.sdk).flatMap(([name, files]) => files.map((file) => ({ path: file, from: served(name, `wasm32-unknown-wasip1/${file}`) })));
|
|
148
|
+
const kitFiles = [...manifest.kit, 'kit.json'].map((file) => ({ path: file, from: served(kitPackage, file) }));
|
|
149
|
+
let done = 0;
|
|
150
|
+
const total = sdkFiles.length + kitFiles.length;
|
|
151
|
+
const files = pooled([...sdkFiles, ...kitFiles], CONCURRENCY, async ({ path, from }) => {
|
|
152
|
+
const data = await bytes(from);
|
|
153
|
+
if (++done % 50 === 0 || done === total) onProgress(`SDK and kit: ${done} of ${total} files`);
|
|
154
|
+
return [path, data] as [string, Uint8Array];
|
|
155
|
+
});
|
|
156
|
+
const [frontend, linker, all, icu, moduleCacheBytes] = await Promise.all([
|
|
157
|
+
download(cache, url(manifest.frontend)).then(compileModule).then((module) => (lap('compiler ready'), module)),
|
|
158
|
+
download(cache, url(manifest.linker)).then(compileModule).then((module) => (lap('linker ready'), module)),
|
|
159
|
+
files.then((list) => (lap('SDK ready'), list)),
|
|
160
|
+
bytes(served(PLAYGROUND_PACKAGE, PLAYGROUND_ICU)),
|
|
161
|
+
bytes(served(PLAYGROUND_PACKAGE, PLAYGROUND_MODULE_CACHE)),
|
|
72
162
|
]);
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
163
|
+
const sdk = all.slice(0, sdkFiles.length);
|
|
164
|
+
const kit = all.slice(sdkFiles.length);
|
|
165
|
+
const kitJson = kit.pop()![1];
|
|
166
|
+
|
|
167
|
+
const plugins = Promise.all(Object.entries(manifest.plugins).map(async ([name, file]) => {
|
|
168
|
+
const data = await bytes(file);
|
|
169
|
+
return [name, { module: await WebAssembly.compile(data), bytes: data }] as const;
|
|
77
170
|
})).then(Object.fromEntries<Toolchain['plugins'][string]>);
|
|
78
171
|
// A failed download surfaces when a compile needs the plugins.
|
|
79
172
|
plugins.catch(() => {});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
173
|
+
// Every file is named by its version: drop what an older one left.
|
|
174
|
+
void plugins.then(async () => {
|
|
175
|
+
const wanted = new Set([
|
|
176
|
+
...[manifest.frontend, manifest.linker, ...Object.values(manifest.plugins), ...sdkFiles.map((f) => f.from), ...kitFiles.map((f) => f.from)].map(url),
|
|
177
|
+
url(own(PLAYGROUND_MANIFEST)),
|
|
178
|
+
...[PLAYGROUND_ICU, PLAYGROUND_MODULE_CACHE].map((file) => url(served(PLAYGROUND_PACKAGE, file))),
|
|
179
|
+
]);
|
|
180
|
+
for (const request of (await cache?.keys()) ?? []) if (!wanted.has(request.url)) await cache!.delete(request);
|
|
181
|
+
}).catch(() => {});
|
|
182
|
+
|
|
83
183
|
return {
|
|
84
184
|
toolchain: {
|
|
85
185
|
tools: { 'swift-frontend': frontend, 'wasm-ld': linker },
|
|
86
186
|
plugins: {},
|
|
87
|
-
sdk: memoryDirectory(
|
|
88
|
-
kit: memoryDirectory(
|
|
187
|
+
sdk: memoryDirectory(sdk),
|
|
188
|
+
kit: memoryDirectory(kit),
|
|
89
189
|
kitManifest: JSON.parse(new TextDecoder().decode(kitJson)) as Toolchain['kitManifest'],
|
|
90
|
-
icu: { symbol:
|
|
190
|
+
icu: { symbol: manifest.icuSymbol, data: icu },
|
|
91
191
|
},
|
|
92
192
|
plugins,
|
|
93
|
-
moduleCache:
|
|
94
|
-
|
|
193
|
+
moduleCache: readPack(moduleCacheBytes).files('module-cache'),
|
|
194
|
+
manifest,
|
|
195
|
+
...(kitWarning(manifest, source) ? { warning: kitWarning(manifest, source)! } : {}),
|
|
95
196
|
};
|
|
96
197
|
}
|
package/src/playground/files.ts
CHANGED
|
@@ -1,18 +1,87 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// What the playground downloads, all from npm packages at one version (the
|
|
2
|
+
// release's lockstep version), through a CDN that serves npm packages file by
|
|
3
|
+
// file (jsDelivr): the compiler, the linker and the toolchain's macro plugins
|
|
4
|
+
// from @swiftbrowser/wasm-tools, the Swift SDK's files from the
|
|
5
|
+
// @swiftbrowser/wasm-sdk packages, the kit's from @swiftbrowser/kit, and what
|
|
6
|
+
// only the playground needs from @swiftbrowser/playground: its manifest
|
|
7
|
+
// (which files of those packages a compile reads), the trimmed ICU data and
|
|
8
|
+
// the module cache. playground/build-assets.ts makes that package.
|
|
6
9
|
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export const PLAYGROUND_PACKAGE = '@swiftbrowser/playground';
|
|
11
|
+
export const PLAYGROUND_MANIFEST = 'playground.json';
|
|
12
|
+
/** The trimmed ICU data, in @swiftbrowser/playground. */
|
|
13
|
+
export const PLAYGROUND_ICU = 'icu.dat';
|
|
14
|
+
/** The module cache, packed (pack.ts), in @swiftbrowser/playground. */
|
|
15
|
+
export const PLAYGROUND_MODULE_CACHE = 'module-cache.pack';
|
|
16
|
+
/** Where the npm packages are fetched from by default. */
|
|
17
|
+
export const JSDELIVR = 'https://cdn.jsdelivr.net/npm/';
|
|
18
|
+
|
|
19
|
+
/** A file of one of the packages. */
|
|
20
|
+
export interface PackageFile {
|
|
21
|
+
package: string;
|
|
22
|
+
/** The path in the package; `.br` files are brotli-compressed. */
|
|
23
|
+
file: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @swiftbrowser/playground's playground.json. */
|
|
27
|
+
export interface PlaygroundManifest {
|
|
28
|
+
/** The symbol Foundation's ICU reads its data through (`icudtNN_dat`). */
|
|
29
|
+
icuSymbol: string;
|
|
30
|
+
/** The apps whose compiles recorded the files. */
|
|
31
|
+
apps: string[];
|
|
32
|
+
frontend: PackageFile;
|
|
33
|
+
linker: PackageFile;
|
|
14
34
|
/** The macro plugins by the module whose macros they expand. */
|
|
15
|
-
plugins: Record<string,
|
|
35
|
+
plugins: Record<string, PackageFile>;
|
|
36
|
+
/**
|
|
37
|
+
* The Swift SDK's files a compile reads, by package, each relative to the
|
|
38
|
+
* SDK's `wasm32-unknown-wasip1` directory (the package's own
|
|
39
|
+
* `wasm32-unknown-wasip1/` holds it there).
|
|
40
|
+
*/
|
|
41
|
+
sdk: Record<string, string[]>;
|
|
42
|
+
/** The kit's files a compile reads, relative to @swiftbrowser/kit (`wasm32-unknown-wasip1/...`). */
|
|
43
|
+
kit: string[];
|
|
44
|
+
/**
|
|
45
|
+
* The SwiftBrowser sources the kit was built from (@swiftbrowser/swift's
|
|
46
|
+
* `sourcesHash`), when build-assets.ts could tell: the page compares it
|
|
47
|
+
* with the sources its own renderer was built from.
|
|
48
|
+
*/
|
|
49
|
+
shimSources?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Files, as `<package>/<path>`, that the site serving them stores
|
|
52
|
+
* brotli-compressed (`<path>.brotli`, SITE_BROTLI): an SDK or kit file,
|
|
53
|
+
* the ICU data or the module cache (plugins/playground.ts). A tool or
|
|
54
|
+
* plugin is named by its compressed file instead.
|
|
55
|
+
*/
|
|
56
|
+
brotli?: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The suffix of a file a site stores brotli-compressed. Not `.br`: a static
|
|
61
|
+
* server may send a `.br` file as `Content-Encoding: br`, which the browser
|
|
62
|
+
* unpacks before the page could.
|
|
63
|
+
*/
|
|
64
|
+
export const SITE_BROTLI = '.brotli';
|
|
65
|
+
|
|
66
|
+
/** Whether a file is brotli-compressed by its name: an npm package's `.br`, or a site's. */
|
|
67
|
+
export const isBrotli = (file: string): boolean => file.endsWith('.br') || file.endsWith(SITE_BROTLI);
|
|
68
|
+
|
|
69
|
+
/** Where the packages come from. */
|
|
70
|
+
export interface PlaygroundSource {
|
|
71
|
+
/** A CDN's npm base URL, which serves every package at `version`... */
|
|
72
|
+
npm: string;
|
|
73
|
+
version: string;
|
|
74
|
+
/**
|
|
75
|
+
* ...but those named here, each served from its own base URL: the site's
|
|
76
|
+
* copy, made from the same commit as its renderer (plugins/playground.ts).
|
|
77
|
+
*/
|
|
78
|
+
packages?: Record<string, string>;
|
|
79
|
+
/** The SwiftBrowser sources the site's renderer was built from (`sourcesHash`). */
|
|
80
|
+
shimSources?: string;
|
|
16
81
|
}
|
|
17
82
|
|
|
18
|
-
|
|
83
|
+
/** The URL of a package's file. */
|
|
84
|
+
export function packageUrl(source: PlaygroundSource, name: string, file: string): string {
|
|
85
|
+
const base = source.packages?.[name];
|
|
86
|
+
return base ? new URL(file, base).href : new URL(`${name}@${source.version}/${file}`, source.npm).href;
|
|
87
|
+
}
|
package/src/playground/main.ts
CHANGED
|
@@ -21,6 +21,7 @@ const run = $<HTMLButtonElement>('#run');
|
|
|
21
21
|
const status = $<HTMLElement>('#status');
|
|
22
22
|
const diagnostics = $<HTMLPreElement>('#diagnostics');
|
|
23
23
|
const frame = $<HTMLIFrameElement>('#app');
|
|
24
|
+
const warning = $<HTMLElement>('#warning');
|
|
24
25
|
|
|
25
26
|
// Path relative to Sources/ → text, as edited.
|
|
26
27
|
const files: Record<string, string> = Object.fromEntries(
|
|
@@ -48,7 +49,13 @@ editor.addEventListener('input', () => (files[current] = editor.value));
|
|
|
48
49
|
showFiles();
|
|
49
50
|
|
|
50
51
|
const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
|
|
51
|
-
const
|
|
52
|
+
const source = {
|
|
53
|
+
...__SB_PLAYGROUND__,
|
|
54
|
+
npm: new URL(__SB_PLAYGROUND__.npm, location.href).href,
|
|
55
|
+
...(__SB_PLAYGROUND__.packages
|
|
56
|
+
? { packages: Object.fromEntries(Object.entries(__SB_PLAYGROUND__.packages).map(([name, base]) => [name, new URL(base, location.href).href])) }
|
|
57
|
+
: {}),
|
|
58
|
+
};
|
|
52
59
|
let nextId = 1;
|
|
53
60
|
let moduleUrl: string | null = null;
|
|
54
61
|
const started = new Map<number, number>();
|
|
@@ -59,6 +66,11 @@ worker.onmessage = (event: MessageEvent<FromWorker>) => {
|
|
|
59
66
|
status.textContent = message.text;
|
|
60
67
|
return;
|
|
61
68
|
}
|
|
69
|
+
if (message.type === 'warning') {
|
|
70
|
+
warning.hidden = false;
|
|
71
|
+
warning.textContent = message.text;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
62
74
|
run.disabled = false;
|
|
63
75
|
if (message.type === 'failure') {
|
|
64
76
|
status.textContent = 'The compiler failed.';
|
|
@@ -87,7 +99,7 @@ function compile(): void {
|
|
|
87
99
|
status.textContent = 'Compiling…';
|
|
88
100
|
const id = nextId++;
|
|
89
101
|
started.set(id, performance.now());
|
|
90
|
-
const message: ToWorker = { type: 'compile', id,
|
|
102
|
+
const message: ToWorker = { type: 'compile', id, source, app: { name: APP, files: { ...files } } };
|
|
91
103
|
worker.postMessage(message);
|
|
92
104
|
}
|
|
93
105
|
run.addEventListener('click', compile);
|
|
@@ -27,4 +27,5 @@ main { display: grid; grid-template-columns: minmax(0, 1fr) 420px; gap: 16px; pa
|
|
|
27
27
|
#run:disabled { opacity: 0.5; cursor: default; }
|
|
28
28
|
#status { color: var(--muted); font-size: 13px; }
|
|
29
29
|
#diagnostics { margin: 0; padding: 12px; border: 1px solid var(--error); border-radius: 8px; background: var(--panel); color: var(--error); font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; overflow: auto; max-height: 320px; }
|
|
30
|
+
#warning { margin: 0; padding: 8px 12px; border: 1px solid var(--error); border-radius: 8px; background: var(--panel); font-size: 13px; }
|
|
30
31
|
.preview iframe { width: 100%; height: 860px; border: none; border-radius: 12px; background: var(--panel); }
|
package/src/playground/worker.ts
CHANGED
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import { type AppSources, missingPlugin, buildApp } from './build';
|
|
9
9
|
import { type LoadedToolchain, loadToolchain } from './assets';
|
|
10
|
-
import type {
|
|
10
|
+
import type { PlaygroundSource } from './files';
|
|
11
11
|
|
|
12
|
-
export type ToWorker = { type: 'compile'; id: number;
|
|
12
|
+
export type ToWorker = { type: 'compile'; id: number; source: PlaygroundSource; app: AppSources };
|
|
13
13
|
export type FromWorker =
|
|
14
14
|
| { type: 'progress'; text: string }
|
|
15
|
+
| { type: 'warning'; text: string }
|
|
15
16
|
| { type: 'result'; id: number; ok: boolean; output: string; wasm?: Uint8Array<ArrayBuffer>; steps: { tool: string; seconds: number }[] }
|
|
16
17
|
| { type: 'failure'; id: number; error: string };
|
|
17
18
|
|
|
@@ -20,11 +21,12 @@ let loaded: Promise<LoadedToolchain> | null = null;
|
|
|
20
21
|
let moduleCache: [string, Uint8Array][] | null = null;
|
|
21
22
|
|
|
22
23
|
self.onmessage = async (event: MessageEvent<ToWorker>) => {
|
|
23
|
-
const { id,
|
|
24
|
+
const { id, source, app } = event.data;
|
|
24
25
|
try {
|
|
25
|
-
loaded ??= loadToolchain(
|
|
26
|
+
loaded ??= loadToolchain(source, (text) => post({ type: 'progress', text })).then((toolchain) => {
|
|
26
27
|
// Later compiles use the plugins as soon as they are in.
|
|
27
28
|
toolchain.plugins.then((plugins) => (toolchain.toolchain.plugins = plugins), () => {});
|
|
29
|
+
if (toolchain.warning) post({ type: 'warning', text: toolchain.warning });
|
|
28
30
|
return toolchain;
|
|
29
31
|
});
|
|
30
32
|
const { toolchain, plugins, moduleCache: prebuilt } = await loaded;
|
package/src/vite-env.d.ts
CHANGED
|
@@ -26,10 +26,8 @@ interface SBLandingApp {
|
|
|
26
26
|
declare const __SB_APPS__: readonly SBLandingApp[];
|
|
27
27
|
/** The app the app page runs without `?app=` (`SiteOptions.defaultApp`, node/config.ts). */
|
|
28
28
|
declare const __SB_DEFAULT_APP__: string;
|
|
29
|
-
/** Where the playground page fetches its
|
|
30
|
-
declare const
|
|
31
|
-
/** The playground's downloads, from the assets' manifest (src/playground/files.ts). */
|
|
32
|
-
declare const __SB_PLAYGROUND_FILES__: import('./playground/files').PlaygroundFiles;
|
|
29
|
+
/** Where the playground page fetches its npm packages, and their version (plugins/playground.ts); null without the page. */
|
|
30
|
+
declare const __SB_PLAYGROUND__: import('./playground/files').PlaygroundSource;
|
|
33
31
|
/** What a wide screen shows: `auto` (the frame) or `device` (the full window, a built single-app site). See plugins/site-layout.ts. */
|
|
34
32
|
declare const __SB_PAGE_MODE__: 'auto' | 'device';
|
|
35
33
|
|