chore(deps): count triple-slash type references as candidates
From Copilot's twelfth pass of #1962. A `/// <reference types="x" />` pulls in a package's declarations exactly like an import does, but TypeScript reports it in `preProcessFile`'s `typeReferenceDirectives`, not `importedFiles` — and only the latter was read. A shared source using one could therefore load the root's copy of a package alongside a client's without it ever entering the candidate set, so its skew would pass. Same silent-miss direction as the earlier syntax gaps, through a channel the parser switch didn't cover because I only read one of its two outputs. `typeReferencePackageNames` maps a directive to both candidates, since the name is the *type* name and not the package: `node` resolves to `@types/node`, while a package shipping its own declarations resolves to itself. Returning both over-approximates, the safe direction — whichever isn't installed drops out downstream. Scoped names mangle `@scope/pkg` → `@types/scope__pkg`, TypeScript's convention. `path` references name a file, not a package, and are ignored. The real candidate set is unchanged at 17: no shared source uses a triple-slash reference today. As with the other syntax fixes, this closes the channel against the one that arrives later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
This commit is contained in:
parent
af63a07817
commit
87c1e05420
1 changed files with 64 additions and 2 deletions
|
|
@ -175,7 +175,30 @@ function typescript() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Every third-party package name imported by a blob of TypeScript source.
|
||||
* The package(s) a `/// <reference types="x" />` directive can resolve to
|
||||
* (Copilot, #1962). Such a directive pulls in declarations exactly like an
|
||||
* import does, but TypeScript reports it separately from `importedFiles`, so
|
||||
* reading only the latter would let a referenced package skew unseen.
|
||||
*
|
||||
* Both candidates are returned because the directive name is the *type* name,
|
||||
* not the package: `node` resolves to `@types/node`, while a package shipping
|
||||
* its own declarations resolves to itself. Returning both over-approximates,
|
||||
* which is the safe direction — whichever isn't installed drops out. A scoped
|
||||
* name mangles as `@scope/pkg` → `@types/scope__pkg`, TypeScript's convention.
|
||||
*/
|
||||
export function typeReferencePackageNames(directive) {
|
||||
const name = packageNameOf(directive);
|
||||
if (!name) return [];
|
||||
const scoped = /^@([^/]+)\/(.+)$/.exec(name);
|
||||
const typesName = scoped
|
||||
? `@types/${scoped[1]}__${scoped[2]}`
|
||||
: `@types/${name}`;
|
||||
return [name, typesName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Every third-party package name whose declarations a blob of TypeScript source
|
||||
* pulls in — via an import of any form, or a triple-slash type reference.
|
||||
* Over-approximating is safe (a name absent from every lockfile contributes
|
||||
* nothing downstream — `@inspector/core` is a build-time alias, not a package,
|
||||
* and drops out that way); under-approximating is not, since a missed package
|
||||
|
|
@ -185,11 +208,14 @@ export function importedPackageNames(source) {
|
|||
const names = new Set();
|
||||
// (source, readImportFiles, detectJavaScriptImports) — the latter two make it
|
||||
// report `require(…)` and dynamic imports as well as static ones.
|
||||
const { importedFiles } = typescript().preProcessFile(source, true, true);
|
||||
const { importedFiles, typeReferenceDirectives } =
|
||||
typescript().preProcessFile(source, true, true);
|
||||
for (const { fileName } of importedFiles) {
|
||||
const name = packageNameOf(fileName);
|
||||
if (name) names.add(name);
|
||||
}
|
||||
for (const { fileName } of typeReferenceDirectives ?? [])
|
||||
for (const name of typeReferencePackageNames(fileName)) names.add(name);
|
||||
return names;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue