diff --git a/.gitattributes b/.gitattributes index 187387ba..631c2af3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,6 +8,13 @@ *.mjs text eol=lf *.js text eol=lf *.py text eol=lf +# Text the tests read and match line by line (CSS rules, HTML, data, docs): LF everywhere, so a Windows checkout +# (core.autocrlf=true) runs `node --test` exactly like Linux and macOS. +*.css text eol=lf +*.html text eol=lf +*.json text eol=lf +*.md text eol=lf +*.yml text eol=lf # binary assets *.png binary *.jpg binary diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57b62cfd..ec0ab57d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,9 +31,9 @@ jobs: RENDER_E2E: '0' NO_COLOR: '1' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version: ${{ matrix.node }} cache: npm @@ -46,8 +46,6 @@ jobs: - name: Unit & integration tests run: node --test - # Linux is the reference platform; Windows results are informative until the suite is proven there. - continue-on-error: ${{ matrix.os == 'windows-latest' }} - name: Server smoke test (boot, /healthz, index.html, doctor) shell: bash diff --git a/docs/DESIGN.md b/docs/DESIGN.md index c802f6c7..81526c32 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -27,7 +27,7 @@ Out of scope v1: matchmaking queue, training/tutorial, DIY (甄选) slots (the 4 ## 1. Tech stack -- **Node.js ≥ 18** (tested on 22), ESM (`"type": "module"`), single dependency `ws@8`. No bundler, no TypeScript. JSDoc types where helpful. +- **Node.js ≥ 22** (CI: 22 and 24), ESM (`"type": "module"`), single dependency `ws@8`. No bundler, no TypeScript. JSDoc types where helpful. - **Server-authoritative simulation.** Clients send *intents*; the server validates, mutates state and pushes state/snapshots. - **Client:** static files, native ES modules. Vendored libs in `public/vendor/`: `pixi.min.js` (PixiJS **7.4.2** UMD, global `PIXI`), `pixi-spine.js` (**4.0.6** UMD, `PIXI.spine`), `preact.module.js` + `hooks.module.js` + `htm.module.js` (Preact 10 + htm, no build step). No CDN at runtime (LAN play must work offline). - **Shared code** in `shared/` is imported by both server and browser (pure ESM, no Node APIs). diff --git a/package-lock.json b/package-lock.json index db54dded..b3f5f491 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "puppeteer-core": "^23.11.1" }, "engines": { - "node": ">=18" + "node": ">=22" } }, "node_modules/@pixi-spine/base": { diff --git a/package.json b/package.json index cc7454b1..8ee16974 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "launch": "node scripts/launch.mjs" }, "engines": { - "node": ">=18" + "node": ">=22" }, "dependencies": { "htm": "^3.1.1", diff --git a/scripts/install-service-windows.ps1 b/scripts/install-service-windows.ps1 index 14fa8434..6b11443e 100644 --- a/scripts/install-service-windows.ps1 +++ b/scripts/install-service-windows.ps1 @@ -118,7 +118,7 @@ try { if (-not $nodeCmd) { throw '未找到 Node.js:请先安装(winget install OpenJS.NodeJS.LTS),然后重新打开 PowerShell。' } $nodeExe = $nodeCmd.Source $major = [int](((& $nodeExe -v) -replace '^v', '').Split('.')[0]) - if ($major -lt 18) { throw "Node.js 版本太旧(需要 18+):$(& $nodeExe -v)" } + if ($major -lt 22) { throw "Node.js 版本太旧(需要 22+):$(& $nodeExe -v)" } if ($nodeExe -like "$env:USERPROFILE*") { Write-Host "注意:node.exe 位于用户目录($nodeExe,nvm/便携版?)。SYSTEM 账户一般也能运行它;若服务起不来,请改用 winget / 官网安装包安装的 Node.js。" -ForegroundColor Yellow } diff --git a/scripts/launch.mjs b/scripts/launch.mjs index 012d5bce..ab675e11 100755 --- a/scripts/launch.mjs +++ b/scripts/launch.mjs @@ -20,8 +20,8 @@ import fs from 'node:fs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const IS_WIN = process.platform === 'win32'; -if (Number(process.versions.node.split('.')[0]) < 18) { - console.error(`Node.js ${process.versions.node} 太旧,需要 18 或更高(推荐 22 LTS):https://nodejs.org/zh-cn/download`); +if (Number(process.versions.node.split('.')[0]) < 22) { + console.error(`Node.js ${process.versions.node} 太旧,需要 22 或更高(22 / 24 LTS):https://nodejs.org/zh-cn/download`); process.exit(1); } diff --git a/scripts/start-windows.bat b/scripts/start-windows.bat index fbbc935a..3aa4521b 100644 --- a/scripts/start-windows.bat +++ b/scripts/start-windows.bat @@ -10,7 +10,7 @@ cd /d "%~dp0.." where node >nul 2>nul if errorlevel 1 goto :nonode -node -e "process.exit(Number(process.versions.node.split('.')[0])>=18?0:1)" +node -e "process.exit(Number(process.versions.node.split('.')[0])>=22?0:1)" if errorlevel 1 goto :oldnode if not exist "node_modules\ws\package.json" ( @@ -25,7 +25,7 @@ exit /b 0 :nonode echo. -echo 未找到 Node.js(需要 18 或更高,推荐 22 LTS)。Node.js not found. +echo 未找到 Node.js(需要 22 或更高,22 / 24 LTS)。Node.js not found. echo. echo 方法一:在 PowerShell 或命令提示符中运行 echo winget install OpenJS.NodeJS.LTS @@ -38,7 +38,7 @@ exit /b 1 :oldnode echo. -for /f "delims=" %%v in ('node -v') do echo 当前 Node.js 版本 %%v 太旧,需要 18 或更高(推荐 22 LTS)。 +for /f "delims=" %%v in ('node -v') do echo 当前 Node.js 版本 %%v 太旧,需要 22 或更高(22 / 24 LTS)。 echo 升级:winget upgrade OpenJS.NodeJS.LTS 或 https://nodejs.org/zh-cn/download echo. pause diff --git a/scripts/start-windows.ps1 b/scripts/start-windows.ps1 index 8511a202..3ab1c44a 100644 --- a/scripts/start-windows.ps1 +++ b/scripts/start-windows.ps1 @@ -31,7 +31,7 @@ function Test-Node { $node = Test-Node if (-not $node) { - Write-Host '未找到 Node.js(需要 18 或更高,推荐 22 LTS)。' -ForegroundColor Yellow + Write-Host '未找到 Node.js(需要 22 或更高,22 / 24 LTS)。' -ForegroundColor Yellow $winget = Get-Command winget -ErrorAction SilentlyContinue if ($winget) { Write-Host '可以用 Windows 自带的 winget 安装: winget install OpenJS.NodeJS.LTS' @@ -49,8 +49,8 @@ if (-not $node) { Pause-Exit 1 } } -if ($node.Major -lt 18) { - Write-Host "Node.js $($node.Version) 太旧,需要 18 或更高:winget upgrade OpenJS.NodeJS.LTS 或 https://nodejs.org/zh-cn/download" -ForegroundColor Red +if ($node.Major -lt 22) { + Write-Host "Node.js $($node.Version) 太旧,需要 22 或更高:winget upgrade OpenJS.NodeJS.LTS 或 https://nodejs.org/zh-cn/download" -ForegroundColor Red Pause-Exit 1 } diff --git a/scripts/start.sh b/scripts/start.sh index d78bc8a7..6ad1a5ba 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash # 卫戍协议:盟约 · macOS / Linux start script. Docs: docs/DEPLOY.md # scripts/start.sh [--port 3001] [--no-open] [--no-local] [--no-assets] … (arguments go to scripts/launch.mjs) -# Checks Node.js ≥ 18, runs `npm ci` on the first run, then scripts/launch.mjs (tools/setup.mjs → server → browser). +# Checks Node.js ≥ 22, runs `npm ci` on the first run, then scripts/launch.mjs (tools/setup.mjs → server → browser). set -euo pipefail cd "$(dirname "$0")/.." if ! command -v node >/dev/null 2>&1; then - echo "未找到 Node.js(需要 18 或更高,推荐 22 LTS)。Node.js not found." + echo "未找到 Node.js(需要 22 或更高,22 / 24 LTS)。Node.js not found." if [ "$(uname -s)" = "Darwin" ]; then echo " 安装:brew install node@22 或 https://nodejs.org/zh-cn/download" else @@ -14,8 +14,8 @@ if ! command -v node >/dev/null 2>&1; then fi exit 1 fi -if ! node -e "process.exit(Number(process.versions.node.split('.')[0])>=18?0:1)"; then - echo "Node.js $(node -v) 太旧,需要 18 或更高(推荐 22 LTS):https://nodejs.org/zh-cn/download" +if ! node -e "process.exit(Number(process.versions.node.split('.')[0])>=22?0:1)"; then + echo "Node.js $(node -v) 太旧,需要 22 或更高(22 / 24 LTS):https://nodejs.org/zh-cn/download" exit 1 fi diff --git a/tools/doctor.mjs b/tools/doctor.mjs index 843ef89d..677a9d69 100755 --- a/tools/doctor.mjs +++ b/tools/doctor.mjs @@ -164,7 +164,7 @@ async function main() { section('运行环境'); const node = checkNode(); - row(node.ok ? (node.recommended ? 'ok' : 'warn') : 'err', 'Node.js', `v${node.version}` + (node.ok ? (node.recommended ? '' : '(推荐 22 LTS)') : `(需要 ≥ ${MIN_NODE}:https://nodejs.org/zh-cn/download)`)); + row(node.ok ? (node.recommended ? 'ok' : 'warn') : 'err', 'Node.js', `v${node.version}` + (node.ok ? (node.recommended ? '' : '(推荐 22 / 24 LTS)') : `(需要 ≥ ${MIN_NODE}:https://nodejs.org/zh-cn/download)`)); const npmV = tool(IS_WIN ? 'npm.cmd' : 'npm', ['--version']); row(npmV ? 'ok' : 'warn', 'npm', npmV ? `v${npmV}` : '未找到(安装 Node.js 时会自带)'); diff --git a/tools/setup.mjs b/tools/setup.mjs index 3c485846..b784fd50 100755 --- a/tools/setup.mjs +++ b/tools/setup.mjs @@ -4,7 +4,7 @@ // node tools/setup.mjs [options] // // Steps (each one is skipped when already done, so re-running is cheap — the start scripts run it on every start): -// 1. Node.js ≥ 18 check (clear message + download link otherwise). +// 1. Node.js ≥ 22 check (clear message + download link otherwise). // 2. Dependencies: `npm ci` (falls back to `npm install`) when node_modules is missing or incomplete. // 3. Client libraries in public/vendor (tools/vendor.mjs) when any is missing. // 4. Game data (data/*.json, committed) present and parseable. @@ -37,7 +37,7 @@ import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; export const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); -export const MIN_NODE = 18; +export const MIN_NODE = 22; export const IS_WIN = process.platform === 'win32'; export const IS_MAC = process.platform === 'darwin'; const NODE_URL = 'https://nodejs.org/zh-cn/download'; @@ -133,7 +133,7 @@ async function ask(question, def, timeoutMs = 60000) { /** Node.js version check. */ export function checkNode() { const major = nodeMajor(); - return { ok: major >= MIN_NODE, version: process.versions.node, major, recommended: major >= 20 }; + return { ok: major >= MIN_NODE, version: process.versions.node, major, recommended: major >= MIN_NODE }; } /** Runtime dependencies installed? */ @@ -353,11 +353,11 @@ async function main() { // 1. Node const node = checkNode(); if (!node.ok) { - log(`${mark.err} Node.js ${node.version} 太旧:需要 ${MIN_NODE} 或更高(推荐 22 LTS)。`); + log(`${mark.err} Node.js ${node.version} 太旧:需要 ${MIN_NODE} 或更高(22 / 24 LTS)。`); log(` 下载:${NODE_URL}` + (IS_WIN ? ' 或在终端运行:winget install OpenJS.NodeJS.LTS' : IS_MAC ? ' 或:brew install node@22' : '')); return 1; } - add('ok', 'Node.js', `v${node.version}${node.recommended ? '' : '(可用;推荐升级到 22 LTS)'}`); + add('ok', 'Node.js', `v${node.version}${node.recommended ? '' : '(可用;推荐 22 / 24 LTS)'}`); // 2. npm dependencies let deps = checkDeps();