diff --git a/package.json b/package.json index b511a0b22..ab9036188 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "type": "module", "dependencies": { "@fontsource/roboto-mono": "^4.5.10", - "@webcontainer/api": "^0.0.8", + "@webcontainer/api": "^1.0.2", "adm-zip": "^0.5.10", "base64-js": "^1.5.1", "marked": "^4.2.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cad8e442..c86ba99ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,7 +11,7 @@ specifiers: '@types/marked': ^4.0.8 '@types/prismjs': ^1.26.0 '@types/ws': ^8.5.4 - '@webcontainer/api': ^0.0.8 + '@webcontainer/api': ^1.0.2 adm-zip: ^0.5.10 base64-js: ^1.5.1 diff: ^5.1.0 @@ -32,7 +32,7 @@ specifiers: dependencies: '@fontsource/roboto-mono': 4.5.10 - '@webcontainer/api': 0.0.8 + '@webcontainer/api': 1.0.2 adm-zip: 0.5.10 base64-js: 1.5.1 marked: 4.2.12 @@ -667,8 +667,8 @@ packages: - supports-color dev: true - /@webcontainer/api/0.0.8: - resolution: {integrity: sha512-t285UFNMzz8mmJlfAP/mloJlN+rHuu5jmFCNW05WGT3FzJ0sNseU6WN5+TiTtWIfjiUfLPtJI6ndsgiTujkXAw==} + /@webcontainer/api/1.0.2: + resolution: {integrity: sha512-JyJSE/EuNmbv2l9M+GE254Z/s0A9Hma8gtc71co6ZznpCr1UyB70556xZapbd8fWcjOTb1sLGb0GnRx9Ofp9sA==} dev: false /abbrev/1.1.1: diff --git a/src/app.d.ts b/src/app.d.ts index 657780a03..f59b884c5 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,12 +1,12 @@ -/// - // See https://kit.svelte.dev/docs/types#app // for information about these interfaces -declare namespace App { - // interface Locals {} - // interface Platform {} - // interface Session {} - interface Stuff { - index: import('$lib/types').PartStub[]; +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface Platform {} } } + +export {}; diff --git a/src/lib/client/adapters/webcontainer/index.js b/src/lib/client/adapters/webcontainer/index.js index ceeeb290d..290bfb10b 100644 --- a/src/lib/client/adapters/webcontainer/index.js +++ b/src/lib/client/adapters/webcontainer/index.js @@ -1,4 +1,4 @@ -import { load } from '@webcontainer/api'; +import { WebContainer } from '@webcontainer/api'; import base64 from 'base64-js'; import { get_depth } from '../../../utils.js'; import { ready } from '../common/index.js'; @@ -6,6 +6,15 @@ import { ready } from '../common/index.js'; /** @type {import('@webcontainer/api').WebContainer} Web container singleton */ let vm; +/** @param {string} label */ +function console_stream(label) { + return new WritableStream({ + write(chunk) { + console.log(`[${label}] ${chunk}`); + } + }); +} + /** * @param {import('$lib/types').Stub[]} stubs * @param {(progress: number, status: string) => void} callback @@ -31,15 +40,12 @@ export async function create(stubs, callback) { /** @type {boolean} Track whether there was an error from vite dev server */ let vite_error = false; - callback(1 / 6, 'loading webcontainer'); - const WebContainer = await load(); - - callback(2 / 6, 'booting webcontainer'); + callback(1 / 5, 'booting webcontainer'); vm = await WebContainer.boot(); - callback(3 / 6, 'writing virtual files'); + callback(2 / 5, 'writing virtual files'); const common = await ready; - await vm.loadFiles({ + await vm.mount({ 'common.zip': { file: { contents: new Uint8Array(common.zipped) } }, @@ -49,24 +55,18 @@ export async function create(stubs, callback) { ...convert_stubs_to_tree(stubs) }); - callback(4 / 6, 'unzipping files'); - const unzip = await vm.run( - { - command: 'node', - args: ['unzip.cjs'] - }, - { - stderr: (data) => console.error(`[unzip] ${data}`) - } - ); - const code = await unzip.onExit; + callback(3 / 5, 'unzipping files'); + const unzip = await vm.spawn('node', ['unzip.cjs']); + unzip.output.pipeTo(console_stream('unzip')); + const code = await unzip.exit; + if (code !== 0) { throw new Error('Failed to initialize WebContainer'); } - await vm.run({ command: 'chmod', args: ['a+x', 'node_modules/vite/bin/vite.js'] }); + await vm.spawn('chmod', ['a+x', 'node_modules/vite/bin/vite.js']); - callback(5 / 6, 'starting dev server'); + callback(4 / 5, 'starting dev server'); const base = await new Promise(async (fulfil, reject) => { const error_unsub = vm.on('error', (error) => { error_unsub(); @@ -75,27 +75,21 @@ export async function create(stubs, callback) { const ready_unsub = vm.on('server-ready', (port, base) => { ready_unsub(); - callback(6 / 6, 'ready'); + callback(5 / 5, 'ready'); fulfil(base); // this will be the last thing that happens if everything goes well }); await run_dev(); async function run_dev() { - const process = await vm.run( - { command: 'turbo', args: ['run', 'dev'] }, - { - stdout: (data) => { - console.log(`[dev] ${data}`); - }, - stderr: (data) => { - vite_error = true; - console.error(`[dev] ${data}`); - } - } - ); + const process = await vm.spawn('turbo', ['run', 'dev']); + + // TODO differentiate between stdout and stderr (sets `vite_error` to `true`) + // https://github.com/stackblitz/webcontainer-core/issues/971 + process.output.pipeTo(console_stream('dev')); + // keep restarting dev server (can crash in case of illegal +files for example) - process.onExit.then((code) => { + process.exit.then((code) => { if (code !== 0) { setTimeout(() => { run_dev(); @@ -185,10 +179,10 @@ export async function create(stubs, callback) { // This will invoke a restart of Vite. Hacky but it works. // TODO: remove when https://github.com/vitejs/vite/issues/12127 is closed if (!previous_env && current_stubs.has('/.env')) { - await vm.run({ command: 'touch', args: ['.env']}); + await vm.spawn('touch', ['.env']); } - await vm.loadFiles(convert_stubs_to_tree(to_write)); + await vm.mount(convert_stubs_to_tree(to_write)); await promise; await new Promise((f) => setTimeout(f, 200)); // wait for chokidar @@ -220,13 +214,13 @@ export async function create(stubs, callback) { }; } - tree = /** @type {import('@webcontainer/api').DirectoryEntry} */ (tree[part]).directory; + tree = /** @type {import('@webcontainer/api').DirectoryNode} */ (tree[part]).directory; } tree[basename] = to_file(stub); } - await vm.loadFiles(root); + await vm.mount(root); stubs_to_map(stubs, current_stubs); @@ -236,8 +230,6 @@ export async function create(stubs, callback) { }, destroy: async () => { vm.teardown(); - // @ts-ignore - vm = null; } }; }