@lark-apaas/coding-template-nestjs-react-fullstack 0.1.27-alpha.20260824122815 → 0.1.27

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.
@@ -1,3098 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import http from 'node:http';
6
- import net from 'node:net';
7
- import { createRequire, Module } from 'node:module';
8
- import { createHash } from 'node:crypto';
9
- import { spawn } from 'node:child_process';
10
- import { pathToFileURL } from 'node:url';
11
- import {
12
- validateCacheGeneration,
13
- validateCacheGenerationReceipt,
14
- } from './cache-generation-preflight.mjs';
15
-
16
- const projectRoot = path.resolve(
17
- process.env.MIAODA_WORKSPACE_ROOT || process.cwd()
18
- );
19
-
20
- const MIAODA_PREVIEW_RENDERED_PLUGIN_NAME = 'miaoda-preview-rendered';
21
- const MIAODA_PREVIEW_RENDERED_CLIENT = String.raw`
22
- (() => {
23
- if (globalThis.__miaodaPreviewRenderedReporterInstalled) return;
24
- globalThis.__miaodaPreviewRenderedReporterInstalled = true;
25
-
26
- const runID = new URLSearchParams(globalThis.location.search).get(
27
- '__miaoda_preview_run_id'
28
- );
29
- if (!/^[A-Za-z0-9_-]{1,64}$/.test(runID || '')) return;
30
-
31
- let targetOrigin = globalThis.location.origin;
32
- try {
33
- if (document.referrer) targetOrigin = new URL(document.referrer).origin;
34
- } catch {}
35
- const allowedParentOrigins = new Set([
36
- globalThis.location.origin,
37
- 'https://miaoda.feishu-boe.cn',
38
- 'https://miaoda.feishu-pre.cn',
39
- 'https://miaoda.feishu.cn',
40
- ]);
41
- if (!allowedParentOrigins.has(targetOrigin)) return;
42
-
43
- let reportSequence = 0;
44
- let reportCycle = 0;
45
- let reportedCycle = -1;
46
- let quietTimer;
47
- let observer;
48
- let firstScheduledAt = performance.now();
49
- let domFallbackNotBefore = firstScheduledAt + 5_000;
50
- const timeOrigin =
51
- performance.timeOrigin ||
52
- performance.timing?.navigationStart ||
53
- Date.now() - performance.now();
54
- const hasRenderedContent = () =>
55
- [...document.body.children].some(element => {
56
- if (['SCRIPT', 'STYLE', 'LINK'].includes(element.tagName)) return false;
57
- return element.childElementCount > 0 || element.textContent?.trim();
58
- });
59
- const report = ({ customTtiMs, renderedAtEpochMs, source }) => {
60
- if (reportedCycle === reportCycle) return;
61
- reportedCycle = reportCycle;
62
- reportSequence += 1;
63
- clearTimeout(quietTimer);
64
- observer?.disconnect();
65
- globalThis.parent.postMessage(
66
- {
67
- type: 'MiaodaPreviewRendered',
68
- data: {
69
- runID,
70
- customTtiMs: Math.max(0, customTtiMs),
71
- renderedAtEpochMs,
72
- occurrence: reportSequence,
73
- source,
74
- },
75
- },
76
- targetOrigin
77
- );
78
- };
79
- const reportFromBuilderTti = event => {
80
- const customTtiMs = event?.detail?.time;
81
- if (typeof customTtiMs !== 'number' || !Number.isFinite(customTtiMs)) {
82
- return;
83
- }
84
- const eventEpochMs = event?.detail?.renderedAtEpochMs;
85
- const renderedAtEpochMs =
86
- typeof eventEpochMs === 'number' && Number.isFinite(eventEpochMs)
87
- ? eventEpochMs
88
- : timeOrigin + performance.now();
89
- report({
90
- customTtiMs,
91
- renderedAtEpochMs,
92
- source: 'builderV3-custom-TTI',
93
- });
94
- };
95
- const reportFromDomFallback = () => {
96
- if (reportedCycle === reportCycle) return;
97
- if (!document.body || !hasRenderedContent()) {
98
- if (performance.now() - firstScheduledAt < 10_000) schedule();
99
- return;
100
- }
101
- const renderedAt = performance.now();
102
- report({
103
- customTtiMs: renderedAt,
104
- renderedAtEpochMs: timeOrigin + renderedAt,
105
- source: 'dom-fallback',
106
- });
107
- };
108
- const schedule = () => {
109
- if (reportedCycle === reportCycle) return;
110
- clearTimeout(quietTimer);
111
- const delay = Math.max(100, domFallbackNotBefore - performance.now());
112
- quietTimer = setTimeout(
113
- () =>
114
- requestAnimationFrame(() => requestAnimationFrame(reportFromDomFallback)),
115
- delay
116
- );
117
- };
118
- const start = async () => {
119
- await document.fonts?.ready?.catch?.(() => undefined);
120
- firstScheduledAt = performance.now();
121
- domFallbackNotBefore = firstScheduledAt + 5_000;
122
- observer = new MutationObserver(schedule);
123
- observer.observe(document.documentElement, {
124
- attributes: true,
125
- childList: true,
126
- subtree: true,
127
- characterData: true,
128
- });
129
- schedule();
130
- setTimeout(() => observer.disconnect(), 10_000);
131
- };
132
- if (document.readyState === 'loading') {
133
- globalThis.addEventListener('DOMContentLoaded', () => void start(), {
134
- once: true,
135
- });
136
- } else {
137
- void start();
138
- }
139
- globalThis.addEventListener('builderV3-custom-TTI', reportFromBuilderTti);
140
- if (import.meta.hot) {
141
- import.meta.hot.on('vite:afterUpdate', () => {
142
- reportCycle += 1;
143
- firstScheduledAt = performance.now();
144
- domFallbackNotBefore = firstScheduledAt + 5_000;
145
- observer?.disconnect();
146
- observer = new MutationObserver(schedule);
147
- observer.observe(document.documentElement, {
148
- attributes: true,
149
- childList: true,
150
- subtree: true,
151
- characterData: true,
152
- });
153
- schedule();
154
- });
155
- }
156
- })();
157
- `;
158
-
159
- function createMiaodaPreviewRenderedPlugin() {
160
- return {
161
- name: MIAODA_PREVIEW_RENDERED_PLUGIN_NAME,
162
- apply: 'serve',
163
- transformIndexHtml: {
164
- order: 'post',
165
- handler() {
166
- return [
167
- {
168
- tag: 'script',
169
- attrs: { type: 'module' },
170
- children: MIAODA_PREVIEW_RENDERED_CLIENT,
171
- injectTo: 'body',
172
- },
173
- ];
174
- },
175
- },
176
- };
177
- }
178
-
179
- async function runFreshRuntimeChild() {
180
- process.chdir(projectRoot);
181
- process.env.MIAODA_CACHE_BOOTSTRAP_ENABLED = 'false';
182
- for (const environmentName of [
183
- 'MIAODA_CACHE_ROOT',
184
- 'MIAODA_CACHE_VALIDATION_RECEIPT_FILE',
185
- 'MIAODA_PLATFORM_RUNTIME_ROOT',
186
- 'MIAODA_PLATFORM_RUNTIME_MANIFEST',
187
- 'MIAODA_VITE_CACHE_PREPARE',
188
- 'MIAODA_VITE_CACHE_DIR',
189
- 'MIAODA_VITE_CONFIG_PACKAGE_ALIASES',
190
- 'MIAODA_VITE_DEPENDENCY_ASSETS_ROOT',
191
- 'MIAODA_VITE_DEPENDENCY_GRAPH_HASH',
192
- 'MIAODA_VITE_FORCE_REOPTIMIZE',
193
- 'MIAODA_VITE_RUNTIME_CACHE_DIR',
194
- ]) {
195
- delete process.env[environmentName];
196
- }
197
- const projectRequire = createRequire(path.join(projectRoot, 'package.json'));
198
- const projectViteEntry = projectRequire.resolve('vite');
199
- const projectVite = await import(pathToFileURL(projectViteEntry).href);
200
- const createFreshServer = readExport(projectVite, 'createServer');
201
- const loadFreshConfigFromFile = readExport(projectVite, 'loadConfigFromFile');
202
- const freshConfigFile = [
203
- 'vite.config.ts',
204
- 'vite.config.mts',
205
- 'vite.config.js',
206
- 'vite.config.mjs',
207
- 'vite.config.cjs',
208
- ]
209
- .map(file => path.join(projectRoot, file))
210
- .find(file => fs.existsSync(file));
211
- let freshProjectConfig = {};
212
- if (freshConfigFile) {
213
- const loaded = await loadFreshConfigFromFile(
214
- { command: 'serve', mode: process.env.NODE_ENV },
215
- freshConfigFile,
216
- projectRoot
217
- );
218
- if (!loaded) {
219
- throw new Error(`MIAODA_VITE_CONFIG_LOAD_FAILED: ${freshConfigFile}`);
220
- }
221
- freshProjectConfig = loaded.config;
222
- }
223
- const freshRuntimePreviewRendered = createMiaodaPreviewRenderedPlugin();
224
- const freshServer = await createFreshServer({
225
- ...freshProjectConfig,
226
- configFile: false,
227
- root: projectRoot,
228
- // The workspace config can already contain the preview reporter. Keep the
229
- // image-owned implementation as the single source of truth so one iframe
230
- // render cannot be reported twice after the fresh runtime takes over.
231
- plugins: [
232
- freshRuntimePreviewRendered,
233
- ...(freshProjectConfig.plugins || []).filter(
234
- plugin => plugin?.name !== MIAODA_PREVIEW_RENDERED_PLUGIN_NAME
235
- ),
236
- ],
237
- server: {
238
- ...(freshProjectConfig.server || {}),
239
- host: '127.0.0.1',
240
- port: 0,
241
- strictPort: false,
242
- },
243
- });
244
- await freshServer.listen();
245
- const freshAppManifestFile = path.resolve(
246
- process.env.MIAODA_APP_RUNTIME_MANIFEST ||
247
- path.join(projectRoot, '.miaoda-cache', 'current', 'app-runtime.json')
248
- );
249
- const freshAppManifest = JSON.parse(
250
- fs.readFileSync(freshAppManifestFile, 'utf8')
251
- );
252
- const freshClientEntry = freshAppManifest.clientEntry;
253
- if (
254
- typeof freshClientEntry !== 'string' ||
255
- !freshClientEntry ||
256
- freshClientEntry.startsWith('/') ||
257
- freshClientEntry.split('/').includes('..')
258
- ) {
259
- await freshServer.close();
260
- throw new Error('MIAODA_VITE_FRESH_CLIENT_ENTRY_INVALID');
261
- }
262
- const freshClientEntryUrl = `/${freshClientEntry}`;
263
- await freshServer.warmupRequest(freshClientEntryUrl);
264
- await freshServer.waitForRequestsIdle(freshClientEntryUrl);
265
- const address = freshServer.httpServer?.address();
266
- if (!address || typeof address === 'string') {
267
- await freshServer.close();
268
- throw new Error('MIAODA_VITE_FRESH_RUNTIME_ADDRESS_MISSING');
269
- }
270
- process.stdout.write(
271
- `${JSON.stringify({
272
- event: 'miaoda_vite_fresh_runtime_child_ready',
273
- pid: process.pid,
274
- port: address.port,
275
- viteVersion: projectRequire('vite/package.json').version,
276
- pluginNames: freshServer.config.plugins.map(plugin => plugin.name),
277
- })}\n`
278
- );
279
- if (typeof process.send === 'function') {
280
- process.send({
281
- event: 'miaoda_vite_fresh_runtime_child_ready',
282
- port: address.port,
283
- webSocketToken: freshServer.config.webSocketToken,
284
- });
285
- }
286
- await new Promise(resolve => {
287
- let closing = false;
288
- const closeFreshServer = async signal => {
289
- if (closing) return;
290
- closing = true;
291
- process.stdout.write(
292
- `${JSON.stringify({
293
- event: 'miaoda_vite_fresh_runtime_child_stopping',
294
- signal,
295
- })}\n`
296
- );
297
- await freshServer.close().catch(() => undefined);
298
- resolve();
299
- };
300
- process.once('SIGINT', () => void closeFreshServer('SIGINT'));
301
- process.once('SIGTERM', () => void closeFreshServer('SIGTERM'));
302
- process.once('SIGHUP', () => void closeFreshServer('SIGHUP'));
303
- });
304
- }
305
-
306
- if (process.argv.includes('--fresh-runtime-child')) {
307
- await runFreshRuntimeChild();
308
- process.exit(0);
309
- }
310
-
311
- const platformRuntimeRoot = path.resolve(
312
- process.env.MIAODA_PLATFORM_RUNTIME_ROOT || '/opt/miaoda/preview-runtime'
313
- );
314
- const fullstackCliRoot = path.resolve(
315
- process.env.MIAODA_FULLSTACK_CLI_ROOT ||
316
- '/usr/lib/node_modules/@lark-apaas/fullstack-cli'
317
- );
318
- const prepareCache = process.env.MIAODA_VITE_CACHE_PREPARE === 'true';
319
- const managedByCoordinator =
320
- process.env.MIAODA_RUNTIME_MANAGED_BY_COORDINATOR === 'true';
321
- const servingReadyMarker = '/tmp/event/MIAODA_VITE_CACHE_SERVING_READY';
322
- const defaultCachePointer = path.join(projectRoot, '.miaoda-cache', 'current');
323
- const requestedCacheRoot = path.resolve(
324
- process.env.MIAODA_CACHE_ROOT || defaultCachePointer
325
- );
326
- const cacheRoot = prepareCache
327
- ? requestedCacheRoot
328
- : fs.realpathSync(requestedCacheRoot);
329
- if (!prepareCache && !process.env.MIAODA_CACHE_ROOT) {
330
- const generationStore = fs.realpathSync(
331
- path.join(projectRoot, '.miaoda-cache', 'generations')
332
- );
333
- const generationRelative = path.relative(generationStore, cacheRoot);
334
- if (!/^[a-f0-9]{64}$/.test(generationRelative)) {
335
- throw new Error('MIAODA_CACHE_GENERATION_POINTER_REJECTED');
336
- }
337
- }
338
- const runtimeManifestFile = path.resolve(
339
- process.env.MIAODA_PLATFORM_RUNTIME_MANIFEST ||
340
- path.join(platformRuntimeRoot, 'runtime-manifest.json')
341
- );
342
- const appManifestFile = path.resolve(
343
- process.env.MIAODA_APP_RUNTIME_MANIFEST ||
344
- path.join(cacheRoot, 'app-runtime.json')
345
- );
346
- const dependencyReadyFile = path.resolve(
347
- process.env.MIAODA_DEPENDENCY_READY_FILE ||
348
- path.join(projectRoot, '.miaoda-runtime', 'dependencies-ready.json')
349
- );
350
- const dependencyHandoffExitCode = 75;
351
- const sealedCacheDir = path.resolve(
352
- process.env.MIAODA_VITE_CACHE_DIR || path.join(cacheRoot, 'vite')
353
- );
354
- let cacheDir = sealedCacheDir;
355
- const prepareTimeoutMs = Number(
356
- process.env.MIAODA_VITE_CACHE_PREPARE_TIMEOUT_MS || 120_000
357
- );
358
- const closeTimeoutMs = Number(
359
- process.env.MIAODA_VITE_CACHE_CLOSE_TIMEOUT_MS || 2_000
360
- );
361
- const runtimeStartedAt = process.hrtime.bigint();
362
- const durationMs = startedAt =>
363
- Number(process.hrtime.bigint() - startedAt) / 1_000_000;
364
- function emitRuntimePhase(phase, startedAt, details = {}) {
365
- process.stdout.write(
366
- `${JSON.stringify({
367
- event: 'miaoda_cache_runtime_phase',
368
- runtime: 'vite',
369
- phase,
370
- durationMs: Number(durationMs(startedAt).toFixed(3)),
371
- ...details,
372
- })}\n`
373
- );
374
- }
375
-
376
- function readLinuxProcessIo() {
377
- if (process.platform !== 'linux') return undefined;
378
- try {
379
- return Object.fromEntries(
380
- fs
381
- .readFileSync('/proc/self/io', 'utf8')
382
- .trim()
383
- .split('\n')
384
- .map(line => {
385
- const separator = line.indexOf(':');
386
- return [
387
- line.slice(0, separator),
388
- Number(line.slice(separator + 1).trim()),
389
- ];
390
- })
391
- .filter(([, value]) => Number.isFinite(value))
392
- );
393
- } catch {
394
- return undefined;
395
- }
396
- }
397
-
398
- function runtimeImportSnapshot() {
399
- return {
400
- startedAt: process.hrtime.bigint(),
401
- cpu: process.cpuUsage(),
402
- resources: process.resourceUsage(),
403
- linuxIo: readLinuxProcessIo(),
404
- };
405
- }
406
-
407
- function runtimeImportMetrics(snapshot) {
408
- const cpu = process.cpuUsage(snapshot.cpu);
409
- const resources = process.resourceUsage();
410
- const linuxIo = readLinuxProcessIo();
411
- const wallMs = durationMs(snapshot.startedAt);
412
- const userCpuMs = cpu.user / 1_000;
413
- const systemCpuMs = cpu.system / 1_000;
414
- const cpuMs = userCpuMs + systemCpuMs;
415
- const delta = name =>
416
- Math.max(
417
- 0,
418
- Number(resources[name] || 0) - Number(snapshot.resources[name] || 0)
419
- );
420
- const linuxDelta = name =>
421
- snapshot.linuxIo && linuxIo
422
- ? Math.max(
423
- 0,
424
- Number(linuxIo[name] || 0) - Number(snapshot.linuxIo[name] || 0)
425
- )
426
- : undefined;
427
- return {
428
- durationMs: Number(wallMs.toFixed(3)),
429
- userCpuMs: Number(userCpuMs.toFixed(3)),
430
- systemCpuMs: Number(systemCpuMs.toFixed(3)),
431
- cpuMs: Number(cpuMs.toFixed(3)),
432
- unaccountedWallMs: Number(Math.max(0, wallMs - cpuMs).toFixed(3)),
433
- cpuToWallRatio: Number((wallMs > 0 ? cpuMs / wallMs : 0).toFixed(3)),
434
- fsReadOperations: delta('fsRead'),
435
- minorPageFaults: delta('minorPageFault'),
436
- majorPageFaults: delta('majorPageFault'),
437
- voluntaryContextSwitches: delta('voluntaryContextSwitches'),
438
- involuntaryContextSwitches: delta('involuntaryContextSwitches'),
439
- ...(linuxIo && snapshot.linuxIo
440
- ? {
441
- linuxReadChars: linuxDelta('rchar'),
442
- linuxReadSyscalls: linuxDelta('syscr'),
443
- linuxStorageReadBytes: linuxDelta('read_bytes'),
444
- }
445
- : {}),
446
- };
447
- }
448
-
449
- function dependencyCacheMetadataState() {
450
- const metadataFile = path.join(cacheDir, 'deps', '_metadata.json');
451
- try {
452
- const source = fs.readFileSync(metadataFile);
453
- const stat = fs.statSync(metadataFile);
454
- const metadata = JSON.parse(source.toString('utf8'));
455
- return {
456
- exists: true,
457
- sizeBytes: source.byteLength,
458
- mtimeMs: Number(stat.mtimeMs.toFixed(3)),
459
- sha256: createHash('sha256').update(source).digest('hex'),
460
- optimizedDependencies: Object.keys(metadata.optimized || {}).length,
461
- optimizedChunks: Object.keys(metadata.chunks || {}).length,
462
- };
463
- } catch {
464
- return { exists: false };
465
- }
466
- }
467
-
468
- let stableProxy;
469
- let cacheBackend;
470
- let activeBackend;
471
- let freshRuntimeChild;
472
- let freshRuntimeRetryTimer;
473
- let freshRuntimeRetryDelayMs = 1_000;
474
- let dependencyReadyPoller;
475
- let webSocketCutoverBackend;
476
- const proxyUpgradeConnections = new Set();
477
- const stableRuntimeId = `vite-cache-${process.pid}`;
478
-
479
- function targetForRequest() {
480
- return activeBackend ?? cacheBackend;
481
- }
482
-
483
- function proxyHttpRequest(request, response) {
484
- if (request.url?.split('?', 1)[0] === '/dev/cache-runtime') {
485
- response.statusCode = 200;
486
- response.setHeader('content-type', 'application/json; charset=utf-8');
487
- response.setHeader('cache-control', 'no-store');
488
- response.end(
489
- JSON.stringify({
490
- mode: projectDependenciesReady() ? 'dependencies-ready' : 'cache-only',
491
- serverId: stableRuntimeId,
492
- pid: process.pid,
493
- runtimeAbiHash: appRuntimeManifest.runtimeAbiHash,
494
- cacheDir,
495
- activeRuntime: activeBackend?.kind ?? 'cache',
496
- dependencySource:
497
- activeBackend?.kind === 'fresh'
498
- ? 'project-node-modules'
499
- : 'generation-vite-cache',
500
- })
501
- );
502
- return;
503
- }
504
- const target = targetForRequest(request.url);
505
- if (!target) {
506
- response.statusCode = 503;
507
- response.end('Vite runtime is starting');
508
- return;
509
- }
510
- const upstream = http.request(
511
- {
512
- host: '127.0.0.1',
513
- port: target.port,
514
- method: request.method,
515
- path: request.url,
516
- headers: request.headers,
517
- },
518
- upstreamResponse => {
519
- const responseHeaders = {
520
- ...upstreamResponse.headers,
521
- 'x-miaoda-vite-runtime': target.kind,
522
- };
523
- response.writeHead(
524
- upstreamResponse.statusCode ?? 502,
525
- upstreamResponse.statusMessage,
526
- responseHeaders
527
- );
528
- upstreamResponse.pipe(response);
529
- }
530
- );
531
- upstream.once('error', error => {
532
- if (!response.headersSent) response.writeHead(502);
533
- response.end(`Vite upstream unavailable: ${error.code || error.message}`);
534
- });
535
- request.pipe(upstream);
536
- }
537
-
538
- function webSocketUpgradeRequest(request, target) {
539
- let upstreamUrl = request.url;
540
- if (target.webSocketToken) {
541
- const parsedUrl = new URL(request.url || '/', 'http://localhost');
542
- if (parsedUrl.searchParams.has('token')) {
543
- parsedUrl.searchParams.set('token', target.webSocketToken);
544
- }
545
- upstreamUrl = `${parsedUrl.pathname}${parsedUrl.search}`;
546
- }
547
- const headers = request.rawHeaders
548
- .reduce((lines, value, index) => {
549
- if (index % 2 === 0) lines.push(`${value}: `);
550
- else lines[lines.length - 1] += `${value}\r\n`;
551
- return lines;
552
- }, [])
553
- .join('');
554
- return `${request.method} ${upstreamUrl} HTTP/${request.httpVersion}\r\n${headers}\r\n`;
555
- }
556
-
557
- function cleanupWebSocketConnection(connection) {
558
- if (connection.closed) return;
559
- connection.closed = true;
560
- proxyUpgradeConnections.delete(connection);
561
- connection.socket.unpipe();
562
- connection.upstream?.unpipe();
563
- connection.upstream?.destroy();
564
- }
565
-
566
- async function replaceWebSocketBackend(
567
- connection,
568
- target,
569
- { forwardHandshake = false, initialHead = Buffer.alloc(0) } = {}
570
- ) {
571
- if (connection.closed || connection.socket.destroyed) {
572
- throw new Error('MIAODA_VITE_HMR_CLIENT_CLOSED');
573
- }
574
- if (connection.replacement) await connection.replacement;
575
- if (
576
- connection.backendKind === target.kind &&
577
- connection.upstream &&
578
- !connection.upstream.destroyed
579
- ) {
580
- return;
581
- }
582
-
583
- const replacement = new Promise((resolve, reject) => {
584
- const client = connection.socket;
585
- const previousUpstream = connection.upstream;
586
- client.pause();
587
- client.unpipe(previousUpstream);
588
- previousUpstream?.unpipe(client);
589
- connection.upstream = undefined;
590
- previousUpstream?.destroy();
591
-
592
- const upstream = net.connect(target.port, '127.0.0.1');
593
- let handshake = Buffer.alloc(0);
594
- let settled = false;
595
- const fail = error => {
596
- if (settled) return;
597
- settled = true;
598
- upstream.destroy();
599
- client.resume();
600
- reject(error);
601
- };
602
- const onHandshakeData = chunk => {
603
- handshake = Buffer.concat([handshake, chunk]);
604
- if (handshake.length > 64 * 1024) {
605
- fail(new Error('MIAODA_VITE_HMR_UPSTREAM_HANDSHAKE_TOO_LARGE'));
606
- return;
607
- }
608
- const headerEnd = handshake.indexOf('\r\n\r\n');
609
- if (headerEnd < 0) return;
610
- const responseHead = handshake.subarray(0, headerEnd + 4);
611
- if (!/^HTTP\/1\.[01] 101\b/.test(responseHead.toString('latin1'))) {
612
- fail(new Error('MIAODA_VITE_HMR_UPSTREAM_HANDSHAKE_REJECTED'));
613
- return;
614
- }
615
- settled = true;
616
- upstream.off('data', onHandshakeData);
617
- connection.upstream = upstream;
618
- connection.backendKind = target.kind;
619
- if (forwardHandshake) client.write(responseHead);
620
- const remaining = handshake.subarray(headerEnd + 4);
621
- if (remaining.length > 0) client.write(remaining);
622
- client.pipe(upstream, { end: false });
623
- upstream.pipe(client, { end: false });
624
- client.resume();
625
- const recover = () => {
626
- if (closing || connection.closed || connection.upstream !== upstream) {
627
- return;
628
- }
629
- connection.upstream = undefined;
630
- client.unpipe(upstream);
631
- upstream.unpipe(client);
632
- const fallback =
633
- target.kind === 'fresh'
634
- ? cacheBackend
635
- : (webSocketCutoverBackend ?? activeBackend);
636
- if (!fallback || fallback.kind === target.kind) {
637
- client.destroy();
638
- return;
639
- }
640
- void replaceWebSocketBackend(connection, fallback).catch(() =>
641
- client.destroy()
642
- );
643
- };
644
- upstream.once('error', recover);
645
- upstream.once('close', recover);
646
- resolve();
647
- };
648
- upstream.once('error', fail);
649
- upstream.once('connect', () => {
650
- upstream.write(webSocketUpgradeRequest(connection.request, target));
651
- if (initialHead.length > 0) upstream.write(initialHead);
652
- upstream.on('data', onHandshakeData);
653
- });
654
- });
655
- connection.replacement = replacement;
656
- try {
657
- await replacement;
658
- } finally {
659
- if (connection.replacement === replacement) {
660
- connection.replacement = undefined;
661
- }
662
- }
663
- }
664
-
665
- async function migrateWebSocketConnections(target) {
666
- const migrated = [];
667
- try {
668
- for (const connection of [...proxyUpgradeConnections]) {
669
- if (connection.closed) continue;
670
- await replaceWebSocketBackend(connection, target);
671
- migrated.push(connection);
672
- }
673
- } catch (error) {
674
- if (target.kind !== 'cache' && cacheBackend) {
675
- await Promise.allSettled(
676
- migrated.map(connection =>
677
- replaceWebSocketBackend(connection, cacheBackend)
678
- )
679
- );
680
- }
681
- throw error;
682
- }
683
- }
684
-
685
- function proxyWebSocketUpgrade(request, socket, head) {
686
- const target = webSocketCutoverBackend ?? targetForRequest(request.url);
687
- if (!target) {
688
- socket.destroy();
689
- return;
690
- }
691
- const connection = {
692
- request,
693
- socket,
694
- upstream: undefined,
695
- backendKind: undefined,
696
- replacement: undefined,
697
- closed: false,
698
- };
699
- proxyUpgradeConnections.add(connection);
700
- socket.once('close', () => cleanupWebSocketConnection(connection));
701
- socket.once('error', () => cleanupWebSocketConnection(connection));
702
- void replaceWebSocketBackend(connection, target, {
703
- forwardHandshake: true,
704
- initialHead: head,
705
- }).catch(() => socket.destroy());
706
- }
707
-
708
- async function startStableProxy(port, host) {
709
- stableProxy = http.createServer(proxyHttpRequest);
710
- stableProxy.on('upgrade', proxyWebSocketUpgrade);
711
- await new Promise((resolve, reject) => {
712
- stableProxy.once('error', reject);
713
- stableProxy.listen(port, host, () => {
714
- stableProxy.off('error', reject);
715
- resolve();
716
- });
717
- });
718
- }
719
-
720
- async function closeStableProxy() {
721
- if (!stableProxy) return;
722
- const proxy = stableProxy;
723
- stableProxy = undefined;
724
- for (const connection of proxyUpgradeConnections) {
725
- connection.socket.destroy();
726
- cleanupWebSocketConnection(connection);
727
- }
728
- proxy.closeIdleConnections?.();
729
- proxy.closeAllConnections?.();
730
- let timeout;
731
- try {
732
- await Promise.race([
733
- new Promise(resolve => proxy.close(() => resolve())),
734
- new Promise(resolve => {
735
- timeout = setTimeout(resolve, closeTimeoutMs);
736
- }),
737
- ]);
738
- } finally {
739
- if (timeout) clearTimeout(timeout);
740
- }
741
- }
742
-
743
- function publishServingReadyMarker(file) {
744
- fs.mkdirSync(path.dirname(file), { recursive: true });
745
- const temporaryFile = `${file}.${process.pid}.tmp`;
746
- fs.writeFileSync(
747
- temporaryFile,
748
- `${JSON.stringify({ schemaVersion: 1, readyAt: new Date().toISOString() })}\n`,
749
- { mode: 0o600 }
750
- );
751
- fs.renameSync(temporaryFile, file);
752
- }
753
- if (!prepareCache) {
754
- fs.rmSync(servingReadyMarker, { force: true });
755
- process.once('exit', () => {
756
- try {
757
- fs.rmSync(servingReadyMarker, { force: true });
758
- } catch {}
759
- });
760
- }
761
- if (
762
- !Number.isSafeInteger(closeTimeoutMs) ||
763
- closeTimeoutMs < 100 ||
764
- closeTimeoutMs > 30_000
765
- ) {
766
- throw new Error('MIAODA_VITE_CACHE_CLOSE_TIMEOUT_INVALID');
767
- }
768
- process.env.NODE_ENV ||= 'development';
769
-
770
- let validatedGeneration;
771
- if (!prepareCache) {
772
- const validationStartedAt = process.hrtime.bigint();
773
- let validatedBy = 'full-sha';
774
- let validationReceiptMiss;
775
- const validationReceiptFile =
776
- process.env.MIAODA_CACHE_VALIDATION_RECEIPT_FILE;
777
- if (validationReceiptFile) {
778
- try {
779
- validatedGeneration = validateCacheGenerationReceipt({
780
- projectRoot,
781
- cacheRoot,
782
- runtimeManifestFile,
783
- receiptFile: validationReceiptFile,
784
- artifactScope: 'vite',
785
- });
786
- validatedBy = 'platform-receipt';
787
- } catch (error) {
788
- validationReceiptMiss = String(
789
- error instanceof Error ? error.message : error
790
- ).split(':')[0];
791
- }
792
- }
793
- if (!validatedGeneration) {
794
- validatedGeneration = validateCacheGeneration({
795
- projectRoot,
796
- cacheRoot,
797
- runtimeManifestFile,
798
- artifactScope: 'vite',
799
- });
800
- }
801
- if (
802
- !process.env.MIAODA_CACHE_ROOT &&
803
- path.basename(cacheRoot) !== validatedGeneration.generation.generationId
804
- ) {
805
- throw new Error('MIAODA_CACHE_GENERATION_POINTER_ID_MISMATCH');
806
- }
807
- if (
808
- validatedGeneration.artifacts.appRuntime !==
809
- fs.realpathSync(appManifestFile)
810
- ) {
811
- throw new Error('MIAODA_CACHE_APP_MANIFEST_PATH_MISMATCH');
812
- }
813
- if (
814
- path.dirname(validatedGeneration.artifacts.viteMetadata) !==
815
- path.join(fs.realpathSync(sealedCacheDir), 'deps')
816
- ) {
817
- throw new Error('MIAODA_CACHE_VITE_METADATA_PATH_MISMATCH');
818
- }
819
- emitRuntimePhase('generation_validation', validationStartedAt, {
820
- artifactScope: 'vite',
821
- validatedBy,
822
- ...(validationReceiptMiss ? { validationReceiptMiss } : {}),
823
- });
824
- }
825
-
826
- function resolveRuntimeCacheDir() {
827
- if (prepareCache) return sealedCacheDir;
828
- // The active server must not read from the sealed generation directly. A
829
- // dependency restore may atomically publish a refreshed generation while
830
- // this process keeps serving the already-rendered page without a handoff.
831
- const runtimeCacheBase = path.resolve(projectRoot, '.miaoda-runtime', 'vite');
832
- const runtimeCacheDir = path.resolve(
833
- process.env.MIAODA_VITE_RUNTIME_CACHE_DIR ||
834
- path.join(runtimeCacheBase, validatedGeneration.generation.generationId)
835
- );
836
- if (path.dirname(runtimeCacheDir) !== runtimeCacheBase) {
837
- throw new Error(
838
- `MIAODA_VITE_RUNTIME_CACHE_PATH_REJECTED: ${runtimeCacheDir}`
839
- );
840
- }
841
- const runtimeParent = path.dirname(runtimeCacheBase);
842
- for (const directory of [runtimeParent, runtimeCacheBase]) {
843
- if (fs.existsSync(directory) && fs.lstatSync(directory).isSymbolicLink()) {
844
- throw new Error(
845
- `MIAODA_VITE_RUNTIME_CACHE_SYMLINK_REJECTED: ${directory}`
846
- );
847
- }
848
- fs.mkdirSync(directory, { recursive: true });
849
- }
850
- const realProjectRoot = fs.realpathSync(projectRoot);
851
- const realRuntimeCacheBase = fs.realpathSync(runtimeCacheBase);
852
- if (!realRuntimeCacheBase.startsWith(`${realProjectRoot}${path.sep}`)) {
853
- throw new Error(
854
- `MIAODA_VITE_RUNTIME_CACHE_PATH_REJECTED: ${runtimeCacheBase}`
855
- );
856
- }
857
- if (
858
- fs.existsSync(runtimeCacheDir) &&
859
- fs.lstatSync(runtimeCacheDir).isSymbolicLink()
860
- ) {
861
- throw new Error(
862
- `MIAODA_VITE_RUNTIME_CACHE_SYMLINK_REJECTED: ${runtimeCacheDir}`
863
- );
864
- }
865
- return runtimeCacheDir;
866
- }
867
-
868
- async function selectRuntimeCacheStrategy(runtimeCacheDir) {
869
- if (prepareCache) {
870
- return {
871
- cacheDir: sealedCacheDir,
872
- strategy: 'sealed-prepare',
873
- ordinaryCopy: false,
874
- };
875
- }
876
- // Never fall back to an ordinary copy: walking and copying thousands of Vite
877
- // cache files is slower than loading Vite itself. Prefer an exact reflink
878
- // clone when the workspace filesystem supports copy-on-write; otherwise use
879
- // the generation restored for this sandbox directly. The recycle producer
880
- // validates/rebuilds the generation before it is archived for the next
881
- // sandbox, so runtime changes cannot be silently published as a valid cache.
882
- const probeSource = path.join(sealedCacheDir, 'deps', '_metadata.json');
883
- const probeTarget = path.join(
884
- path.dirname(runtimeCacheDir),
885
- `.reflink-probe-${process.pid}-${Date.now()}`
886
- );
887
- const directResult = reason => ({
888
- cacheDir: sealedCacheDir,
889
- strategy: 'direct',
890
- ordinaryCopy: false,
891
- reflinkFallbackReason: reason,
892
- });
893
- try {
894
- await fs.promises.copyFile(
895
- probeSource,
896
- probeTarget,
897
- fs.constants.COPYFILE_FICLONE_FORCE
898
- );
899
- } catch (error) {
900
- return directResult(error?.code || 'REFLINK_PROBE_FAILED');
901
- } finally {
902
- await fs.promises.rm(probeTarget, { force: true }).catch(() => undefined);
903
- }
904
-
905
- return {
906
- cacheDir: runtimeCacheDir,
907
- strategy: 'reflink',
908
- ordinaryCopy: false,
909
- };
910
- }
911
-
912
- async function materializeRuntimeCache(selection) {
913
- if (selection.strategy !== 'reflink') {
914
- return {
915
- ...selection,
916
- completedAt: process.hrtime.bigint(),
917
- };
918
- }
919
-
920
- await fs.promises.rm(selection.cacheDir, {
921
- recursive: true,
922
- force: true,
923
- maxRetries: 2,
924
- });
925
- // The probe already proved that this filesystem supports reflinks. Force
926
- // copy-on-write for every regular file; an unexpected clone failure rejects
927
- // the cache candidate instead of silently performing an ordinary copy.
928
- await fs.promises.cp(sealedCacheDir, selection.cacheDir, {
929
- recursive: true,
930
- force: false,
931
- errorOnExist: true,
932
- preserveTimestamps: true,
933
- mode: fs.constants.COPYFILE_FICLONE_FORCE,
934
- });
935
- return {
936
- ...selection,
937
- completedAt: process.hrtime.bigint(),
938
- };
939
- }
940
-
941
- const runtimeCacheCandidateDir = resolveRuntimeCacheDir();
942
- const runtimeCacheSelection = await selectRuntimeCacheStrategy(
943
- runtimeCacheCandidateDir
944
- );
945
- cacheDir = runtimeCacheSelection.cacheDir;
946
- // Config loaders bypass Vite hooks, so expose exactly the immutable image
947
- // runtime. Never inherit NODE_PATH: it may point back at Workspace node_modules
948
- // and would invalidate the node_modules-free contract.
949
- process.env.NODE_PATH = path.join(platformRuntimeRoot, 'node_modules');
950
- // PostCSS/Tailwind config files execute through Node's CJS loader and bypass
951
- // Vite resolveId hooks. Point that loader only at the image-owned runtime.
952
- Module._initPaths();
953
-
954
- async function waitForPreparedCache() {
955
- const metadataFile = path.join(cacheDir, 'deps', '_metadata.json');
956
- const deadline = Date.now() + prepareTimeoutMs;
957
- while (Date.now() < deadline) {
958
- try {
959
- const metadata = JSON.parse(fs.readFileSync(metadataFile, 'utf8'));
960
- return {
961
- metadataFile,
962
- optimizedDependencies: Object.keys(metadata.optimized || {}).length,
963
- };
964
- } catch {
965
- await new Promise(resolve => setTimeout(resolve, 100));
966
- }
967
- }
968
- throw new Error(
969
- `MIAODA_VITE_CACHE_PREPARE_TIMEOUT: ${metadataFile} was not committed within ${prepareTimeoutMs}ms`
970
- );
971
- }
972
-
973
- for (const requiredPath of [
974
- platformRuntimeRoot,
975
- runtimeManifestFile,
976
- appManifestFile,
977
- ]) {
978
- if (!fs.existsSync(requiredPath)) {
979
- throw new Error(`MIAODA_CACHE_BOOTSTRAP_INPUT_MISSING: ${requiredPath}`);
980
- }
981
- }
982
- const realPlatformRuntimeRoot = fs.realpathSync(platformRuntimeRoot);
983
- const platformRuntimeManifest = JSON.parse(
984
- fs.readFileSync(runtimeManifestFile, 'utf8')
985
- );
986
- const vitePatch = platformRuntimeManifest.viteDependencyHashPatch;
987
- const vitePatchTarget = path.resolve(
988
- realPlatformRuntimeRoot,
989
- String(vitePatch?.target || '')
990
- );
991
- if (
992
- vitePatch?.schemaVersion !== 1 ||
993
- vitePatch?.patchId !== 'vite-8.1.5-cache-runtime-identity-v4' ||
994
- !/^[a-f0-9]{64}$/.test(vitePatch?.targetSha256 || '') ||
995
- !vitePatchTarget.startsWith(`${realPlatformRuntimeRoot}${path.sep}`) ||
996
- !fs.existsSync(vitePatchTarget) ||
997
- fs.lstatSync(vitePatchTarget).isSymbolicLink() ||
998
- createHash('sha256')
999
- .update(fs.readFileSync(vitePatchTarget))
1000
- .digest('hex') !== vitePatch.targetSha256
1001
- ) {
1002
- throw new Error('MIAODA_RUNTIME_VITE_PATCH_INVALID');
1003
- }
1004
- // The image launcher intentionally does not need to export this optional
1005
- // override when it uses the default root. Vite's patched config bundler runs
1006
- // later in this same process and must still receive the canonical trust root
1007
- // used to validate the finite image-owned package alias map.
1008
- process.env.MIAODA_PLATFORM_RUNTIME_ROOT = realPlatformRuntimeRoot;
1009
-
1010
- const runtimeRequire = createRequire(
1011
- path.join(platformRuntimeRoot, 'package.json')
1012
- );
1013
- const appRuntimeManifest = JSON.parse(fs.readFileSync(appManifestFile, 'utf8'));
1014
- if (
1015
- !/^[a-f0-9]{64}$/.test(appRuntimeManifest.clientDependencyGraphHash || '')
1016
- ) {
1017
- throw new Error('MIAODA_CLIENT_DEPENDENCY_GRAPH_INVALID');
1018
- }
1019
- const supportedVitePresets = new Set([
1020
- '@lark-apaas/fullstack-vite-preset',
1021
- '@lark-apaas/coding-vite-preset',
1022
- '@lark-apaas/coding-preset-vite-react',
1023
- ]);
1024
- const activeVitePreset =
1025
- appRuntimeManifest.vitePreset || '@lark-apaas/fullstack-vite-preset';
1026
- if (!supportedVitePresets.has(activeVitePreset)) {
1027
- throw new Error(
1028
- `MIAODA_VITE_PRESET_UNSUPPORTED: ${String(activeVitePreset)}`
1029
- );
1030
- }
1031
-
1032
- function projectDependenciesReady() {
1033
- if (prepareCache || !validatedGeneration?.generation?.generationId) {
1034
- return false;
1035
- }
1036
- try {
1037
- const markerStat = fs.lstatSync(dependencyReadyFile);
1038
- if (!markerStat.isFile() || markerStat.isSymbolicLink()) return false;
1039
- const marker = JSON.parse(fs.readFileSync(dependencyReadyFile, 'utf8'));
1040
- if (marker.generationId !== validatedGeneration.generation.generationId) {
1041
- return false;
1042
- }
1043
- const projectNodeModules = fs.realpathSync(
1044
- path.join(projectRoot, 'node_modules')
1045
- );
1046
- const resolvedProjectVite = fs.realpathSync(
1047
- createRequire(path.join(projectRoot, 'package.json')).resolve('vite')
1048
- );
1049
- const viteRelativePath = path.relative(
1050
- projectNodeModules,
1051
- resolvedProjectVite
1052
- );
1053
- if (
1054
- !viteRelativePath ||
1055
- viteRelativePath === '..' ||
1056
- viteRelativePath.startsWith(`..${path.sep}`) ||
1057
- path.isAbsolute(viteRelativePath)
1058
- ) {
1059
- return false;
1060
- }
1061
- return true;
1062
- } catch {
1063
- return false;
1064
- }
1065
- }
1066
-
1067
- function scheduleFreshRuntimeRetry() {
1068
- if (
1069
- closing ||
1070
- prepareCache ||
1071
- freshRuntimeChild ||
1072
- freshRuntimeRetryTimer ||
1073
- !projectDependenciesReady()
1074
- ) {
1075
- return;
1076
- }
1077
- const delayMs = freshRuntimeRetryDelayMs;
1078
- freshRuntimeRetryDelayMs = Math.min(freshRuntimeRetryDelayMs * 2, 30_000);
1079
- freshRuntimeRetryTimer = setTimeout(() => {
1080
- freshRuntimeRetryTimer = undefined;
1081
- startFreshRuntime();
1082
- }, delayMs);
1083
- freshRuntimeRetryTimer.unref();
1084
- }
1085
-
1086
- function startFreshRuntime() {
1087
- if (
1088
- closing ||
1089
- prepareCache ||
1090
- freshRuntimeChild ||
1091
- !projectDependenciesReady()
1092
- ) {
1093
- return;
1094
- }
1095
- const startedAt = process.hrtime.bigint();
1096
- const childEnvironment = {
1097
- ...process.env,
1098
- NODE_PATH: '',
1099
- MIAODA_CACHE_BOOTSTRAP_ENABLED: 'false',
1100
- MIAODA_APP_RUNTIME_MANIFEST: appManifestFile,
1101
- };
1102
- delete childEnvironment.MIAODA_VITE_CACHE_PREPARE;
1103
- delete childEnvironment.MIAODA_VITE_CACHE_DIR;
1104
- delete childEnvironment.MIAODA_VITE_FORCE_REOPTIMIZE;
1105
- const child = spawn(
1106
- process.execPath,
1107
- [process.argv[1], '--fresh-runtime-child'],
1108
- {
1109
- cwd: projectRoot,
1110
- env: childEnvironment,
1111
- stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
1112
- }
1113
- );
1114
- freshRuntimeChild = child;
1115
- let ready = false;
1116
- let cutover = false;
1117
- const childStartedAtMs = Date.now();
1118
- const timeout = setTimeout(() => {
1119
- if (!ready) child.kill('SIGTERM');
1120
- }, 30_000);
1121
- timeout.unref();
1122
- child.stdout?.on('data', chunk => process.stdout.write(chunk));
1123
- child.stderr?.on('data', chunk => process.stderr.write(chunk));
1124
- child.on('message', event => {
1125
- if (
1126
- ready ||
1127
- event?.event !== 'miaoda_vite_fresh_runtime_child_ready' ||
1128
- !Number.isSafeInteger(event.port) ||
1129
- event.port <= 0 ||
1130
- typeof event.webSocketToken !== 'string' ||
1131
- !event.webSocketToken
1132
- ) {
1133
- return;
1134
- }
1135
- ready = true;
1136
- clearTimeout(timeout);
1137
- const freshBackend = {
1138
- kind: 'fresh',
1139
- port: event.port,
1140
- pid: child.pid,
1141
- webSocketToken: event.webSocketToken,
1142
- };
1143
- webSocketCutoverBackend = freshBackend;
1144
- void migrateWebSocketConnections(freshBackend)
1145
- .then(() => {
1146
- if (
1147
- freshRuntimeChild !== child ||
1148
- child.exitCode !== null ||
1149
- child.signalCode !== null
1150
- ) {
1151
- throw new Error('MIAODA_VITE_FRESH_RUNTIME_EXITED_DURING_CUTOVER');
1152
- }
1153
- activeBackend = freshBackend;
1154
- webSocketCutoverBackend = undefined;
1155
- cutover = true;
1156
- emitRuntimePhase('fresh_cutover', startedAt, {
1157
- childPid: child.pid,
1158
- childPort: event.port,
1159
- migratedHmrConnections: proxyUpgradeConnections.size,
1160
- refresh: false,
1161
- });
1162
- })
1163
- .catch(async error => {
1164
- activeBackend = cacheBackend;
1165
- webSocketCutoverBackend = cacheBackend;
1166
- await migrateWebSocketConnections(cacheBackend).catch(() => undefined);
1167
- webSocketCutoverBackend = undefined;
1168
- process.stderr.write(
1169
- `${JSON.stringify({
1170
- event: 'miaoda_vite_fresh_cutover_failed',
1171
- message: error.message,
1172
- })}\n`
1173
- );
1174
- if (child.exitCode === null && child.signalCode === null) {
1175
- child.kill('SIGTERM');
1176
- }
1177
- });
1178
- });
1179
- child.once('error', error => {
1180
- process.stderr.write(
1181
- `${JSON.stringify({
1182
- event: 'miaoda_vite_fresh_runtime_error',
1183
- message: error.message,
1184
- })}\n`
1185
- );
1186
- });
1187
- child.once('exit', (code, signal) => {
1188
- clearTimeout(timeout);
1189
- if (freshRuntimeChild === child) freshRuntimeChild = undefined;
1190
- if (
1191
- activeBackend?.pid === child.pid ||
1192
- webSocketCutoverBackend?.pid === child.pid
1193
- ) {
1194
- activeBackend = cacheBackend;
1195
- webSocketCutoverBackend = cacheBackend;
1196
- void migrateWebSocketConnections(cacheBackend).finally(() => {
1197
- if (webSocketCutoverBackend === cacheBackend) {
1198
- webSocketCutoverBackend = undefined;
1199
- }
1200
- });
1201
- }
1202
- process.stdout.write(
1203
- `${JSON.stringify({
1204
- event: 'miaoda_vite_fresh_runtime_exited',
1205
- pid: child.pid,
1206
- code,
1207
- signal,
1208
- wasReady: ready,
1209
- cutover,
1210
- })}\n`
1211
- );
1212
- if (Date.now() - childStartedAtMs >= 60_000) {
1213
- freshRuntimeRetryDelayMs = 1_000;
1214
- }
1215
- scheduleFreshRuntimeRetry();
1216
- });
1217
- }
1218
-
1219
- const normalizeProjectRelativePath = (value, fallback, name) => {
1220
- const relativePath = value || fallback;
1221
- if (
1222
- typeof relativePath !== 'string' ||
1223
- !relativePath ||
1224
- relativePath !== path.posix.normalize(relativePath) ||
1225
- relativePath === '..' ||
1226
- relativePath.startsWith('../') ||
1227
- path.posix.isAbsolute(relativePath) ||
1228
- relativePath.includes('\\')
1229
- ) {
1230
- throw new Error(`MIAODA_${name}_INVALID`);
1231
- }
1232
- return relativePath;
1233
- };
1234
- const clientRootRelative = normalizeProjectRelativePath(
1235
- appRuntimeManifest.clientRoot,
1236
- 'client',
1237
- 'CLIENT_ROOT'
1238
- );
1239
- const clientEntryRelative = normalizeProjectRelativePath(
1240
- appRuntimeManifest.clientEntry,
1241
- 'client/src/index.tsx',
1242
- 'CLIENT_ENTRY'
1243
- );
1244
- const clientRoot = path.resolve(projectRoot, clientRootRelative);
1245
- const clientEntryFile = path.resolve(projectRoot, clientEntryRelative);
1246
- for (const candidate of [clientRoot, clientEntryFile]) {
1247
- if (
1248
- candidate !== projectRoot &&
1249
- !candidate.startsWith(`${projectRoot}${path.sep}`)
1250
- ) {
1251
- throw new Error(`MIAODA_CLIENT_PATH_ESCAPE: ${candidate}`);
1252
- }
1253
- }
1254
- const runtimeConfigPackageNames = [
1255
- '@lark-apaas/fullstack-presets',
1256
- '@lark-apaas/fullstack-vite-preset',
1257
- '@lark-apaas/coding-vite-preset',
1258
- '@lark-apaas/coding-preset-vite-react',
1259
- 'vite',
1260
- '@lark-apaas/vite-inspector-plugin',
1261
- '@vitejs/plugin-react',
1262
- 'vite-tsconfig-paths',
1263
- ];
1264
- const requiredRuntimeConfigPackageNames = new Set([
1265
- '@lark-apaas/fullstack-presets',
1266
- '@lark-apaas/fullstack-vite-preset',
1267
- activeVitePreset,
1268
- 'vite',
1269
- ]);
1270
- const runtimeConfigPackageAliases = Object.fromEntries(
1271
- runtimeConfigPackageNames.flatMap(packageName => {
1272
- let entry;
1273
- try {
1274
- entry = fs.realpathSync(runtimeRequire.resolve(packageName));
1275
- } catch (error) {
1276
- if (requiredRuntimeConfigPackageNames.has(packageName)) throw error;
1277
- return [];
1278
- }
1279
- const relative = path.relative(realPlatformRuntimeRoot, entry);
1280
- if (
1281
- relative === '..' ||
1282
- relative.startsWith(`..${path.sep}`) ||
1283
- path.isAbsolute(relative)
1284
- ) {
1285
- if (requiredRuntimeConfigPackageNames.has(packageName)) {
1286
- throw new Error(
1287
- `MIAODA_VITE_CONFIG_RUNTIME_PACKAGE_PATH_REJECTED: ${packageName}`
1288
- );
1289
- }
1290
- return [];
1291
- }
1292
- return [[packageName, entry]];
1293
- })
1294
- );
1295
- // Vite bundles vite.config before normal plugin resolution. Force the finite,
1296
- // image-owned config toolchain there as well, otherwise producer startup with
1297
- // restored node_modules and consumer startup without it can load different
1298
- // preset versions and invalidate Vite's own optimizer metadata.
1299
- process.env.MIAODA_VITE_CONFIG_PACKAGE_ALIASES = JSON.stringify(
1300
- runtimeConfigPackageAliases
1301
- );
1302
- if (prepareCache) {
1303
- const clientDependencyGraphModule = await import(
1304
- pathToFileURL(
1305
- path.join(fullstackCliRoot, 'dist', 'client-dependency-graph.js')
1306
- ).href
1307
- );
1308
- const buildClientDependencyGraph =
1309
- clientDependencyGraphModule.buildClientDependencyGraph ??
1310
- clientDependencyGraphModule.default?.buildClientDependencyGraph;
1311
- if (typeof buildClientDependencyGraph !== 'function') {
1312
- throw new Error(
1313
- 'MIAODA_FULLSTACK_CLI_RUNTIME_EXPORT_MISSING: buildClientDependencyGraph'
1314
- );
1315
- }
1316
- const currentClientDependencyGraph = buildClientDependencyGraph({
1317
- projectRoot,
1318
- runtimeAbiHash: appRuntimeManifest.runtimeAbiHash,
1319
- includeAllProductionDependencies:
1320
- appRuntimeManifest.applicationKind === 'frontend-only',
1321
- });
1322
- if (
1323
- currentClientDependencyGraph.hash !==
1324
- appRuntimeManifest.clientDependencyGraphHash
1325
- ) {
1326
- throw new Error('MIAODA_CLIENT_DEPENDENCY_GRAPH_MISMATCH');
1327
- }
1328
- }
1329
- // A small, exact-version image patch replaces only Vite's coarse whole-lock
1330
- // hash. Vite's native resolved-config hash, _metadata.json, optimized chunks
1331
- // and CJS interop remain Vite-owned end to end.
1332
- process.env.MIAODA_VITE_DEPENDENCY_GRAPH_HASH =
1333
- appRuntimeManifest.clientDependencyGraphHash;
1334
- const dependencyAssetsRoot = path.join(cacheDir, 'dependency-assets');
1335
- // Tailwind's Jiti loader bypasses Vite resolution when it evaluates
1336
- // tailwind.config.*. Give the finite image adapter a sealed dependency source
1337
- // root so its content globs never need Workspace node_modules.
1338
- process.env.MIAODA_VITE_DEPENDENCY_ASSETS_ROOT = dependencyAssetsRoot;
1339
- // Importing Vite is more than a filesystem warm-up: Node resolves, parses and
1340
- // evaluates the module graph once, then keeps that exact graph in this process.
1341
- // Finalize every cache-runtime value that a preset may capture at module scope
1342
- // before starting the import. App configuration, plugins and sockets are still
1343
- // created only at the original startup barrier below.
1344
- process.env.MIAODA_CACHE_BOOTSTRAP_ENABLED = 'true';
1345
- process.env.MIAODA_VITE_CACHE_DIR = cacheDir;
1346
- process.env.MIAODA_VITE_FORCE_REOPTIMIZE = 'false';
1347
- const materializeStartedAt = process.hrtime.bigint();
1348
- const materializeRuntimeCachePromise = materializeRuntimeCache(
1349
- runtimeCacheSelection
1350
- );
1351
- const runtimeImportsStartedAt = process.hrtime.bigint();
1352
- const runtimeImportsPromise = (async () => {
1353
- // The preset's CommonJS output imports Vite for mergeConfig. Keep these
1354
- // imports sequential even though the whole sequence now overlaps the
1355
- // remaining cache-runtime preparation.
1356
- const viteModule = await importRuntimeBootstrap('vite');
1357
- const presetModule = await importRuntimeBootstrap(activeVitePreset);
1358
- const resolverPresetModule =
1359
- activeVitePreset === '@lark-apaas/fullstack-vite-preset'
1360
- ? presetModule
1361
- : await importRuntimeBootstrap('@lark-apaas/fullstack-vite-preset');
1362
- return { viteModule, presetModule, resolverPresetModule };
1363
- })().then(
1364
- value => ({
1365
- ok: true,
1366
- value,
1367
- completedAt: process.hrtime.bigint(),
1368
- }),
1369
- error => ({
1370
- ok: false,
1371
- error,
1372
- completedAt: process.hrtime.bigint(),
1373
- })
1374
- );
1375
- const runtimePreparationWaitStartedAt = process.hrtime.bigint();
1376
- const [runtimeImportsResult, materializeResult] = await Promise.all([
1377
- runtimeImportsPromise,
1378
- materializeRuntimeCachePromise,
1379
- ]);
1380
- const runtimePreparationWaitDurationMs = durationMs(
1381
- runtimePreparationWaitStartedAt
1382
- );
1383
- const runtimeImportsDurationMs =
1384
- Number(runtimeImportsResult.completedAt - runtimeImportsStartedAt) /
1385
- 1_000_000;
1386
- const materializeDurationMs =
1387
- Number(materializeResult.completedAt - materializeStartedAt) / 1_000_000;
1388
- const runtimePreparationCompletedAt =
1389
- runtimeImportsResult.completedAt > materializeResult.completedAt
1390
- ? runtimeImportsResult.completedAt
1391
- : materializeResult.completedAt;
1392
- const runtimePreparationDurationMs =
1393
- Number(runtimePreparationCompletedAt - materializeStartedAt) / 1_000_000;
1394
- const overlapStartedAt =
1395
- runtimeImportsStartedAt > materializeStartedAt
1396
- ? runtimeImportsStartedAt
1397
- : materializeStartedAt;
1398
- const overlapCompletedAt =
1399
- runtimeImportsResult.completedAt < materializeResult.completedAt
1400
- ? runtimeImportsResult.completedAt
1401
- : materializeResult.completedAt;
1402
- const runtimePreparationOverlapDurationMs = Math.max(
1403
- 0,
1404
- Number(overlapCompletedAt - overlapStartedAt) / 1_000_000
1405
- );
1406
- const materializedBeforeImportMs = Math.max(
1407
- 0,
1408
- Number(
1409
- (materializeResult.completedAt < runtimeImportsStartedAt
1410
- ? materializeResult.completedAt
1411
- : runtimeImportsStartedAt) - materializeStartedAt
1412
- ) / 1_000_000
1413
- );
1414
- cacheDir = materializeResult.cacheDir;
1415
- emitRuntimePhase('materialize_runtime_cache', materializeStartedAt, {
1416
- durationMs: Number(materializeDurationMs.toFixed(3)),
1417
- prepareCache,
1418
- parallelWithRuntimeImports: true,
1419
- strategy: materializeResult.strategy,
1420
- ordinaryCopy: materializeResult.ordinaryCopy,
1421
- ...(materializeResult.reflinkFallbackReason
1422
- ? { reflinkFallbackReason: materializeResult.reflinkFallbackReason }
1423
- : {}),
1424
- });
1425
- emitRuntimePhase('runtime_imports', runtimeImportsStartedAt, {
1426
- durationMs: Number(runtimeImportsDurationMs.toFixed(3)),
1427
- waitDurationMs: Number(runtimePreparationWaitDurationMs.toFixed(3)),
1428
- overlapDurationMs: Number(runtimePreparationOverlapDurationMs.toFixed(3)),
1429
- preloaded: true,
1430
- });
1431
- emitRuntimePhase('runtime_preparation_parallel', materializeStartedAt, {
1432
- durationMs: Number(runtimePreparationDurationMs.toFixed(3)),
1433
- waitDurationMs: Number(runtimePreparationWaitDurationMs.toFixed(3)),
1434
- serialDurationMs: Number(
1435
- (runtimeImportsDurationMs + materializeDurationMs).toFixed(3)
1436
- ),
1437
- overlappedDurationMs: Number(runtimePreparationOverlapDurationMs.toFixed(3)),
1438
- materializedBeforeImportMs: Number(materializedBeforeImportMs.toFixed(3)),
1439
- });
1440
- if (!runtimeImportsResult.ok) throw runtimeImportsResult.error;
1441
- const { viteModule, presetModule, resolverPresetModule } =
1442
- runtimeImportsResult.value;
1443
- const dependencyAssetsManifestFile = path.join(
1444
- dependencyAssetsRoot,
1445
- 'manifest.json'
1446
- );
1447
- const emptyDependencyAssetsManifest = () => ({
1448
- schemaVersion: 3,
1449
- cssSpecifiers: {},
1450
- tailwindPluginSpecifiers: {},
1451
- sourcePackages: {},
1452
- });
1453
- let dependencyAssetsManifest = fs.existsSync(dependencyAssetsManifestFile)
1454
- ? JSON.parse(fs.readFileSync(dependencyAssetsManifestFile, 'utf8'))
1455
- : emptyDependencyAssetsManifest();
1456
- if (dependencyAssetsManifest.schemaVersion !== 3) {
1457
- throw new Error('MIAODA_VITE_DEPENDENCY_ASSETS_SCHEMA_UNSUPPORTED');
1458
- }
1459
- if (prepareCache) {
1460
- fs.rmSync(dependencyAssetsRoot, { recursive: true, force: true });
1461
- fs.mkdirSync(dependencyAssetsRoot, { recursive: true });
1462
- dependencyAssetsManifest = emptyDependencyAssetsManifest();
1463
- }
1464
-
1465
- const inlineTsconfigFile = path.join(cacheDir, 'tsconfig.raw.json');
1466
-
1467
- function writeJsonAtomic(file, value) {
1468
- fs.mkdirSync(path.dirname(file), { recursive: true });
1469
- const temporaryFile = `${file}.${process.pid}.tmp`;
1470
- fs.writeFileSync(temporaryFile, `${JSON.stringify(value, null, 2)}\n`);
1471
- fs.renameSync(temporaryFile, file);
1472
- }
1473
-
1474
- function formatTypescriptDiagnostics(typescript, diagnostics) {
1475
- return diagnostics
1476
- .map(diagnostic => {
1477
- const message = typescript.flattenDiagnosticMessageText(
1478
- diagnostic.messageText,
1479
- '\n'
1480
- );
1481
- if (!diagnostic.file || diagnostic.start === undefined) return message;
1482
- const position = diagnostic.file.getLineAndCharacterOfPosition(
1483
- diagnostic.start
1484
- );
1485
- return `${diagnostic.file.fileName}:${position.line + 1}:${
1486
- position.character + 1
1487
- }: ${message}`;
1488
- })
1489
- .join('\n');
1490
- }
1491
-
1492
- function normalizeInlineCompilerOptions(typescript, compilerOptions) {
1493
- const normalized = {};
1494
- const copyString = key => {
1495
- if (typeof compilerOptions[key] === 'string') {
1496
- normalized[key] = compilerOptions[key];
1497
- }
1498
- };
1499
- const copyBoolean = key => {
1500
- if (typeof compilerOptions[key] === 'boolean') {
1501
- normalized[key] = compilerOptions[key];
1502
- }
1503
- };
1504
- for (const key of ['jsxFactory', 'jsxFragmentFactory', 'jsxImportSource']) {
1505
- copyString(key);
1506
- }
1507
- for (const key of [
1508
- 'experimentalDecorators',
1509
- 'emitDecoratorMetadata',
1510
- 'strict',
1511
- 'strictNullChecks',
1512
- 'verbatimModuleSyntax',
1513
- 'useDefineForClassFields',
1514
- 'preserveValueImports',
1515
- ]) {
1516
- copyBoolean(key);
1517
- }
1518
-
1519
- const jsxName = typescript.JsxEmit[compilerOptions.jsx];
1520
- const jsxValues = {
1521
- React: 'react',
1522
- ReactJSX: 'react-jsx',
1523
- ReactJSXDev: 'react-jsxdev',
1524
- Preserve: 'preserve',
1525
- ReactNative: 'react-native',
1526
- };
1527
- if (jsxValues[jsxName]) normalized.jsx = jsxValues[jsxName];
1528
-
1529
- const targetName = typescript.ScriptTarget[compilerOptions.target];
1530
- if (targetName) normalized.target = targetName.toLowerCase();
1531
-
1532
- const importsName =
1533
- typescript.ImportsNotUsedAsValues?.[compilerOptions.importsNotUsedAsValues];
1534
- if (importsName)
1535
- normalized.importsNotUsedAsValues = importsName.toLowerCase();
1536
- return normalized;
1537
- }
1538
-
1539
- function resolveInlineTsconfig() {
1540
- if (!prepareCache) {
1541
- if (!fs.existsSync(inlineTsconfigFile)) {
1542
- throw new Error(
1543
- `MIAODA_VITE_INLINE_TSCONFIG_MISSING: ${inlineTsconfigFile}`
1544
- );
1545
- }
1546
- const cached = JSON.parse(fs.readFileSync(inlineTsconfigFile, 'utf8'));
1547
- if (
1548
- cached.schemaVersion !== 1 ||
1549
- !cached.compilerOptions ||
1550
- typeof cached.compilerOptions !== 'object' ||
1551
- Array.isArray(cached.compilerOptions)
1552
- ) {
1553
- throw new Error('MIAODA_VITE_INLINE_TSCONFIG_INVALID');
1554
- }
1555
- return { compilerOptions: cached.compilerOptions };
1556
- }
1557
-
1558
- const typescript = runtimeRequire('typescript');
1559
- const configFile = ['tsconfig.app.json', 'tsconfig.json']
1560
- .map(file => path.join(projectRoot, file))
1561
- .find(file => fs.existsSync(file));
1562
- let compilerOptions = {};
1563
- if (configFile) {
1564
- const readResult = typescript.readConfigFile(
1565
- configFile,
1566
- typescript.sys.readFile
1567
- );
1568
- if (readResult.error) {
1569
- throw new Error(
1570
- `MIAODA_VITE_TSCONFIG_RESOLVE_FAILED:\n${formatTypescriptDiagnostics(
1571
- typescript,
1572
- [readResult.error]
1573
- )}`
1574
- );
1575
- }
1576
- const parsed = typescript.parseJsonConfigFileContent(
1577
- readResult.config,
1578
- typescript.sys,
1579
- projectRoot,
1580
- undefined,
1581
- configFile
1582
- );
1583
- if (parsed.errors.length > 0) {
1584
- throw new Error(
1585
- `MIAODA_VITE_TSCONFIG_RESOLVE_FAILED:\n${formatTypescriptDiagnostics(
1586
- typescript,
1587
- parsed.errors
1588
- )}`
1589
- );
1590
- }
1591
- compilerOptions = normalizeInlineCompilerOptions(
1592
- typescript,
1593
- parsed.options
1594
- );
1595
- }
1596
- writeJsonAtomic(inlineTsconfigFile, {
1597
- schemaVersion: 1,
1598
- compilerOptions,
1599
- });
1600
- return { compilerOptions };
1601
- }
1602
-
1603
- const inlineTsconfig = resolveInlineTsconfig();
1604
-
1605
- function isInside(parent, candidate) {
1606
- const relative = path.relative(path.resolve(parent), path.resolve(candidate));
1607
- return (
1608
- relative === '' ||
1609
- (relative !== '..' &&
1610
- !relative.startsWith(`..${path.sep}`) &&
1611
- !path.isAbsolute(relative))
1612
- );
1613
- }
1614
-
1615
- function cssSpecifierCacheKey(specifier, basedir) {
1616
- const resolvedBasedir = path.resolve(basedir);
1617
- const realBasedir = fs.existsSync(resolvedBasedir)
1618
- ? fs.realpathSync(resolvedBasedir)
1619
- : resolvedBasedir;
1620
- let importerIdentity;
1621
- const cachedPackage = Object.entries(dependencyAssetsManifest.sourcePackages)
1622
- .map(([packagePath, relativeRoot]) => ({
1623
- packagePath,
1624
- packageRoot: path.resolve(cacheDir, relativeRoot),
1625
- }))
1626
- .filter(({ packageRoot }) => fs.existsSync(packageRoot))
1627
- .map(packageEntry => ({
1628
- ...packageEntry,
1629
- realPackageRoot: fs.realpathSync(packageEntry.packageRoot),
1630
- }))
1631
- .filter(({ realPackageRoot }) => isInside(realPackageRoot, realBasedir))
1632
- .sort(
1633
- (left, right) =>
1634
- right.realPackageRoot.length - left.realPackageRoot.length
1635
- )[0];
1636
- if (cachedPackage) {
1637
- importerIdentity = `package:${cachedPackage.packagePath}:${path
1638
- .relative(cachedPackage.realPackageRoot, realBasedir)
1639
- .split(path.sep)
1640
- .join('/')}`;
1641
- } else {
1642
- const installedPackage = [...clientGraphPackages.entries()]
1643
- .map(([packagePath, graphPackage]) => {
1644
- const packageRoot = path.resolve(projectRoot, packagePath);
1645
- if (!fs.existsSync(packageRoot)) return undefined;
1646
- const realPackageRoot = fs.realpathSync(packageRoot);
1647
- if (!isInside(realPackageRoot, realBasedir)) return undefined;
1648
- const packageJson = JSON.parse(
1649
- fs.readFileSync(path.join(realPackageRoot, 'package.json'), 'utf8')
1650
- );
1651
- const packageName = packageNameFromNodeModulesRoot(
1652
- path.join(projectRoot, 'node_modules'),
1653
- packageRoot
1654
- );
1655
- if (
1656
- !packageName ||
1657
- packageJson.name !== packageName ||
1658
- packageJson.version !== graphPackage.version
1659
- ) {
1660
- throw new Error(
1661
- `MIAODA_VITE_DEPENDENCY_PACKAGE_IDENTITY_REJECTED: ${packageRoot}`
1662
- );
1663
- }
1664
- return { packagePath, realPackageRoot };
1665
- })
1666
- .filter(Boolean)
1667
- .sort(
1668
- (left, right) =>
1669
- right.realPackageRoot.length - left.realPackageRoot.length
1670
- )[0];
1671
- if (installedPackage) {
1672
- importerIdentity = `package:${installedPackage.packagePath}:${path
1673
- .relative(installedPackage.realPackageRoot, realBasedir)
1674
- .split(path.sep)
1675
- .join('/')}`;
1676
- }
1677
- }
1678
- if (importerIdentity) {
1679
- return JSON.stringify([importerIdentity, specifier]);
1680
- }
1681
- const realCacheDir = fs.realpathSync(cacheDir);
1682
- const realProjectRoot = fs.realpathSync(projectRoot);
1683
- if (isInside(realCacheDir, realBasedir)) {
1684
- importerIdentity = `cache:${path
1685
- .relative(realCacheDir, realBasedir)
1686
- .split(path.sep)
1687
- .join('/')}`;
1688
- } else if (isInside(realProjectRoot, realBasedir)) {
1689
- importerIdentity = `project:${path
1690
- .relative(realProjectRoot, realBasedir)
1691
- .split(path.sep)
1692
- .join('/')}`;
1693
- } else {
1694
- throw new Error(
1695
- `MIAODA_VITE_CSS_IMPORTER_PATH_REJECTED: ${resolvedBasedir}`
1696
- );
1697
- }
1698
- return JSON.stringify([importerIdentity, specifier]);
1699
- }
1700
-
1701
- function clientStyleUrls() {
1702
- const styleRoot = path.join(clientRoot, 'src');
1703
- if (!fs.existsSync(styleRoot)) return [];
1704
- const urls = [];
1705
- const visit = directory => {
1706
- for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
1707
- const absolute = path.join(directory, entry.name);
1708
- if (entry.isSymbolicLink()) continue;
1709
- if (entry.isDirectory()) {
1710
- visit(absolute);
1711
- } else if (entry.isFile() && /\.(?:css|pcss|postcss)$/.test(entry.name)) {
1712
- urls.push(
1713
- `/${path.relative(projectRoot, absolute).split(path.sep).join('/')}`
1714
- );
1715
- }
1716
- }
1717
- };
1718
- visit(styleRoot);
1719
- return urls.sort();
1720
- }
1721
-
1722
- function withViteBase(base, urlPath) {
1723
- const basePath = new URL(base || '/', 'http://vite.local').pathname;
1724
- if (basePath === '/' || basePath === './') return urlPath;
1725
- return `${basePath.replace(/\/$/, '')}/${urlPath.replace(/^\//, '')}`;
1726
- }
1727
-
1728
- function writeDependencyAssetsManifest() {
1729
- fs.mkdirSync(dependencyAssetsRoot, { recursive: true });
1730
- const temporaryFile = `${dependencyAssetsManifestFile}.${process.pid}.tmp`;
1731
- fs.writeFileSync(
1732
- temporaryFile,
1733
- `${JSON.stringify(dependencyAssetsManifest, null, 2)}\n`
1734
- );
1735
- fs.renameSync(temporaryFile, dependencyAssetsManifestFile);
1736
- }
1737
-
1738
- function packageNameFromNodeModulesRoot(projectNodeModules, packageRoot) {
1739
- const relative = path.relative(projectNodeModules, packageRoot);
1740
- if (
1741
- !relative ||
1742
- relative === '..' ||
1743
- relative.startsWith(`..${path.sep}`) ||
1744
- path.isAbsolute(relative)
1745
- ) {
1746
- return undefined;
1747
- }
1748
- const segments = relative.split(path.sep);
1749
- const nestedIndex = segments.lastIndexOf('node_modules');
1750
- const packageSegments = segments.slice(nestedIndex + 1);
1751
- const expectedLength = packageSegments[0]?.startsWith('@') ? 2 : 1;
1752
- if (packageSegments.length !== expectedLength) return undefined;
1753
- const packageName = packageSegments.join('/');
1754
- return /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/i.test(
1755
- packageName
1756
- )
1757
- ? packageName
1758
- : undefined;
1759
- }
1760
-
1761
- function canonicalPackagePath(projectNodeModules, packageRoot) {
1762
- const relative = path.relative(projectNodeModules, packageRoot);
1763
- if (
1764
- !relative ||
1765
- relative === '..' ||
1766
- relative.startsWith(`..${path.sep}`) ||
1767
- path.isAbsolute(relative)
1768
- ) {
1769
- return undefined;
1770
- }
1771
- const packagePath = `node_modules/${relative.split(path.sep).join('/')}`;
1772
- return packagePath === path.posix.normalize(packagePath) &&
1773
- !packagePath.includes('\\')
1774
- ? packagePath
1775
- : undefined;
1776
- }
1777
-
1778
- const clientGraphPackages = new Map();
1779
- for (const graphPackage of appRuntimeManifest.clientDependencyGraph.packages) {
1780
- if (
1781
- typeof graphPackage?.path !== 'string' ||
1782
- graphPackage.path !== path.posix.normalize(graphPackage.path) ||
1783
- !graphPackage.path.startsWith('node_modules/') ||
1784
- graphPackage.path.includes('\\') ||
1785
- clientGraphPackages.has(graphPackage.path)
1786
- ) {
1787
- throw new Error('MIAODA_CLIENT_DEPENDENCY_GRAPH_PACKAGE_PATH_INVALID');
1788
- }
1789
- clientGraphPackages.set(graphPackage.path, graphPackage);
1790
- }
1791
-
1792
- function findProjectPackage(resolvedFile) {
1793
- const projectNodeModules = fs.realpathSync(
1794
- path.join(projectRoot, 'node_modules')
1795
- );
1796
- let current = path.dirname(fs.realpathSync(resolvedFile));
1797
- while (isInside(projectNodeModules, current)) {
1798
- const packageJsonFile = path.join(current, 'package.json');
1799
- if (fs.existsSync(packageJsonFile)) {
1800
- const packageJson = JSON.parse(fs.readFileSync(packageJsonFile, 'utf8'));
1801
- const expectedPackageName = packageNameFromNodeModulesRoot(
1802
- projectNodeModules,
1803
- current
1804
- );
1805
- const packagePath = canonicalPackagePath(projectNodeModules, current);
1806
- const graphPackage = packagePath
1807
- ? clientGraphPackages.get(packagePath)
1808
- : undefined;
1809
- if (
1810
- expectedPackageName &&
1811
- packageJson.name === expectedPackageName &&
1812
- packageJson.version === graphPackage?.version &&
1813
- packagePath &&
1814
- isInside(fs.realpathSync(projectNodeModules), fs.realpathSync(current))
1815
- ) {
1816
- return {
1817
- packageName: expectedPackageName,
1818
- packagePath,
1819
- packageRoot: current,
1820
- };
1821
- }
1822
- throw new Error(
1823
- `MIAODA_VITE_DEPENDENCY_PACKAGE_IDENTITY_REJECTED: ${current}`
1824
- );
1825
- }
1826
- const parent = path.dirname(current);
1827
- if (parent === current) break;
1828
- current = parent;
1829
- }
1830
- throw new Error(
1831
- `MIAODA_VITE_DEPENDENCY_ASSET_OUTSIDE_PROJECT: ${resolvedFile}`
1832
- );
1833
- }
1834
-
1835
- function snapshotProjectPackage(packageName, packagePath, packageRoot) {
1836
- if (!prepareCache) {
1837
- const relativeRoot = dependencyAssetsManifest.sourcePackages[packagePath];
1838
- if (!relativeRoot) {
1839
- throw new Error(`MIAODA_VITE_DEPENDENCY_ASSET_MISSING: ${packagePath}`);
1840
- }
1841
- const cachedRoot = path.resolve(cacheDir, relativeRoot);
1842
- if (
1843
- !isInside(cacheDir, cachedRoot) ||
1844
- !fs.existsSync(cachedRoot) ||
1845
- !isInside(fs.realpathSync(cacheDir), fs.realpathSync(cachedRoot))
1846
- ) {
1847
- throw new Error(
1848
- `MIAODA_VITE_DEPENDENCY_ASSET_PATH_REJECTED: ${packagePath}`
1849
- );
1850
- }
1851
- return cachedRoot;
1852
- }
1853
- const destination = path.resolve(
1854
- dependencyAssetsRoot,
1855
- ...packagePath.split('/')
1856
- );
1857
- if (!isInside(dependencyAssetsRoot, destination)) {
1858
- throw new Error(
1859
- `MIAODA_VITE_DEPENDENCY_ASSET_PATH_REJECTED: ${packagePath}`
1860
- );
1861
- }
1862
- if (!fs.existsSync(destination)) {
1863
- fs.mkdirSync(path.dirname(destination), { recursive: true });
1864
- fs.cpSync(packageRoot, destination, {
1865
- recursive: true,
1866
- errorOnExist: true,
1867
- force: false,
1868
- preserveTimestamps: true,
1869
- filter(source) {
1870
- if (fs.lstatSync(source).isSymbolicLink()) {
1871
- throw new Error(
1872
- `MIAODA_VITE_DEPENDENCY_ASSET_SYMLINK_REJECTED: ${source}`
1873
- );
1874
- }
1875
- return true;
1876
- },
1877
- });
1878
- }
1879
- dependencyAssetsManifest.sourcePackages[packagePath] = path.relative(
1880
- cacheDir,
1881
- destination
1882
- );
1883
- writeDependencyAssetsManifest();
1884
- return destination;
1885
- }
1886
-
1887
- const snapshottedClientPackageClosures = new Set();
1888
-
1889
- function snapshotClientPackageClosure(packagePath) {
1890
- const graphPackage = clientGraphPackages.get(packagePath);
1891
- if (!graphPackage) {
1892
- throw new Error(
1893
- `MIAODA_VITE_DEPENDENCY_GRAPH_PACKAGE_MISSING: ${packagePath}`
1894
- );
1895
- }
1896
- if (!prepareCache) {
1897
- const packageName = packageNameFromNodeModulesRoot(
1898
- path.join(projectRoot, 'node_modules'),
1899
- path.join(projectRoot, ...packagePath.split('/'))
1900
- );
1901
- if (!packageName) {
1902
- throw new Error(
1903
- `MIAODA_VITE_DEPENDENCY_PACKAGE_IDENTITY_REJECTED: ${packagePath}`
1904
- );
1905
- }
1906
- return snapshotProjectPackage(packageName, packagePath, undefined);
1907
- }
1908
-
1909
- const packageRoot = path.join(projectRoot, ...packagePath.split('/'));
1910
- const identity = findProjectPackage(path.join(packageRoot, 'package.json'));
1911
- const cachedRoot = snapshotProjectPackage(
1912
- identity.packageName,
1913
- identity.packagePath,
1914
- identity.packageRoot
1915
- );
1916
- if (snapshottedClientPackageClosures.has(packagePath)) return cachedRoot;
1917
- snapshottedClientPackageClosures.add(packagePath);
1918
- for (const dependencyPath of Object.values(graphPackage.edges).sort()) {
1919
- snapshotClientPackageClosure(dependencyPath);
1920
- }
1921
- return cachedRoot;
1922
- }
1923
-
1924
- function snapshotResolvedProjectFile(resolvedFile) {
1925
- const { packageName, packagePath, packageRoot } =
1926
- findProjectPackage(resolvedFile);
1927
- const cachedPackageRoot = snapshotProjectPackage(
1928
- packageName,
1929
- packagePath,
1930
- packageRoot
1931
- );
1932
- return {
1933
- packageName,
1934
- cachedFile: path.join(
1935
- cachedPackageRoot,
1936
- path.relative(packageRoot, resolvedFile)
1937
- ),
1938
- };
1939
- }
1940
-
1941
- function originalProjectBasedir(basedir) {
1942
- const resolvedBasedir = path.resolve(basedir);
1943
- const realBasedir = fs.existsSync(resolvedBasedir)
1944
- ? fs.realpathSync(resolvedBasedir)
1945
- : resolvedBasedir;
1946
- const cachedPackage = Object.entries(dependencyAssetsManifest.sourcePackages)
1947
- .map(([packagePath, relativeRoot]) => ({
1948
- packagePath,
1949
- packageRoot: path.resolve(cacheDir, relativeRoot),
1950
- }))
1951
- .filter(({ packageRoot }) => fs.existsSync(packageRoot))
1952
- .map(packageEntry => ({
1953
- ...packageEntry,
1954
- realPackageRoot: fs.realpathSync(packageEntry.packageRoot),
1955
- }))
1956
- .filter(({ realPackageRoot }) => isInside(realPackageRoot, realBasedir))
1957
- .sort(
1958
- (left, right) =>
1959
- right.realPackageRoot.length - left.realPackageRoot.length
1960
- )[0];
1961
- if (!cachedPackage) return resolvedBasedir;
1962
- return path.join(
1963
- projectRoot,
1964
- ...cachedPackage.packagePath.split('/'),
1965
- path.relative(cachedPackage.realPackageRoot, realBasedir)
1966
- );
1967
- }
1968
-
1969
- function resolveCachedTailwindPluginSpecifier(specifier, basedir) {
1970
- const cacheKey = cssSpecifierCacheKey(specifier, basedir);
1971
- const cachedSpecifier =
1972
- dependencyAssetsManifest.tailwindPluginSpecifiers[cacheKey];
1973
- if (!prepareCache) {
1974
- if (!cachedSpecifier) {
1975
- throw new Error(`MIAODA_VITE_TAILWIND_PLUGIN_CACHE_MISS: ${specifier}`);
1976
- }
1977
- const cachedFile = path.resolve(cacheDir, cachedSpecifier);
1978
- if (
1979
- !isInside(cacheDir, cachedFile) ||
1980
- !fs.existsSync(cachedFile) ||
1981
- !isInside(fs.realpathSync(cacheDir), fs.realpathSync(cachedFile))
1982
- ) {
1983
- throw new Error(
1984
- `MIAODA_VITE_TAILWIND_PLUGIN_CACHE_PATH_REJECTED: ${specifier}`
1985
- );
1986
- }
1987
- return cachedFile;
1988
- }
1989
-
1990
- const projectBasedir = originalProjectBasedir(basedir);
1991
- const projectRequire = createRequire(
1992
- path.join(projectBasedir, '__miaoda_tailwind_plugin_resolver.cjs')
1993
- );
1994
- const resolvedFile = fs.realpathSync(projectRequire.resolve(specifier));
1995
- const identity = findProjectPackage(resolvedFile);
1996
- const cachedPackageRoot = snapshotClientPackageClosure(identity.packagePath);
1997
- const cachedFile = path.join(
1998
- cachedPackageRoot,
1999
- path.relative(identity.packageRoot, resolvedFile)
2000
- );
2001
- dependencyAssetsManifest.tailwindPluginSpecifiers[cacheKey] = path
2002
- .relative(cacheDir, cachedFile)
2003
- .split(path.sep)
2004
- .join('/');
2005
- writeDependencyAssetsManifest();
2006
- return cachedFile;
2007
- }
2008
-
2009
- function listFilesRecursively(root) {
2010
- const files = [];
2011
- const visit = directory => {
2012
- for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
2013
- const absolute = path.join(directory, entry.name);
2014
- if (entry.isDirectory()) {
2015
- visit(absolute);
2016
- } else if (entry.isFile()) {
2017
- files.push(absolute);
2018
- }
2019
- }
2020
- };
2021
- visit(root);
2022
- return files;
2023
- }
2024
-
2025
- async function snapshotPreparedDependencyImports() {
2026
- if (!prepareCache) return { rewrittenImports: 0, sourcePackages: 0 };
2027
- const depsRoot = path.join(cacheDir, 'deps');
2028
- if (!fs.existsSync(depsRoot)) {
2029
- throw new Error('MIAODA_VITE_PREPARED_DEPS_MISSING');
2030
- }
2031
- const projectNodeModules = fs.realpathSync(
2032
- path.join(projectRoot, 'node_modules')
2033
- );
2034
- const lexicalProjectNodeModules = path.join(projectRoot, 'node_modules');
2035
- const lexer = await importFromRuntime('es-module-lexer');
2036
- await lexer.init;
2037
- let rewrittenImports = 0;
2038
- for (const moduleFile of listFilesRecursively(depsRoot).filter(file =>
2039
- file.endsWith('.js')
2040
- )) {
2041
- const source = fs.readFileSync(moduleFile, 'utf8');
2042
- const [imports] = lexer.parse(source);
2043
- const replacements = [];
2044
- for (const imported of imports) {
2045
- const specifier = imported.n;
2046
- if (!specifier) continue;
2047
- const suffixIndex = specifier.search(/[?#]/);
2048
- const pathname =
2049
- suffixIndex === -1 ? specifier : specifier.slice(0, suffixIndex);
2050
- const suffix = suffixIndex === -1 ? '' : specifier.slice(suffixIndex);
2051
- if (!path.isAbsolute(pathname)) continue;
2052
- if (!fs.existsSync(pathname)) {
2053
- if (isInside(lexicalProjectNodeModules, pathname)) {
2054
- throw new Error(
2055
- `MIAODA_VITE_PREPARED_DEPENDENCY_IMPORT_MISSING: ${pathname}`
2056
- );
2057
- }
2058
- continue;
2059
- }
2060
- const canonicalPathname = fs.realpathSync(pathname);
2061
- if (!isInside(projectNodeModules, canonicalPathname)) continue;
2062
- const { cachedFile } = snapshotResolvedProjectFile(canonicalPathname);
2063
- let relative = path.relative(path.dirname(moduleFile), cachedFile);
2064
- if (!relative.startsWith('.')) relative = `./${relative}`;
2065
- replacements.push({
2066
- start: imported.s,
2067
- end: imported.e,
2068
- value: `${relative.split(path.sep).join('/')}${suffix}`,
2069
- });
2070
- }
2071
- if (replacements.length === 0) continue;
2072
- let rewritten = source;
2073
- for (const replacement of replacements.sort(
2074
- (left, right) => right.start - left.start
2075
- )) {
2076
- rewritten =
2077
- rewritten.slice(0, replacement.start) +
2078
- replacement.value +
2079
- rewritten.slice(replacement.end);
2080
- }
2081
- fs.writeFileSync(moduleFile, rewritten);
2082
- rewrittenImports += replacements.length;
2083
- }
2084
- writeDependencyAssetsManifest();
2085
- return {
2086
- rewrittenImports,
2087
- sourcePackages: Object.keys(dependencyAssetsManifest.sourcePackages).length,
2088
- };
2089
- }
2090
-
2091
- async function importFromRuntime(specifier) {
2092
- const resolved = runtimeRequire.resolve(specifier);
2093
- return import(pathToFileURL(resolved).href);
2094
- }
2095
-
2096
- async function importRuntimeBootstrap(specifier) {
2097
- const resolved = fs.realpathSync(runtimeRequire.resolve(specifier));
2098
- const relativeEntry = path.relative(realPlatformRuntimeRoot, resolved);
2099
- if (
2100
- relativeEntry === '..' ||
2101
- relativeEntry.startsWith(`..${path.sep}`) ||
2102
- path.isAbsolute(relativeEntry)
2103
- ) {
2104
- throw new Error(`MIAODA_VITE_RUNTIME_IMPORT_PATH_REJECTED: ${specifier}`);
2105
- }
2106
- const snapshot = runtimeImportSnapshot();
2107
- try {
2108
- const module = await import(pathToFileURL(resolved).href);
2109
- process.stdout.write(
2110
- `${JSON.stringify({
2111
- event: 'miaoda_vite_runtime_import_diagnostics',
2112
- schemaVersion: 1,
2113
- specifier,
2114
- entry: relativeEntry.split(path.sep).join('/'),
2115
- ok: true,
2116
- ...runtimeImportMetrics(snapshot),
2117
- })}\n`
2118
- );
2119
- return module;
2120
- } catch (error) {
2121
- process.stdout.write(
2122
- `${JSON.stringify({
2123
- event: 'miaoda_vite_runtime_import_diagnostics',
2124
- schemaVersion: 1,
2125
- specifier,
2126
- entry: relativeEntry.split(path.sep).join('/'),
2127
- ok: false,
2128
- ...runtimeImportMetrics(snapshot),
2129
- })}\n`
2130
- );
2131
- throw error;
2132
- }
2133
- }
2134
-
2135
- function readExport(module, name) {
2136
- const value = module[name] ?? module.default?.[name];
2137
- if (typeof value !== 'function') {
2138
- throw new Error(`MIAODA_RUNTIME_EXPORT_MISSING: ${name}`);
2139
- }
2140
- return value;
2141
- }
2142
-
2143
- function readDefaultExport(module, specifier) {
2144
- const value = module.default ?? module;
2145
- if (typeof value !== 'function') {
2146
- throw new Error(`MIAODA_RUNTIME_DEFAULT_EXPORT_MISSING: ${specifier}`);
2147
- }
2148
- return value;
2149
- }
2150
-
2151
- const enhancedResolve = runtimeRequire('enhanced-resolve');
2152
- const cssResolver = enhancedResolve.create.sync({
2153
- conditionNames: ['style', 'import', 'browser', 'default'],
2154
- extensions: ['.css'],
2155
- mainFields: ['style', 'main'],
2156
- symlinks: false,
2157
- });
2158
- const resolveCss = (specifier, basedir) => cssResolver(basedir, specifier);
2159
-
2160
- function debugCssResolution(specifier, basedir, resolved) {
2161
- if (process.env.MIAODA_DEBUG_CACHE_RESOLUTION === 'true') {
2162
- process.stdout.write(
2163
- `${JSON.stringify({
2164
- event: 'miaoda_cache_css_resolved',
2165
- specifier,
2166
- basedir,
2167
- resolved,
2168
- })}\n`
2169
- );
2170
- }
2171
- return resolved;
2172
- }
2173
-
2174
- const preparedCssImportFiles = new Set();
2175
-
2176
- function snapshotCssImportClosure(cssFile) {
2177
- if (!prepareCache || preparedCssImportFiles.has(cssFile)) return;
2178
- preparedCssImportFiles.add(cssFile);
2179
- const source = fs.readFileSync(cssFile, 'utf8');
2180
- for (const match of source.matchAll(/@import\s+["']([^"']+)["']/g)) {
2181
- const specifier = match[1];
2182
- if (specifier === 'tailwindcss') continue;
2183
- const resolved =
2184
- specifier.startsWith('.') || path.isAbsolute(specifier)
2185
- ? resolveCss(specifier, path.dirname(cssFile))
2186
- : resolveCachedCssSpecifier(specifier, path.dirname(cssFile));
2187
- if (/\.(?:css|pcss|postcss)$/.test(resolved)) {
2188
- snapshotCssImportClosure(resolved);
2189
- }
2190
- }
2191
- }
2192
-
2193
- function resolveCachedCssSpecifier(specifier, basedir) {
2194
- if (specifier === '@/inspector.dev.css') {
2195
- return debugCssResolution(
2196
- specifier,
2197
- basedir,
2198
- runtimeRequire.resolve(`${activeVitePreset}/src/empty.css`)
2199
- );
2200
- }
2201
- if (specifier.startsWith('@/')) {
2202
- return debugCssResolution(
2203
- specifier,
2204
- basedir,
2205
- path.join(clientRoot, 'src', specifier.slice(2))
2206
- );
2207
- }
2208
- if (specifier.startsWith('.') || path.isAbsolute(specifier)) {
2209
- return debugCssResolution(
2210
- specifier,
2211
- basedir,
2212
- resolveCss(specifier, basedir)
2213
- );
2214
- }
2215
- if (specifier === 'tailwindcss') {
2216
- return debugCssResolution(
2217
- specifier,
2218
- basedir,
2219
- resolveCss(specifier, platformRuntimeRoot)
2220
- );
2221
- }
2222
- const cacheKey = cssSpecifierCacheKey(specifier, basedir);
2223
- const cachedSpecifier = dependencyAssetsManifest.cssSpecifiers[cacheKey];
2224
- if (!prepareCache && cachedSpecifier) {
2225
- const cachedFile = path.resolve(cacheDir, cachedSpecifier);
2226
- if (
2227
- !isInside(cacheDir, cachedFile) ||
2228
- !fs.existsSync(cachedFile) ||
2229
- !isInside(fs.realpathSync(cacheDir), fs.realpathSync(cachedFile))
2230
- ) {
2231
- throw new Error(
2232
- `MIAODA_VITE_CSS_DEPENDENCY_CACHE_PATH_REJECTED: ${specifier}`
2233
- );
2234
- }
2235
- return debugCssResolution(specifier, basedir, cachedFile);
2236
- }
2237
- if (!prepareCache) {
2238
- throw new Error(`MIAODA_VITE_CSS_DEPENDENCY_CACHE_MISS: ${specifier}`);
2239
- }
2240
- const projectResolved = resolveCss(specifier, basedir);
2241
- const realResolved = fs.realpathSync(projectResolved);
2242
- const realCacheDir = fs.realpathSync(cacheDir);
2243
- let cachedFile;
2244
- if (isInside(realCacheDir, realResolved)) {
2245
- const ownedBySnapshot = Object.values(
2246
- dependencyAssetsManifest.sourcePackages
2247
- ).some(relativeRoot => {
2248
- const packageRoot = path.resolve(cacheDir, relativeRoot);
2249
- return (
2250
- fs.existsSync(packageRoot) &&
2251
- isInside(fs.realpathSync(packageRoot), realResolved)
2252
- );
2253
- });
2254
- if (!ownedBySnapshot) {
2255
- throw new Error(
2256
- `MIAODA_VITE_CSS_DEPENDENCY_CACHE_PATH_REJECTED: ${specifier}`
2257
- );
2258
- }
2259
- cachedFile = realResolved;
2260
- } else {
2261
- ({ cachedFile } = snapshotResolvedProjectFile(realResolved));
2262
- }
2263
- const cachedRelative = path.relative(
2264
- fs.realpathSync(cacheDir),
2265
- fs.realpathSync(cachedFile)
2266
- );
2267
- if (
2268
- !cachedRelative ||
2269
- cachedRelative === '..' ||
2270
- cachedRelative.startsWith(`..${path.sep}`) ||
2271
- path.isAbsolute(cachedRelative)
2272
- ) {
2273
- throw new Error(
2274
- `MIAODA_VITE_CSS_DEPENDENCY_CACHE_PATH_REJECTED: ${specifier}`
2275
- );
2276
- }
2277
- dependencyAssetsManifest.cssSpecifiers[cacheKey] = cachedRelative
2278
- .split(path.sep)
2279
- .join('/');
2280
- writeDependencyAssetsManifest();
2281
- snapshotCssImportClosure(cachedFile);
2282
- return debugCssResolution(specifier, basedir, cachedFile);
2283
- }
2284
-
2285
- function snapshotClientCssImports() {
2286
- if (!prepareCache) return;
2287
- for (const url of clientStyleUrls()) {
2288
- const sourceFile = path.join(projectRoot, url.replace(/^\//, ''));
2289
- const source = fs.readFileSync(sourceFile, 'utf8');
2290
- for (const match of source.matchAll(/@import\s+["']([^"']+)["']/g)) {
2291
- const specifier = match[1];
2292
- if (
2293
- specifier === 'tailwindcss' ||
2294
- specifier.startsWith('.') ||
2295
- path.isAbsolute(specifier)
2296
- ) {
2297
- continue;
2298
- }
2299
- const cachedFile = resolveCachedCssSpecifier(
2300
- specifier,
2301
- path.dirname(sourceFile)
2302
- );
2303
- snapshotCssImportClosure(cachedFile);
2304
- }
2305
- for (const match of source.matchAll(/@plugin\s+["']([^"']+)["']/g)) {
2306
- const specifier = match[1];
2307
- if (specifier.startsWith('.') || path.isAbsolute(specifier)) continue;
2308
- resolveCachedTailwindPluginSpecifier(specifier, path.dirname(sourceFile));
2309
- }
2310
- }
2311
- }
2312
-
2313
- snapshotClientCssImports();
2314
-
2315
- const stableDependencyAssetsRoot = path.join(
2316
- projectRoot,
2317
- '.miaoda-runtime',
2318
- 'dependency-assets',
2319
- appRuntimeManifest.clientDependencyGraphHash
2320
- );
2321
- function publishStableDependencyAssetsPointer() {
2322
- const runtimeStateRoot = path.join(projectRoot, '.miaoda-runtime');
2323
- const stableParent = path.dirname(stableDependencyAssetsRoot);
2324
- for (const directory of [runtimeStateRoot, stableParent]) {
2325
- if (fs.existsSync(directory) && fs.lstatSync(directory).isSymbolicLink()) {
2326
- throw new Error(
2327
- `MIAODA_VITE_DEPENDENCY_ASSET_RUNTIME_SYMLINK_REJECTED: ${directory}`
2328
- );
2329
- }
2330
- fs.mkdirSync(directory, { recursive: true });
2331
- }
2332
- if (
2333
- fs.existsSync(stableDependencyAssetsRoot) &&
2334
- !fs.lstatSync(stableDependencyAssetsRoot).isSymbolicLink()
2335
- ) {
2336
- throw new Error(
2337
- `MIAODA_VITE_DEPENDENCY_ASSET_RUNTIME_PATH_REJECTED: ${stableDependencyAssetsRoot}`
2338
- );
2339
- }
2340
- const temporary = `${stableDependencyAssetsRoot}.${process.pid}.tmp`;
2341
- if (fs.existsSync(temporary)) {
2342
- if (!fs.lstatSync(temporary).isSymbolicLink()) {
2343
- throw new Error(
2344
- `MIAODA_VITE_DEPENDENCY_ASSET_RUNTIME_PATH_REJECTED: ${temporary}`
2345
- );
2346
- }
2347
- fs.unlinkSync(temporary);
2348
- }
2349
- fs.symlinkSync(path.relative(stableParent, dependencyAssetsRoot), temporary);
2350
- fs.renameSync(temporary, stableDependencyAssetsRoot);
2351
- }
2352
- publishStableDependencyAssetsPointer();
2353
-
2354
- function resolveTailwindSourceSpecifier(specifier, sourceFile) {
2355
- const marker = `${path.sep}node_modules${path.sep}`;
2356
- const normalized = path.normalize(
2357
- path.resolve(path.dirname(sourceFile || projectRoot), specifier)
2358
- );
2359
- const markerIndex = normalized.indexOf(marker);
2360
- if (markerIndex < 0) return specifier;
2361
- const packagePattern = normalized.slice(markerIndex + marker.length);
2362
- const segments = packagePattern.split(path.sep);
2363
- const packageName = packagePattern.startsWith('@')
2364
- ? segments.slice(0, 2).join('/')
2365
- : segments[0];
2366
- const packagePath = `node_modules/${packageName}`;
2367
- let cachedPackageRoot;
2368
- if (prepareCache) {
2369
- const packageRoot = path.join(
2370
- projectRoot,
2371
- 'node_modules',
2372
- ...packageName.split('/')
2373
- );
2374
- const resolvedPackage = findProjectPackage(
2375
- path.join(packageRoot, 'package.json')
2376
- );
2377
- cachedPackageRoot = snapshotProjectPackage(
2378
- resolvedPackage.packageName,
2379
- resolvedPackage.packagePath,
2380
- resolvedPackage.packageRoot
2381
- );
2382
- } else {
2383
- cachedPackageRoot = snapshotProjectPackage(
2384
- packageName,
2385
- packagePath,
2386
- undefined
2387
- );
2388
- }
2389
- const packageRelativePattern = segments
2390
- .slice(packageName.startsWith('@') ? 2 : 1)
2391
- .join(path.sep);
2392
- return path.join(cachedPackageRoot, packageRelativePattern);
2393
- }
2394
-
2395
- function snapshotDefaultTailwindContentPackage() {
2396
- const packageName = '@lark-apaas/client-toolkit';
2397
- const packagePath = `node_modules/${packageName}`;
2398
- if (!clientGraphPackages.has(packagePath)) return;
2399
- if (!prepareCache) {
2400
- snapshotProjectPackage(packageName, packagePath, undefined);
2401
- return;
2402
- }
2403
- const packageRoot = path.join(
2404
- projectRoot,
2405
- 'node_modules',
2406
- ...packageName.split('/')
2407
- );
2408
- const resolvedPackage = findProjectPackage(
2409
- path.join(packageRoot, 'package.json')
2410
- );
2411
- snapshotProjectPackage(
2412
- resolvedPackage.packageName,
2413
- resolvedPackage.packagePath,
2414
- resolvedPackage.packageRoot
2415
- );
2416
- }
2417
-
2418
- // The image-owned fullstack-presets adapter evaluates tailwind.config.* via
2419
- // Jiti, outside Vite's resolver. Persist only the default content package when
2420
- // it is part of the actual client graph, then make both producer and consumer
2421
- // resolve the same version from the sealed generation.
2422
- snapshotDefaultTailwindContentPackage();
2423
-
2424
- async function createCachePostcssConfig() {
2425
- const configCandidates = [
2426
- 'postcss.config.js',
2427
- 'postcss.config.cjs',
2428
- 'postcss.config.mjs',
2429
- ];
2430
- if (
2431
- !configCandidates.some(file => fs.existsSync(path.join(projectRoot, file)))
2432
- ) {
2433
- return undefined;
2434
- }
2435
- const postcssImport = readDefaultExport(
2436
- await importFromRuntime('postcss-import'),
2437
- 'postcss-import'
2438
- );
2439
- const tailwindPostcss = readDefaultExport(
2440
- await importFromRuntime('@tailwindcss/postcss'),
2441
- '@tailwindcss/postcss'
2442
- );
2443
- const autoprefixer = readDefaultExport(
2444
- await importFromRuntime('autoprefixer'),
2445
- 'autoprefixer'
2446
- );
2447
- const runtimeTailwindPaths = {
2448
- postcssPlugin: 'miaoda-runtime-tailwind-paths',
2449
- Once(root) {
2450
- // Tailwind consumes @plugin in its own Once hook. Rewrite after
2451
- // postcss-import expands local files but before Tailwind runs; AtRule
2452
- // visitors execute too late for imported plugin directives.
2453
- root.walkAtRules(rule => {
2454
- const quoted = /^(["'])(.+)\1$/.exec(rule.params.trim());
2455
- if (!quoted) return;
2456
- const [, quote, specifier] = quoted;
2457
- if (
2458
- rule.name === 'plugin' &&
2459
- !specifier.startsWith('.') &&
2460
- !path.isAbsolute(specifier)
2461
- ) {
2462
- rule.params = `${quote}${resolveCachedTailwindPluginSpecifier(
2463
- specifier,
2464
- path.dirname(rule.source?.input.file || projectRoot)
2465
- )}${quote}`;
2466
- }
2467
- if (rule.name === 'source') {
2468
- rule.params = `${quote}${resolveTailwindSourceSpecifier(
2469
- specifier,
2470
- rule.source?.input.file
2471
- )}${quote}`;
2472
- }
2473
- });
2474
- },
2475
- };
2476
- return {
2477
- plugins: [
2478
- postcssImport({
2479
- skipDuplicates: false,
2480
- resolve(specifier, basedir) {
2481
- return resolveCachedCssSpecifier(specifier, basedir);
2482
- },
2483
- }),
2484
- runtimeTailwindPaths,
2485
- tailwindPostcss({ base: projectRoot }),
2486
- autoprefixer(),
2487
- ],
2488
- };
2489
- }
2490
-
2491
- const createServer = readExport(viteModule, 'createServer');
2492
- const loadConfigFromFile = readExport(viteModule, 'loadConfigFromFile');
2493
- const mergeConfig = readExport(viteModule, 'mergeConfig');
2494
- const createDefaultViteConfig = readExport(
2495
- presetModule,
2496
- activeVitePreset === '@lark-apaas/fullstack-vite-preset'
2497
- ? 'createFullstackViteConfig'
2498
- : 'defineConfig'
2499
- );
2500
- const platformRuntimeResolverPlugin = readExport(
2501
- resolverPresetModule,
2502
- 'platformRuntimeResolverPlugin'
2503
- );
2504
- const capabilitiesPlugin = readExport(
2505
- resolverPresetModule,
2506
- 'capabilitiesPlugin'
2507
- );
2508
-
2509
- process.chdir(projectRoot);
2510
-
2511
- const runtimeResolver = platformRuntimeResolverPlugin({
2512
- projectRoot,
2513
- platformRuntimeRoot,
2514
- runtimeManifestFile,
2515
- appManifestFile,
2516
- dependencyReadyFile,
2517
- generationId: validatedGeneration?.generation.generationId,
2518
- cacheDir,
2519
- entryFile: clientEntryFile,
2520
- allowProjectDependencies: prepareCache,
2521
- });
2522
- // Presets historically shipped different capability implementations. Cache
2523
- // startup must never bake the generation-time JSON map into optimized deps.
2524
- // Use one image-owned implementation for both FullStack and JSP Page: the
2525
- // virtual module stays stable while current workspace data is injected into
2526
- // every HTML response and capability edits trigger a browser reload.
2527
- const runtimeCapabilities = capabilitiesPlugin();
2528
- const runtimePreviewRendered = createMiaodaPreviewRenderedPlugin();
2529
- const cacheCssDependencyPaths = {
2530
- name: 'miaoda-cache-css-dependency-paths',
2531
- enforce: 'pre',
2532
- resolveId(source) {
2533
- return cachedCssAliases.get(source) ?? null;
2534
- },
2535
- transform(source, id) {
2536
- const cleanId = id.split(/[?#]/, 1)[0];
2537
- if (!/\.(?:css|pcss|postcss)$/.test(cleanId)) return null;
2538
- const sourceFile = cleanId.startsWith('/@fs/')
2539
- ? cleanId.slice('/@fs'.length)
2540
- : cleanId;
2541
- const basedir = path.dirname(sourceFile);
2542
- let changed = false;
2543
- let code = source.replace(
2544
- /(@import\s+)(["'])([^"']+)\2/g,
2545
- (match, prefix, quote, specifier) => {
2546
- if (specifier.startsWith('.') || path.isAbsolute(specifier)) {
2547
- return match;
2548
- }
2549
- changed = true;
2550
- return `${prefix}${quote}${resolveCachedCssSpecifier(
2551
- specifier,
2552
- basedir
2553
- )}${quote}`;
2554
- }
2555
- );
2556
- code = code.replace(
2557
- /(@source\s+)(["'])([^"']+)\2/g,
2558
- (match, prefix, quote, specifier) => {
2559
- const resolved = resolveTailwindSourceSpecifier(specifier, sourceFile);
2560
- if (resolved === specifier) return match;
2561
- changed = true;
2562
- return `${prefix}${quote}${resolved}${quote}`;
2563
- }
2564
- );
2565
- code = code.replace(
2566
- /(@plugin\s+)(["'])([^"']+)\2/g,
2567
- (match, prefix, quote, specifier) => {
2568
- if (specifier.startsWith('.') || path.isAbsolute(specifier)) {
2569
- return match;
2570
- }
2571
- changed = true;
2572
- return `${prefix}${quote}${resolveCachedTailwindPluginSpecifier(
2573
- specifier,
2574
- basedir
2575
- )}${quote}`;
2576
- }
2577
- );
2578
- return changed ? { code, map: null } : null;
2579
- },
2580
- };
2581
- const clientEntryUrl = `/${path
2582
- .relative(projectRoot, clientEntryFile)
2583
- .split(path.sep)
2584
- .join('/')}`;
2585
- const cacheClientEntryPlugin = {
2586
- name: 'miaoda-cache-client-entry',
2587
- transformIndexHtml: {
2588
- order: 'pre',
2589
- handler(html) {
2590
- if (!fs.existsSync(clientEntryFile) || html.includes(clientEntryUrl)) {
2591
- return html;
2592
- }
2593
- return {
2594
- html,
2595
- tags: [
2596
- {
2597
- tag: 'script',
2598
- attrs: { type: 'module', src: clientEntryUrl },
2599
- injectTo: 'body',
2600
- },
2601
- ],
2602
- };
2603
- },
2604
- },
2605
- };
2606
- const lifecycleRoots = [
2607
- 'node_modules',
2608
- 'dist',
2609
- '.miaoda-cache',
2610
- '.miaoda-runtime',
2611
- '.githooks',
2612
- ].map(relative => path.join(projectRoot, relative));
2613
- const lifecycleFiles = new Set(
2614
- [
2615
- '.spark_project',
2616
- 'package.json',
2617
- 'package-lock.json',
2618
- 'npm-shrinkwrap.json',
2619
- 'pnpm-lock.yaml',
2620
- 'yarn.lock',
2621
- 'scripts/build.sh',
2622
- 'scripts/cache-runtime-coordinator.mjs',
2623
- 'scripts/cache-generation-prune.mjs',
2624
- 'scripts/cache-generation-preflight.mjs',
2625
- 'scripts/dev-local.js',
2626
- 'scripts/dev.js',
2627
- 'scripts/dev.sh',
2628
- 'scripts/hooks/run-precommit.js',
2629
- 'scripts/lint.js',
2630
- 'scripts/patch-vite-dependency-graph-hash.mjs',
2631
- 'scripts/prune-smart.js',
2632
- 'scripts/run.sh',
2633
- 'scripts/server-cache-runtime.mjs',
2634
- 'scripts/vite-cache-runtime.mjs',
2635
- ].map(relative => path.join(projectRoot, relative))
2636
- );
2637
- const isLifecycleWrite = file => {
2638
- const resolved = path.resolve(file);
2639
- return (
2640
- lifecycleFiles.has(resolved) ||
2641
- lifecycleRoots.some(
2642
- root => resolved === root || resolved.startsWith(`${root}${path.sep}`)
2643
- )
2644
- );
2645
- };
2646
- const cacheLifecycleHmrGuard = {
2647
- name: 'miaoda-cache-lifecycle-hmr-guard',
2648
- // Tailwind can map a change in an unrelated generated file back to
2649
- // client/src/index.css. Run last and decide from the actual changed file,
2650
- // otherwise dependency restore would emit hundreds of synthetic CSS HMRs.
2651
- hotUpdate: {
2652
- order: 'post',
2653
- handler(options) {
2654
- if (!prepareCache && isLifecycleWrite(options.file)) return [];
2655
- },
2656
- },
2657
- };
2658
- const cachePostcssConfig = await createCachePostcssConfig();
2659
- const cachedCssAliases = new Map();
2660
- const ambiguousCachedCssAliases = new Set();
2661
- for (const specifierMap of [
2662
- dependencyAssetsManifest.cssSpecifiers,
2663
- dependencyAssetsManifest.tailwindPluginSpecifiers,
2664
- ]) {
2665
- for (const [cacheKey, relativeFile] of Object.entries(specifierMap)) {
2666
- const parsedKey = JSON.parse(cacheKey);
2667
- const specifier = parsedKey?.[1];
2668
- if (typeof specifier !== 'string') {
2669
- throw new Error('MIAODA_VITE_CSS_DEPENDENCY_CACHE_KEY_INVALID');
2670
- }
2671
- const cachedFile = path.resolve(cacheDir, relativeFile);
2672
- const assetRelative = path.relative(dependencyAssetsRoot, cachedFile);
2673
- if (
2674
- !assetRelative ||
2675
- assetRelative === '..' ||
2676
- assetRelative.startsWith(`..${path.sep}`) ||
2677
- path.isAbsolute(assetRelative)
2678
- ) {
2679
- throw new Error(
2680
- `MIAODA_VITE_CSS_DEPENDENCY_CACHE_PATH_REJECTED: ${specifier}`
2681
- );
2682
- }
2683
- const stableCachedFile = path.join(
2684
- stableDependencyAssetsRoot,
2685
- assetRelative
2686
- );
2687
- if (ambiguousCachedCssAliases.has(specifier)) continue;
2688
- const previous = cachedCssAliases.get(specifier);
2689
- if (previous && previous !== stableCachedFile) {
2690
- // A nested package can intentionally resolve the same CSS specifier to
2691
- // a different version. The transform hook above rewrites each import
2692
- // with its importer-aware cached path; a global Vite alias would destroy
2693
- // that distinction, so omit only the ambiguous shortcut.
2694
- cachedCssAliases.delete(specifier);
2695
- ambiguousCachedCssAliases.add(specifier);
2696
- continue;
2697
- }
2698
- cachedCssAliases.set(specifier, stableCachedFile);
2699
- }
2700
- }
2701
- const runtimeCssAliases = [
2702
- {
2703
- find: /^@\/inspector\.dev\.css$/,
2704
- replacement: runtimeRequire.resolve(`${activeVitePreset}/src/empty.css`),
2705
- },
2706
- {
2707
- find: /^tailwindcss$/,
2708
- replacement: runtimeRequire.resolve('tailwindcss/index.css'),
2709
- },
2710
- ...[...cachedCssAliases.entries()].map(([find, replacement]) => ({
2711
- find,
2712
- replacement,
2713
- })),
2714
- ];
2715
- const runtimeOverrides = {
2716
- root: projectRoot,
2717
- cacheDir,
2718
- // Vite 8/Oxc otherwise auto-discovers the project tsconfig for every TSX
2719
- // transform. A package-based `extends` would make cache-only startup depend
2720
- // on Workspace node_modules. Use the producer-resolved, sealed subset in
2721
- // both producer and consumer so transform identity remains stable.
2722
- oxc: {
2723
- tsconfig: inlineTsconfig,
2724
- },
2725
- // Both producer and cache consumer bind internally. In consumer mode the
2726
- // stable public port is owned by the switch proxy so a normal project Vite
2727
- // can take over without changing the iframe URL.
2728
- server: {
2729
- host: '127.0.0.1',
2730
- port: 0,
2731
- strictPort: false,
2732
- },
2733
- resolve: {
2734
- // Cache-only mode already seals the aliases below. Avoid asking Vite's
2735
- // tsconfig-path resolver to load package-based `extends` from Workspace
2736
- // node_modules before dependency restore completes.
2737
- tsconfigPaths: false,
2738
- alias: [
2739
- ...runtimeCssAliases,
2740
- { find: '@', replacement: path.join(clientRoot, 'src') },
2741
- { find: '@client', replacement: clientRoot },
2742
- { find: '@shared', replacement: path.join(projectRoot, 'shared') },
2743
- ],
2744
- },
2745
- plugins: [
2746
- cacheCssDependencyPaths,
2747
- runtimeResolver,
2748
- cacheClientEntryPlugin,
2749
- cacheLifecycleHmrGuard,
2750
- ],
2751
- optimizeDeps: {
2752
- include: appRuntimeManifest.optimizeDependencies || [],
2753
- // The manifest is the complete dependency set for this generation. Source
2754
- // discovery would re-enter unresolved Workspace dependencies before the
2755
- // external npm restore has finished.
2756
- noDiscovery: true,
2757
- rolldownOptions: {
2758
- transform: {
2759
- tsconfig: inlineTsconfig,
2760
- },
2761
- },
2762
- },
2763
- ...(cachePostcssConfig ? { css: { postcss: cachePostcssConfig } } : {}),
2764
- };
2765
- const configFile = [
2766
- 'vite.config.ts',
2767
- 'vite.config.mts',
2768
- 'vite.config.js',
2769
- 'vite.config.mjs',
2770
- 'vite.config.cjs',
2771
- ]
2772
- .map(file => path.join(projectRoot, file))
2773
- .find(file => fs.existsSync(file));
2774
- let config;
2775
- const configLoadStartedAt = process.hrtime.bigint();
2776
- if (configFile) {
2777
- const loaded = await loadConfigFromFile(
2778
- { command: 'serve', mode: process.env.NODE_ENV },
2779
- configFile,
2780
- projectRoot
2781
- );
2782
- if (!loaded) {
2783
- throw new Error(`MIAODA_VITE_CONFIG_LOAD_FAILED: ${configFile}`);
2784
- }
2785
- // A template config already expands defineConfig() into the complete preset.
2786
- // Merge only the image-owned delta or every preset plugin/middleware would
2787
- // be registered twice. NODE_PATH above never points at Workspace dependencies.
2788
- config = mergeConfig(loaded.config, runtimeOverrides);
2789
- } else {
2790
- config = createDefaultViteConfig(runtimeOverrides);
2791
- }
2792
- if (cachePostcssConfig) {
2793
- config.css = {
2794
- ...(config.css || {}),
2795
- postcss: cachePostcssConfig,
2796
- };
2797
- }
2798
- // The JSP preset registers Tailwind as an early CSS transform. Snapshot and
2799
- // rewrite package CSS first, otherwise Tailwind would resolve @import against
2800
- // Workspace node_modules before the cache plugin sees it.
2801
- config.plugins = [
2802
- cacheCssDependencyPaths,
2803
- runtimeCapabilities,
2804
- runtimePreviewRendered,
2805
- ...(config.plugins || []).filter(
2806
- plugin =>
2807
- plugin !== cacheCssDependencyPaths &&
2808
- plugin?.name !== 'miaoda-capabilities-bundle' &&
2809
- plugin?.name !== MIAODA_PREVIEW_RENDERED_PLUGIN_NAME
2810
- ),
2811
- ];
2812
- emitRuntimePhase('config_load', configLoadStartedAt, {
2813
- configFile: Boolean(configFile),
2814
- });
2815
-
2816
- let server;
2817
- let closing = false;
2818
- async function closeServerBounded() {
2819
- if (!server) return { closeTimedOut: false };
2820
- let timeout;
2821
- try {
2822
- return await Promise.race([
2823
- server.close().then(() => ({ closeTimedOut: false })),
2824
- new Promise(resolve => {
2825
- // Keep this timer referenced. Some Vite/plugin teardown paths return
2826
- // an unsettled promise after all handles are gone, which otherwise
2827
- // makes Node exit with code 13 before the prepared cache is published.
2828
- timeout = setTimeout(
2829
- () => resolve({ closeTimedOut: true }),
2830
- closeTimeoutMs
2831
- );
2832
- }),
2833
- ]);
2834
- } finally {
2835
- if (timeout) clearTimeout(timeout);
2836
- }
2837
- }
2838
- async function close(signal) {
2839
- if (closing) return;
2840
- closing = true;
2841
- process.stdout.write(
2842
- `${JSON.stringify({ event: 'miaoda_vite_cache_runtime_stopping', signal })}\n`
2843
- );
2844
- if (dependencyReadyPoller) clearInterval(dependencyReadyPoller);
2845
- if (freshRuntimeRetryTimer) clearTimeout(freshRuntimeRetryTimer);
2846
- freshRuntimeRetryTimer = undefined;
2847
- if (freshRuntimeChild) freshRuntimeChild.kill('SIGTERM');
2848
- await Promise.all([
2849
- closeStableProxy().catch(() => undefined),
2850
- closeServerBounded().catch(() => undefined),
2851
- ]);
2852
- process.exit(0);
2853
- }
2854
-
2855
- let dependencyHandoffStarted = false;
2856
- async function handoffToOriginalVite() {
2857
- if (
2858
- closing ||
2859
- dependencyHandoffStarted ||
2860
- managedByCoordinator ||
2861
- prepareCache ||
2862
- !projectDependenciesReady()
2863
- ) {
2864
- return;
2865
- }
2866
- dependencyHandoffStarted = true;
2867
- process.stdout.write(
2868
- `${JSON.stringify({
2869
- event: 'miaoda_vite_dependency_handoff_started',
2870
- exitCode: dependencyHandoffExitCode,
2871
- })}\n`
2872
- );
2873
- closing = true;
2874
- if (dependencyReadyPoller) clearInterval(dependencyReadyPoller);
2875
- await Promise.all([
2876
- closeStableProxy().catch(() => undefined),
2877
- closeServerBounded().catch(() => undefined),
2878
- ]);
2879
- process.exit(dependencyHandoffExitCode);
2880
- }
2881
-
2882
- process.once('SIGINT', () => void close('SIGINT'));
2883
- process.once('SIGTERM', () => void close('SIGTERM'));
2884
- process.once('SIGHUP', () => void close('SIGHUP'));
2885
-
2886
- const dependencyCacheMetadataBeforeCreate = prepareCache
2887
- ? undefined
2888
- : dependencyCacheMetadataState();
2889
- try {
2890
- const createServerStartedAt = process.hrtime.bigint();
2891
- server = await createServer({
2892
- ...config,
2893
- configFile: false,
2894
- root: projectRoot,
2895
- });
2896
- emitRuntimePhase('create_server', createServerStartedAt);
2897
- } catch (error) {
2898
- throw error;
2899
- }
2900
- if (process.env.MIAODA_DEBUG_VITE_CONFIG_HASH === 'true') {
2901
- const hashConfig = server.environments.client.config;
2902
- const stringify = value =>
2903
- JSON.stringify(value, (_key, item) => {
2904
- if (typeof item === 'function' || item instanceof RegExp) {
2905
- return item.toString();
2906
- }
2907
- return item;
2908
- });
2909
- const hash = value =>
2910
- createHash('sha256').update(stringify(value)).digest('hex');
2911
- const configHashInput = {
2912
- define: !hashConfig.keepProcessEnv
2913
- ? process.env.NODE_ENV || hashConfig.mode
2914
- : null,
2915
- root: hashConfig.root,
2916
- resolve: hashConfig.resolve,
2917
- assetsInclude: hashConfig.assetsInclude,
2918
- plugins: hashConfig.plugins.map(plugin => plugin.name),
2919
- optimizeDeps: {
2920
- include: hashConfig.optimizeDeps.include
2921
- ? [...new Set(hashConfig.optimizeDeps.include)].sort()
2922
- : undefined,
2923
- exclude: hashConfig.optimizeDeps.exclude
2924
- ? [...new Set(hashConfig.optimizeDeps.exclude)].sort()
2925
- : undefined,
2926
- rolldownOptions: {
2927
- ...hashConfig.optimizeDeps.rolldownOptions,
2928
- plugins: undefined,
2929
- onLog: undefined,
2930
- onwarn: undefined,
2931
- checks: undefined,
2932
- output: {
2933
- ...hashConfig.optimizeDeps.rolldownOptions?.output,
2934
- plugins: undefined,
2935
- },
2936
- },
2937
- },
2938
- optimizeDepsPluginNames: hashConfig.optimizeDepsPluginNames,
2939
- };
2940
- let cachedConfigHash;
2941
- try {
2942
- cachedConfigHash = JSON.parse(
2943
- fs.readFileSync(path.join(cacheDir, 'deps', '_metadata.json'), 'utf8')
2944
- ).configHash;
2945
- } catch {
2946
- cachedConfigHash = undefined;
2947
- }
2948
- process.stdout.write(
2949
- `${JSON.stringify({
2950
- event: 'miaoda_vite_config_hash_inputs',
2951
- prepareCache,
2952
- calculatedConfigHash: hash(configHashInput).slice(0, 8),
2953
- cachedConfigHash,
2954
- define: configHashInput.define,
2955
- root: configHashInput.root,
2956
- resolveHash: hash(hashConfig.resolve),
2957
- assetsIncludeHash: hash(hashConfig.assetsInclude),
2958
- plugins: hashConfig.plugins.map(plugin => plugin.name),
2959
- optimizeDepsInclude: [
2960
- ...new Set(hashConfig.optimizeDeps.include || []),
2961
- ].sort(),
2962
- optimizeDepsExclude: [
2963
- ...new Set(hashConfig.optimizeDeps.exclude || []),
2964
- ].sort(),
2965
- optimizeDepsRolldownHash: hash({
2966
- ...hashConfig.optimizeDeps.rolldownOptions,
2967
- plugins: undefined,
2968
- onLog: undefined,
2969
- onwarn: undefined,
2970
- checks: undefined,
2971
- output: {
2972
- ...hashConfig.optimizeDeps.rolldownOptions?.output,
2973
- plugins: undefined,
2974
- },
2975
- }),
2976
- optimizeDepsPluginNames: hashConfig.optimizeDepsPluginNames,
2977
- })}\n`
2978
- );
2979
- }
2980
- try {
2981
- const listenStartedAt = process.hrtime.bigint();
2982
- await server.listen();
2983
- emitRuntimePhase('listen', listenStartedAt);
2984
-
2985
- if (!prepareCache) {
2986
- const dependencyCacheMetadataAfterListen = dependencyCacheMetadataState();
2987
- process.stdout.write(
2988
- `${JSON.stringify({
2989
- event: 'miaoda_vite_dependency_cache_diagnostics',
2990
- schemaVersion: 1,
2991
- noDiscovery: true,
2992
- forceReoptimize: false,
2993
- metadataExistedBeforeCreate:
2994
- dependencyCacheMetadataBeforeCreate?.exists === true,
2995
- metadataExistsAfterListen:
2996
- dependencyCacheMetadataAfterListen.exists === true,
2997
- metadataContentChanged:
2998
- dependencyCacheMetadataBeforeCreate?.sha256 !==
2999
- dependencyCacheMetadataAfterListen.sha256,
3000
- metadataMtimeChanged:
3001
- dependencyCacheMetadataBeforeCreate?.mtimeMs !==
3002
- dependencyCacheMetadataAfterListen.mtimeMs,
3003
- optimizedDependenciesBefore:
3004
- dependencyCacheMetadataBeforeCreate?.optimizedDependencies ?? 0,
3005
- optimizedDependenciesAfter:
3006
- dependencyCacheMetadataAfterListen.optimizedDependencies ?? 0,
3007
- optimizedChunksBefore:
3008
- dependencyCacheMetadataBeforeCreate?.optimizedChunks ?? 0,
3009
- optimizedChunksAfter:
3010
- dependencyCacheMetadataAfterListen.optimizedChunks ?? 0,
3011
- })}\n`
3012
- );
3013
- }
3014
-
3015
- const address = server.httpServer?.address();
3016
- const internalPort =
3017
- address && typeof address !== 'string' ? address.port : config.server?.port;
3018
- let port = internalPort;
3019
- if (!prepareCache) {
3020
- const publicPort = Number(process.env.CLIENT_DEV_PORT || 8080);
3021
- if (
3022
- !Number.isSafeInteger(publicPort) ||
3023
- publicPort <= 0 ||
3024
- publicPort > 65_535
3025
- ) {
3026
- throw new Error('MIAODA_VITE_PUBLIC_PORT_INVALID');
3027
- }
3028
- cacheBackend = {
3029
- kind: 'cache',
3030
- port: internalPort,
3031
- pid: process.pid,
3032
- webSocketToken: server.config.webSocketToken,
3033
- };
3034
- activeBackend = cacheBackend;
3035
- await startStableProxy(
3036
- publicPort,
3037
- process.env.CLIENT_DEV_HOST || '0.0.0.0'
3038
- );
3039
- port = publicPort;
3040
- publishServingReadyMarker(servingReadyMarker);
3041
- if (!managedByCoordinator) {
3042
- dependencyReadyPoller = setInterval(() => {
3043
- if (projectDependenciesReady()) void handoffToOriginalVite();
3044
- }, 250);
3045
- dependencyReadyPoller.unref();
3046
- if (projectDependenciesReady()) void handoffToOriginalVite();
3047
- }
3048
- }
3049
- if (prepareCache) {
3050
- await server.warmupRequest(clientEntryUrl);
3051
- for (const styleUrl of clientStyleUrls()) {
3052
- const publicStyleUrl = withViteBase(server.config.base, styleUrl);
3053
- const styleResponse = await fetch(
3054
- `http://127.0.0.1:${port}${publicStyleUrl}?direct`
3055
- );
3056
- if (!styleResponse.ok) {
3057
- throw new Error(
3058
- `MIAODA_VITE_STYLE_PREPARE_FAILED: ${publicStyleUrl} ${styleResponse.status} ${await styleResponse.text()}`
3059
- );
3060
- }
3061
- await styleResponse.arrayBuffer();
3062
- }
3063
- await server.waitForRequestsIdle();
3064
- const preparedCache = await waitForPreparedCache();
3065
- const closeResult = await closeServerBounded();
3066
- const dependencyAssets = await snapshotPreparedDependencyImports();
3067
- process.stdout.write(
3068
- `${JSON.stringify({
3069
- event: 'miaoda_vite_cache_runtime_prepared',
3070
- pid: process.pid,
3071
- port,
3072
- cacheDir,
3073
- ...preparedCache,
3074
- ...dependencyAssets,
3075
- ...closeResult,
3076
- })}\n`
3077
- );
3078
- process.exit(0);
3079
- }
3080
- process.stdout.write(
3081
- `${JSON.stringify({
3082
- event: 'miaoda_vite_cache_runtime_ready',
3083
- pid: process.pid,
3084
- port,
3085
- cachePort: internalPort,
3086
- cacheDir,
3087
- sealedCacheDir,
3088
- dependencyReadyFile,
3089
- startupValidation: 'sealed-cache-only',
3090
- startupDurationMs: Number(durationMs(runtimeStartedAt).toFixed(3)),
3091
- pluginNames: server.config.plugins.map(plugin => plugin.name),
3092
- })}\n`
3093
- );
3094
- } catch (error) {
3095
- await closeStableProxy().catch(() => undefined);
3096
- await closeServerBounded().catch(() => undefined);
3097
- throw error;
3098
- }