mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-30 01:51:20 +00:00
chore: compile first-party packages with TypeScript 7 (#3311)
Some checks are pending
CodeQL / Analyze (python) (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Some checks are pending
CodeQL / Analyze (python) (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
* fix(web): drop TypeScript 7-incompatible tsconfig paths Remove baseUrl and the dead ../shared include so web project references typecheck under TypeScript 7. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): parse TypeScript with a TypeScript 6 API package Keep AST guards working after the named typescript package becomes 7, which no longer ships the Compiler API. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(lint): pin root TypeScript to the 6 API package Give typescript-eslint a TypeScript 6 peer so syntax-only lint still installs after CLI and web move to TypeScript 7. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(deps): compile first-party packages with TypeScript 7.0.2 Unify CLI and web on the same native compiler line as gitnexus-shared so typecheck and emit no longer split 5.x versus 7.x. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(ci): describe parent TypeScript 7 as the shared compiler Stop saying web compiles shared with TypeScript 5 now that the parent lockfile is 7. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ci): compile shared from parent TypeScript on Vercel and skill-evolution Stop isolated npm installs in gitnexus-shared so those paths do not pull a second TypeScript 7 optional-platform tree. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: record TypeScript 7 typecheck and Dependabot major-split policy Keep contributor typecheck commands, and stop Dependabot from bumping shared onto a different TypeScript major than CLI and web. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(lint): pin root TypeScript to 5.9 so npm ci satisfies eslint peers typescript-eslint 8 peers typescript below 6.0.0, so the typescript6 alias made quality lint npm ci fail with ERESOLVE. Co-authored-by: Cursor <cursoragent@cursor.com> * test(cli): drop the TypeScript 6 Compiler API package TypeScript 7.0 has no classic createProgram surface, so parse-only guards now use Babel and Mode 4 uses the TypeScript 7 Checker. Co-authored-by: Cursor <cursoragent@cursor.com> * docs: align contributor setup with parent TypeScript 7 compile Stop telling clones to npm-install gitnexus-shared; CI and Vercel already emit that package from a parent lib/tsc.js shim. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): typecheck React JSX on TypeScript 7 with explicit DOM libs TypeScript 7 no longer implies DOM or auto-includes @types, so the web app must declare React/JSX settings while Vite keeps plugin-react. Co-authored-by: Cursor <cursoragent@cursor.com> * test: pin Vercel --include=dev and share parse-only string helpers Production npm ci omits the web TypeScript unless --include=dev is on that install. Move staticStringValue next to the other Babel walk helpers so CLI help and contract tests share one source. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(autofix): apply prettier + eslint fixes via /autofix command --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
a2e1710ac0
commit
56feb85c97
29 changed files with 1548 additions and 347 deletions
|
|
@ -19,8 +19,8 @@ runs:
|
|||
# Browsers are installed explicitly by e2e. Typecheck only needs types.
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
||||
|
||||
# Compile shared with the web package's TypeScript 5. Do not npm-ci
|
||||
# gitnexus-shared (TypeScript 7 optional-platform install, ~7 minutes).
|
||||
# Compile shared with the web package's TypeScript 7. Do not npm-ci
|
||||
# gitnexus-shared (a second optional-platform install, ~7 minutes).
|
||||
- name: Build gitnexus-shared
|
||||
# node + lib/tsc.js — same on Windows/macOS/Linux. Do not use .bin/tsc
|
||||
# (tsc.cmd on Windows; execFileSync cannot launch .cmd without a shell).
|
||||
|
|
|
|||
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
|
|
@ -93,6 +93,9 @@ updates:
|
|||
# The extension version is a separate upstream constant, not derivable
|
||||
# from the core version (see vendor/lbug-fts/manifest.json).
|
||||
- dependency-name: '@ladybugdb/core'
|
||||
- dependency-name: typescript
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
|
||||
# gitnexus-web (thin frontend client).
|
||||
- package-ecosystem: npm
|
||||
|
|
@ -111,6 +114,10 @@ updates:
|
|||
labels:
|
||||
- dependencies
|
||||
- frontend
|
||||
ignore:
|
||||
- dependency-name: typescript
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
|
||||
# Shared types package.
|
||||
- package-ecosystem: npm
|
||||
|
|
@ -128,3 +135,9 @@ updates:
|
|||
include: scope
|
||||
labels:
|
||||
- dependencies
|
||||
ignore:
|
||||
# Keep shared on the same TypeScript major as CLI/web. A shared-only
|
||||
# major bump reintroduces a compiler split this repo unified.
|
||||
- dependency-name: typescript
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
|
|
|
|||
18
.github/workflows/gitnexus-skill-evolution.yml
vendored
18
.github/workflows/gitnexus-skill-evolution.yml
vendored
|
|
@ -330,24 +330,20 @@ jobs:
|
|||
set -euo pipefail
|
||||
# The benchmark's task bindings sandbox-copy node_modules from the
|
||||
# monorepo root as well as gitnexus-shared and gitnexus (see the
|
||||
# sandbox_copy entries in tasks.scenarios.yaml). The two steps below
|
||||
# install the subpackage trees; the root tree needs its own install
|
||||
# or capture_task_dependency_binding aborts at task binding on the
|
||||
# missing root node_modules.
|
||||
# sandbox_dependencies entries in tasks.scenarios.yaml). gitnexus
|
||||
# npm ci plus build compiles shared through parent lib/tsc.js; do
|
||||
# not npm ci gitnexus-shared (second TypeScript 7 optional install).
|
||||
npm ci
|
||||
|
||||
- name: Build pinned shared runtime
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm ci
|
||||
npm run build
|
||||
working-directory: gitnexus-shared
|
||||
|
||||
- name: Install and build pinned GitNexus runtime
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm ci
|
||||
npm run build
|
||||
# Task YAML still mounts gitnexus-shared/node_modules. Keep an empty
|
||||
# directory so capture_task_dependency_binding does not abort, without
|
||||
# installing TypeScript 7 inside shared.
|
||||
mkdir -p ../gitnexus-shared/node_modules
|
||||
working-directory: gitnexus
|
||||
|
||||
- name: Point the benchmark task repo at the checkout
|
||||
|
|
|
|||
|
|
@ -16,14 +16,19 @@ This project uses the [PolyForm Noncommercial License 1.0.0](https://polyformpro
|
|||
**Prerequisites:** Node.js — `gitnexus/` requires `^22.18.0 || >=24.11.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version.
|
||||
|
||||
1. Clone the repository.
|
||||
2. **Shared package:** `cd gitnexus-shared && npm install && npm run build`
|
||||
3. **CLI / MCP package:** `cd ../gitnexus && npm install && npm run build`
|
||||
4. **Web UI (if needed):** `cd ../gitnexus-web && npm install`
|
||||
5. Run tests as described in [TESTING.md](TESTING.md).
|
||||
2. **CLI / MCP package:** `cd gitnexus && npm install && npm run build`
|
||||
`prepare` / `scripts/build.js` compiles `gitnexus-shared` with
|
||||
`node …/typescript/lib/tsc.js` from this package. Do not `npm install` or
|
||||
`npm ci` inside `gitnexus-shared/` — that is a second TypeScript 7
|
||||
optional-platform install and is not what `setup-gitnexus` does.
|
||||
3. **Web UI (if needed):** `cd gitnexus-web && npm install`
|
||||
If you skipped step 2, compile shared with the web compiler first:
|
||||
`cd gitnexus-shared && node ../gitnexus-web/node_modules/typescript/lib/tsc.js`
|
||||
4. Run tests as described in [TESTING.md](TESTING.md).
|
||||
|
||||
The CLI build imports `gitnexus-shared`, so a fresh clone must install and build
|
||||
the shared package before running `npm install` in `gitnexus/`. This is the same
|
||||
order used by the repository's `setup-gitnexus` CI action.
|
||||
The CLI build imports `gitnexus-shared`, so `gitnexus-shared/dist` must exist
|
||||
before `gitnexus` typecheck. That emit uses a parent package's TypeScript 7
|
||||
`lib/tsc.js`, matching `setup-gitnexus`, `setup-gitnexus-web`, and Vercel.
|
||||
|
||||
### Containerized development (optional)
|
||||
|
||||
|
|
@ -70,7 +75,7 @@ Commits within a PR may use any style — only the **merged PR title** shows up
|
|||
## Before you open a PR
|
||||
|
||||
- [ ] Tests pass for the packages you touched (`gitnexus` and/or `gitnexus-web`).
|
||||
- [ ] Typecheck passes: `npx tsc --noEmit` in `gitnexus/` and `npx tsc -b --noEmit` in `gitnexus-web/`.
|
||||
- [ ] Typecheck passes: `npx tsc --noEmit` in `gitnexus/` and `npx tsc -b --noEmit` in `gitnexus-web/`. Those commands use TypeScript 7. The web app tsconfig lists `lib` `DOM`/`DOM.Iterable` and `jsx: react-jsx` so React/JSX typecheck on 7; Vite/`vitest` keep `@vitejs/plugin-react` with the automatic JSX runtime. Build `gitnexus-shared/dist` first with a parent `lib/tsc.js` (a `gitnexus` install/`npm run build` does this). Repo ESLint stays syntax-only on a TypeScript 5.x peer until typescript-eslint supports 7.
|
||||
- [ ] No secrets, tokens, or machine-specific paths committed.
|
||||
- [ ] Documentation updated if behavior or public CLI/MCP contract changes.
|
||||
- [ ] Every new `GITNEXUS_*` environment variable has a row in the **Environment variables** table in [README.md](README.md) — variable, default, effect, and when to tune it.
|
||||
|
|
|
|||
6
DoD.md
6
DoD.md
|
|
@ -113,7 +113,7 @@ Run the commands relevant to the touched area. If something cannot be run in the
|
|||
|
||||
### 4.1 Build ordering
|
||||
|
||||
- [ ] `gitnexus-shared/` dist is built before consuming packages are typechecked or tested (CI uses the `setup-gitnexus` action for this — local runs must match).
|
||||
- [ ] `gitnexus-shared/` dist is built before consuming packages are typechecked or tested (CI uses the `setup-gitnexus` action, which compiles shared with the parent TypeScript 7 `lib/tsc.js` — local runs must match).
|
||||
|
||||
### 4.2 If `gitnexus/` changed
|
||||
|
||||
|
|
@ -123,13 +123,13 @@ Run the commands relevant to the touched area. If something cannot be run in the
|
|||
|
||||
### 4.3 If `gitnexus-web/` changed
|
||||
|
||||
- [ ] `cd gitnexus-web && npx tsc -b --noEmit`
|
||||
- [ ] `cd gitnexus-web && npx tsc -b --noEmit` (TypeScript 7 typechecks React/JSX: `jsx: react-jsx`, `lib` includes `DOM`)
|
||||
- [ ] `cd gitnexus-web && npm test`
|
||||
- [ ] `cd gitnexus-web && npm run test:e2e` when browser flows or user-facing UI behavior changed
|
||||
|
||||
### 4.4 If `gitnexus-shared/` changed
|
||||
|
||||
- [ ] Shared package builds cleanly (`npm run build` in `gitnexus-shared/`)
|
||||
- [ ] Shared package builds cleanly from a parent TypeScript 7 shim after that parent is installed (`cd gitnexus-shared && node ../gitnexus/node_modules/typescript/lib/tsc.js`, or `node ../gitnexus-web/node_modules/typescript/lib/tsc.js` after a web install). Do not `npm install` / `npm ci` in `gitnexus-shared/` for this check.
|
||||
- [ ] Dependent packages still typecheck and test after the shared change — verify both CLI and web consumers together
|
||||
|
||||
### 4.5 If CI workflows or release pipelines changed
|
||||
|
|
|
|||
|
|
@ -902,9 +902,10 @@ The web UI uses the same indexing pipeline as the CLI but runs entirely in WebAs
|
|||
|
||||
```bash
|
||||
git clone https://github.com/abhigyanpatwari/gitnexus.git
|
||||
cd gitnexus/gitnexus-shared && npm install && npm run build
|
||||
cd ../gitnexus-web && npm install
|
||||
npm run dev
|
||||
cd gitnexus/gitnexus-web && npm install
|
||||
# Compile sibling gitnexus-shared with this package's TypeScript 7 (do not npm ci shared).
|
||||
cd ../gitnexus-shared && node ../gitnexus-web/node_modules/typescript/lib/tsc.js
|
||||
cd ../gitnexus-web && npm run dev
|
||||
# Then in another terminal, start the backend the frontend connects to:
|
||||
npx gitnexus@latest serve
|
||||
```
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ From `gitnexus-web/`:
|
|||
### Before opening a PR
|
||||
|
||||
```bash
|
||||
# gitnexus-shared/dist must exist first. `npm install` / `npm run build` in
|
||||
# gitnexus/ compiles it via parent `lib/tsc.js` (do not npm ci gitnexus-shared).
|
||||
cd gitnexus && npx tsc --noEmit && npm test
|
||||
cd ../gitnexus-web && npx tsc -b --noEmit && npm test
|
||||
```
|
||||
|
|
|
|||
389
gitnexus-web/package-lock.json
generated
389
gitnexus-web/package-lock.json
generated
|
|
@ -62,7 +62,7 @@
|
|||
"@vitest/coverage-v8": "^4.1.11",
|
||||
"jsdom": "^30.0.1",
|
||||
"tree-sitter-wasms": "^0.1.13",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^7.0.2",
|
||||
"vite": "^8.1.5",
|
||||
"vitest": "^4.1.10",
|
||||
"wait-on": "^9.1.0"
|
||||
|
|
@ -2550,6 +2550,346 @@
|
|||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript/typescript-aix-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
|
||||
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-loong64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
|
||||
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-mips64el": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
|
||||
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-riscv64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
|
||||
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-s390x": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
|
||||
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-sunos-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ungap/structured-clone": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
|
||||
|
|
@ -2659,6 +2999,20 @@
|
|||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/node/node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@vercel/node/node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
|
|
@ -8038,17 +8392,38 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
||||
"integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
"tsc": "bin/tsc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
"node": ">=16.20.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript/typescript-aix-ppc64": "7.0.2",
|
||||
"@typescript/typescript-darwin-arm64": "7.0.2",
|
||||
"@typescript/typescript-darwin-x64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-x64": "7.0.2",
|
||||
"@typescript/typescript-linux-arm": "7.0.2",
|
||||
"@typescript/typescript-linux-arm64": "7.0.2",
|
||||
"@typescript/typescript-linux-loong64": "7.0.2",
|
||||
"@typescript/typescript-linux-mips64el": "7.0.2",
|
||||
"@typescript/typescript-linux-ppc64": "7.0.2",
|
||||
"@typescript/typescript-linux-riscv64": "7.0.2",
|
||||
"@typescript/typescript-linux-s390x": "7.0.2",
|
||||
"@typescript/typescript-linux-x64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-sunos-x64": "7.0.2",
|
||||
"@typescript/typescript-win32-arm64": "7.0.2",
|
||||
"@typescript/typescript-win32-x64": "7.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@
|
|||
"@vitest/coverage-v8": "^4.1.11",
|
||||
"jsdom": "^30.0.1",
|
||||
"tree-sitter-wasms": "^0.1.13",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^7.0.2",
|
||||
"vite": "^8.1.5",
|
||||
"vitest": "^4.1.10",
|
||||
"wait-on": "^9.1.0"
|
||||
|
|
|
|||
16
gitnexus-web/test/unit/tsconfig-react.test.ts
Normal file
16
gitnexus-web/test/unit/tsconfig-react.test.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const webRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
|
||||
|
||||
describe('TypeScript 7 + React frontend toolchain', () => {
|
||||
it('keeps Vite and Vitest on the automatic JSX runtime that matches react-jsx', () => {
|
||||
for (const configFile of ['vite.config.ts', 'vitest.config.ts'] as const) {
|
||||
const source = readFileSync(path.join(webRoot, configFile), 'utf8');
|
||||
expect(source).toContain('@vitejs/plugin-react');
|
||||
expect(source).toContain("jsxRuntime: 'automatic'");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1,26 +1,29 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./",
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"rootDir": "./src",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@shared/*": ["../shared/*"]
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["src", "../shared"],
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/*.test.ts", "src/**/*.test.tsx"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,15 @@
|
|||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "ESNext",
|
||||
"lib": ["ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"moduleDetection": "force",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"rootDir": ".",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"installCommand": "cd ../gitnexus-shared && npm install && npm run build && cd ../gitnexus-web && npm ci --include=dev"
|
||||
"installCommand": "npm ci --include=dev && cd ../gitnexus-shared && node ../gitnexus-web/node_modules/typescript/lib/tsc.js"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const _require = createRequire(import.meta.url);
|
|||
const gitnexusPkg = _require('../gitnexus/package.json');
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
plugins: [react({ jsxRuntime: 'automatic' }), tailwindcss()],
|
||||
define: {
|
||||
__REQUIRED_NODE_VERSION__: JSON.stringify(gitnexusPkg.engines.node.replace(/[>=^~\s]/g, '')),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const _require = createRequire(import.meta.url);
|
|||
const gitnexusPkg = _require('../gitnexus/package.json');
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [react({ jsxRuntime: 'automatic' })],
|
||||
define: {
|
||||
__REQUIRED_NODE_VERSION__: JSON.stringify(gitnexusPkg.engines.node.replace(/[>=^~\s]/g, '')),
|
||||
},
|
||||
|
|
|
|||
375
gitnexus/package-lock.json
generated
375
gitnexus/package-lock.json
generated
|
|
@ -68,7 +68,7 @@
|
|||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"gitnexus-shared": "file:../gitnexus-shared",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^7.0.2",
|
||||
"vitest": "^4.0.18"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -1331,6 +1331,346 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-aix-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-darwin-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-freebsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
|
||||
"integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-loong64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
|
||||
"integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-mips64el": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
|
||||
"integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-ppc64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
|
||||
"integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-riscv64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
|
||||
"integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-s390x": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
|
||||
"integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-linux-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-netbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-openbsd-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-sunos-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-arm64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
|
||||
"integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript/typescript-win32-x64": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
|
||||
"integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/coverage-v8": {
|
||||
"version": "4.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz",
|
||||
|
|
@ -4671,17 +5011,38 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
|
||||
"integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
"tsc": "bin/tsc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
"node": ">=16.20.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@typescript/typescript-aix-ppc64": "7.0.2",
|
||||
"@typescript/typescript-darwin-arm64": "7.0.2",
|
||||
"@typescript/typescript-darwin-x64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-freebsd-x64": "7.0.2",
|
||||
"@typescript/typescript-linux-arm": "7.0.2",
|
||||
"@typescript/typescript-linux-arm64": "7.0.2",
|
||||
"@typescript/typescript-linux-loong64": "7.0.2",
|
||||
"@typescript/typescript-linux-mips64el": "7.0.2",
|
||||
"@typescript/typescript-linux-ppc64": "7.0.2",
|
||||
"@typescript/typescript-linux-riscv64": "7.0.2",
|
||||
"@typescript/typescript-linux-s390x": "7.0.2",
|
||||
"@typescript/typescript-linux-x64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-netbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-arm64": "7.0.2",
|
||||
"@typescript/typescript-openbsd-x64": "7.0.2",
|
||||
"@typescript/typescript-sunos-x64": "7.0.2",
|
||||
"@typescript/typescript-win32-arm64": "7.0.2",
|
||||
"@typescript/typescript-win32-x64": "7.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/typical": {
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@
|
|||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"gitnexus-shared": "file:../gitnexus-shared",
|
||||
"tsx": "^4.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript": "^7.0.2",
|
||||
"vitest": "^4.0.18"
|
||||
},
|
||||
"overrides": {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ if (!fs.existsSync(SHARED_ROOT)) {
|
|||
|
||||
// Launch tsc as `node typescript/lib/tsc.js` on every OS. The `.bin/tsc` /
|
||||
// `tsc.cmd` shims are Windows-only wrappers; `execFileSync` cannot spawn a
|
||||
// `.cmd` without a shell, and a separate `npm ci` in gitnexus-shared pulls
|
||||
// TypeScript 7 optional platform packages (7+ minutes in CI).
|
||||
// `.cmd` without a shell, and a separate `npm ci` in gitnexus-shared would
|
||||
// pull a second TypeScript 7 optional-platform install (7+ minutes in CI).
|
||||
const tscJs = path.join(ROOT, 'node_modules', 'typescript', 'lib', 'tsc.js');
|
||||
if (!fs.existsSync(tscJs)) {
|
||||
console.error(
|
||||
|
|
|
|||
|
|
@ -4,18 +4,17 @@
|
|||
* Those sets are module-private, and exporting them purely to be testable would
|
||||
* widen a production surface to satisfy a test — the call
|
||||
* `receiver-twin-list-drift.test.ts` documents. So the guards read the source
|
||||
* instead, through the TypeScript parser the repo already vendors and already
|
||||
* uses this way (`literal-collectors.ts`, `query-determinism-guard.test.ts`,
|
||||
* `cli-index-help.test.ts`).
|
||||
* instead, through `@babel/parser` (`parse-typescript-source.ts`).
|
||||
*
|
||||
* Using the real parser is what makes the guards trustworthy. A text scanner has
|
||||
* to decide whether a delimiter opens a comment or sits inside a string, and it
|
||||
* gets that wrong in both directions here: the ignore-list comments quote paths
|
||||
* and carry an apostrophe (`Next.js's`), while a glob string such as `'** / *'`
|
||||
* contains a comment-open sequence. It also has to guess which bracket belongs
|
||||
* to the declaration rather than to a type annotation. Each of those is a way to
|
||||
* silently read fewer members — and a guard that quietly stops seeing members is
|
||||
* the exact defect these guards exist to catch.
|
||||
* Using an AST parser for TypeScript syntax is what makes the guards
|
||||
* trustworthy. A text scanner has to decide whether a delimiter opens a comment
|
||||
* or sits inside a string, and it gets that wrong in both directions here: the
|
||||
* ignore-list comments quote paths and carry an apostrophe (`Next.js's`), while
|
||||
* a glob string such as `'** / *'` contains a comment-open sequence. It also
|
||||
* has to guess which bracket belongs to the declaration rather than to a type
|
||||
* annotation. Each of those is a way to silently read fewer members — and a
|
||||
* guard that quietly stops seeing members is the exact defect these guards
|
||||
* exist to catch.
|
||||
*
|
||||
* `setEntries` therefore refuses anything that is not a plain list of string
|
||||
* literals, rather than skipping the members it cannot resolve.
|
||||
|
|
@ -23,7 +22,8 @@
|
|||
import { readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript';
|
||||
import * as t from '@babel/types';
|
||||
import { forEachChild, nodeText, parseTypeScript } from './parse-typescript-source.js';
|
||||
|
||||
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
||||
|
||||
|
|
@ -55,46 +55,44 @@ export const readSource = (file: string): string => readFileSync(file, 'utf8');
|
|||
* concatenation, a computed value).
|
||||
*/
|
||||
export const setEntries = (source: string, setName: string): string[] => {
|
||||
const sourceFile = ts.createSourceFile(
|
||||
'ignore-set-source.ts',
|
||||
source,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
);
|
||||
const { ast } = parseTypeScript('ignore-set-source.ts', source);
|
||||
|
||||
let elements: ts.NodeArray<ts.Expression> | undefined;
|
||||
const visit = (node: ts.Node): void => {
|
||||
let elements: t.ArrayExpression['elements'] | undefined;
|
||||
const visit = (node: t.Node): void => {
|
||||
if (
|
||||
elements === undefined &&
|
||||
ts.isVariableDeclaration(node) &&
|
||||
ts.isIdentifier(node.name) &&
|
||||
node.name.text === setName &&
|
||||
node.initializer !== undefined &&
|
||||
ts.isNewExpression(node.initializer) &&
|
||||
node.initializer.arguments?.length === 1 &&
|
||||
ts.isArrayLiteralExpression(node.initializer.arguments[0])
|
||||
t.isVariableDeclarator(node) &&
|
||||
t.isIdentifier(node.id) &&
|
||||
node.id.name === setName &&
|
||||
node.init !== undefined &&
|
||||
node.init !== null &&
|
||||
t.isNewExpression(node.init) &&
|
||||
node.init.arguments.length === 1 &&
|
||||
t.isArrayExpression(node.init.arguments[0])
|
||||
) {
|
||||
elements = node.initializer.arguments[0].elements;
|
||||
elements = node.init.arguments[0].elements;
|
||||
return;
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
forEachChild(node, visit);
|
||||
};
|
||||
visit(sourceFile);
|
||||
visit(ast);
|
||||
|
||||
if (elements === undefined) {
|
||||
throw new Error(`${setName} is not declared as \`new Set([...])\` — update this test`);
|
||||
}
|
||||
|
||||
const unresolvable = elements.filter((element) => !ts.isStringLiteral(element));
|
||||
const unresolvable = elements.filter((element) => !t.isStringLiteral(element));
|
||||
if (unresolvable.length > 0) {
|
||||
const first = unresolvable[0];
|
||||
const excerpt = first && typeof first === 'object' ? nodeText(source, first) : String(first);
|
||||
throw new Error(
|
||||
`${setName} holds ${unresolvable.length} member(s) that are not plain string literals ` +
|
||||
`(first: \`${unresolvable[0].getText(sourceFile)}\`). A source-reading guard cannot resolve ` +
|
||||
`(first: \`${excerpt}\`). A source-reading guard cannot resolve ` +
|
||||
`those, so switch this set to a runtime assertion rather than letting the guard see fewer members.`,
|
||||
);
|
||||
}
|
||||
|
||||
return elements.map((element) => (element as ts.StringLiteral).text);
|
||||
return elements.map((element) => (element as t.StringLiteral).value);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
* FOUR modes (plan KTD3):
|
||||
* 1. Config reflection — import `*-extractors/configs/*.ts`, read each
|
||||
* config-shaped export's node-type-array keys. Exact `config.language`.
|
||||
* 2. AST scan (`typescript` parser, no type-checker) over the EXTRACTION
|
||||
* 2. AST scan (TypeScript 7 program SourceFile; no Checker gating) over the EXTRACTION
|
||||
* surface — `*-extractors/**`, every `languages/<lang>/captures.ts`, and
|
||||
* `export-detection.ts`. Collected BY CONSUMPTION SITE: `<n>.type === '..'`,
|
||||
* `childForFieldName('..')` (capturing the receiver node type when an
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
* `ingestion/` (e.g. `type-env.ts`). These files MIX SyntaxNode `.type` with
|
||||
* resolved-symbol `.type` (kinds like 'Class'), so a literal is collected
|
||||
* ONLY when its `.type` / `childForFieldName` receiver resolves to a
|
||||
* tree-sitter SyntaxNode (via the TS TypeChecker). Per-`languages/<lang>/`
|
||||
* tree-sitter SyntaxNode (via the TypeScript 7 Checker). Per-`languages/<lang>/`
|
||||
* files tag to that one grammar; shared (non-`languages/<lang>/`) files tag
|
||||
* to the full gated set (valid-if-any).
|
||||
*
|
||||
|
|
@ -35,8 +35,8 @@
|
|||
*
|
||||
* Test-only file: allowed to name languages.
|
||||
*/
|
||||
import ts from 'typescript';
|
||||
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
||||
import * as ts from './ts7-ast.js';
|
||||
import { readdirSync, existsSync, statSync, realpathSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { SupportedLanguages } from '../../src/config/supported-languages.js';
|
||||
|
|
@ -449,11 +449,16 @@ function receiverNodeTypeOf(call: ts.CallExpression, sf: ts.SourceFile): string
|
|||
return receiverMutatedIn(recvText, scope) ? undefined : found;
|
||||
}
|
||||
|
||||
function scanFile(file: string): ScanResult {
|
||||
function scanFile(
|
||||
file: string,
|
||||
built: { program: ts.Program; checker: ts.Checker } | null,
|
||||
): ScanResult {
|
||||
const relPath = rel(file);
|
||||
const langs = fileLanguages(relPath);
|
||||
const src = readFileSync(file, 'utf8');
|
||||
const sf = ts.createSourceFile(file, src, ts.ScriptTarget.Latest, true);
|
||||
const empty: ScanResult = { nodeTypes: [], fields: [] };
|
||||
if (!built) return empty;
|
||||
const sf = programSourceFile(built.program, file);
|
||||
if (!sf) return empty;
|
||||
const nodeTypes: CollectedNodeType[] = [];
|
||||
const fields: CollectedField[] = [];
|
||||
|
||||
|
|
@ -558,8 +563,9 @@ function scanFile(file: string): ScanResult {
|
|||
function collectInCodeLiterals(): ScanResult {
|
||||
const nodeTypes: CollectedNodeType[] = [];
|
||||
const fields: CollectedField[] = [];
|
||||
const built = buildProgram();
|
||||
for (const file of mode2Files()) {
|
||||
const r = scanFile(file);
|
||||
const r = scanFile(file, built);
|
||||
nodeTypes.push(...r.nodeTypes);
|
||||
fields.push(...r.fields);
|
||||
}
|
||||
|
|
@ -571,7 +577,7 @@ function collectInCodeLiterals(): ScanResult {
|
|||
// binding/interpret/arity/import-decomposer/...), the production path for
|
||||
// migrated languages. These files mix SyntaxNode `.type` (grammar nodes) with
|
||||
// resolved-symbol `.type` (kinds like 'Class'); a naive scan would false-
|
||||
// positive on the latter. So this mode uses the TS TypeChecker to collect a
|
||||
// positive on the latter. So this mode uses the TypeScript 7 Checker to collect a
|
||||
// literal ONLY when its `.type` receiver / childForFieldName target resolves to
|
||||
// a tree-sitter SyntaxNode. Per-language dir => grammar (no cross-lang ambiguity).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -613,25 +619,35 @@ function resolutionLayerFiles(): { file: string; langs: SupportedLanguages[] }[]
|
|||
}
|
||||
|
||||
let _program: ts.Program | null = null;
|
||||
let _checker: ts.TypeChecker | null = null;
|
||||
function buildProgram(
|
||||
rootFiles: string[],
|
||||
): { program: ts.Program; checker: ts.TypeChecker } | null {
|
||||
let _checker: ts.Checker | null = null;
|
||||
function buildProgram(): { program: ts.Program; checker: ts.Checker } | null {
|
||||
if (_program && _checker) return { program: _program, checker: _checker };
|
||||
try {
|
||||
const cfg = ts.readConfigFile(join(REPO_ROOT, 'tsconfig.json'), ts.sys.readFile);
|
||||
const parsed = ts.parseJsonConfigFileContent(cfg.config ?? {}, ts.sys, REPO_ROOT);
|
||||
const options: ts.CompilerOptions = { ...parsed.options, noEmit: true, skipLibCheck: true };
|
||||
_program = ts.createProgram(rootFiles, options);
|
||||
_checker = _program.getTypeChecker();
|
||||
const api = new ts.API({ cwd: REPO_ROOT });
|
||||
const snapshot = api.updateSnapshot({
|
||||
openProjects: [join(REPO_ROOT, 'tsconfig.json')],
|
||||
});
|
||||
const project = snapshot.getProjects()[0];
|
||||
if (!project) return null;
|
||||
_program = project.program;
|
||||
_checker = project.checker;
|
||||
return { program: _program, checker: _checker };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceFileByPath = new Map<string, ts.SourceFile | undefined>();
|
||||
|
||||
function programSourceFile(program: ts.Program, file: string): ts.SourceFile | undefined {
|
||||
if (sourceFileByPath.has(file)) return sourceFileByPath.get(file);
|
||||
const sf = program.getSourceFile(file) ?? program.getSourceFile(realpathSync(file));
|
||||
sourceFileByPath.set(file, sf);
|
||||
return sf;
|
||||
}
|
||||
|
||||
/** True when `node`'s resolved type is (or includes) a tree-sitter SyntaxNode. */
|
||||
function isSyntaxNodeReceiver(checker: ts.TypeChecker, node: ts.Node): boolean {
|
||||
function isSyntaxNodeReceiver(checker: ts.Checker, node: ts.Node): boolean {
|
||||
try {
|
||||
const s = checker.typeToString(checker.getTypeAtLocation(node));
|
||||
return /\bSyntaxNode\b/.test(s);
|
||||
|
|
@ -647,7 +663,7 @@ function collectResolutionLayerLiterals(): ScanResult {
|
|||
const nodeTypes: CollectedNodeType[] = [];
|
||||
const fields: CollectedField[] = [];
|
||||
const entries = resolutionLayerFiles();
|
||||
const built = buildProgram(entries.map((e) => e.file));
|
||||
const built = buildProgram();
|
||||
if (!built) {
|
||||
resolutionLayerProgramOk = false;
|
||||
return { nodeTypes, fields };
|
||||
|
|
@ -655,7 +671,7 @@ function collectResolutionLayerLiterals(): ScanResult {
|
|||
const { program, checker } = built;
|
||||
|
||||
for (const { file, langs } of entries) {
|
||||
const sf = program.getSourceFile(file);
|
||||
const sf = programSourceFile(program, file);
|
||||
if (!sf) continue;
|
||||
const relPath = rel(file);
|
||||
const constMembers = new Map<string, string[]>();
|
||||
|
|
|
|||
162
gitnexus/test/helpers/parse-typescript-source.ts
Normal file
162
gitnexus/test/helpers/parse-typescript-source.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Parse-only TypeScript AST walks for tests.
|
||||
*
|
||||
* TypeScript 7.0 does not ship the classic Compiler API (`createSourceFile`).
|
||||
* These guards only need syntax, so they parse with `@babel/parser` (already a
|
||||
* CLI dependency) rather than spawning the native TypeScript 7 program API.
|
||||
*/
|
||||
import { parse } from '@babel/parser';
|
||||
import {
|
||||
VISITOR_KEYS,
|
||||
isBinaryExpression,
|
||||
isNode,
|
||||
isStringLiteral,
|
||||
isTemplateLiteral,
|
||||
type Comment,
|
||||
type File,
|
||||
type MemberExpression,
|
||||
type Node,
|
||||
type OptionalMemberExpression,
|
||||
type TemplateLiteral,
|
||||
} from '@babel/types';
|
||||
|
||||
export type AstNode = Node & { parent?: AstNode };
|
||||
|
||||
export interface ParsedSource {
|
||||
ast: File & { parent?: AstNode };
|
||||
source: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
const PARSE_PLUGINS: NonNullable<Parameters<typeof parse>[1]>['plugins'] = [
|
||||
'typescript',
|
||||
'explicitResourceManagement',
|
||||
'importAttributes',
|
||||
'decoratorAutoAccessors',
|
||||
['decorators', { decoratorsBeforeExport: true }],
|
||||
];
|
||||
|
||||
export function forEachChild(node: Node, visit: (child: AstNode) => void): void {
|
||||
const keys = VISITOR_KEYS[node.type] ?? [];
|
||||
for (const key of keys) {
|
||||
const value = (node as unknown as Record<string, unknown>)[key];
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (isNode(item)) visit(item);
|
||||
}
|
||||
} else if (isNode(value)) {
|
||||
visit(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Direct descendants in source order (does not include `node` itself). */
|
||||
export function collectDescendants(node: Node): AstNode[] {
|
||||
const out: AstNode[] = [];
|
||||
const visit = (child: AstNode): void => {
|
||||
out.push(child);
|
||||
forEachChild(child, visit);
|
||||
};
|
||||
forEachChild(node, visit);
|
||||
return out;
|
||||
}
|
||||
|
||||
export function staticMemberName(
|
||||
node: MemberExpression | OptionalMemberExpression,
|
||||
): string | undefined {
|
||||
return node.computed || node.property.type !== 'Identifier' ? undefined : node.property.name;
|
||||
}
|
||||
|
||||
function templateLiteralText(node: TemplateLiteral): string | undefined {
|
||||
if (node.expressions.length > 0) return undefined;
|
||||
return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join('');
|
||||
}
|
||||
|
||||
/** Compile-time string from a literal, template without holes, or `+` chain. */
|
||||
export function staticStringValue(node: Node | undefined | null): string | undefined {
|
||||
if (!node) return undefined;
|
||||
if (isStringLiteral(node)) return node.value;
|
||||
if (isTemplateLiteral(node)) return templateLiteralText(node);
|
||||
if (isBinaryExpression(node) && node.operator === '+') {
|
||||
const left = staticStringValue(node.left);
|
||||
const right = staticStringValue(node.right);
|
||||
if (left !== undefined && right !== undefined) return `${left}${right}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function attachParents(node: AstNode, parent?: AstNode): void {
|
||||
node.parent = parent;
|
||||
forEachChild(node, (child) => attachParents(child, node));
|
||||
}
|
||||
|
||||
export function parseTypeScript(fileName: string, source: string): ParsedSource {
|
||||
const ast = parse(source, {
|
||||
sourceFilename: fileName,
|
||||
sourceType: 'unambiguous',
|
||||
plugins: PARSE_PLUGINS,
|
||||
errorRecovery: true,
|
||||
attachComment: true,
|
||||
ranges: true,
|
||||
}) as File & { parent?: AstNode };
|
||||
attachParents(ast);
|
||||
return { ast, source, fileName };
|
||||
}
|
||||
|
||||
export function nodeStart(node: Node): number {
|
||||
return node.start ?? 0;
|
||||
}
|
||||
|
||||
export function nodeEnd(node: Node): number {
|
||||
return node.end ?? 0;
|
||||
}
|
||||
|
||||
export function nodeText(source: string, node: Node): string {
|
||||
return source.slice(nodeStart(node), nodeEnd(node));
|
||||
}
|
||||
|
||||
let lineAtSource = '';
|
||||
let lineAtStarts: number[] = [0];
|
||||
|
||||
function lineStarts(source: string): number[] {
|
||||
if (source === lineAtSource) return lineAtStarts;
|
||||
const starts = [0];
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
if (source[i] === '\n') starts.push(i + 1);
|
||||
}
|
||||
lineAtSource = source;
|
||||
lineAtStarts = starts;
|
||||
return starts;
|
||||
}
|
||||
|
||||
export function lineAt(source: string, position: number): number {
|
||||
if (position <= 0) return 1;
|
||||
const starts = lineStarts(source);
|
||||
const pos = Math.min(position, source.length);
|
||||
let lo = 0;
|
||||
let hi = starts.length - 1;
|
||||
while (lo < hi) {
|
||||
const mid = (lo + hi + 1) >> 1;
|
||||
if (starts[mid] <= pos) lo = mid;
|
||||
else hi = mid - 1;
|
||||
}
|
||||
return lo + 1;
|
||||
}
|
||||
|
||||
export interface CommentRange {
|
||||
pos: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
function toRange(comment: Comment): CommentRange | undefined {
|
||||
if (comment.start == null || comment.end == null) return undefined;
|
||||
return { pos: comment.start, end: comment.end };
|
||||
}
|
||||
|
||||
export function leadingCommentRanges(node: Node): CommentRange[] {
|
||||
return (node.leadingComments ?? []).map(toRange).filter((range) => range !== undefined);
|
||||
}
|
||||
|
||||
export function trailingCommentRanges(node: Node): CommentRange[] {
|
||||
return (node.trailingComments ?? []).map(toRange).filter((range) => range !== undefined);
|
||||
}
|
||||
48
gitnexus/test/helpers/ts7-ast.ts
Normal file
48
gitnexus/test/helpers/ts7-ast.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* TypeScript 7 AST + Checker surface for test-only walks that need types.
|
||||
*
|
||||
* TypeScript 7.0 dropped the classic Compiler API (`createProgram` /
|
||||
* `getTypeChecker`). The native `typescript/unstable/sync` client exposes the
|
||||
* same Checker methods against the Go compiler, and `typescript/unstable/ast`
|
||||
* is the matching syntax tree.
|
||||
*/
|
||||
export { SyntaxKind } from 'typescript/unstable/ast';
|
||||
export type {
|
||||
CallExpression,
|
||||
Expression,
|
||||
Node,
|
||||
PropertyAccessExpression,
|
||||
SourceFile,
|
||||
StringLiteral,
|
||||
} from 'typescript/unstable/ast';
|
||||
export {
|
||||
isArrayLiteralExpression,
|
||||
isArrowFunction,
|
||||
isAsExpression,
|
||||
isBinaryExpression,
|
||||
isCallExpression,
|
||||
isCaseClause,
|
||||
isConstructorDeclaration,
|
||||
isFunctionDeclaration,
|
||||
isFunctionExpression,
|
||||
isGetAccessorDeclaration,
|
||||
isIdentifier,
|
||||
isIfStatement,
|
||||
isMethodDeclaration,
|
||||
isNewExpression,
|
||||
isPostfixUnaryExpression,
|
||||
isPrefixUnaryExpression,
|
||||
isPropertyAccessExpression,
|
||||
isSetAccessorDeclaration,
|
||||
isStringLiteral,
|
||||
isStringLiteralLikeNode as isStringLiteralLike,
|
||||
isSwitchStatement,
|
||||
isVariableDeclaration,
|
||||
} from 'typescript/unstable/ast/is';
|
||||
export { API, type Checker, type Program } from 'typescript/unstable/sync';
|
||||
|
||||
import type { Node } from 'typescript/unstable/ast';
|
||||
|
||||
export function forEachChild(node: Node, visitor: (child: Node) => void): void {
|
||||
node.forEachChild(visitor);
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ import { runWebBuild, shouldBuildWeb, shouldPreserveWebOutput } from '../../scri
|
|||
|
||||
/** Default prepare/build stay CLI-only; the web UI ships only via prepack --web. */
|
||||
const REPO_ROOT = path.resolve(__dirname, '../../..');
|
||||
const CLI_TSC_JS = 'node ../gitnexus/node_modules/typescript/lib/tsc.js';
|
||||
const WEB_TSC_JS = 'node ../gitnexus-web/node_modules/typescript/lib/tsc.js';
|
||||
const PACKAGE_JSON = JSON.parse(
|
||||
readFileSync(path.join(REPO_ROOT, 'gitnexus/package.json'), 'utf8'),
|
||||
) as { scripts?: Record<string, string> };
|
||||
|
|
@ -283,7 +285,7 @@ describe('workflows that need the web UI install it themselves', () => {
|
|||
describe('setup-gitnexus job budget', () => {
|
||||
it('does not npm-ci gitnexus-shared (TypeScript 7 optional-platform install stalls CI)', () => {
|
||||
const shared = setupGitnexus.runs?.steps?.find((step) => step.name === 'Build gitnexus-shared');
|
||||
expect(String(shared?.run)).toBe('node ../gitnexus/node_modules/typescript/lib/tsc.js');
|
||||
expect(String(shared?.run)).toBe(CLI_TSC_JS);
|
||||
expect(String(shared?.run)).not.toContain('.bin');
|
||||
expect(shared?.if).toContain("lifecycle-scripts == 'false'");
|
||||
expect(
|
||||
|
|
@ -314,12 +316,28 @@ describe('setup-gitnexus job budget', () => {
|
|||
expect(String(setupNode?.with?.['cache-dependency-path'])).toBe(
|
||||
'gitnexus-web/package-lock.json',
|
||||
);
|
||||
expect(String(shared?.run)).toBe('node ../gitnexus-web/node_modules/typescript/lib/tsc.js');
|
||||
expect(String(shared?.run)).toBe(WEB_TSC_JS);
|
||||
expect(String(shared?.run)).not.toContain('.bin');
|
||||
expect(String(shared?.run)).not.toContain('npm ci');
|
||||
expect(webInstall?.env?.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD).toBe('1');
|
||||
});
|
||||
|
||||
it('Vercel installs web first and compiles shared with the web TypeScript', () => {
|
||||
const vercel = JSON.parse(
|
||||
readFileSync(path.join(REPO_ROOT, 'gitnexus-web/vercel.json'), 'utf8'),
|
||||
) as { installCommand?: string };
|
||||
const install = String(vercel.installCommand);
|
||||
// Vercel runs installCommand with NODE_ENV=production, so npm ci drops
|
||||
// typescript unless --include=dev is on that install (not a later step).
|
||||
expect(install).toContain('npm ci --include=dev');
|
||||
expect(install).toContain('gitnexus-shared');
|
||||
expect(install.indexOf('npm ci --include=dev')).toBeLessThan(
|
||||
install.indexOf('gitnexus-shared'),
|
||||
);
|
||||
expect(install).toContain(WEB_TSC_JS);
|
||||
expect(install).not.toMatch(/gitnexus-shared[^&]*npm (?:ci|install)/);
|
||||
});
|
||||
|
||||
it('quality typecheck skips prepare/postinstall so tsc --noEmit fits in 10 minutes', () => {
|
||||
const job = qualityJobs.typecheck;
|
||||
const setup = job?.steps?.find((step) => step.uses === './.github/actions/setup-gitnexus');
|
||||
|
|
@ -327,6 +345,28 @@ describe('setup-gitnexus job budget', () => {
|
|||
expect(setup?.with?.['lifecycle-scripts']).toBe('false');
|
||||
});
|
||||
|
||||
it('web app tsconfig typechecks React JSX on TypeScript 7 without baseUrl', () => {
|
||||
const tsconfig = JSON.parse(
|
||||
readFileSync(path.join(REPO_ROOT, 'gitnexus-web/tsconfig.app.json'), 'utf8'),
|
||||
) as {
|
||||
compilerOptions?: {
|
||||
baseUrl?: string;
|
||||
jsx?: string;
|
||||
jsxImportSource?: string;
|
||||
lib?: string[];
|
||||
rootDir?: string;
|
||||
types?: string[];
|
||||
};
|
||||
};
|
||||
const options = tsconfig.compilerOptions ?? {};
|
||||
expect(options.baseUrl).toBeUndefined();
|
||||
expect(options.jsx).toBe('react-jsx');
|
||||
expect(options.jsxImportSource).toBe('react');
|
||||
expect(options.lib).toEqual(expect.arrayContaining(['ESNext', 'DOM', 'DOM.Iterable']));
|
||||
expect(options.rootDir).toBe('./src');
|
||||
expect(options.types).toEqual(['vite/client']);
|
||||
});
|
||||
|
||||
it('quality typecheck-web can finish a cold web install instead of canceling before cache save', () => {
|
||||
expect(qualityJobs['typecheck-web']?.['timeout-minutes']).toBe(15);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,14 @@ import os from 'node:os';
|
|||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Command, Option } from 'commander';
|
||||
import * as ts from 'typescript';
|
||||
import * as t from '@babel/types';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import {
|
||||
forEachChild,
|
||||
parseTypeScript,
|
||||
staticMemberName,
|
||||
staticStringValue,
|
||||
} from '../helpers/parse-typescript-source.js';
|
||||
import { CLI_SPAWN_PREFIX } from '../helpers/cli-entry.js';
|
||||
import { localizeCliHelp } from '../../src/cli/help-i18n.js';
|
||||
import { setCliLanguage, type SupportedCliLanguage } from '../../src/cli/i18n/index.js';
|
||||
|
|
@ -69,17 +75,6 @@ const allHelpCommands = [
|
|||
['group', 'contracts'],
|
||||
];
|
||||
|
||||
function staticStringValue(node: ts.Node | undefined): string | undefined {
|
||||
if (!node) return undefined;
|
||||
if (ts.isStringLiteralLike(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
|
||||
if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {
|
||||
const left = staticStringValue(node.left);
|
||||
const right = staticStringValue(node.right);
|
||||
if (left !== undefined && right !== undefined) return `${left}${right}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractRegisteredHelpDescriptions(): string[] {
|
||||
const descriptions = new Set<string>();
|
||||
const sourceFiles = ['src/cli/index.ts', 'src/cli/group.ts'];
|
||||
|
|
@ -87,27 +82,30 @@ function extractRegisteredHelpDescriptions(): string[] {
|
|||
for (const relativePath of sourceFiles) {
|
||||
const filePath = path.join(repoRoot, relativePath);
|
||||
const source = fs.readFileSync(filePath, 'utf8');
|
||||
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
|
||||
const { ast } = parseTypeScript(filePath, source);
|
||||
|
||||
function visit(node: ts.Node): void {
|
||||
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
||||
const method = node.expression.name.text;
|
||||
const description =
|
||||
method === 'description'
|
||||
? staticStringValue(node.arguments[0])
|
||||
: method === 'option' || method === 'requiredOption'
|
||||
? staticStringValue(node.arguments[1])
|
||||
: undefined;
|
||||
function visit(node: t.Node): void {
|
||||
if (
|
||||
t.isCallExpression(node) &&
|
||||
(t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee))
|
||||
) {
|
||||
const method = staticMemberName(node.callee);
|
||||
let description: string | undefined;
|
||||
if (method === 'description') {
|
||||
description = staticStringValue(node.arguments[0]);
|
||||
} else if (method === 'option' || method === 'requiredOption') {
|
||||
description = staticStringValue(node.arguments[1]);
|
||||
}
|
||||
|
||||
if (description && /[A-Za-z]/.test(description)) {
|
||||
descriptions.add(description.replace(/\s+/g, ' ').trim());
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, visit);
|
||||
forEachChild(node, visit);
|
||||
}
|
||||
|
||||
visit(sourceFile);
|
||||
visit(ast);
|
||||
}
|
||||
|
||||
return [...descriptions].filter((description) => description.length > 0).sort();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,16 @@ import * as fs from 'node:fs';
|
|||
import * as path from 'node:path';
|
||||
import * as os from 'node:os';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript';
|
||||
import * as t from '@babel/types';
|
||||
import {
|
||||
type AstNode,
|
||||
collectDescendants,
|
||||
lineAt,
|
||||
nodeStart,
|
||||
nodeText,
|
||||
parseTypeScript,
|
||||
staticMemberName,
|
||||
} from '../../helpers/parse-typescript-source.js';
|
||||
import type {
|
||||
ContractRegistry,
|
||||
ExtractedContract,
|
||||
|
|
@ -182,65 +191,63 @@ describe('syncGroup when one extractor fails partway through a repo', () => {
|
|||
*/
|
||||
const SYNC_SOURCE_PATH = fileURLToPath(new URL('../../../src/core/group/sync.ts', import.meta.url));
|
||||
|
||||
/** Every node under `node`, in source order. No branching, so nothing is skippable. */
|
||||
function descendants(node: ts.Node): ts.Node[] {
|
||||
const out: ts.Node[] = [];
|
||||
const visit = (n: ts.Node): void => {
|
||||
out.push(n);
|
||||
n.forEachChild(visit);
|
||||
};
|
||||
node.forEachChild(visit);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** `const <name>: StoredContract[] = []` — the per-repo staging buffer. */
|
||||
function isStagingBufferDeclaration(node: ts.Node): node is ts.VariableDeclaration {
|
||||
function isStagingBufferDeclaration(node: t.Node): node is t.VariableDeclarator {
|
||||
if (!t.isVariableDeclarator(node) || !t.isIdentifier(node.id)) return false;
|
||||
const annotation = node.id.typeAnnotation;
|
||||
if (
|
||||
!annotation ||
|
||||
!t.isTSTypeAnnotation(annotation) ||
|
||||
!t.isTSArrayType(annotation.typeAnnotation)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const elementType = annotation.typeAnnotation.elementType;
|
||||
return (
|
||||
ts.isVariableDeclaration(node) &&
|
||||
node.type !== undefined &&
|
||||
ts.isArrayTypeNode(node.type) &&
|
||||
ts.isTypeReferenceNode(node.type.elementType) &&
|
||||
ts.isIdentifier(node.type.elementType.typeName) &&
|
||||
node.type.elementType.typeName.text === 'StoredContract' &&
|
||||
node.initializer !== undefined &&
|
||||
ts.isArrayLiteralExpression(node.initializer) &&
|
||||
node.initializer.elements.length === 0 &&
|
||||
ts.isVariableDeclarationList(node.parent) &&
|
||||
(node.parent.flags & ts.NodeFlags.Const) !== 0
|
||||
t.isTSTypeReference(elementType) &&
|
||||
t.isIdentifier(elementType.typeName) &&
|
||||
elementType.typeName.name === 'StoredContract' &&
|
||||
node.init !== undefined &&
|
||||
node.init !== null &&
|
||||
t.isArrayExpression(node.init) &&
|
||||
node.init.elements.length === 0 &&
|
||||
t.isVariableDeclaration((node as AstNode).parent) &&
|
||||
((node as AstNode).parent as t.VariableDeclaration).kind === 'const'
|
||||
);
|
||||
}
|
||||
|
||||
/** `x.apply(dest, args)` — an argument-limited append in non-spread clothing. */
|
||||
function isApplyCall(call: ts.CallExpression): boolean {
|
||||
return ts.isPropertyAccessExpression(call.expression) && call.expression.name.text === 'apply';
|
||||
function isApplyCall(call: t.CallExpression): boolean {
|
||||
return (
|
||||
(t.isMemberExpression(call.callee) || t.isOptionalMemberExpression(call.callee)) &&
|
||||
staticMemberName(call.callee) === 'apply'
|
||||
);
|
||||
}
|
||||
|
||||
function describeCall(sourceFile: ts.SourceFile, call: ts.CallExpression): string {
|
||||
const { line } = sourceFile.getLineAndCharacterOfPosition(call.getStart(sourceFile));
|
||||
return `${line + 1}: ${call.getText(sourceFile).replace(/\s+/g, ' ')}`;
|
||||
function describeCall(source: string, call: t.CallExpression): string {
|
||||
const line = lineAt(source, nodeStart(call));
|
||||
return `${line}: ${nodeText(source, call).replace(/\s+/g, ' ')}`;
|
||||
}
|
||||
|
||||
describe('the per-repo staging append in sync.ts', () => {
|
||||
it('appends the staged contracts without spreading them into a call', () => {
|
||||
const source = fs.readFileSync(SYNC_SOURCE_PATH, 'utf-8');
|
||||
const sourceFile = ts.createSourceFile(
|
||||
SYNC_SOURCE_PATH,
|
||||
source,
|
||||
ts.ScriptTarget.Latest,
|
||||
true,
|
||||
ts.ScriptKind.TS,
|
||||
);
|
||||
const { ast } = parseTypeScript(SYNC_SOURCE_PATH, source);
|
||||
|
||||
const allNodes = descendants(sourceFile);
|
||||
const allNodes = collectDescendants(ast);
|
||||
const stagingBuffers = allNodes.filter(isStagingBufferDeclaration);
|
||||
// One staging buffer, or this gate no longer knows which code it guards.
|
||||
expect(stagingBuffers.map((d) => d.name.getText(sourceFile))).toHaveLength(1);
|
||||
const stagingNames = stagingBuffers.map((d) => d.name.getText(sourceFile));
|
||||
const stagingNames = stagingBuffers.map((d) =>
|
||||
t.isIdentifier(d.id) ? d.id.name : nodeText(source, d.id),
|
||||
);
|
||||
expect(stagingNames).toHaveLength(1);
|
||||
|
||||
// The block the buffer is declared in — the per-repo loop body.
|
||||
// VariableDeclarator → VariableDeclaration → BlockStatement (Babel has no
|
||||
// extra VariableStatement wrapper).
|
||||
const declaringBlocks = stagingBuffers
|
||||
.map((d) => d.parent.parent.parent) // declaration → list → statement → block
|
||||
.filter(ts.isBlock);
|
||||
.map((d) => (d as AstNode).parent?.parent)
|
||||
.filter((node): node is t.BlockStatement => t.isBlockStatement(node));
|
||||
expect(declaringBlocks).toHaveLength(1);
|
||||
|
||||
// The extractor try-block: a DIRECT statement of that block whose `try` reads
|
||||
|
|
@ -249,22 +256,22 @@ describe('the per-repo staging append in sync.ts', () => {
|
|||
// ancestor reads the buffer too. Widening to "any try that mentions it" pulls
|
||||
// in the entire function body, manifest-window spreads and all.
|
||||
const extractorTryBlocks = declaringBlocks.flatMap((block) =>
|
||||
block.statements
|
||||
.filter(ts.isTryStatement)
|
||||
block.body
|
||||
.filter((statement): statement is t.TryStatement => t.isTryStatement(statement))
|
||||
.filter((statement) =>
|
||||
descendants(statement.tryBlock).some(
|
||||
(n) => ts.isIdentifier(n) && stagingNames.includes(n.text),
|
||||
collectDescendants(statement.block).some(
|
||||
(n) => t.isIdentifier(n) && stagingNames.includes(n.name),
|
||||
),
|
||||
)
|
||||
.map((statement) => statement.tryBlock),
|
||||
.map((statement) => statement.block),
|
||||
);
|
||||
expect(extractorTryBlocks).toHaveLength(1);
|
||||
|
||||
const unboundedAppends = extractorTryBlocks.flatMap((block) =>
|
||||
descendants(block)
|
||||
.filter(ts.isCallExpression)
|
||||
.filter((call) => call.arguments.some(ts.isSpreadElement) || isApplyCall(call))
|
||||
.map((call) => describeCall(sourceFile, call)),
|
||||
collectDescendants(block)
|
||||
.filter((node): node is t.CallExpression => t.isCallExpression(node))
|
||||
.filter((call) => call.arguments.some((arg) => t.isSpreadElement(arg)) || isApplyCall(call))
|
||||
.map((call) => describeCall(source, call)),
|
||||
);
|
||||
|
||||
// Every staged contract must reach `autoContracts` through a bounded loop:
|
||||
|
|
|
|||
|
|
@ -47,10 +47,21 @@
|
|||
* and `detect-changes-local-id-stability.test.ts`.
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import ts from 'typescript';
|
||||
import * as t from '@babel/types';
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
type AstNode,
|
||||
type CommentRange,
|
||||
forEachChild,
|
||||
leadingCommentRanges,
|
||||
lineAt,
|
||||
nodeStart,
|
||||
nodeText,
|
||||
parseTypeScript,
|
||||
trailingCommentRanges,
|
||||
} from '../helpers/parse-typescript-source.js';
|
||||
|
||||
const SRC_DIR = fileURLToPath(new URL('../../src/', import.meta.url));
|
||||
|
||||
|
|
@ -135,18 +146,18 @@ function walkTs(dir: string, relBase: string, out: string[]): string[] {
|
|||
* helpers (`seedBlockQuery`, `buildFtsQueryCypher`), and anything built from
|
||||
* those. Resolved to a fixpoint so an ordering constant can be composed.
|
||||
*/
|
||||
function collectOrderingNames(sf: ts.SourceFile): Set<string> {
|
||||
function collectOrderingNames(source: string, ast: t.Node): Set<string> {
|
||||
const initializers = new Map<string, string>();
|
||||
const visit = (node: ts.Node): void => {
|
||||
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
||||
initializers.set(node.name.text, node.initializer.getText(sf));
|
||||
const visit = (node: t.Node): void => {
|
||||
if (t.isVariableDeclarator(node) && t.isIdentifier(node.id) && node.init) {
|
||||
initializers.set(node.id.name, nodeText(source, node.init));
|
||||
}
|
||||
if (ts.isFunctionDeclaration(node) && node.name !== undefined) {
|
||||
initializers.set(node.name.text, node.getText(sf));
|
||||
if (t.isFunctionDeclaration(node) && t.isIdentifier(node.id)) {
|
||||
initializers.set(node.id.name, nodeText(source, node));
|
||||
}
|
||||
ts.forEachChild(node, visit);
|
||||
forEachChild(node, visit);
|
||||
};
|
||||
visit(sf);
|
||||
visit(ast);
|
||||
|
||||
const ordering = new Set<string>();
|
||||
const referencesOrdering = (text: string): boolean =>
|
||||
|
|
@ -166,38 +177,38 @@ function collectOrderingNames(sf: ts.SourceFile): Set<string> {
|
|||
// ---------------------------------------------------------------------------
|
||||
// Query units
|
||||
// ---------------------------------------------------------------------------
|
||||
const isLiteralUnit = (node: ts.Node): boolean =>
|
||||
ts.isStringLiteralLike(node) || ts.isTemplateExpression(node);
|
||||
const isLiteralUnit = (node: t.Node): boolean =>
|
||||
t.isStringLiteral(node) || t.isTemplateLiteral(node);
|
||||
|
||||
/** Climb `+` concatenation so a query split across fragments is ONE unit. */
|
||||
function concatenationRoot(node: ts.Node): ts.Node {
|
||||
let current = node;
|
||||
function concatenationRoot(node: AstNode): AstNode {
|
||||
let current: AstNode = node;
|
||||
while (
|
||||
current.parent !== undefined &&
|
||||
ts.isBinaryExpression(current.parent) &&
|
||||
current.parent.operatorToken.kind === ts.SyntaxKind.PlusToken
|
||||
t.isBinaryExpression(current.parent) &&
|
||||
current.parent.operator === '+'
|
||||
) {
|
||||
current = current.parent;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function collectQueryUnits(sf: ts.SourceFile): ts.Node[] {
|
||||
const byPosition = new Map<number, ts.Node>();
|
||||
const visit = (node: ts.Node): void => {
|
||||
function collectQueryUnits(source: string, ast: t.Node): AstNode[] {
|
||||
const byPosition = new Map<number, AstNode>();
|
||||
const visit = (node: AstNode): void => {
|
||||
if (!isLiteralUnit(node)) {
|
||||
ts.forEachChild(node, visit);
|
||||
forEachChild(node, visit);
|
||||
return;
|
||||
}
|
||||
// Never descend INTO a literal: a nested template inside `${...}` is
|
||||
// already covered by the outer unit's text.
|
||||
const unit = concatenationRoot(node);
|
||||
const text = unit.getText(sf);
|
||||
const text = nodeText(source, unit);
|
||||
if (LIMIT_RE.test(text) && (CYPHER_CLAUSE_RE.test(text) || BARE_LIMIT_FRAGMENT_RE.test(text))) {
|
||||
byPosition.set(unit.getStart(sf), unit);
|
||||
byPosition.set(nodeStart(unit), unit);
|
||||
}
|
||||
};
|
||||
visit(sf);
|
||||
visit(ast as AstNode);
|
||||
return [...byPosition.entries()].sort((a, b) => a[0] - b[0]).map(([, node]) => node);
|
||||
}
|
||||
|
||||
|
|
@ -216,14 +227,14 @@ interface CommentBlock {
|
|||
end: number;
|
||||
}
|
||||
|
||||
const isFunctionLike = (node: ts.Node): boolean =>
|
||||
ts.isFunctionDeclaration(node) ||
|
||||
ts.isFunctionExpression(node) ||
|
||||
ts.isArrowFunction(node) ||
|
||||
ts.isMethodDeclaration(node) ||
|
||||
ts.isConstructorDeclaration(node) ||
|
||||
ts.isGetAccessorDeclaration(node) ||
|
||||
ts.isSetAccessorDeclaration(node);
|
||||
const isFunctionLike = (node: t.Node): boolean =>
|
||||
t.isFunctionDeclaration(node) ||
|
||||
t.isFunctionExpression(node) ||
|
||||
t.isArrowFunctionExpression(node) ||
|
||||
t.isClassMethod(node) ||
|
||||
t.isClassPrivateMethod(node) ||
|
||||
t.isObjectMethod(node) ||
|
||||
t.isTSDeclareMethod(node);
|
||||
|
||||
/**
|
||||
* Comment blocks a marker for `node` may live in, NEAREST FIRST: the query's
|
||||
|
|
@ -239,22 +250,21 @@ const isFunctionLike = (node: ts.Node): boolean =>
|
|||
* Consecutive `//` lines arrive as one range EACH, so they are merged back into
|
||||
* a block: a reason may then wrap across lines and still fit `printWidth: 100`.
|
||||
*/
|
||||
function markerBlocks(source: string, node: ts.Node): CommentBlock[] {
|
||||
const nearestFirst: ts.CommentRange[] = [];
|
||||
function markerBlocks(source: string, node: AstNode): CommentBlock[] {
|
||||
const nearestFirst: CommentRange[] = [];
|
||||
const seen = new Set<number>();
|
||||
let current: ts.Node | undefined = node;
|
||||
let current: AstNode | undefined = node;
|
||||
for (let depth = 0; current !== undefined && depth < 20; depth++) {
|
||||
for (const range of [
|
||||
...(ts.getLeadingCommentRanges(source, current.getFullStart()) ?? []),
|
||||
...(ts.getTrailingCommentRanges(source, current.getEnd()) ?? []),
|
||||
]) {
|
||||
for (const range of [...leadingCommentRanges(current), ...trailingCommentRanges(current)]) {
|
||||
if (!seen.has(range.pos)) {
|
||||
seen.add(range.pos);
|
||||
nearestFirst.push(range);
|
||||
}
|
||||
}
|
||||
const parent: ts.Node | undefined = current.parent;
|
||||
if (parent === undefined || isFunctionLike(parent) || ts.isSourceFile(parent)) break;
|
||||
const parent: AstNode | undefined = current.parent;
|
||||
if (parent === undefined || isFunctionLike(parent) || t.isFile(parent) || t.isProgram(parent)) {
|
||||
break;
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
|
||||
|
|
@ -318,15 +328,15 @@ const reasonIsSubstantive = (reason: string): boolean =>
|
|||
// ---------------------------------------------------------------------------
|
||||
export function analyzeSource(file: string, source: string): QueryFinding[] {
|
||||
if (!LIMIT_RE.test(source)) return [];
|
||||
const sf = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true);
|
||||
const ordering = collectOrderingNames(sf);
|
||||
const { ast } = parseTypeScript(file, source);
|
||||
const ordering = collectOrderingNames(source, ast);
|
||||
/** A marker declares ONE query: a claimed range cannot also cover the next. */
|
||||
const claimed = new Map<number, number>();
|
||||
|
||||
return collectQueryUnits(sf).map((unit) => {
|
||||
const text = unit.getText(sf);
|
||||
return collectQueryUnits(source, ast).map((unit) => {
|
||||
const text = nodeText(source, unit);
|
||||
const limitOffset = Math.max(text.search(LIMIT_RE), 0);
|
||||
const line = sf.getLineAndCharacterOfPosition(unit.getStart(sf) + limitOffset).line + 1;
|
||||
const line = lineAt(source, nodeStart(unit) + limitOffset);
|
||||
const excerpt = text.replace(/\s+/g, ' ').slice(0, 160);
|
||||
const base = { file, line, excerpt };
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const workflowDocument = load(workflow) as {
|
|||
'timeout-minutes'?: unknown;
|
||||
with?: Record<string, unknown>;
|
||||
env?: Record<string, string>;
|
||||
'working-directory'?: string;
|
||||
}>;
|
||||
}
|
||||
>;
|
||||
|
|
@ -476,16 +477,26 @@ exit 1`);
|
|||
expect(containment).toContain('gitnexus-cursor-integration/skills/gitnexus-review/*');
|
||||
});
|
||||
|
||||
it('installs node_modules for the monorepo root, gitnexus-shared, and gitnexus', () => {
|
||||
// The benchmark sandbox-copies node_modules from all three (tasks.scenarios.yaml).
|
||||
// The root tree was absent on the first real run because only the two subpackage
|
||||
// steps ran, so capture_task_dependency_binding aborted at task binding.
|
||||
it('installs node_modules for the monorepo root and gitnexus, and compiles shared from the parent', () => {
|
||||
// The benchmark sandbox-copies node_modules from root and gitnexus
|
||||
// (tasks.scenarios.yaml). Shared is compiled by gitnexus `npm run build`
|
||||
// (scripts/build.js); a dedicated npm ci in gitnexus-shared stalls CI.
|
||||
const rootStep = findStep('Install monorepo root dependencies');
|
||||
expect(rootStep).toBeDefined();
|
||||
expect(rootStep).not.toHaveProperty('working-directory'); // installs at the repo root
|
||||
expect(String(rootStep?.run)).toContain('npm ci');
|
||||
expect(stepRun('Build pinned shared runtime')).toContain('npm ci');
|
||||
expect(stepRun('Install and build pinned GitNexus runtime')).toContain('npm ci');
|
||||
expect(findStep('Build pinned shared runtime')).toBeUndefined();
|
||||
const gitnexusRun = stepRun('Install and build pinned GitNexus runtime');
|
||||
expect(gitnexusRun).toContain('npm ci');
|
||||
expect(gitnexusRun).toContain('npm run build');
|
||||
expect(gitnexusRun).toContain('mkdir -p ../gitnexus-shared/node_modules');
|
||||
expect(
|
||||
evolveJob?.steps?.some(
|
||||
(step) =>
|
||||
step['working-directory'] === 'gitnexus-shared' &&
|
||||
String(step.run ?? '').includes('npm ci'),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('waits for the runner boot-time package lock before installing containment tools', () => {
|
||||
|
|
|
|||
362
package-lock.json
generated
362
package-lock.json
generated
|
|
@ -15,7 +15,8 @@
|
|||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^3.8.0",
|
||||
"prettier-plugin-tailwindcss": "^0.7.0"
|
||||
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
|
|
@ -626,6 +627,189 @@
|
|||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/typescript-estree": "8.57.2",
|
||||
"@typescript-eslint/utils": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
|
||||
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.57.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/visitor-keys": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
|
||||
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.57.2",
|
||||
"@typescript-eslint/types": "^8.57.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
|
||||
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/typescript-estree": "8.57.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
|
||||
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.57.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/visitor-keys": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
|
||||
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.57.2",
|
||||
"@typescript-eslint/types": "^8.57.2",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz",
|
||||
|
|
@ -651,7 +835,35 @@
|
|||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
|
||||
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.57.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/visitor-keys": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz",
|
||||
"integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==",
|
||||
|
|
@ -673,6 +885,23 @@
|
|||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz",
|
||||
|
|
@ -691,48 +920,6 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz",
|
||||
"integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/typescript-estree": "8.57.2",
|
||||
"@typescript-eslint/utils": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz",
|
||||
|
|
@ -747,58 +934,6 @@
|
|||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz",
|
||||
"integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.57.2",
|
||||
"@typescript-eslint/tsconfig-utils": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/visitor-keys": "8.57.2",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"ts-api-utils": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz",
|
||||
"integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.57.2",
|
||||
"@typescript-eslint/types": "8.57.2",
|
||||
"@typescript-eslint/typescript-estree": "8.57.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.57.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz",
|
||||
|
|
@ -943,9 +1078,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
||||
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -2154,13 +2289,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.2"
|
||||
"brace-expansion": "^5.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
|
|
@ -2520,9 +2655,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"version": "7.8.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
|
||||
"integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
|
|
@ -2669,14 +2804,14 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
|
|
@ -2704,9 +2839,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
|
@ -2761,7 +2896,6 @@
|
|||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^3.8.0",
|
||||
"prettier-plugin-tailwindcss": "^0.7.0"
|
||||
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue