Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@

// 必须放最前:Node 版本守卫,先于任何会用到全局 fetch 的模块求值
import './lib/preflight.js';
import { createProgram } from './lib/command.js';
import { logger } from './utils/logger.js';

// CLI 入口
const program = createProgram();
program.parseAsync(process.argv).catch((error) => {
logger.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
// CLI 入口依赖动态加载,确保低版本 Node 先执行 preflight 并给出可读提示
Promise.all([import('./lib/command.js'), import('./utils/logger.js')])
.then(async ([{ createProgram }, { logger }]) => {
const program = createProgram();
try {
await program.parseAsync(process.argv);
} catch (error) {
logger.error(error instanceof Error ? error.message : String(error));
process.exit(1);
}
})
.catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
4 changes: 2 additions & 2 deletions src/lib/preflight.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// 启动期前置检查:Node 版本守卫
// 必须在任何会用到全局 fetch 的模块求值之前执行
// 因为还在 i18n 初始化前,文案直接硬编码中英双语,避免循环依赖
import { writeSync } from 'node:fs';
import { writeSync } from 'fs';

const major = Number.parseInt(process.versions.node.split('.')[0] ?? '0', 10);
const major = Number.parseInt(process.versions.node.split('.')[0] || '0', 10);
const MIN_MAJOR = 18;

if (Number.isFinite(major) && major < MIN_MAJOR) {
Expand Down
21 changes: 21 additions & 0 deletions tests/preflight-compat.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import test from 'node:test';

test('preflight can be parsed by older Node before showing the version guard', () => {
const source = readFileSync(new URL('../dist/lib/preflight.js', import.meta.url), 'utf8');

assert.doesNotMatch(source, /\?\?/, 'preflight must not use nullish coalescing before the Node version guard runs');
assert.doesNotMatch(source, /\?\./, 'preflight must not use optional chaining before the Node version guard runs');
assert.doesNotMatch(source, /from ['"]node:/, 'preflight must not use node: imports before the Node version guard runs');
});

test('cli defers application imports until after preflight runs', () => {
const source = readFileSync(new URL('../dist/cli.js', import.meta.url), 'utf8');

assert.match(source, /import ['"]\.\/lib\/preflight\.js['"];/);
assert.doesNotMatch(source, /import \{ createProgram \} from ['"]\.\/lib\/command\.js['"];/);
assert.doesNotMatch(source, /import \{ logger \} from ['"]\.\/utils\/logger\.js['"];/);
assert.match(source, /import\(['"]\.\/lib\/command\.js['"]\)/);
assert.match(source, /import\(['"]\.\/utils\/logger\.js['"]\)/);
});
Loading