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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,17 @@ jobs:
# jobs (volar-test, witnesses, sim-nav-shard) restore it with
# fail-on-cache-miss, and their needs: build guarantees this job's
# post-job save has landed. The tree is BUILT (install + pnpm run build,
# same as nightly) — the sim-nav shards need the compiled plugin; v4
# splits off stale install-only v2 entries so a shard never restores an
# unbuilt tree.
# same as nightly) — the sim-nav shards need the compiled plugin; v5
# splits off the link:-override v4 trees (install-volar.sh's frozen
# install + repointed store entries replaced them).
- name: Clone volar (cache)
id: volar-cache
uses: actions/cache@v4
with:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-v4
key: volar-${{ env.VOLAR_SHA }}-v5

- name: Clone volar
if: steps.volar-cache.outputs.cache-hit != 'true'
Expand All @@ -144,19 +144,12 @@ jobs:
git -C "$RUNNER_TEMP/volar" fetch --deepen 50 origin "$VOLAR_BRANCH"
git -C "$RUNNER_TEMP/volar" checkout "$VOLAR_SHA"
}
# Point volar's typescript override at the CI-built TNB — volar
# master's override is `npm:typescript-native-bridge@<release>`, which
# would fetch the released package instead of the commit under test.
cd "$RUNNER_TEMP/volar"
node -e "const fs=require('fs');const p='pnpm-workspace.yaml';let s=fs.readFileSync(p,'utf8');s=s.replace(/typescript: npm:typescript-native-bridge@.*/,'typescript: link:'+process.env.GITHUB_WORKSPACE);fs.writeFileSync(p,s);console.log(s.match(/overrides:[\s\S]*/)[0])"

- name: Install + build volar
if: steps.volar-cache.outputs.cache-hit != 'true'
run: |
cd "$RUNNER_TEMP/volar"
corepack enable
pnpm install --no-frozen-lockfile
pnpm run build
# volar pins a released TNB; install-volar.sh repoints it at the
# commit under test.
run: tools/install-volar.sh "$RUNNER_TEMP/volar"

- name: Pack stock typescript
if: steps.volar-cache.outputs.cache-hit != 'true'
Expand Down Expand Up @@ -277,7 +270,7 @@ jobs:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-v4
key: volar-${{ env.VOLAR_SHA }}-v5
fail-on-cache-miss: true

# Runs on every CI run: the cached node_modules links
Expand All @@ -286,7 +279,8 @@ jobs:
run: |
# Volar's tsserver harness resolves typescript/lib/tsserver via a
# fresh module lookup from dirname(realpath(typescript/package.json)).
# With the link: override that realpath is this repo, so `typescript`
# install-volar.sh repoints the store entries here, so that realpath
# is this repo and `typescript`
# must resolve from here too — self-link it (TNB has no typescript
# dependency of its own; node_modules is CI-local state).
# node_modules may not exist at all: the root package has no
Expand Down Expand Up @@ -332,7 +326,7 @@ jobs:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-v4
key: volar-${{ env.VOLAR_SHA }}-v5
fail-on-cache-miss: true

- name: sweep-ls-throws
Expand Down Expand Up @@ -449,7 +443,7 @@ jobs:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-v4
key: volar-${{ env.VOLAR_SHA }}-v5
fail-on-cache-miss: true

- name: Run sim-nav shard
Expand Down
19 changes: 6 additions & 13 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,33 +97,26 @@ jobs:
id: volar-cache
# Both workflows need a BUILT volar tree (install + pnpm run build —
# the sim-nav shards load the compiled plugin). Keys stay per-workflow
# (nightly-v2 vs ci.yml's v4): first-writer-wins on a shared key would
# (nightly-v3 vs ci.yml's v5): first-writer-wins on a shared key would
# let one workflow's write timing define the other's content.
uses: actions/cache@v4
with:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-nightly-v2
key: volar-${{ env.VOLAR_SHA }}-nightly-v3

