From 258a2d21f6c1aca951b300384ee28a68d40c33c9 Mon Sep 17 00:00:00 2001 From: Citrons <909229731@qq.com> Date: Thu, 13 Aug 2026 15:54:07 +0800 Subject: [PATCH] =?UTF-8?q?build(GOV-009):=20=E5=BB=BA=E7=AB=8B=20Node=20?= =?UTF-8?q?=E4=B8=8E=E5=B7=A5=E4=BD=9C=E5=8C=BA=E5=9F=BA=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .npmrc | 3 ++ .prettierrc.json | 6 ++++ eslint.config.mjs | 5 +++ package.json | 18 ++++++++++- pnpm-lock.yaml | 24 ++++++++++++++ pnpm-workspace.yaml | 3 ++ tools/runtime/verify-runtime.mjs | 45 +++++++++++++++++++++++++++ tools/runtime/verify-runtime.test.mjs | 35 +++++++++++++++++++++ 8 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 .npmrc create mode 100644 .prettierrc.json create mode 100644 eslint.config.mjs create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tools/runtime/verify-runtime.mjs create mode 100644 tools/runtime/verify-runtime.test.mjs diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..ee990ba --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +engine-strict=true +save-exact=true +strict-peer-dependencies=true diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..e5ce635 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "semi": true +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..70fe3da --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,5 @@ +export default [ + { + ignores: ['**/dist/**', '**/coverage/**', '**/node_modules/**', '**/.local/**'], + }, +]; diff --git a/package.json b/package.json index 8861394..da40cc5 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,14 @@ "version": "0.0.0", "private": true, "type": "module", + "packageManager": "pnpm@10.25.0", + "engines": { + "node": "22.x", + "pnpm": "10.x" + }, "scripts": { + "check:runtime": "node tools/runtime/verify-runtime.mjs", + "test:runtime": "node --test tools/runtime/verify-runtime.test.mjs", "test:governance": "node --test tools/governance/feature-record.test.mjs", "test:mysql-baseline": "node --test tools/governance/mysql-baseline.test.mjs", "test:environment-baseline": "node --test tools/governance/environment-baseline.test.mjs", @@ -11,6 +18,15 @@ "schema:baseline": "node tools/database/schema-baseline.mjs generate", "schema:verify": "node tools/database/schema-baseline.mjs verify", "governance:start": "node tools/governance/start.mjs", - "governance:finish": "node tools/governance/finish.mjs" + "governance:finish": "node tools/governance/finish.mjs", + "lint": "pnpm --filter @a-share-analysis/web --if-present lint && pnpm --filter @a-share-analysis/server --if-present lint", + "format:check": "pnpm exec prettier --check .", + "typecheck": "pnpm --filter @a-share-analysis/web --if-present typecheck && pnpm --filter @a-share-analysis/server --if-present typecheck", + "test": "pnpm test:runtime && pnpm test:governance && pnpm test:mysql-baseline && pnpm test:environment-baseline && pnpm test:schema-baseline && pnpm --filter @a-share-analysis/web --if-present test && pnpm --filter @a-share-analysis/server --if-present test", + "build": "pnpm --filter @a-share-analysis/web --if-present build && pnpm --filter @a-share-analysis/server --if-present build", + "verify": "pnpm check:runtime && pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm build" + }, + "devDependencies": { + "prettier": "3.9.6" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..c165340 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,24 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + prettier: + specifier: 3.9.6 + version: 3.9.6 + +packages: + + prettier@3.9.6: + resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==} + engines: {node: '>=14'} + hasBin: true + +snapshots: + + prettier@3.9.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..07505e2 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - web + - server diff --git a/tools/runtime/verify-runtime.mjs b/tools/runtime/verify-runtime.mjs new file mode 100644 index 0000000..0aea949 --- /dev/null +++ b/tools/runtime/verify-runtime.mjs @@ -0,0 +1,45 @@ +import { execFileSync } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const VERSION_PATTERN = /^(?:v)?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/; + +function parseVersion(value, label) { + const normalized = String(value ?? '').trim(); + const match = normalized.match(VERSION_PATTERN); + if (!match) { + throw new Error(`${label} 版本格式无效:${normalized || '(空)'}`); + } + return Object.freeze({ raw: normalized.replace(/^v/, ''), major: Number(match[1]) }); +} + +/** + * 项目只验证开发机已经提供的运行时,不安装或切换 Node。严格固定主版本可以让 + * 本地源码运行、CI 和生产镜像使用同一语义,避免跨主版本的 ESM 与工具链差异。 + */ +export function assertSupportedRuntime({ nodeVersion, pnpmVersion }) { + const node = parseVersion(nodeVersion, 'Node.js'); + const pnpm = parseVersion(pnpmVersion, 'pnpm'); + if (node.major !== 22) { + throw new Error(`Node.js 必须为 22.x,实际 ${node.raw}`); + } + if (pnpm.major !== 10) { + throw new Error(`pnpm 必须为 10.x,实际 ${pnpm.raw}`); + } + return Object.freeze({ nodeVersion: node.raw, pnpmVersion: pnpm.raw }); +} + +function runCommandLine() { + const pnpmVersion = execFileSync('pnpm', ['--version'], { + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }).trim(); + const result = assertSupportedRuntime({ nodeVersion: process.version, pnpmVersion }); + process.stdout.write( + `运行时验证通过:Node.js ${result.nodeVersion},pnpm ${result.pnpmVersion}\n`, + ); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + runCommandLine(); +} diff --git a/tools/runtime/verify-runtime.test.mjs b/tools/runtime/verify-runtime.test.mjs new file mode 100644 index 0000000..8f12517 --- /dev/null +++ b/tools/runtime/verify-runtime.test.mjs @@ -0,0 +1,35 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { assertSupportedRuntime } from './verify-runtime.mjs'; + +test('只接受 Node 22 与 pnpm 10', () => { + assert.doesNotThrow(() => + assertSupportedRuntime({ nodeVersion: '22.11.0', pnpmVersion: '10.25.0' }), + ); + + for (const nodeVersion of ['20.20.2', '24.0.0']) { + assert.throws( + () => assertSupportedRuntime({ nodeVersion, pnpmVersion: '10.25.0' }), + new RegExp(`Node\\.js 必须为 22\\.x,实际 ${nodeVersion.replaceAll('.', '\\.')}`), + ); + } + + for (const pnpmVersion of ['9.15.0', '11.0.0']) { + assert.throws( + () => assertSupportedRuntime({ nodeVersion: '22.11.0', pnpmVersion }), + new RegExp(`pnpm 必须为 10\\.x,实际 ${pnpmVersion.replaceAll('.', '\\.')}`), + ); + } +}); + +test('拒绝无法解析的运行时版本', () => { + for (const runtime of [ + { nodeVersion: '', pnpmVersion: '10.25.0' }, + { nodeVersion: 'v22', pnpmVersion: '10.25.0' }, + { nodeVersion: '22.11.0', pnpmVersion: '' }, + { nodeVersion: '22.11.0', pnpmVersion: 'ten' }, + ]) { + assert.throws(() => assertSupportedRuntime(runtime), /版本格式无效/); + } +});