@lark-apaas/fullstack-cli 1.1.59-alpha.3 → 1.1.59
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/dist/index.js +162 -37
- package/package.json +1 -1
- package/templates/.spark_project +2 -2
- package/templates/nest-cli.json +1 -5
- package/templates/scripts/dev-local.js +113 -0
- package/templates/scripts/dev.js +18 -238
- package/templates/scripts/dev.sh +23 -1
- package/templates/scripts/lint.js +51 -16
- package/templates/scripts/prune-smart.js +41 -1
- package/templates/scripts/preview-startup-timing.cjs +0 -377
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const net = require('net');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const PREFIX = '[MiaodaPreviewPhase] ';
|
|
8
|
-
const RUN_ID_PATTERN = /^prv_[A-Za-z0-9_-]{1,128}$/;
|
|
9
|
-
|
|
10
|
-
function resolveAppId(env) {
|
|
11
|
-
const basePath = env.CLIENT_BASE_PATH || '';
|
|
12
|
-
const match = /^\/(?:app|af\/p)\/([^/]+)/.exec(basePath);
|
|
13
|
-
return match ? match[1] : env.MIAODA_APP_ID || '';
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createPreviewPhaseReporter(options = {}) {
|
|
17
|
-
const env = options.env || process.env;
|
|
18
|
-
const write = options.write || (line => process.stdout.write(`${line}\n`));
|
|
19
|
-
const now = options.now || Date.now;
|
|
20
|
-
|
|
21
|
-
function emit(phase, status, detail = {}) {
|
|
22
|
-
const runId = env.MIAODA_PREVIEW_RUN_ID || '';
|
|
23
|
-
if (!RUN_ID_PATTERN.test(runId)) return false;
|
|
24
|
-
const atMs = detail.at_ms == null ? now() : detail.at_ms;
|
|
25
|
-
const event = {
|
|
26
|
-
...detail,
|
|
27
|
-
schema_version: 1,
|
|
28
|
-
run_id: runId,
|
|
29
|
-
app_id: resolveAppId(env),
|
|
30
|
-
sandbox_id: env.SANDBOX_ID || '',
|
|
31
|
-
phase,
|
|
32
|
-
status,
|
|
33
|
-
at_ms: atMs,
|
|
34
|
-
};
|
|
35
|
-
write(`${PREFIX}${JSON.stringify(event)}`);
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return { emit };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function createTscServerCommand(reason, missingModules = []) {
|
|
43
|
-
return {
|
|
44
|
-
command: 'npm',
|
|
45
|
-
args: ['run', 'dev:server', '--', '--builder', 'tsc'],
|
|
46
|
-
compiler: 'tsc',
|
|
47
|
-
type_check: true,
|
|
48
|
-
type_check_mode: 'compiler_integrated',
|
|
49
|
-
type_check_command: null,
|
|
50
|
-
reason,
|
|
51
|
-
missing_modules: missingModules,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function normalizeTcpPort(value, fallback, name = 'TCP port') {
|
|
56
|
-
const candidate = value == null || value === '' ? fallback : value;
|
|
57
|
-
const serialized = String(candidate);
|
|
58
|
-
if (!/^\d+$/.test(serialized)) {
|
|
59
|
-
throw new Error(`${name} must be a valid TCP port`);
|
|
60
|
-
}
|
|
61
|
-
const port = Number(serialized);
|
|
62
|
-
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
63
|
-
throw new Error(`${name} must be a valid TCP port`);
|
|
64
|
-
}
|
|
65
|
-
return port;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function buildListeningPortLookupArgs(port) {
|
|
69
|
-
const validatedPort = normalizeTcpPort(port, 0);
|
|
70
|
-
return [`-tiTCP:${validatedPort}`, '-sTCP:LISTEN'];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function hasRuntimeCompilerPluginConsumer(projectRoot, sourceRoot) {
|
|
74
|
-
const root = path.resolve(projectRoot, sourceRoot || 'src');
|
|
75
|
-
const stack = [root];
|
|
76
|
-
let scannedFiles = 0;
|
|
77
|
-
let scannedBytes = 0;
|
|
78
|
-
const maxFiles = 2000;
|
|
79
|
-
const maxBytes = 16 * 1024 * 1024;
|
|
80
|
-
const consumerPatterns = [
|
|
81
|
-
/\bSwaggerModule\s*\.\s*(?:createDocument|loadPluginMetadata)\b/,
|
|
82
|
-
/\bcreateDocument\s*\(/,
|
|
83
|
-
/\bloadPluginMetadata\s*\(/,
|
|
84
|
-
/\bDevTools(?:V2)?Module\s*\.\s*mount\b/,
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
if (!fs.existsSync(root)) return true;
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
while (stack.length > 0) {
|
|
91
|
-
const current = stack.pop();
|
|
92
|
-
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
93
|
-
if (entry.isDirectory()) {
|
|
94
|
-
if (!['node_modules', 'dist', '.git'].includes(entry.name)) {
|
|
95
|
-
stack.push(path.join(current, entry.name));
|
|
96
|
-
}
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (!entry.isFile() || !/\.(?:[cm]?js|tsx?)$/.test(entry.name)) {
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
scannedFiles += 1;
|
|
103
|
-
const filePath = path.join(current, entry.name);
|
|
104
|
-
const stat = fs.statSync(filePath);
|
|
105
|
-
scannedBytes += stat.size;
|
|
106
|
-
if (scannedFiles > maxFiles || scannedBytes > maxBytes) return true;
|
|
107
|
-
const source = fs.readFileSync(filePath, 'utf8');
|
|
108
|
-
if (consumerPatterns.some(pattern => pattern.test(source))) return true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} catch {
|
|
112
|
-
// If we cannot prove the compiler plugin is unobservable at runtime, keep TSC.
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function resolvePreviewServerCommand(options = {}) {
|
|
119
|
-
const projectRoot = options.project_root || path.resolve(__dirname, '..');
|
|
120
|
-
let packageJson = options.package_json;
|
|
121
|
-
if (!packageJson) {
|
|
122
|
-
try {
|
|
123
|
-
packageJson = JSON.parse(
|
|
124
|
-
fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8')
|
|
125
|
-
);
|
|
126
|
-
} catch {
|
|
127
|
-
packageJson = {};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
let nestCliConfig = options.nest_cli_config;
|
|
132
|
-
if (!nestCliConfig) {
|
|
133
|
-
try {
|
|
134
|
-
nestCliConfig = JSON.parse(
|
|
135
|
-
fs.readFileSync(path.join(projectRoot, 'nest-cli.json'), 'utf8')
|
|
136
|
-
);
|
|
137
|
-
} catch {
|
|
138
|
-
nestCliConfig = {};
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const serverScript = packageJson?.scripts?.['dev:server'] || '';
|
|
143
|
-
const normalizedServerScript = serverScript
|
|
144
|
-
.trim()
|
|
145
|
-
.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*/, '')
|
|
146
|
-
.replace(/\s+/g, ' ');
|
|
147
|
-
const isNestWatchScript = normalizedServerScript === 'nest start --watch';
|
|
148
|
-
if (!isNestWatchScript) {
|
|
149
|
-
return {
|
|
150
|
-
command: 'npm',
|
|
151
|
-
args: ['run', 'dev:server'],
|
|
152
|
-
compiler: 'configured',
|
|
153
|
-
type_check: false,
|
|
154
|
-
type_check_mode: 'configured',
|
|
155
|
-
type_check_command: null,
|
|
156
|
-
reason: 'custom_server_script',
|
|
157
|
-
missing_modules: [],
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const configuredBuilder = nestCliConfig?.compilerOptions?.builder;
|
|
162
|
-
if (configuredBuilder && configuredBuilder !== 'tsc') {
|
|
163
|
-
return {
|
|
164
|
-
command: 'npm',
|
|
165
|
-
args: ['run', 'dev:server'],
|
|
166
|
-
compiler: 'configured',
|
|
167
|
-
type_check: false,
|
|
168
|
-
type_check_mode: 'configured',
|
|
169
|
-
type_check_command: null,
|
|
170
|
-
reason: 'custom_nest_builder',
|
|
171
|
-
missing_modules: [],
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const serverTypecheckScript =
|
|
176
|
-
packageJson?.scripts?.['type:check:server'] || '';
|
|
177
|
-
const normalizedTypecheckScript = serverTypecheckScript
|
|
178
|
-
.trim()
|
|
179
|
-
.replace(/^(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*/, '')
|
|
180
|
-
.replace(/\s+/g, ' ');
|
|
181
|
-
const isNonEmittingTscScript =
|
|
182
|
-
/^(?:npx )?tsc --noEmit --project [A-Za-z0-9_./-]+$/.test(
|
|
183
|
-
normalizedTypecheckScript
|
|
184
|
-
);
|
|
185
|
-
if (!isNonEmittingTscScript) {
|
|
186
|
-
return createTscServerCommand('server_typecheck_script_unavailable');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const previewCapability = nestCliConfig?.['x-miaoda-preview'];
|
|
190
|
-
if (
|
|
191
|
-
previewCapability?.compiler !== 'swc' ||
|
|
192
|
-
previewCapability?.typeCheck !== 'deferred'
|
|
193
|
-
) {
|
|
194
|
-
return createTscServerCommand('preview_swc_capability_not_declared');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const compilerPlugins = Array.isArray(nestCliConfig?.compilerOptions?.plugins)
|
|
198
|
-
? nestCliConfig.compilerOptions.plugins
|
|
199
|
-
: [];
|
|
200
|
-
const compilerPluginNames = compilerPlugins.map(plugin =>
|
|
201
|
-
typeof plugin === 'string' ? plugin : plugin?.name || ''
|
|
202
|
-
);
|
|
203
|
-
if (
|
|
204
|
-
compilerPluginNames.some(pluginName => pluginName !== '@nestjs/swagger')
|
|
205
|
-
) {
|
|
206
|
-
return createTscServerCommand('unsupported_nest_compiler_plugins');
|
|
207
|
-
}
|
|
208
|
-
if (compilerPluginNames.includes('@nestjs/swagger')) {
|
|
209
|
-
const runtimeConsumer =
|
|
210
|
-
typeof options.has_runtime_compiler_plugin_consumer === 'boolean'
|
|
211
|
-
? options.has_runtime_compiler_plugin_consumer
|
|
212
|
-
: hasRuntimeCompilerPluginConsumer(
|
|
213
|
-
projectRoot,
|
|
214
|
-
nestCliConfig?.sourceRoot || 'src'
|
|
215
|
-
);
|
|
216
|
-
if (runtimeConsumer) {
|
|
217
|
-
return createTscServerCommand(
|
|
218
|
-
'runtime_compiler_plugin_consumer_detected'
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const resolveModule =
|
|
224
|
-
options.resolve_module ||
|
|
225
|
-
(moduleName => {
|
|
226
|
-
const resolved = require.resolve(moduleName, { paths: [projectRoot] });
|
|
227
|
-
if (moduleName === '@swc/core') {
|
|
228
|
-
const swc = require(resolved);
|
|
229
|
-
if (typeof swc.transformSync !== 'function') {
|
|
230
|
-
throw new Error('@swc/core native binding is unavailable');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return resolved;
|
|
234
|
-
});
|
|
235
|
-
const requiredModules = ['@swc/cli', '@swc/core'];
|
|
236
|
-
const missingModules = requiredModules.filter(moduleName => {
|
|
237
|
-
try {
|
|
238
|
-
resolveModule(moduleName);
|
|
239
|
-
return false;
|
|
240
|
-
} catch {
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
if (missingModules.length > 0) {
|
|
246
|
-
return createTscServerCommand('swc_dependencies_missing', missingModules);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return {
|
|
250
|
-
command: 'npm',
|
|
251
|
-
args: ['run', 'dev:server', '--', '--builder', 'swc'],
|
|
252
|
-
compiler: 'swc',
|
|
253
|
-
type_check: true,
|
|
254
|
-
type_check_mode: 'deferred_tsc_watch',
|
|
255
|
-
type_check_command: {
|
|
256
|
-
command: 'npm',
|
|
257
|
-
args: [
|
|
258
|
-
'run',
|
|
259
|
-
'type:check:server',
|
|
260
|
-
'--',
|
|
261
|
-
'--watch',
|
|
262
|
-
'--preserveWatchOutput',
|
|
263
|
-
'--locale',
|
|
264
|
-
'en',
|
|
265
|
-
],
|
|
266
|
-
},
|
|
267
|
-
reason: 'swc_dependencies_ready',
|
|
268
|
-
missing_modules: [],
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function parseTscWatchSummary(line) {
|
|
273
|
-
const match = /Found\s+(\d+)\s+errors?\.\s+Watching for file changes\./i.exec(
|
|
274
|
-
line
|
|
275
|
-
);
|
|
276
|
-
if (!match) return null;
|
|
277
|
-
const errorCount = Number(match[1]);
|
|
278
|
-
return { error_count: errorCount, passed: errorCount === 0 };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function connectTcpOnce({ host, port, timeout_ms }) {
|
|
282
|
-
return new Promise(resolve => {
|
|
283
|
-
const socket = net.createConnection({ host, port });
|
|
284
|
-
let settled = false;
|
|
285
|
-
const finish = ready => {
|
|
286
|
-
if (settled) return;
|
|
287
|
-
settled = true;
|
|
288
|
-
socket.destroy();
|
|
289
|
-
resolve(ready);
|
|
290
|
-
};
|
|
291
|
-
socket.setTimeout(timeout_ms, () => finish(false));
|
|
292
|
-
socket.once('connect', () => finish(true));
|
|
293
|
-
socket.once('error', () => finish(false));
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async function waitForTcpReady(options) {
|
|
298
|
-
const hosts = options.host ? [options.host] : ['127.0.0.1', '::1'];
|
|
299
|
-
const port = Number(options.port);
|
|
300
|
-
const timeoutMs = options.timeout_ms == null ? 120000 : options.timeout_ms;
|
|
301
|
-
const intervalMs = options.interval_ms == null ? 50 : options.interval_ms;
|
|
302
|
-
const now = options.now || Date.now;
|
|
303
|
-
const sleep =
|
|
304
|
-
options.sleep || (ms => new Promise(resolve => setTimeout(resolve, ms)));
|
|
305
|
-
const connect = options.connect || connectTcpOnce;
|
|
306
|
-
const shouldContinue = options.should_continue || (() => true);
|
|
307
|
-
const startedAtMs =
|
|
308
|
-
options.started_at_ms == null ? now() : options.started_at_ms;
|
|
309
|
-
let attempts = 0;
|
|
310
|
-
|
|
311
|
-
while (now() - startedAtMs < timeoutMs) {
|
|
312
|
-
if (!shouldContinue()) {
|
|
313
|
-
const atMs = now();
|
|
314
|
-
return {
|
|
315
|
-
ready: false,
|
|
316
|
-
cancelled: true,
|
|
317
|
-
attempts,
|
|
318
|
-
at_ms: atMs,
|
|
319
|
-
duration_ms: atMs - startedAtMs,
|
|
320
|
-
precision_ms: intervalMs,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
attempts += 1;
|
|
324
|
-
const ready = (
|
|
325
|
-
await Promise.all(
|
|
326
|
-
hosts.map(host =>
|
|
327
|
-
connect({
|
|
328
|
-
host,
|
|
329
|
-
port,
|
|
330
|
-
timeout_ms: Math.max(1, Math.min(intervalMs, timeoutMs)),
|
|
331
|
-
})
|
|
332
|
-
)
|
|
333
|
-
)
|
|
334
|
-
).some(Boolean);
|
|
335
|
-
const atMs = now();
|
|
336
|
-
if (!shouldContinue()) {
|
|
337
|
-
return {
|
|
338
|
-
ready: false,
|
|
339
|
-
cancelled: true,
|
|
340
|
-
attempts,
|
|
341
|
-
at_ms: atMs,
|
|
342
|
-
duration_ms: atMs - startedAtMs,
|
|
343
|
-
precision_ms: intervalMs,
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
if (ready) {
|
|
347
|
-
return {
|
|
348
|
-
ready: true,
|
|
349
|
-
attempts,
|
|
350
|
-
at_ms: atMs,
|
|
351
|
-
duration_ms: atMs - startedAtMs,
|
|
352
|
-
precision_ms: intervalMs,
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
const elapsed = now() - startedAtMs;
|
|
356
|
-
if (elapsed >= timeoutMs) break;
|
|
357
|
-
await sleep(Math.min(intervalMs, timeoutMs - elapsed));
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const atMs = now();
|
|
361
|
-
return {
|
|
362
|
-
ready: false,
|
|
363
|
-
attempts,
|
|
364
|
-
at_ms: atMs,
|
|
365
|
-
duration_ms: atMs - startedAtMs,
|
|
366
|
-
precision_ms: intervalMs,
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
module.exports = {
|
|
371
|
-
buildListeningPortLookupArgs,
|
|
372
|
-
createPreviewPhaseReporter,
|
|
373
|
-
normalizeTcpPort,
|
|
374
|
-
parseTscWatchSummary,
|
|
375
|
-
resolvePreviewServerCommand,
|
|
376
|
-
waitForTcpReady,
|
|
377
|
-
};
|