@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.
- package/package.json +1 -1
- package/template/package-lock.json +4385 -4385
- package/template/package.json +5 -5
- package/template/scripts/build.sh +0 -12
- package/template/scripts/dev.js +57 -415
- package/template/scripts/dev.sh +0 -2
- package/template/vite.config.ts +1 -1
- package/template/scripts/cache-generation-preflight.mjs +0 -1346
- package/template/scripts/cache-generation-prune.mjs +0 -174
- package/template/scripts/cache-runtime-coordinator.mjs +0 -482
- package/template/scripts/lib/preserve-dev-cache.cjs +0 -123
- package/template/scripts/patch-nest-cli-startup-instrumentation.mjs +0 -376
- package/template/scripts/patch-vite-dependency-graph-hash.mjs +0 -141
- package/template/scripts/server-cache-runtime.mjs +0 -865
- package/template/scripts/server-startup-runtime.mjs +0 -445
- package/template/scripts/vite-cache-runtime.mjs +0 -3098
- package/template/scripts/vite-runtime.config.mjs +0 -420
- package/template/scripts/workspace-client-runtime.mjs +0 -466
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// dev.js 启动时替代无脑 rm -rf dist/ 的 lib: 保留 dist/server (nest --watch 增量编译起点)
|
|
4
|
-
// + 按 HTML mode marker 决定 dist/client 是保留还是清污染。
|
|
5
|
-
// 跟 preset 侧 html-output plugin 的 injectHtmlModeMarker / detectHtmlMode 配套:
|
|
6
|
-
// preset 写盘时打 marker (dev 版 vs prod 污染版), 这里读 marker 定去留。
|
|
7
|
-
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 从 HTML 里回读 <meta name="miaoda-html-mode" content="dev|prod"> marker。
|
|
13
|
-
* 与 packages/tools/fullstack-vite-preset/src/vite-plugins/html-output-plugin.ts
|
|
14
|
-
* 的 detectHtmlMode 同源, 为了让 dev.js 模板脚本零依赖跑起来这里重复一份。
|
|
15
|
-
*/
|
|
16
|
-
function detectHtmlMode(htmlContent) {
|
|
17
|
-
const match = htmlContent.match(
|
|
18
|
-
/<meta\s+name=["']miaoda-html-mode["']\s+content=["'](dev|prod)["'][^>]*>/i
|
|
19
|
-
);
|
|
20
|
-
return (match && match[1]) || 'unknown';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 定向清理 dist/ 的可复用副本, 替代 dev.js 之前无脑 `rm -rf dist/` 的 cleanStaleDist。
|
|
25
|
-
*
|
|
26
|
-
* 决策规则:
|
|
27
|
-
* - FORCE_CLEAN_DIST=true → 走 escape hatch 全清 (preserveDevCache 逻辑 / 保留的
|
|
28
|
-
* dist/server 出 stale 时兜底)
|
|
29
|
-
* - dist/ 不存在 → 无动作
|
|
30
|
-
* - dist/client/index.html 存在且 marker=dev → 整个 dist/client/ 保留 (dev cache hit)
|
|
31
|
-
* - 存在但 marker=prod 或缺失 → 只清 dist/client/index.html + dist/client/assets/
|
|
32
|
-
* (build 污染部分), dist/server/ 与 routes JSON 保留
|
|
33
|
-
* - 不存在 → 无动作, htmlOutputPlugin 稍后会写入
|
|
34
|
-
*
|
|
35
|
-
* @param {string} projectRoot 项目根目录
|
|
36
|
-
* @param {object} [options]
|
|
37
|
-
* @param {(level: string, msg: string) => void} [options.log] 日志回调, 默认 console.log
|
|
38
|
-
* @param {boolean} [options.forceClean] 覆盖 FORCE_CLEAN_DIST env var 判断 (给测试用)
|
|
39
|
-
* @returns {{ action: 'no_dist' | 'force_cleaned' | 'no_index' | 'preserved' | 'cleaned_polluted' | 'read_failed_cleaned' | 'cleanup_failed', mode: string }}
|
|
40
|
-
*/
|
|
41
|
-
function preserveDevCache(projectRoot, options) {
|
|
42
|
-
const log = (options && options.log) || defaultLogger;
|
|
43
|
-
const forceClean =
|
|
44
|
-
options && typeof options.forceClean === 'boolean'
|
|
45
|
-
? options.forceClean
|
|
46
|
-
: process.env.FORCE_CLEAN_DIST === 'true';
|
|
47
|
-
|
|
48
|
-
const distPath = path.join(projectRoot, 'dist');
|
|
49
|
-
if (!fs.existsSync(distPath)) {
|
|
50
|
-
log('INFO', 'preserveDevCache: no dist/, will be created by vite/nest');
|
|
51
|
-
return { action: 'no_dist', mode: 'unknown' };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (forceClean) {
|
|
55
|
-
fs.rmSync(distPath, { recursive: true, force: true });
|
|
56
|
-
log(
|
|
57
|
-
'INFO',
|
|
58
|
-
'preserveDevCache: FORCE_CLEAN_DIST=true, cleaned entire dist/'
|
|
59
|
-
);
|
|
60
|
-
return { action: 'force_cleaned', mode: 'unknown' };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const clientDir = path.join(distPath, 'client');
|
|
64
|
-
const indexHtml = path.join(clientDir, 'index.html');
|
|
65
|
-
|
|
66
|
-
if (!fs.existsSync(indexHtml)) {
|
|
67
|
-
log(
|
|
68
|
-
'INFO',
|
|
69
|
-
'preserveDevCache: no dist/client/index.html, other artifacts preserved'
|
|
70
|
-
);
|
|
71
|
-
return { action: 'no_index', mode: 'unknown' };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
let mode = 'unknown';
|
|
75
|
-
let readFailed = false;
|
|
76
|
-
try {
|
|
77
|
-
const content = fs.readFileSync(indexHtml, 'utf-8');
|
|
78
|
-
mode = detectHtmlMode(content);
|
|
79
|
-
} catch (e) {
|
|
80
|
-
log(
|
|
81
|
-
'WARN',
|
|
82
|
-
`preserveDevCache: read dist/client/index.html failed: ${e.message}, treating as polluted`
|
|
83
|
-
);
|
|
84
|
-
readFailed = true;
|
|
85
|
-
mode = 'unknown';
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (mode === 'dev') {
|
|
89
|
-
log('INFO', 'preserveDevCache: dist/client preserved (dev cache hit)');
|
|
90
|
-
return { action: 'preserved', mode: 'dev' };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// marker=prod 或 unknown → build:client 污染 (或人为放的产物), 定向清 index.html + assets/
|
|
94
|
-
// dist/server/, dist/*.json 保留
|
|
95
|
-
try {
|
|
96
|
-
fs.rmSync(indexHtml, { force: true });
|
|
97
|
-
const assetsDir = path.join(clientDir, 'assets');
|
|
98
|
-
if (fs.existsSync(assetsDir)) {
|
|
99
|
-
fs.rmSync(assetsDir, { recursive: true, force: true });
|
|
100
|
-
}
|
|
101
|
-
log(
|
|
102
|
-
'INFO',
|
|
103
|
-
`preserveDevCache: dist/client/index.html was ${mode}, cleaned index.html + assets/ (dist/server preserved)`
|
|
104
|
-
);
|
|
105
|
-
return {
|
|
106
|
-
action: readFailed ? 'read_failed_cleaned' : 'cleaned_polluted',
|
|
107
|
-
mode,
|
|
108
|
-
};
|
|
109
|
-
} catch (e) {
|
|
110
|
-
log(
|
|
111
|
-
'WARN',
|
|
112
|
-
`preserveDevCache: cleanup failed: ${e.message}, continuing anyway (index.html / assets may still be polluted)`
|
|
113
|
-
);
|
|
114
|
-
return { action: 'cleanup_failed', mode };
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function defaultLogger(level, msg) {
|
|
119
|
-
// eslint-disable-next-line no-console
|
|
120
|
-
console.log(`[${level}] [main] ${msg}`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
module.exports = { detectHtmlMode, preserveDevCache };
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
|
|
7
|
-
const EXPECTED_NEST_CLI_VERSION = '10.4.9';
|
|
8
|
-
|
|
9
|
-
function replaceOnce(source, search, replacement, label) {
|
|
10
|
-
const first = source.indexOf(search);
|
|
11
|
-
if (first < 0 || source.indexOf(search, first + search.length) >= 0) {
|
|
12
|
-
throw new Error(`MIAODA_NEST_CLI_PATCH_ANCHOR_MISMATCH: ${label}`);
|
|
13
|
-
}
|
|
14
|
-
return `${source.slice(0, first)}${replacement}${source.slice(
|
|
15
|
-
first + search.length
|
|
16
|
-
)}`;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function patchWatchCompilerSource(source) {
|
|
20
|
-
let patched = source;
|
|
21
|
-
if (!patched.includes('miaoda-startup-instrumentation')) {
|
|
22
|
-
patched = replaceOnce(
|
|
23
|
-
patched,
|
|
24
|
-
'const tsconfig_paths_hook_1 = require("./hooks/tsconfig-paths.hook");',
|
|
25
|
-
'const tsconfig_paths_hook_1 = require("./hooks/tsconfig-paths.hook");\nconst miaoda_startup_instrumentation_1 = require("../utils/miaoda-startup-instrumentation");',
|
|
26
|
-
'watch-compiler-import'
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
patched = patched.replace(
|
|
30
|
-
' (0, miaoda_startup_instrumentation_1.instrumentTypeScriptCompilerHost)(host);\n',
|
|
31
|
-
''
|
|
32
|
-
);
|
|
33
|
-
return replaceOnce(
|
|
34
|
-
patched,
|
|
35
|
-
" const manualRestart = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.manualRestart', appName);",
|
|
36
|
-
" (0, miaoda_startup_instrumentation_1.instrumentTypeScriptCompilerHost)(host);\n const manualRestart = (0, get_value_or_default_1.getValueOrDefault)(configuration, 'compilerOptions.manualRestart', appName);",
|
|
37
|
-
'watch-compiler-host'
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function hasEarlyWatchHostInstrumentation(source) {
|
|
42
|
-
const instrumentation = source.indexOf(
|
|
43
|
-
'instrumentTypeScriptCompilerHost)(host)'
|
|
44
|
-
);
|
|
45
|
-
const createWatchProgram = source.indexOf('createWatchProgram(host)');
|
|
46
|
-
return (
|
|
47
|
-
instrumentation >= 0 &&
|
|
48
|
-
createWatchProgram >= 0 &&
|
|
49
|
-
instrumentation < createWatchProgram
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function instrumentationRuntimeSource() {
|
|
54
|
-
return `'use strict';
|
|
55
|
-
const path = require('node:path');
|
|
56
|
-
|
|
57
|
-
const stateKey = Symbol.for('miaoda.nest-cli.startup-instrumentation');
|
|
58
|
-
const state = globalThis[stateKey] || (globalThis[stateKey] = {
|
|
59
|
-
sequence: 0,
|
|
60
|
-
emittedPhases: new Set(),
|
|
61
|
-
phaseStartedAt: new Map(),
|
|
62
|
-
tsbuildinfoReads: new Map(),
|
|
63
|
-
jsEmittedFiles: new Set(),
|
|
64
|
-
initialCompileCompleted: false,
|
|
65
|
-
});
|
|
66
|
-
state.tsbuildinfoReads ||= new Map();
|
|
67
|
-
state.jsEmittedFiles ||= new Set();
|
|
68
|
-
state.initialCompileCompleted ||= false;
|
|
69
|
-
|
|
70
|
-
const instrumentedCompilerHostKey = Symbol.for(
|
|
71
|
-
'miaoda.nest-cli.instrumented-compiler-host'
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
function enabled() {
|
|
75
|
-
return process.env.MIAODA_NEST_CLI_INSTRUMENTATION === 'true';
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function emitNestCliPhase(phase, details = {}) {
|
|
79
|
-
if (!enabled()) return;
|
|
80
|
-
if (state.emittedPhases.has(phase)) return;
|
|
81
|
-
const atEpochMs = Date.now();
|
|
82
|
-
if (phase.endsWith('_start')) state.phaseStartedAt.set(phase, atEpochMs);
|
|
83
|
-
const pairedStart = phase === 'nest_compile_end'
|
|
84
|
-
? state.phaseStartedAt.get('nest_compile_start')
|
|
85
|
-
: undefined;
|
|
86
|
-
const typescriptEvidence = phase === 'nest_compile_end'
|
|
87
|
-
? {
|
|
88
|
-
tsbuildinfoRead: state.tsbuildinfoReads.size > 0,
|
|
89
|
-
tsbuildinfoReadPaths: Array.from(state.tsbuildinfoReads.keys()),
|
|
90
|
-
jsEmitCount: state.jsEmittedFiles.size,
|
|
91
|
-
jsEmittedFiles: Array.from(state.jsEmittedFiles).slice(0, 20),
|
|
92
|
-
jsEmittedFilesTruncated: state.jsEmittedFiles.size > 20,
|
|
93
|
-
}
|
|
94
|
-
: {};
|
|
95
|
-
state.emittedPhases.add(phase);
|
|
96
|
-
if (phase === 'nest_compile_end') state.initialCompileCompleted = true;
|
|
97
|
-
process.stdout.write(JSON.stringify({
|
|
98
|
-
event: 'miaoda_nest_cli_phase',
|
|
99
|
-
phase,
|
|
100
|
-
sequence: ++state.sequence,
|
|
101
|
-
atEpochMs,
|
|
102
|
-
pid: process.pid,
|
|
103
|
-
...(pairedStart === undefined ? {} : {
|
|
104
|
-
phaseStartedAtEpochMs: pairedStart,
|
|
105
|
-
durationMs: atEpochMs - pairedStart,
|
|
106
|
-
}),
|
|
107
|
-
...typescriptEvidence,
|
|
108
|
-
...details,
|
|
109
|
-
}) + '\\n');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function instrumentTypeScriptCompilerHost(host) {
|
|
113
|
-
if (!enabled() || !host || host[instrumentedCompilerHostKey]) return host;
|
|
114
|
-
Object.defineProperty(host, instrumentedCompilerHostKey, { value: true });
|
|
115
|
-
if (typeof host.readFile === 'function') {
|
|
116
|
-
const originalReadFile = host.readFile;
|
|
117
|
-
host.readFile = function (fileName, ...args) {
|
|
118
|
-
const contents = originalReadFile.call(this, fileName, ...args);
|
|
119
|
-
if (
|
|
120
|
-
!state.initialCompileCompleted &&
|
|
121
|
-
typeof fileName === 'string' &&
|
|
122
|
-
fileName.endsWith('.tsbuildinfo') &&
|
|
123
|
-
typeof contents === 'string'
|
|
124
|
-
) {
|
|
125
|
-
const bytes = Buffer.byteLength(contents, 'utf8');
|
|
126
|
-
if (!state.tsbuildinfoReads.has(fileName)) {
|
|
127
|
-
state.tsbuildinfoReads.set(fileName, bytes);
|
|
128
|
-
process.stdout.write(JSON.stringify({
|
|
129
|
-
event: 'miaoda_nest_typescript_evidence',
|
|
130
|
-
phase: 'tsbuildinfo_read',
|
|
131
|
-
atEpochMs: Date.now(),
|
|
132
|
-
pid: process.pid,
|
|
133
|
-
path: fileName,
|
|
134
|
-
bytes,
|
|
135
|
-
}) + '\\n');
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return contents;
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (typeof host.writeFile === 'function') {
|
|
142
|
-
const originalWriteFile = host.writeFile;
|
|
143
|
-
host.writeFile = function (fileName, ...args) {
|
|
144
|
-
if (
|
|
145
|
-
!state.initialCompileCompleted &&
|
|
146
|
-
typeof fileName === 'string' &&
|
|
147
|
-
/\\.(?:[cm]?js|jsx)$/i.test(fileName)
|
|
148
|
-
) {
|
|
149
|
-
state.jsEmittedFiles.add(fileName);
|
|
150
|
-
}
|
|
151
|
-
return originalWriteFile.call(this, fileName, ...args);
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
return host;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function createInstrumentedChildEnv(environment) {
|
|
158
|
-
if (!enabled()) return environment;
|
|
159
|
-
const preload = path.join(__dirname, 'miaoda-listen-ready.js');
|
|
160
|
-
const requireOption = '--require=' + preload;
|
|
161
|
-
return {
|
|
162
|
-
...environment,
|
|
163
|
-
NODE_OPTIONS: [environment.NODE_OPTIONS || '', requireOption]
|
|
164
|
-
.filter(Boolean)
|
|
165
|
-
.join(' '),
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
module.exports = {
|
|
170
|
-
createInstrumentedChildEnv,
|
|
171
|
-
emitNestCliPhase,
|
|
172
|
-
instrumentTypeScriptCompilerHost,
|
|
173
|
-
};
|
|
174
|
-
`;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function listenReadyRuntimeSource() {
|
|
178
|
-
return `'use strict';
|
|
179
|
-
const net = require('node:net');
|
|
180
|
-
|
|
181
|
-
if (process.env.MIAODA_NEST_CLI_INSTRUMENTATION === 'true') {
|
|
182
|
-
const originalListen = net.Server.prototype.listen;
|
|
183
|
-
let emitted = false;
|
|
184
|
-
net.Server.prototype.listen = function (...args) {
|
|
185
|
-
this.once('listening', () => {
|
|
186
|
-
if (emitted) return;
|
|
187
|
-
const address = this.address();
|
|
188
|
-
const expectedPort = Number(process.env.SERVER_PORT || 3000);
|
|
189
|
-
if (
|
|
190
|
-
!address ||
|
|
191
|
-
typeof address === 'string' ||
|
|
192
|
-
(Number.isSafeInteger(expectedPort) && address.port !== expectedPort)
|
|
193
|
-
) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
emitted = true;
|
|
197
|
-
process.stdout.write(JSON.stringify({
|
|
198
|
-
event: 'miaoda_nest_cli_phase',
|
|
199
|
-
phase: 'nest_listen_ready',
|
|
200
|
-
atEpochMs: Date.now(),
|
|
201
|
-
pid: process.pid,
|
|
202
|
-
host: address.address,
|
|
203
|
-
port: address.port,
|
|
204
|
-
}) + '\\n');
|
|
205
|
-
});
|
|
206
|
-
return originalListen.apply(this, args);
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
`;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function patchNestCliInstallation(installRoot) {
|
|
213
|
-
const packageRoot = path.resolve(
|
|
214
|
-
installRoot,
|
|
215
|
-
'node_modules',
|
|
216
|
-
'@nestjs',
|
|
217
|
-
'cli'
|
|
218
|
-
);
|
|
219
|
-
const packageFile = path.join(packageRoot, 'package.json');
|
|
220
|
-
const binFile = path.join(packageRoot, 'bin', 'nest.js');
|
|
221
|
-
const startActionFile = path.join(packageRoot, 'actions', 'start.action.js');
|
|
222
|
-
const buildActionFile = path.join(packageRoot, 'actions', 'build.action.js');
|
|
223
|
-
const watchCompilerFile = path.join(
|
|
224
|
-
packageRoot,
|
|
225
|
-
'lib',
|
|
226
|
-
'compiler',
|
|
227
|
-
'watch-compiler.js'
|
|
228
|
-
);
|
|
229
|
-
const instrumentationFile = path.join(
|
|
230
|
-
packageRoot,
|
|
231
|
-
'lib',
|
|
232
|
-
'utils',
|
|
233
|
-
'miaoda-startup-instrumentation.js'
|
|
234
|
-
);
|
|
235
|
-
const listenReadyFile = path.join(
|
|
236
|
-
packageRoot,
|
|
237
|
-
'lib',
|
|
238
|
-
'utils',
|
|
239
|
-
'miaoda-listen-ready.js'
|
|
240
|
-
);
|
|
241
|
-
const packageJson = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
|
|
242
|
-
if (
|
|
243
|
-
packageJson.name !== '@nestjs/cli' ||
|
|
244
|
-
packageJson.version !== EXPECTED_NEST_CLI_VERSION
|
|
245
|
-
) {
|
|
246
|
-
throw new Error(
|
|
247
|
-
`MIAODA_NEST_CLI_VERSION_MISMATCH: expected=${EXPECTED_NEST_CLI_VERSION} actual=${packageJson.version}`
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
let binSource = fs.readFileSync(binFile, 'utf8');
|
|
252
|
-
let startAction = fs.readFileSync(startActionFile, 'utf8');
|
|
253
|
-
let buildAction = fs.readFileSync(buildActionFile, 'utf8');
|
|
254
|
-
let watchCompiler = fs.readFileSync(watchCompilerFile, 'utf8');
|
|
255
|
-
const instrumentationSource = fs.existsSync(instrumentationFile)
|
|
256
|
-
? fs.readFileSync(instrumentationFile, 'utf8')
|
|
257
|
-
: '';
|
|
258
|
-
const previousStartupPatchComplete =
|
|
259
|
-
startAction.includes('miaoda-startup-instrumentation') &&
|
|
260
|
-
binSource.includes('miaoda-startup-instrumentation') &&
|
|
261
|
-
binSource.includes("'nest_cli_start'") &&
|
|
262
|
-
startAction.includes("'nest_compile_end'") &&
|
|
263
|
-
startAction.includes("'nest_child_spawn'") &&
|
|
264
|
-
buildAction.includes('miaoda-startup-instrumentation') &&
|
|
265
|
-
buildAction.includes("'nest_compile_start'") &&
|
|
266
|
-
fs.existsSync(instrumentationFile) &&
|
|
267
|
-
fs.existsSync(listenReadyFile);
|
|
268
|
-
const fullyPatched =
|
|
269
|
-
previousStartupPatchComplete &&
|
|
270
|
-
hasEarlyWatchHostInstrumentation(watchCompiler) &&
|
|
271
|
-
instrumentationSource.includes('tsbuildinfo_read');
|
|
272
|
-
if (fullyPatched) return { status: 'already-patched' };
|
|
273
|
-
if (
|
|
274
|
-
!previousStartupPatchComplete &&
|
|
275
|
-
(binSource.includes('miaoda-startup-instrumentation') ||
|
|
276
|
-
startAction.includes('miaoda-startup-instrumentation') ||
|
|
277
|
-
buildAction.includes('miaoda-startup-instrumentation') ||
|
|
278
|
-
watchCompiler.includes('miaoda-startup-instrumentation') ||
|
|
279
|
-
fs.existsSync(instrumentationFile) ||
|
|
280
|
-
fs.existsSync(listenReadyFile))
|
|
281
|
-
) {
|
|
282
|
-
throw new Error('MIAODA_NEST_CLI_PATCH_PARTIAL');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (previousStartupPatchComplete) {
|
|
286
|
-
if (!hasEarlyWatchHostInstrumentation(watchCompiler)) {
|
|
287
|
-
watchCompiler = patchWatchCompilerSource(watchCompiler);
|
|
288
|
-
fs.writeFileSync(watchCompilerFile, watchCompiler);
|
|
289
|
-
}
|
|
290
|
-
fs.writeFileSync(instrumentationFile, instrumentationRuntimeSource(), {
|
|
291
|
-
mode: 0o644,
|
|
292
|
-
});
|
|
293
|
-
return { status: 'patched', version: EXPECTED_NEST_CLI_VERSION };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
binSource = replaceOnce(
|
|
297
|
-
binSource,
|
|
298
|
-
'const commander = require("commander");',
|
|
299
|
-
'const miaoda_startup_instrumentation_1 = require("../lib/utils/miaoda-startup-instrumentation");\n(0, miaoda_startup_instrumentation_1.emitNestCliPhase)(\'nest_cli_start\', { command: process.argv[2] || \'help\' });\nconst commander = require("commander");',
|
|
300
|
-
'cli-start'
|
|
301
|
-
);
|
|
302
|
-
startAction = replaceOnce(
|
|
303
|
-
startAction,
|
|
304
|
-
'const build_action_1 = require("./build.action");',
|
|
305
|
-
'const build_action_1 = require("./build.action");\nconst miaoda_startup_instrumentation_1 = require("../lib/utils/miaoda-startup-instrumentation");',
|
|
306
|
-
'start-import'
|
|
307
|
-
);
|
|
308
|
-
startAction = replaceOnce(
|
|
309
|
-
startAction,
|
|
310
|
-
' return () => {\n if (childProcessRef) {',
|
|
311
|
-
" return () => {\n (0, miaoda_startup_instrumentation_1.emitNestCliPhase)('nest_compile_end');\n if (childProcessRef) {",
|
|
312
|
-
'compile-end'
|
|
313
|
-
);
|
|
314
|
-
startAction = replaceOnce(
|
|
315
|
-
startAction,
|
|
316
|
-
` return (0, child_process_1.spawn)(binaryToRun, processArgs, {
|
|
317
|
-
stdio: 'inherit',
|
|
318
|
-
shell: true,
|
|
319
|
-
});`,
|
|
320
|
-
` const childProcessRef = (0, child_process_1.spawn)(binaryToRun, processArgs, {
|
|
321
|
-
stdio: 'inherit',
|
|
322
|
-
shell: true,
|
|
323
|
-
env: (0, miaoda_startup_instrumentation_1.createInstrumentedChildEnv)(process.env),
|
|
324
|
-
});
|
|
325
|
-
(0, miaoda_startup_instrumentation_1.emitNestCliPhase)('nest_child_spawn', {
|
|
326
|
-
childPid: childProcessRef.pid,
|
|
327
|
-
entry: outputFilePath,
|
|
328
|
-
binary: binaryToRun,
|
|
329
|
-
});
|
|
330
|
-
return childProcessRef;`,
|
|
331
|
-
'child-spawn'
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
buildAction = replaceOnce(
|
|
335
|
-
buildAction,
|
|
336
|
-
'const abstract_action_1 = require("./abstract.action");',
|
|
337
|
-
'const abstract_action_1 = require("./abstract.action");\nconst miaoda_startup_instrumentation_1 = require("../lib/utils/miaoda-startup-instrumentation");',
|
|
338
|
-
'build-import'
|
|
339
|
-
);
|
|
340
|
-
buildAction = replaceOnce(
|
|
341
|
-
buildAction,
|
|
342
|
-
' switch (builder.type) {',
|
|
343
|
-
` (0, miaoda_startup_instrumentation_1.emitNestCliPhase)('nest_compile_start', {
|
|
344
|
-
builder: builder.type,
|
|
345
|
-
watch: watchMode,
|
|
346
|
-
tsconfig: pathToTsconfig,
|
|
347
|
-
outDir,
|
|
348
|
-
});
|
|
349
|
-
switch (builder.type) {`,
|
|
350
|
-
'compile-start'
|
|
351
|
-
);
|
|
352
|
-
watchCompiler = patchWatchCompilerSource(watchCompiler);
|
|
353
|
-
|
|
354
|
-
fs.writeFileSync(binFile, binSource);
|
|
355
|
-
fs.writeFileSync(startActionFile, startAction);
|
|
356
|
-
fs.writeFileSync(buildActionFile, buildAction);
|
|
357
|
-
fs.writeFileSync(watchCompilerFile, watchCompiler);
|
|
358
|
-
fs.writeFileSync(instrumentationFile, instrumentationRuntimeSource(), {
|
|
359
|
-
mode: 0o644,
|
|
360
|
-
});
|
|
361
|
-
fs.writeFileSync(listenReadyFile, listenReadyRuntimeSource(), {
|
|
362
|
-
mode: 0o644,
|
|
363
|
-
});
|
|
364
|
-
return { status: 'patched', version: EXPECTED_NEST_CLI_VERSION };
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
const invokedFile = process.argv[1]
|
|
368
|
-
? fs.realpathSync(process.argv[1])
|
|
369
|
-
: undefined;
|
|
370
|
-
if (invokedFile === fs.realpathSync(fileURLToPath(import.meta.url))) {
|
|
371
|
-
const installRoot = process.argv[2];
|
|
372
|
-
if (!installRoot) throw new Error('MIAODA_NEST_CLI_INSTALL_ROOT_REQUIRED');
|
|
373
|
-
process.stdout.write(
|
|
374
|
-
`${JSON.stringify(patchNestCliInstallation(installRoot))}\n`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import crypto from 'node:crypto';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
|
|
7
|
-
const runtimeRoot = path.resolve(
|
|
8
|
-
process.argv[2] ||
|
|
9
|
-
process.env.MIAODA_PLATFORM_RUNTIME_ROOT ||
|
|
10
|
-
'/opt/miaoda/preview-runtime'
|
|
11
|
-
);
|
|
12
|
-
const vitePackageFile = path.join(
|
|
13
|
-
runtimeRoot,
|
|
14
|
-
'node_modules',
|
|
15
|
-
'vite',
|
|
16
|
-
'package.json'
|
|
17
|
-
);
|
|
18
|
-
const targetFile = path.join(
|
|
19
|
-
runtimeRoot,
|
|
20
|
-
'node_modules',
|
|
21
|
-
'vite',
|
|
22
|
-
'dist',
|
|
23
|
-
'node',
|
|
24
|
-
'chunks',
|
|
25
|
-
'node.js'
|
|
26
|
-
);
|
|
27
|
-
const markerFile = path.join(
|
|
28
|
-
runtimeRoot,
|
|
29
|
-
'.miaoda-vite-dependency-hash-patch.json'
|
|
30
|
-
);
|
|
31
|
-
const expectedVersion = '8.1.5';
|
|
32
|
-
const patchId = 'vite-8.1.5-cache-runtime-identity-v4';
|
|
33
|
-
const expectedTargetSha256 =
|
|
34
|
-
'3968fab97a9d0882f0e2e754bd0d5b417f0a84596e293179a9a6e585919f041c';
|
|
35
|
-
const originalLockfileHash = `function getLockfileHash(environment) {
|
|
36
|
-
\tconst lockfilePath = lookupFile(environment.config.root, lockfilePaths);`;
|
|
37
|
-
const patchedLockfileHash = `function getLockfileHash(environment) {
|
|
38
|
-
\tconst miaodaClientDependencyGraphHash = process.env.MIAODA_VITE_DEPENDENCY_GRAPH_HASH;
|
|
39
|
-
\tif (miaodaClientDependencyGraphHash !== void 0) {
|
|
40
|
-
\t\tif (!/^[a-f0-9]{64}$/.test(miaodaClientDependencyGraphHash)) throw new Error("MIAODA_VITE_DEPENDENCY_GRAPH_HASH_INVALID");
|
|
41
|
-
\t\treturn miaodaClientDependencyGraphHash;
|
|
42
|
-
\t}
|
|
43
|
-
\tconst lockfilePath = lookupFile(environment.config.root, lockfilePaths);`;
|
|
44
|
-
const originalConfigBundleRoot = `async function bundleConfigFile(fileName, isESM) {
|
|
45
|
-
\tlet importMetaResolverRegistered = false;
|
|
46
|
-
\tconst root = path.dirname(fileName);
|
|
47
|
-
\tconst dirnameVarName = "__vite_injected_original_dirname";`;
|
|
48
|
-
const patchedConfigBundleRoot = `async function bundleConfigFile(fileName, isESM) {
|
|
49
|
-
\tlet importMetaResolverRegistered = false;
|
|
50
|
-
\tconst root = path.dirname(fileName);
|
|
51
|
-
\tlet miaodaConfigPackageAliases = Object.create(null);
|
|
52
|
-
\tconst miaodaConfigPackageAliasesRaw = process.env.MIAODA_VITE_CONFIG_PACKAGE_ALIASES;
|
|
53
|
-
\tconst miaodaConfigRuntimeRoot = process.env.MIAODA_PLATFORM_RUNTIME_ROOT;
|
|
54
|
-
\tif (process.env.MIAODA_CACHE_BOOTSTRAP_ENABLED === "true" && miaodaConfigPackageAliasesRaw !== void 0) {
|
|
55
|
-
\t\ttry {
|
|
56
|
-
\t\t\tconst parsed = JSON.parse(miaodaConfigPackageAliasesRaw);
|
|
57
|
-
\t\t\tconst realRuntimeRoot = fs.realpathSync(miaodaConfigRuntimeRoot);
|
|
58
|
-
\t\t\tif (!parsed || Array.isArray(parsed) || typeof parsed !== "object") throw new Error("aliases");
|
|
59
|
-
\t\t\tfor (const [packageName, entry] of Object.entries(parsed)) {
|
|
60
|
-
\t\t\t\tif (!/^(?:@[a-z0-9][a-z0-9._~-]*\\/)?[a-z0-9][a-z0-9._~-]*$/i.test(packageName) || typeof entry !== "string" || !path.isAbsolute(entry)) throw new Error("entry");
|
|
61
|
-
\t\t\t\tconst realEntry = fs.realpathSync(entry);
|
|
62
|
-
\t\t\t\tconst relative = path.relative(realRuntimeRoot, realEntry);
|
|
63
|
-
\t\t\t\tif (relative === ".." || relative.startsWith(\`..\${path.sep}\`) || path.isAbsolute(relative)) throw new Error("path");
|
|
64
|
-
\t\t\t\tmiaodaConfigPackageAliases[packageName] = realEntry;
|
|
65
|
-
\t\t\t}
|
|
66
|
-
\t\t} catch {
|
|
67
|
-
\t\t\tthrow new Error("MIAODA_VITE_CONFIG_PACKAGE_ALIASES_INVALID");
|
|
68
|
-
\t\t}
|
|
69
|
-
\t}
|
|
70
|
-
\tconst dirnameVarName = "__vite_injected_original_dirname";`;
|
|
71
|
-
const originalConfigPackageResolve = `idFsPath = nodeResolveWithVite(id, importer, {`;
|
|
72
|
-
const patchedConfigPackageResolve = `idFsPath = Object.hasOwn(miaodaConfigPackageAliases, id) ? miaodaConfigPackageAliases[id] : nodeResolveWithVite(id, importer, {`;
|
|
73
|
-
|
|
74
|
-
for (const file of [vitePackageFile, targetFile]) {
|
|
75
|
-
if (!fs.existsSync(file)) {
|
|
76
|
-
throw new Error(`MIAODA_VITE_PATCH_INPUT_MISSING: ${file}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const vitePackage = JSON.parse(fs.readFileSync(vitePackageFile, 'utf8'));
|
|
80
|
-
if (vitePackage.version !== expectedVersion) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
`MIAODA_VITE_PATCH_VERSION_MISMATCH: expected=${expectedVersion} actual=${vitePackage.version}`
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
let source = fs.readFileSync(targetFile, 'utf8');
|
|
87
|
-
const alreadyPatched =
|
|
88
|
-
source.includes(patchedLockfileHash) &&
|
|
89
|
-
source.includes(patchedConfigBundleRoot) &&
|
|
90
|
-
source.includes(patchedConfigPackageResolve);
|
|
91
|
-
if (alreadyPatched) {
|
|
92
|
-
if (
|
|
93
|
-
source.includes(originalLockfileHash) ||
|
|
94
|
-
source.includes(originalConfigBundleRoot) ||
|
|
95
|
-
source.includes(originalConfigPackageResolve)
|
|
96
|
-
) {
|
|
97
|
-
throw new Error('MIAODA_VITE_PATCH_STATE_INVALID');
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
const targetSha256 = crypto.createHash('sha256').update(source).digest('hex');
|
|
101
|
-
if (targetSha256 !== expectedTargetSha256) {
|
|
102
|
-
throw new Error(
|
|
103
|
-
`MIAODA_VITE_PATCH_TARGET_SHA_MISMATCH: expected=${expectedTargetSha256} actual=${targetSha256}`
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
const sentinels = [
|
|
107
|
-
originalLockfileHash,
|
|
108
|
-
originalConfigBundleRoot,
|
|
109
|
-
originalConfigPackageResolve,
|
|
110
|
-
];
|
|
111
|
-
if (sentinels.some(sentinel => source.split(sentinel).length - 1 !== 1)) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
'MIAODA_VITE_PATCH_SENTINEL_MISMATCH: expected exactly one of each sentinel'
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
source = source
|
|
117
|
-
.replace(originalLockfileHash, patchedLockfileHash)
|
|
118
|
-
.replace(originalConfigBundleRoot, patchedConfigBundleRoot)
|
|
119
|
-
.replace(originalConfigPackageResolve, patchedConfigPackageResolve);
|
|
120
|
-
fs.writeFileSync(targetFile, source);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const targetSha256 = crypto
|
|
124
|
-
.createHash('sha256')
|
|
125
|
-
.update(fs.readFileSync(targetFile))
|
|
126
|
-
.digest('hex');
|
|
127
|
-
fs.writeFileSync(
|
|
128
|
-
markerFile,
|
|
129
|
-
`${JSON.stringify(
|
|
130
|
-
{
|
|
131
|
-
schemaVersion: 1,
|
|
132
|
-
patchId,
|
|
133
|
-
viteVersion: expectedVersion,
|
|
134
|
-
target: 'node_modules/vite/dist/node/chunks/node.js',
|
|
135
|
-
targetSha256,
|
|
136
|
-
},
|
|
137
|
-
null,
|
|
138
|
-
2
|
|
139
|
-
)}\n`
|
|
140
|
-
);
|
|
141
|
-
process.stdout.write(`${patchId}\n`);
|