- name: Clone volar
if: steps.volar-cache.outputs.cache-hit != 'true'
run: |
git clone --depth 50 --branch "$VOLAR_BRANCH" https://github.com/vuejs/language-tools.git "$RUNNER_TEMP/volar"
git -C "$RUNNER_TEMP/volar" checkout "$VOLAR_SHA"
# Point volar's typescript override at the CI-built TNB — volar
# master's override is `npm:typescript-native-bridge@<release>`, which
# would fetch the released package instead of the commit under test.
cd "$RUNNER_TEMP/volar"
node -e "const fs=require('fs');const p='pnpm-workspace.yaml';let s=fs.readFileSync(p,'utf8');s=s.replace(/typescript: npm:typescript-native-bridge@.*/,'typescript: link:'+process.env.GITHUB_WORKSPACE);fs.writeFileSync(p,s)"

- name: Install + build volar
if: steps.volar-cache.outputs.cache-hit != 'true'
run: |
cd "$RUNNER_TEMP/volar"
corepack enable
pnpm install --no-frozen-lockfile
pnpm run build
# volar pins a released TNB; install-volar.sh repoints it at the
# commit under test.
run: tools/install-volar.sh "$RUNNER_TEMP/volar"

- name: Pack stock typescript
if: steps.volar-cache.outputs.cache-hit != 'true'
Expand Down Expand Up @@ -238,7 +231,7 @@ jobs:
path: |
${{ runner.temp }}/volar
${{ runner.temp }}/volar-stock
key: volar-${{ env.VOLAR_SHA }}-nightly-v2
key: volar-${{ env.VOLAR_SHA }}-nightly-v3
fail-on-cache-miss: true

- name: Run sim-nav shard
Expand Down
7 changes: 3 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ TNB is a tsgo-backed TypeScript fork: upstream `microsoft/TypeScript` and `micro
export VOLAR_SHA=<.github/workflows/ci.yml value>
git clone --depth 50 --branch master https://github.com/vuejs/language-tools.git /tmp/volar
git -C /tmp/volar checkout "$VOLAR_SHA" || { git -C /tmp/volar fetch --deepen 50 origin master; git -C /tmp/volar checkout "$VOLAR_SHA"; }
# point /tmp/volar/pnpm-workspace.yaml's typescript override at this repo (link:<repo>)
cd /tmp/volar && corepack enable && pnpm install --no-frozen-lockfile && pnpm run build
cd <repo> && VOLAR_ROOT=/tmp/volar STOCK_TSSERVER_PATH=/tmp/stock-ts-p3/package/lib/tsserver.js npm run check:sim-nav
cd <repo> && tools/install-volar.sh /tmp/volar # frozen install, released TNB repointed at <repo>
VOLAR_ROOT=/tmp/volar STOCK_TSSERVER_PATH=/tmp/stock-ts-p3/package/lib/tsserver.js npm run check:sim-nav
```
`check:sourcefile-guard` needs only `VOLAR_ROOT`. Both were run in full for #72/#73. `check-pristine-attribution` clones pristine tsgo into `/tmp/tnb-pristine-tsgo` and refuses to run only if one of its own repro files is already present there — a stray file of another name goes unnoticed, so check `git -C /tmp/tnb-pristine-tsgo status --porcelain` yourself.
- `node tools/ci-witness-groups.mjs all` (the orphan / local-only / baseline sweep) runs only locally and in nightly — ci.yml's prepare job calls `matrix` mode, so a witness added without a group entry is not caught per-commit.
Expand Down Expand Up @@ -95,7 +94,7 @@ Mechanism-level compromises the convergence audit decided to keep as-is. Each en

