@nimbus-sh/worker 0.6.0 → 0.7.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/README.md +2 -2
- package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
- package/dist/cirrus-npm-cjs.generated.js +1 -1
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +6 -5
- package/dist/facets/compose.d.ts +148 -0
- package/dist/facets/compose.d.ts.map +1 -0
- package/dist/facets/compose.js +146 -0
- package/dist/facets/durable-images.d.ts +9 -5
- package/dist/facets/durable-images.d.ts.map +1 -1
- package/dist/facets/durable-images.js +17 -7
- package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
- package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
- package/dist/facets/esbuild-bundle-pool.js +122 -0
- package/dist/facets/exec-telemetry.d.ts +8 -0
- package/dist/facets/exec-telemetry.d.ts.map +1 -1
- package/dist/facets/manager.d.ts +240 -109
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +661 -337
- package/dist/facets/process.d.ts +20 -16
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +59 -28
- package/dist/facets/real-vite-fs-shim.js +2 -2
- package/dist/facets/vite-dev-server.d.ts +25 -47
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +185 -219
- package/dist/facets/wasm-image-digest.d.ts +25 -0
- package/dist/facets/wasm-image-digest.d.ts.map +1 -0
- package/dist/facets/wasm-image-digest.js +25 -0
- package/dist/facets/wasm-swap-registry.d.ts +51 -92
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
- package/dist/facets/wasm-swap-registry.js +68 -227
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +3 -2
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -3
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
- package/dist/loaders/npm-resolve-preamble.js +34 -108
- package/dist/node-shims-artifact.generated.js +3 -3
- package/dist/npm/install-batch-facet.d.ts +3 -2
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +45 -15
- package/dist/npm/installer.d.ts +22 -17
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +209 -193
- package/dist/npm/pre-bundle-facet.d.ts +1 -1
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +1 -1
- package/dist/npm/resolve-facet.d.ts +6 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.d.ts +21 -20
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +85 -84
- package/dist/npm/semver.d.ts +59 -0
- package/dist/npm/semver.d.ts.map +1 -0
- package/dist/npm/semver.js +226 -0
- package/dist/router/remote-api.js +31 -1
- package/dist/runtime/html-entrypoint.d.ts +7 -0
- package/dist/runtime/html-entrypoint.d.ts.map +1 -1
- package/dist/runtime/html-entrypoint.js +18 -2
- package/dist/runtime/node-runner.d.ts +3 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +7 -0
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +305 -29
- package/dist/runtime/opencode-facet-runner.js +2 -2
- package/dist/session/helpers.d.ts +38 -0
- package/dist/session/helpers.d.ts.map +1 -1
- package/dist/session/helpers.js +76 -0
- package/dist/session/init-phases.d.ts +1 -1
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +2 -12
- package/dist/session/init.d.ts +15 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +82 -603
- package/dist/session/nimbus-session.d.ts +56 -22
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +130 -101
- package/dist/session/npm-install-port.d.ts +16 -0
- package/dist/session/npm-install-port.d.ts.map +1 -0
- package/dist/session/npm-install-port.js +37 -0
- package/dist/session/port-capability.d.ts +44 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +110 -0
- package/dist/session/programmatic.d.ts +31 -5
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +94 -68
- package/dist/session/routes.d.ts +30 -17
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +42 -131
- package/dist/session/rpc.d.ts +15 -15
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +97 -47
- package/dist/session/serving-port.d.ts +27 -0
- package/dist/session/serving-port.d.ts.map +1 -0
- package/dist/session/serving-port.js +27 -0
- package/dist/session/start-real-vite.d.ts +12 -0
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +4 -4
- package/dist/session/supervisor-op.d.ts +2 -2
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +13 -7
- package/dist/session/supervisor-rpc.d.ts +7 -7
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +4 -0
- package/dist/session/vite-command.d.ts +18 -0
- package/dist/session/vite-command.d.ts.map +1 -0
- package/dist/session/vite-command.js +609 -0
- package/dist/session/ws.d.ts +61 -0
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +122 -12
- package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +12 -85
- package/package.json +24 -4
- package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
- package/public/index.html +1 -1
- package/scripts/bundle-facet-workers.mjs +1 -1
- package/scripts/bundle-npm-cjs.mjs +2 -2
- package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
- package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
- package/dist/facets/on-demand-bundle-gate.js +0 -125
- package/dist/npm/install-args.d.ts +0 -21
- package/dist/npm/install-args.d.ts.map +0 -1
- package/dist/npm/install-args.js +0 -70
- package/dist/npm/npm-log.d.ts +0 -31
- package/dist/npm/npm-log.d.ts.map +0 -1
- package/dist/npm/npm-log.js +0 -51
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session/vite-command.ts — the `vite` builtin command.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from init.ts so the handler can be driven through the
|
|
5
|
+
* command registry in unit tests — the module's only session coupling
|
|
6
|
+
* is `self`, the same InitHost view initSession passes to every other
|
|
7
|
+
* registration.
|
|
8
|
+
*
|
|
9
|
+
* Subcommands: `vite` (dev server), `vite build`, `vite preview`,
|
|
10
|
+
* `vite stop`. Build honours build.outDir with Vite parity: output
|
|
11
|
+
* writes to the resolved path, only an outDir strictly inside the
|
|
12
|
+
* project root is emptied first, and the bundle is validated before
|
|
13
|
+
* old output is cleared.
|
|
14
|
+
*/
|
|
15
|
+
import { normalizeVfsPath, parentVfsPath, resolveVfsPath, stripLeadingSlashes } from '@nimbus-sh/core/vfs/path.js';
|
|
16
|
+
import { errorText } from '@nimbus-sh/core/_shared/error-text.js';
|
|
17
|
+
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
18
|
+
import { parseViteConfigSource, viteBuildBlockingPlugins, unhandledVitePlugins } from '@nimbus-sh/core/runtime/vite-config-parser.js';
|
|
19
|
+
import { findHtmlScriptEntrypoint, rewriteViteBuildHtml } from '../runtime/html-entrypoint.js';
|
|
20
|
+
import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
21
|
+
import { ViteDevServer } from '../facets/vite-dev-server.js';
|
|
22
|
+
import { shouldUseRealVite } from '../facets/cirrus-real.js';
|
|
23
|
+
import { makeLongRunningPortStub, resolveLongRunningPort, expandArgvShellDefaults, } from '@nimbus-sh/core/runtime/long-running-handle.js';
|
|
24
|
+
import { checkNodeModulesGuard, withLoudTimeout, VITE_BUILD_TIMEOUT_MS, } from './helpers.js';
|
|
25
|
+
import { startRealVite } from './start-real-vite.js';
|
|
26
|
+
import { notifyTerminalEvent } from '../runtime/process-logs-api.js';
|
|
27
|
+
import { VITE_CONFIG_KEY } from './keys.js';
|
|
28
|
+
import { registerServingPort } from './serving-port.js';
|
|
29
|
+
export function createViteCommand(self) {
|
|
30
|
+
return async (ctx) => {
|
|
31
|
+
const args = ctx.args || [];
|
|
32
|
+
const cwd = normalizeVfsPath(ctx.cwd || '/home/user');
|
|
33
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
34
|
+
ctx.stdout.write('Usage: vite [command] [options]\n\n');
|
|
35
|
+
ctx.stdout.write('Commands:\n');
|
|
36
|
+
ctx.stdout.write(' (default) Start dev server\n');
|
|
37
|
+
ctx.stdout.write(' build Build for production\n');
|
|
38
|
+
ctx.stdout.write(' preview Serve the built dist/\n');
|
|
39
|
+
ctx.stdout.write(' stop Stop dev server\n\n');
|
|
40
|
+
ctx.stdout.write('Options:\n');
|
|
41
|
+
ctx.stdout.write(' --root <dir> Project root\n');
|
|
42
|
+
ctx.stdout.write(' --port <n> Server port\n');
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
self.ensureSqliteFs();
|
|
46
|
+
const kernelFs = self.sqliteFs.as(CRED_KERNEL);
|
|
47
|
+
const viteConfig = {};
|
|
48
|
+
for (const cfgName of ['vite.config.ts', 'vite.config.js', 'vite.config.mjs']) {
|
|
49
|
+
const cfgPath = cwd + '/' + cfgName;
|
|
50
|
+
if (kernelFs.exists(cfgPath)) {
|
|
51
|
+
try {
|
|
52
|
+
let cfgCode = kernelFs.readFileString(cfgPath);
|
|
53
|
+
// Transform TS to JS
|
|
54
|
+
if (cfgName.endsWith('.ts')) {
|
|
55
|
+
if (!self.esbuildService)
|
|
56
|
+
self.esbuildService = new EsbuildService(kernelFs);
|
|
57
|
+
const t = await self.esbuildService.transform(cfgCode, { loader: 'ts', format: 'esm' });
|
|
58
|
+
cfgCode = t.code;
|
|
59
|
+
}
|
|
60
|
+
Object.assign(viteConfig, parseViteConfigSource(cfgCode));
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
ctx.stderr.write(`Warning: could not parse ${cfgName}: ${e?.message}\n`);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ── vite build ──
|
|
69
|
+
if (args[0] === 'build') {
|
|
70
|
+
// Capability gate: the built-in builder is esbuild underneath and
|
|
71
|
+
// never evaluates vite.config `plugins`. Only framework plugins
|
|
72
|
+
// (SvelteKit, Vue, Solid) mean the project is not a plain-Vite
|
|
73
|
+
// app — they would die deep in esbuild on an entry point that
|
|
74
|
+
// does not exist or framework syntax the JS loader cannot parse,
|
|
75
|
+
// so say what is actually wrong. Every other plugin is a
|
|
76
|
+
// warning, not a refusal.
|
|
77
|
+
const blockingPlugins = viteBuildBlockingPlugins(viteConfig);
|
|
78
|
+
if (blockingPlugins.length) {
|
|
79
|
+
ctx.stderr.write('\x1b[31m✘\x1b[0m vite build: this project needs Vite plugins the built-in build server cannot run' +
|
|
80
|
+
' (' + blockingPlugins.join(', ') + ').\n' +
|
|
81
|
+
' The built-in Vite server supports plain Vite projects (React, JSX/TS, CSS, and asset imports);\n' +
|
|
82
|
+
' framework projects like SvelteKit, Vue, Solid, or Astro require a real Vite —\n' +
|
|
83
|
+
' Nimbus does not run one for `vite build` yet.\n');
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
const buildSkippedPlugins = unhandledVitePlugins(viteConfig);
|
|
87
|
+
if (buildSkippedPlugins.length) {
|
|
88
|
+
ctx.stderr.write('\x1b[33m!\x1b[0m vite build: skipping plugins the built-in build cannot run' +
|
|
89
|
+
' (' + buildSkippedPlugins.join(', ') + '); output is the plain-Vite bundle.\n');
|
|
90
|
+
}
|
|
91
|
+
if (!self.esbuildService)
|
|
92
|
+
self.esbuildService = new EsbuildService(kernelFs);
|
|
93
|
+
const htmlPath = cwd + '/index.html';
|
|
94
|
+
let entryPoint = cwd + '/src/main.tsx';
|
|
95
|
+
let origHtml = '';
|
|
96
|
+
try {
|
|
97
|
+
origHtml = kernelFs.readFileString(htmlPath);
|
|
98
|
+
const htmlEntrypoint = await findHtmlScriptEntrypoint(origHtml);
|
|
99
|
+
if (htmlEntrypoint)
|
|
100
|
+
entryPoint = cwd + '/' + stripLeadingSlashes(htmlEntrypoint);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
ctx.stderr.write('Warning: no index.html\n');
|
|
104
|
+
}
|
|
105
|
+
if (!kernelFs.exists(entryPoint)) {
|
|
106
|
+
const alts = [cwd + '/src/main.tsx', cwd + '/src/main.ts', cwd + '/src/index.tsx', cwd + '/src/index.ts'];
|
|
107
|
+
entryPoint = alts.find(p => kernelFs.exists(p)) || entryPoint;
|
|
108
|
+
}
|
|
109
|
+
if (!kernelFs.exists(entryPoint)) {
|
|
110
|
+
ctx.stderr.write('\x1b[31m✘\x1b[0m vite build: no entry point — index.html declares no <script src> and none of\n' +
|
|
111
|
+
' src/main.{tsx,ts} or src/index.{tsx,ts} exists. The built-in build server handles plain\n' +
|
|
112
|
+
' Vite apps; projects with other layouts need a real Vite.\n');
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
ctx.stdout.write('Building for production...\n');
|
|
116
|
+
ctx.stdout.write(' Entry: ' + entryPoint + '\n');
|
|
117
|
+
const t0 = Date.now();
|
|
118
|
+
try {
|
|
119
|
+
// Vite parity: build writes to the resolved outDir wherever it
|
|
120
|
+
// lands inside the VFS — the common monorepo layout builds the
|
|
121
|
+
// frontend into ../server/public. Only the empty step is gated:
|
|
122
|
+
// an outDir not strictly inside the project root is never
|
|
123
|
+
// emptied (outDir == root is not inside), and Vite's warning is
|
|
124
|
+
// printed verbatim.
|
|
125
|
+
const outDir = viteConfig.outDir || 'dist';
|
|
126
|
+
const resolvedOutDir = resolveVfsPath(outDir, cwd);
|
|
127
|
+
const insideRoot = resolvedOutDir.length > cwd.length && resolvedOutDir.startsWith(cwd + '/');
|
|
128
|
+
const distDir = resolvedOutDir;
|
|
129
|
+
if (!insideRoot) {
|
|
130
|
+
ctx.stderr.write(`\x1b[33m(!)\x1b[0m outDir ${resolvedOutDir} is not inside project root and will not be emptied.\n`);
|
|
131
|
+
}
|
|
132
|
+
const publicDir = cwd + '/public';
|
|
133
|
+
const hasPublic = kernelFs.exists(publicDir) && kernelFs.isDirectory(publicDir);
|
|
134
|
+
// Detect which packages are installed vs need CDN
|
|
135
|
+
const nmDir = cwd + '/node_modules';
|
|
136
|
+
const externals = [];
|
|
137
|
+
const cdnPackages = [];
|
|
138
|
+
for (const pkg of ['react', 'react-dom', 'react/jsx-runtime', 'react-dom/client']) {
|
|
139
|
+
const pkgBase = pkg.split('/')[0];
|
|
140
|
+
if (!kernelFs.exists(nmDir + '/' + pkgBase)) {
|
|
141
|
+
externals.push(pkg);
|
|
142
|
+
if (!cdnPackages.includes(pkgBase))
|
|
143
|
+
cdnPackages.push(pkgBase);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (viteConfig.alias)
|
|
147
|
+
externals.push(...Object.keys(viteConfig.alias));
|
|
148
|
+
// vite-equivalent output layout: dist/assets/<name>-<hash>.<ext>
|
|
149
|
+
// for both the entry and every emitted asset — entryNames +
|
|
150
|
+
// assetNames is what makes esbuild's `file` loader land there.
|
|
151
|
+
// Bounded (G5): an esbuild stall must surface as a loud timeout
|
|
152
|
+
// naming the entry, never a silent hang. The existing catch below
|
|
153
|
+
// renders the timeout as `Build error: …`.
|
|
154
|
+
ctx.stdout.write(' Bundling ' + entryPoint + ' …\n');
|
|
155
|
+
const result = await withLoudTimeout(self.esbuildService.build([entryPoint], {
|
|
156
|
+
bundle: true, format: 'esm', target: 'es2020', platform: 'browser',
|
|
157
|
+
minify: true, outdir: distDir,
|
|
158
|
+
entryNames: 'assets/[name]-[hash]',
|
|
159
|
+
chunkNames: 'assets/[name]-[hash]',
|
|
160
|
+
assetNames: 'assets/[name]-[hash]',
|
|
161
|
+
external: externals.length > 0 ? externals : undefined,
|
|
162
|
+
viteAssets: true,
|
|
163
|
+
vitePublicDir: hasPublic ? publicDir : undefined,
|
|
164
|
+
}), VITE_BUILD_TIMEOUT_MS, `vite build of ${entryPoint}`);
|
|
165
|
+
if (result.errors?.length) {
|
|
166
|
+
for (const e of result.errors)
|
|
167
|
+
ctx.stderr.write(' error: ' + e.text + '\n');
|
|
168
|
+
return 1;
|
|
169
|
+
}
|
|
170
|
+
// The entry output must exist before old output is cleared —
|
|
171
|
+
// a failed build must never wipe the previously-good dist/.
|
|
172
|
+
const entryOutputRel = Object.entries(result.metafile?.outputs || {})
|
|
173
|
+
.find(([, o]) => o.entryPoint)?.[0]?.replace(/^\/+/, '');
|
|
174
|
+
const entryJs = (entryOutputRel
|
|
175
|
+
? result.outputFiles.find((f) => f.path.replace(/^\/+/, '') === entryOutputRel)
|
|
176
|
+
: undefined) ?? result.outputFiles.find((f) => f.path.endsWith('.js'));
|
|
177
|
+
if (!entryJs) {
|
|
178
|
+
ctx.stderr.write('Build error: bundler produced no JS output\n');
|
|
179
|
+
return 1;
|
|
180
|
+
}
|
|
181
|
+
const jsFilename = entryJs.path.slice(entryJs.path.lastIndexOf('/') + 1);
|
|
182
|
+
// Vite's emptyOutDir: stale hashed outputs must not accumulate.
|
|
183
|
+
// Only an outDir strictly inside the project root is emptied —
|
|
184
|
+
// the warning above covered the rest.
|
|
185
|
+
if (insideRoot && kernelFs.exists(distDir)) {
|
|
186
|
+
kernelFs.removeRecursive(distDir);
|
|
187
|
+
}
|
|
188
|
+
kernelFs.mkdir(distDir, { recursive: true });
|
|
189
|
+
// CSS bundled through the entry imports (and its url() assets)
|
|
190
|
+
// arrives as the entry's cssBundle sidecar — esbuild already
|
|
191
|
+
// rewrote every url() to the hashed emitted path.
|
|
192
|
+
const cssBundlePath = entryOutputRel
|
|
193
|
+
? result.metafile?.outputs?.[entryOutputRel]?.cssBundle
|
|
194
|
+
: undefined;
|
|
195
|
+
let cssFilename = cssBundlePath
|
|
196
|
+
? cssBundlePath.slice(cssBundlePath.lastIndexOf('/') + 1)
|
|
197
|
+
: result.outputFiles
|
|
198
|
+
.find((f) => f.path.endsWith('.css'))
|
|
199
|
+
?.path.split('/').pop();
|
|
200
|
+
// Fallback for HTML-linked stylesheets the bundle never saw
|
|
201
|
+
// (`<link href="src/site.css">` in index.html): concatenate what
|
|
202
|
+
// src/ declares, like the pre-asset-pipeline path did.
|
|
203
|
+
let fallbackCss = '';
|
|
204
|
+
if (!cssFilename) {
|
|
205
|
+
const collectCss = (dir) => {
|
|
206
|
+
try {
|
|
207
|
+
for (const e of kernelFs.readdir(dir)) {
|
|
208
|
+
const fp = dir + '/' + e.name;
|
|
209
|
+
if (e.type === 'directory')
|
|
210
|
+
collectCss(fp);
|
|
211
|
+
else if (e.name.endsWith('.css')) {
|
|
212
|
+
try {
|
|
213
|
+
fallbackCss += kernelFs.readFileString(fp) + '\n';
|
|
214
|
+
}
|
|
215
|
+
catch { }
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch { }
|
|
220
|
+
};
|
|
221
|
+
collectCss(cwd + '/src');
|
|
222
|
+
if (fallbackCss.trim()) {
|
|
223
|
+
const cssHashNum = fallbackCss.split('').reduce((h, c) => ((h << 5) - h + c.charCodeAt(0)) | 0, 0);
|
|
224
|
+
cssFilename = 'index-' + (cssHashNum >>> 0).toString(36).padStart(6, '0') + '.css';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
// Write every emitted output (bytes, not text — `file` assets are
|
|
228
|
+
// binary), printing vite-style size lines.
|
|
229
|
+
for (const f of result.outputFiles) {
|
|
230
|
+
const outPath = normalizeVfsPath(f.path);
|
|
231
|
+
const parent = parentVfsPath(outPath);
|
|
232
|
+
if (parent && !kernelFs.exists(parent))
|
|
233
|
+
kernelFs.mkdir(parent, { recursive: true });
|
|
234
|
+
kernelFs.writeFile(outPath, f.bytes);
|
|
235
|
+
const rel = outPath.slice(cwd.length + 1);
|
|
236
|
+
ctx.stdout.write(' \x1b[2m' + rel + '\x1b[0m ' + (f.bytes.length / 1024).toFixed(2) + ' kB\n');
|
|
237
|
+
}
|
|
238
|
+
if (fallbackCss.trim() && cssFilename) {
|
|
239
|
+
const cssPath = distDir + '/assets/' + cssFilename;
|
|
240
|
+
kernelFs.mkdir(distDir + '/assets', { recursive: true });
|
|
241
|
+
kernelFs.writeFile(cssPath, fallbackCss);
|
|
242
|
+
ctx.stdout.write(' \x1b[2m' + outDir + '/assets/' + cssFilename + '\x1b[0m ' + (fallbackCss.length / 1024).toFixed(2) + ' kB\n');
|
|
243
|
+
}
|
|
244
|
+
// public/ copies verbatim to dist/ (same `vite build` semantics —
|
|
245
|
+
// the favicon the template references lives there).
|
|
246
|
+
if (hasPublic) {
|
|
247
|
+
const copyTree = (src, dst) => {
|
|
248
|
+
for (const e of kernelFs.readdir(src)) {
|
|
249
|
+
const s = src + '/' + e.name;
|
|
250
|
+
const d = dst + '/' + e.name;
|
|
251
|
+
if (e.type === 'directory') {
|
|
252
|
+
copyTree(s, d);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const parent = parentVfsPath(d);
|
|
256
|
+
if (parent && !kernelFs.exists(parent))
|
|
257
|
+
kernelFs.mkdir(parent, { recursive: true });
|
|
258
|
+
kernelFs.writeFile(d, kernelFs.readFile(s));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
copyTree(publicDir, distDir);
|
|
263
|
+
}
|
|
264
|
+
// Generate dist/index.html
|
|
265
|
+
if (origHtml) {
|
|
266
|
+
const distHtml = await rewriteViteBuildHtml(origHtml, {
|
|
267
|
+
jsFilename,
|
|
268
|
+
cssFilename,
|
|
269
|
+
removeImportMap: cdnPackages.length === 0,
|
|
270
|
+
injectCss: true,
|
|
271
|
+
});
|
|
272
|
+
kernelFs.writeFile(distDir + '/index.html', distHtml);
|
|
273
|
+
ctx.stdout.write(' \x1b[2m' + outDir + '/index.html\x1b[0m ' + (distHtml.length / 1024).toFixed(2) + ' kB\n');
|
|
274
|
+
if (cdnPackages.length > 0) {
|
|
275
|
+
ctx.stdout.write(' \x1b[33mNote: ' + cdnPackages.join(', ') + ' loaded from CDN (not bundled)\x1b[0m\n');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
ctx.stdout.write('\n\x1b[32m✓ built in ' + ((Date.now() - t0) / 1000).toFixed(2) + 's\x1b[0m\n');
|
|
279
|
+
return 0;
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
ctx.stderr.write('Build error: ' + errorText(e) + '\n');
|
|
283
|
+
return 1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// ── vite preview ──
|
|
287
|
+
if (args[0] === 'preview') {
|
|
288
|
+
ctx.stdout.write('Serving dist/ — open ' + self.viteBasePath + '/\n');
|
|
289
|
+
// Vite parity: preview serves the resolved outDir wherever it
|
|
290
|
+
// landed — build wrote there, so preview must read there.
|
|
291
|
+
const distRoot = resolveVfsPath(viteConfig.outDir || 'dist', cwd);
|
|
292
|
+
if (!kernelFs.exists(distRoot)) {
|
|
293
|
+
ctx.stderr.write('dist/ not found. Run vite build first.\n');
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
// Start vite on the dist directory
|
|
297
|
+
if (!self.esbuildService)
|
|
298
|
+
self.esbuildService = new EsbuildService(kernelFs);
|
|
299
|
+
if (self.viteDevServer?.isRunning)
|
|
300
|
+
self.viteDevServer.stop();
|
|
301
|
+
const previewBasePath = self.viteBasePath;
|
|
302
|
+
// process metadata support: same long-running treatment as the
|
|
303
|
+
// dev path, just on the dist/ directory.
|
|
304
|
+
const previewPort = viteConfig.port || 4173; // vite preview default
|
|
305
|
+
const previewProcEntry = self.processes.spawn('vite preview (' + distRoot + ')', ['vite', ...args], distRoot, { longRunning: true });
|
|
306
|
+
self.viteDevServer = new ViteDevServer({
|
|
307
|
+
vfs: self.sqliteFs, esbuild: self.esbuildService, root: distRoot,
|
|
308
|
+
onHmrMessage: () => { },
|
|
309
|
+
sql: self.ctx.storage.sql,
|
|
310
|
+
basePath: previewBasePath,
|
|
311
|
+
env: self.env,
|
|
312
|
+
ctx: self.ctx,
|
|
313
|
+
bundlePool: self.ensureBundlePool(),
|
|
314
|
+
port: previewPort,
|
|
315
|
+
pid: previewProcEntry.pid,
|
|
316
|
+
processes: self.processes,
|
|
317
|
+
});
|
|
318
|
+
self.viteDevServer.start();
|
|
319
|
+
try {
|
|
320
|
+
const previewStub = makeLongRunningPortStub(self.viteDevServer);
|
|
321
|
+
self.portRegistry.bindFacetStub(previewProcEntry.pid, previewStub);
|
|
322
|
+
await registerServingPort(self, previewProcEntry.pid, previewPort);
|
|
323
|
+
self._viteShimPid = previewProcEntry.pid;
|
|
324
|
+
self._viteShimPort = previewPort;
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
327
|
+
try {
|
|
328
|
+
await self.ctx.storage.put(VITE_CONFIG_KEY, {
|
|
329
|
+
root: distRoot, basePath: previewBasePath, port: previewPort,
|
|
330
|
+
identity: { cwd: previewProcEntry.cwd, argv: previewProcEntry.argv },
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
catch { }
|
|
334
|
+
ctx.stdout.write('Serving at ' + previewBasePath + '/ \x1b[2m(pid=' + previewProcEntry.pid + ', port=' + previewPort + ')\x1b[0m\n');
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
// ── vite stop ──
|
|
338
|
+
if (args[0] === 'stop') {
|
|
339
|
+
let stopped = false;
|
|
340
|
+
if (self.cirrusReal?.isRunning) {
|
|
341
|
+
self.cirrusReal.stop(self.ctx);
|
|
342
|
+
self.cirrusReal = null;
|
|
343
|
+
stopped = true;
|
|
344
|
+
}
|
|
345
|
+
if (self.viteDevServer?.isRunning) {
|
|
346
|
+
self.viteDevServer.stop();
|
|
347
|
+
self.viteDevServer = null;
|
|
348
|
+
try {
|
|
349
|
+
await self.ctx.storage.delete(VITE_CONFIG_KEY);
|
|
350
|
+
}
|
|
351
|
+
catch { }
|
|
352
|
+
stopped = true;
|
|
353
|
+
}
|
|
354
|
+
// Primitive #3 teardown — symmetric with the start path. Always
|
|
355
|
+
// safe to call: unregisterByPid is idempotent, exit() guards
|
|
356
|
+
// against re-marking already-terminal entries.
|
|
357
|
+
if (self._viteShimPid != null) {
|
|
358
|
+
try {
|
|
359
|
+
self.portRegistry.unregisterByPid(self._viteShimPid);
|
|
360
|
+
}
|
|
361
|
+
catch { }
|
|
362
|
+
try {
|
|
363
|
+
self.processes.exit(self._viteShimPid, 0);
|
|
364
|
+
}
|
|
365
|
+
catch { }
|
|
366
|
+
notifyTerminalEvent(self.terminal, {
|
|
367
|
+
type: 'exit', pid: self._viteShimPid, code: 0, command: 'vite',
|
|
368
|
+
});
|
|
369
|
+
self._viteShimPid = null;
|
|
370
|
+
self._viteShimPort = null;
|
|
371
|
+
}
|
|
372
|
+
if (stopped) {
|
|
373
|
+
ctx.stdout.write('\x1b[33mDev server stopped.\x1b[0m\n');
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
ctx.stdout.write('No dev server running.\n');
|
|
377
|
+
}
|
|
378
|
+
return 0;
|
|
379
|
+
}
|
|
380
|
+
// ── vite (default: dev server) ──
|
|
381
|
+
let vfsRoot = cwd;
|
|
382
|
+
for (let i = 0; i < args.length; i++) {
|
|
383
|
+
if (args[i] === '--root' && args[i + 1])
|
|
384
|
+
vfsRoot = resolveVfsPath(args[i + 1], cwd);
|
|
385
|
+
}
|
|
386
|
+
if (viteConfig.root && viteConfig.root !== '.') {
|
|
387
|
+
vfsRoot = resolveVfsPath(viteConfig.root, cwd);
|
|
388
|
+
}
|
|
389
|
+
vfsRoot = normalizeVfsPath(vfsRoot);
|
|
390
|
+
// Argv expansion: package.json scripts commonly write
|
|
391
|
+
// `--port ${PORT:-3000}`. Resolve it once and feed both Vite
|
|
392
|
+
// backends so `/api/stats`, `/preview/`, and port tabs agree.
|
|
393
|
+
const expandedArgs = expandArgvShellDefaults(args, ctx.env || {});
|
|
394
|
+
const vitePortDefault = 5173;
|
|
395
|
+
const resolvedPort = resolveLongRunningPort({
|
|
396
|
+
argv: expandedArgs,
|
|
397
|
+
env: ctx.env,
|
|
398
|
+
configPort: viteConfig.port,
|
|
399
|
+
fallback: vitePortDefault,
|
|
400
|
+
});
|
|
401
|
+
// ── Preflight: node_modules guard ────────────────────────────────────
|
|
402
|
+
// Direct `vite` invocation requires installed deps. Bail loudly BEFORE
|
|
403
|
+
// spawning a dev server that would just serve broken modules and
|
|
404
|
+
// confuse the user. --force / --no-install-check bypasses the check.
|
|
405
|
+
const bypassInstallCheck = expandedArgs.includes('--force') || expandedArgs.includes('--no-install-check');
|
|
406
|
+
if (!bypassInstallCheck) {
|
|
407
|
+
const guard = checkNodeModulesGuard(kernelFs, vfsRoot);
|
|
408
|
+
if (guard.missing) {
|
|
409
|
+
ctx.stderr.write('\x1b[31m\u2718\x1b[0m \x1b[1mnode_modules/ not found\x1b[0m' +
|
|
410
|
+
(guard.depCount > 0 ? ` (${guard.depCount} dependencies declared)` : '') + '\n' +
|
|
411
|
+
' Run \x1b[36mnpm install\x1b[0m in ' + vfsRoot + ' first,\n' +
|
|
412
|
+
' or re-run with \x1b[36m--force\x1b[0m to skip this check.\n');
|
|
413
|
+
return 1;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (self.viteDevServer?.isRunning)
|
|
417
|
+
self.viteDevServer.stop();
|
|
418
|
+
// ── Real-vite mode (Phase 0 spike, opt-in) ─────────────────────────
|
|
419
|
+
// NIMBUS_REAL_VITE=1 or `nimbusDevServer: 'real'` in vite.config.ts
|
|
420
|
+
// routes the session through a dynamic-worker facet running the
|
|
421
|
+
// real `vite` npm package. The in-process Cirrus shim is bypassed.
|
|
422
|
+
//
|
|
423
|
+
// This is EXPERIMENTAL and gated behind an explicit opt-in. Any
|
|
424
|
+
// error here falls back to Cirrus by the user re-running without
|
|
425
|
+
// the env flag — we do not silently fall back (fidelity over
|
|
426
|
+
// magic).
|
|
427
|
+
const sessionEnv = (ctx && ctx.env) || {};
|
|
428
|
+
const useReal = shouldUseRealVite({ env: sessionEnv, viteConfig });
|
|
429
|
+
// Long-running handoff (bin-spawn contract): when invoked from a
|
|
430
|
+
// wrapper that already allocated a pid (`npm run dev` via
|
|
431
|
+
// shellExecuteTracked, the npm-bin resolver, the SDK's startProcess),
|
|
432
|
+
// ADOPT that pid instead of spawning a second one. One pid, one start
|
|
433
|
+
// banner, no false exit — and one identity: the process table's
|
|
434
|
+
// cwd+argv for that pid is what the app verbs derive the dev server's
|
|
435
|
+
// owner from, so it is persisted with the config and given back to
|
|
436
|
+
// the entry a hibernation restore allocates.
|
|
437
|
+
const binSpawn = ctx.__nimbusBinSpawn;
|
|
438
|
+
const adoptedEntry = binSpawn?.skipSpawn && binSpawn.callerPid != null
|
|
439
|
+
? self.processes.get(binSpawn.callerPid)
|
|
440
|
+
: undefined;
|
|
441
|
+
const handedOff = adoptedEntry != null;
|
|
442
|
+
const identity = adoptedEntry
|
|
443
|
+
? { cwd: adoptedEntry.cwd, argv: adoptedEntry.argv }
|
|
444
|
+
: { cwd: vfsRoot, argv: expandedArgs };
|
|
445
|
+
if (useReal) {
|
|
446
|
+
const vitePort = resolvedPort;
|
|
447
|
+
const previewBasePath = self.viteBasePath;
|
|
448
|
+
// One boot path, shared with hibernation-restore (start-real-vite.ts):
|
|
449
|
+
// pre-bundles the user's vite.config, boots the facet, registers the
|
|
450
|
+
// port, and persists the vite-config so a woken session rebuilds the
|
|
451
|
+
// same real-vite server. Only the banner below is command-specific.
|
|
452
|
+
const { cirrusReal, userConfigBundle, cfgPath } = await startRealVite(self, {
|
|
453
|
+
root: vfsRoot,
|
|
454
|
+
port: vitePort,
|
|
455
|
+
basePath: previewBasePath,
|
|
456
|
+
configDir: cwd,
|
|
457
|
+
identity,
|
|
458
|
+
signal: ctx.signal,
|
|
459
|
+
onConfigError: (msg) => {
|
|
460
|
+
ctx.stderr.write('\x1b[33m!\x1b[0m vite.config bundling failed: ' + msg + '\n');
|
|
461
|
+
ctx.stderr.write(' Real-vite will run with default config.\n');
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
{
|
|
465
|
+
// ── Boot banner (§4.3 of PHASE2-REAL-VITE-PLAN.md) ──────
|
|
466
|
+
const snap = cirrusReal.stats.snapshot;
|
|
467
|
+
ctx.stdout.write('\n\x1b[1;36m Nimbus: real-vite mode\x1b[0m \x1b[2m(experimental, Phase 1-4)\x1b[0m\n\n');
|
|
468
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Preview: \x1b[36m' + previewBasePath + '/\x1b[0m\n');
|
|
469
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Vite: ' + cirrusReal.stats.viteVersion + ' (bundled)\n');
|
|
470
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Root: ' + vfsRoot + '\n');
|
|
471
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Port: ' + vitePort + ' \x1b[2m(virtual routing key)\x1b[0m\n');
|
|
472
|
+
if (snap) {
|
|
473
|
+
const kb = (snap.totalBytes / 1024).toFixed(1);
|
|
474
|
+
const pkgJson = snap.packageJsonCount;
|
|
475
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Snapshot: ' + snap.fileCount + ' files / ' +
|
|
476
|
+
kb + ' KB ' +
|
|
477
|
+
(pkgJson ? '\x1b[2m(incl. ' + pkgJson + ' package.json, rest lazy)\x1b[0m' : '') + '\n');
|
|
478
|
+
}
|
|
479
|
+
if (userConfigBundle) {
|
|
480
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Config: ' + cfgPath + ' \x1b[2m(' +
|
|
481
|
+
(userConfigBundle.length / 1024).toFixed(0) + ' KB bundled)\x1b[0m\n');
|
|
482
|
+
}
|
|
483
|
+
ctx.stdout.write('\n \x1b[2mWorks:\x1b[0m @vitejs/plugin-react, JSX/TSX transforms, SPA fallback, HMR.\n');
|
|
484
|
+
ctx.stdout.write(' \x1b[2mPartial:\x1b[0m other plugins (Babel-family generally OK; SWC/Rolldown blocked).\n');
|
|
485
|
+
ctx.stdout.write(' \x1b[2mBlocked:\x1b[0m vite build (rolldown needs node:wasi). Use cirrus for build.\n');
|
|
486
|
+
ctx.stdout.write('\n \x1b[2mRun \x1b[0mvite stop\x1b[2m, or \x1b[0mNIMBUS_REAL_VITE=0 vite\x1b[2m for Cirrus.\x1b[0m\n\n');
|
|
487
|
+
return 0;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
// The built-in dev server evaluates no vite.config `plugins`, but it
|
|
491
|
+
// never refuses on them: plain-Vite apps routinely declare plugins
|
|
492
|
+
// the shim covers (react, cloudflare, tailwind) or doesn't need
|
|
493
|
+
// (watch/reload hooks). One warning line for the plugins it can't
|
|
494
|
+
// run — frameworks land there too: their pages won't render, but the
|
|
495
|
+
// server itself still serves, which is the pre-diagnostic behavior.
|
|
496
|
+
const devSkipped = [
|
|
497
|
+
...viteBuildBlockingPlugins(viteConfig),
|
|
498
|
+
...unhandledVitePlugins(viteConfig),
|
|
499
|
+
];
|
|
500
|
+
if (devSkipped.length) {
|
|
501
|
+
ctx.stderr.write('\x1b[33m!\x1b[0m vite: plugins are not evaluated by the built-in dev server' +
|
|
502
|
+
' (' + devSkipped.join(', ') + '); serving the plain-Vite app.\n');
|
|
503
|
+
}
|
|
504
|
+
if (!self.esbuildService)
|
|
505
|
+
self.esbuildService = new EsbuildService(kernelFs);
|
|
506
|
+
const previewBasePath = self.viteBasePath;
|
|
507
|
+
const viteDefine = viteConfig.define;
|
|
508
|
+
// Vite dev servers are represented as long-running process-table
|
|
509
|
+
// entries and port-registry handlers, so `ps`, logs, preview routing,
|
|
510
|
+
// `vite stop`, and `kill <pid>` share the same lifecycle primitives.
|
|
511
|
+
// Allocate PID FIRST so we can plumb it into ViteDevServer's
|
|
512
|
+
// process-log wiring at construction time. The PID stays valid
|
|
513
|
+
// for the life of this dev-server instance; subsequent log lines
|
|
514
|
+
// emitted by ViteDevServer flow into the pid's stderr ring,
|
|
515
|
+
// visible in the Process tab.
|
|
516
|
+
//
|
|
517
|
+
// The adopted wrapper pid stays `running` in /api/processes with this
|
|
518
|
+
// port; a fresh spawn carries the identity inputs computed above.
|
|
519
|
+
const viteProcEntry = adoptedEntry ?? self.processes.spawn('vite (' + vfsRoot + ')', identity.argv, identity.cwd, { longRunning: true });
|
|
520
|
+
if (handedOff)
|
|
521
|
+
self.processes.setLongRunning(viteProcEntry.pid);
|
|
522
|
+
self.viteDevServer = new ViteDevServer({
|
|
523
|
+
vfs: self.sqliteFs,
|
|
524
|
+
esbuild: self.esbuildService,
|
|
525
|
+
root: vfsRoot,
|
|
526
|
+
port: resolvedPort,
|
|
527
|
+
aliases: viteConfig.alias,
|
|
528
|
+
define: viteDefine,
|
|
529
|
+
onHmrMessage: (msg) => {
|
|
530
|
+
if (self.terminal)
|
|
531
|
+
try {
|
|
532
|
+
self.terminal.ws.send(JSON.stringify({ type: 'hmr', data: msg }));
|
|
533
|
+
}
|
|
534
|
+
catch { }
|
|
535
|
+
},
|
|
536
|
+
sql: self.ctx.storage.sql,
|
|
537
|
+
injectBasename: viteConfig.injectBasename,
|
|
538
|
+
basePath: previewBasePath,
|
|
539
|
+
env: self.env,
|
|
540
|
+
ctx: self.ctx,
|
|
541
|
+
// The shared bundle pool puts cold /@modules/ misses on the facet
|
|
542
|
+
// path. This site never passed it before, so `vite` served every
|
|
543
|
+
// cold miss through in-supervisor esbuild-wasm.
|
|
544
|
+
bundlePool: self.ensureBundlePool(),
|
|
545
|
+
// Process diagnostics support: wire dev-server diagnostics into the
|
|
546
|
+
// supervisor's per-PID log store so the Process tab is not silent
|
|
547
|
+
// after the banner.
|
|
548
|
+
pid: viteProcEntry.pid,
|
|
549
|
+
processes: self.processes,
|
|
550
|
+
});
|
|
551
|
+
self.viteDevServer.start();
|
|
552
|
+
try {
|
|
553
|
+
await self.ctx.storage.put(VITE_CONFIG_KEY, {
|
|
554
|
+
root: vfsRoot, aliases: viteConfig.alias, define: viteDefine,
|
|
555
|
+
injectBasename: viteConfig.injectBasename, basePath: previewBasePath,
|
|
556
|
+
port: resolvedPort,
|
|
557
|
+
identity: { cwd: viteProcEntry.cwd, argv: viteProcEntry.argv },
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
catch { }
|
|
561
|
+
// Register the port and build the long-running stub. The stub
|
|
562
|
+
// forwards into the in-process viteDevServer through the generic
|
|
563
|
+
// long-running adapter — same hook every future long-running
|
|
564
|
+
// facet uses (Express, Bun.serve, http.createServer().listen()).
|
|
565
|
+
const viteStub = makeLongRunningPortStub(self.viteDevServer);
|
|
566
|
+
self.portRegistry.bindFacetStub(viteProcEntry.pid, viteStub);
|
|
567
|
+
await registerServingPort(self, viteProcEntry.pid, resolvedPort);
|
|
568
|
+
// Track the wiring so `vite stop` and crash-handlers can tear it
|
|
569
|
+
// down without searching the registry.
|
|
570
|
+
self._viteShimPid = viteProcEntry.pid;
|
|
571
|
+
self._viteShimPort = resolvedPort;
|
|
572
|
+
// Spawn / long-running event for the Process tab UI. Mirrors the
|
|
573
|
+
// shellExecuteTracked banner so the user sees the same shape no
|
|
574
|
+
// matter how vite was invoked. Suppressed on handoff — the wrapper
|
|
575
|
+
// already emitted the single start banner and spawn event for this pid.
|
|
576
|
+
if (!handedOff) {
|
|
577
|
+
if (self.terminal) {
|
|
578
|
+
self.terminal.write(`\x1b[2m[shell started (long-running): pid=${viteProcEntry.pid} cmd="vite ${expandedArgs.join(' ')}"]\x1b[0m\r\n`);
|
|
579
|
+
}
|
|
580
|
+
notifyTerminalEvent(self.terminal, {
|
|
581
|
+
type: 'spawn',
|
|
582
|
+
pid: viteProcEntry.pid,
|
|
583
|
+
command: 'vite ' + expandedArgs.join(' '),
|
|
584
|
+
longRunning: true,
|
|
585
|
+
attachedTty: false,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
// Banner — reports the resolved port and PID so the user can
|
|
589
|
+
// verify the multi-target routing.
|
|
590
|
+
ctx.stdout.write('\n\x1b[1;36m Nimbus Vite Dev Server\x1b[0m\n\n');
|
|
591
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Preview: \x1b[36m' + previewBasePath + '/\x1b[0m');
|
|
592
|
+
if (resolvedPort !== vitePortDefault) {
|
|
593
|
+
ctx.stdout.write(' \x1b[2m(also: ' + previewBasePath + '/?port=' + resolvedPort + ')\x1b[0m');
|
|
594
|
+
}
|
|
595
|
+
ctx.stdout.write('\n');
|
|
596
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Root: ' + vfsRoot + '\n');
|
|
597
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Port: ' + resolvedPort + ' \x1b[2m(pid=' + viteProcEntry.pid + ')\x1b[0m\n');
|
|
598
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Transforms: .ts .tsx .jsx (React JSX automatic)\n');
|
|
599
|
+
if (viteConfig.alias)
|
|
600
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Aliases: ' + Object.keys(viteConfig.alias).join(', ') + '\n');
|
|
601
|
+
if (viteDefine)
|
|
602
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Define: ' + Object.keys(viteDefine).join(', ') + '\n');
|
|
603
|
+
const twCfg = [vfsRoot + '/tailwind.config.js', vfsRoot + '/tailwind.config.ts'].find(p => kernelFs.exists(p));
|
|
604
|
+
if (twCfg)
|
|
605
|
+
ctx.stdout.write(' \x1b[32m\u279C\x1b[0m Tailwind: edge-vendored Play CDN \x1b[2m(detected)\x1b[0m\n');
|
|
606
|
+
ctx.stdout.write('\n \x1b[2mRun \x1b[0mvite stop\x1b[2m, or \x1b[0mkill ' + viteProcEntry.pid + '\x1b[2m, to stop.\x1b[0m\n\n');
|
|
607
|
+
return 0;
|
|
608
|
+
};
|
|
609
|
+
}
|