- **Arena oversize escapes to the JSON path** — an arena encode that would overflow (record/string regions meet) fails and the call's JSON document returns out-of-band as the napi value instead; 4 MiB is ~80× the largest expected hot-class response, so capacity is by design. Accepted because the escape is the same transport every method already has, and it turns an overflow into a loud failed encode rather than memory corruption. Policed by `triage-arena-parity.mjs` (every arena-capable method runs once per transport — JSON and arena — and must byte-equal).
- **Stale type handles die loudly, by construction** — Go TypeIDs are per-checker counters (`t.id = TypeId(c.TypeCount)`) with no generation marker, so the JS side pins handles in a snapshot-scoped registry (`projectRegistries`, keyed by TypeID = `t.Id()`): a handle from an old snapshot either still names the same type or fails with "type handle N not found in project registry" — it can never silently resolve to a rebuilt snapshot's same-id type. Accepted because stable ids are what let unchanged types reuse across generations; the cost is the loud-error contract. Policed by `triage-crossgen-reuse.mjs` (negative test: a handle captured pre-edit must die, not re-resolve).
- **Stable-SF cross-generation reuse rides external-change event completeness** — a reused stable host SourceFile is only as fresh as the events that evict it: every external rewrite must reach `_pendingExternalChangePaths`, drained by the overlay collect — the single transport choke (host≠disk rides the overlay push, host==disk becomes updateSnapshot `fileChanges.changed`). Accepted because one choke keeps the invariant checkable. Policed by `triage-external-edits.mjs` (local-only), incl. the stablecache phase.
- **Stable-SF cross-generation reuse rides external-change event completeness** — a reused stable host SourceFile is only as fresh as the events that evict it: every external rewrite must reach `_pendingExternalChangePaths`, drained by `drainExternalFileChanges` — the single transport choke, called from both updateSnapshot sites (the createTsgoProgram collect and the reused-program query sync `pushHostOverlayToTsgo`, issue #74) (host≠disk rides the overlay push, host==disk becomes updateSnapshot `fileChanges.changed`). Accepted because one choke keeps the invariant checkable. Policed by `triage-external-edits.mjs` (local-only), incl. the stablecache phase.
- **Patch monoliths are accepted debt** — 0001-bridge-inplace (napi-shim overlay files + checker accessors + native-preview client — 48 files) and 0004-api-surface (the whole RPC surface: `internal/api/proto.go` + `session.go` + the native-preview api files) stay as single patches; splitting is deferred. Accepted because the carve-outs that do rebase independently (osvfs, noembed) already have their own patches. Policed by `npm run save-patches` (regenerates both from the submodule diff).
- **tsgoChecker.ts is one monolith** — the overlay adapter is a single ~13.8k-line file. Accepted because it mirrors one bridge contract; splitting it would invent an interface for ceremony's sake. Policed by review, not a gate.
- **SubstitutionType.constraint alias is code-path parity only** — the stock-name `constraint` resolving alias fires the same fetch the NESTED wire loop uses for `substConstraint`. Accepted because it is a one-line alias over an already-gated fetch. Policed by `triage-checker-fullwalk`'s FW_SUBST_CONSTRAINT keys (the census corpus's `NoInfer<T>` is the reliable source-level trigger); the alias-vs-wire field-name divergence stays pinned there.
Expand Down
16 changes: 13 additions & 3 deletions patches/typescript/0001-tsgo-hooks.patch
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ index aa578c7ed0..0e49d57945 100644
const result = this.tryFindDefaultConfiguredProjectAndLoadAncestorsForOpenScriptInfo(
info,
diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts
index 8635d543cd..bcc848c1a6 100644
index 8635d543cd..99cad4cab8 100644
--- a/src/server/scriptInfo.ts
+++ b/src/server/scriptInfo.ts
@@ -34,6 +34,8 @@ import {
Expand Down Expand Up @@ -563,7 +563,17 @@ index 8635d543cd..bcc848c1a6 100644
/**
* Reads the contents from tempFile(if supplied) or own file and sets it as contents
* returns true if text changed
@@ -440,7 +471,7 @@ export class ScriptInfo {
@@ -179,6 +210,9 @@ export class TextStorage {
this.getFileTextAndSize(tempFileName) :
{ text: "", fileSize: undefined };
const reloaded = this.reload(newText);
+ // #74: tsserver `reload` of an open file (the client re-read it from
+ // disk after an external rewrite) — same signal as reloadForOpen.
+ if (reloaded) tnbNoteExternalFileChange(this.info.fileName);
this.fileSize = fileSize; // NB: after reload since reload clears it
this.ownFileText = !tempFileName || tempFileName === this.info.fileName;
// In case we update this text before mTime gets updated to present file modified time
@@ -440,7 +474,7 @@ export class ScriptInfo {
this.textStorage.isOpen = true;
if (
newText !== undefined &&
Expand All @@ -572,7 +582,7 @@ index 8635d543cd..bcc848c1a6 100644
) {
// reload new contents only if the existing contents changed
this.markContainingProjectsAsDirty();
@@ -675,6 +706,13 @@ export class ScriptInfo {
@@ -675,6 +709,13 @@ export class ScriptInfo {

editContent(start: number, end: number, newText: string): void {
this.textStorage.edit(start, end, newText);
Expand Down
Loading
Loading