Repository navigation
feat(model): anchors, release tags and manifest facts in model v3 - #15
systemfsoftware-maker wants to merge 2 commits into
Conversation
Every item in the model now carries its source anchor, release tag and
`@stability` value, and the model carries the package's `exports` and
`imports` maps, the analysis messages with their anchors, the anchor base
and the coverage. The schema version moves to 3 and a version 2 model is
refused.
A source index parses every source both emitter routes read with
oxc-parser, so the two routes give the same anchors without declaration
maps; the TSDoc helpers are shared with the message resolver. `readPackage`
resolves targets through the requested condition, then `types`, `import`,
`default` and the first remaining key, accepts a bare-string `exports` and
bare path targets, refuses a target that is not TypeScript source, and
records an `imports` map as internal surface. `analyse` returns
`{ route, model }` with the messages and coverage in the model.
Type parameters keep their `in`, `out` and `const` modifiers, closing the
PR #7 review's deferred finding. The generated schema-law suite is enabled
through `inlineSchemaTests()`.
There was a problem hiding this comment.
Independent review at 4b700c8 (checks green, version bump consistent, no attribution trailers).
Blocking:
- src/front-end/source-index.ts:148 with recordBlock/precedingEnd: the first member of a namespace takes its release tag and @stability from the wrong comment. Inside a namespace, siblings = block.body, so for the first member precedingEnd returns 0 and leadingDocComment picks the last doc comment in the whole file before the member: the namespace's own comment or an earlier top-level declaration's.
/** @beta */ export namespace Config { export const load = 1 }records Config.load as beta (should be untagged);/** @internal */ export const x = 1thenexport namespace NS { export const a = 1 }marks NS.a internal. Floor should be the block's opening brace (block.start). Add a test for a tag on a namespace member.
Non-blocking:
2. read-package.ts entryPointsOf: one non-TS export (e.g. "./styles.css") now fails the whole package with NoSourceEntry; the ./legacy fixture case was removed instead of kept; TYPESCRIPT_SOURCE matches .d.ts, so a types: ./dist/x.d.ts fallback becomes an entry point in dist. Confirm intended vs. an unresolved coverage reason.
3. api-model.schema.ts:135-141: coverage kinds unsupported/unresolved/external-star and CoverageReason.construct are declared but never emitted.
4. recordBlock: dotted namespaces (namespace A.B {}) lose anchors because the nested TSModuleDeclaration body is ignored.
5. lineAt (source-index.ts:93): the mutable cursor assumes increasing offsets; a line-start table with binary search removes the order dependence.
leadreview
The first statement in a namespace block has no preceding sibling, so precedingEnd returned 0 and the leading-doc-comment lookup reached back past the namespace to an unrelated top-level comment. The first member of a @beta namespace inherited beta, and a member of a later namespace inherited an earlier declaration's internal tag. Carry the block's opening brace as the comment floor in the source index context and use the greater of that floor and the preceding sibling's end. The namespace's own tag still comes from its own leading comment. Adds a model v3 scenario covering a tagged namespace whose first member is untagged and whose second member carries its own tag, plus a later namespace after an internal declaration.
What
Model schema version 3, the source index and manifest target resolution: the model additions the report and the change report read.
function,variable,typeAlias,interface,class,enum,namespace) carriesanchor(repository-relative file and 1-based line, ornull),releaseTag(public,beta,alpha,internaloruntagged) andstability(the TSDoc@stabilityvalue, ornull).ApiModelv3 carriesanchorBase,package.exportsandpackage.imports(ManifestEntry),messages(moved tosrc/model/message.schema.ts, each with its anchor) andcoveragewith typed reasons (unsupported,unresolved,unmapped-anchor,pattern-empty,parse,external-star). A model declaring schema version 2 is refused by the decoder.Declarationsgainssources; both emitter routes populate it, so the source index never re-reads the filesystem.source-index.tsparses each source withoxc-parserand records declarations, release tags,@stability, re-export statements and ambient block positions; the comment and tag helpers shared with the message resolver live intsdoc.ts.readPackageresolves every export through the requested condition, thentypes,import,default, then the first remaining key; a bare-stringexportsnames.; a resolved target that is not TypeScript source is refused withNoSourceEntry, naming the export key and target;importsmaps are decoded and recorded as internal surface, so a package with an imports map and noexportsanalyses to zero entry points with complete coverage.FrontEnd.analysereturns{ route, model }; the messages and coverage live in the model.buildModel(declarations)still builds from declarations alone, with every item unanchored and anunmapped-anchorreason.in,outandconstmodifiers, closing the PR feat(front-end): build the API model from a package's source #7 review's deferred finding.inlineSchemaTests(), so every exported schema carries round-trip and encode-stability laws.Evidence
vitest run: 335 tests pass across 13 files (the existing suite, the generated schema laws and the new version 3 feature), and the e2e front-end feature passes 4/4.tsc --noEmitandoxlint: clean for the package and both apps.model-v3.integration.test.ts: a bare-string export resolves.; anchors, release tags and@stabilityland on items; a namespace export anchors to its re-export line while its member anchors; a declarations-only model leaves itemsanchor: nullwith anunmapped-anchorreason; an untagged export carriesae-missing-release-tagat its own line; an imports-only package has zero entry points and complete coverage; a namespace member takes its tag from its own doc comment rather than the namespace's or an earlier declaration's.serialise.integration.test.ts: aschemaVersion: 2document is refused and canonical round-tripping still holds.Notes
No new runtime dependency. The package keeps its single
.export and onemod.tsbarrel. No private repository, package or path is named. A changeset records the breaking model version.Review follow-ups (non-blocking, from the review at 4b700c8; not committed)
precedingEndreturned 0 and the leading-doc-comment lookup reached back past the namespace to an unrelated top-level comment. The block's opening brace is now the comment floor. A model v3 scenario covers a@betanamespace whose first member is untagged and whose second member carries its own@internaltag, plus a later namespace after an internal declaration. The scenario was confirmed to fail without the floor fix.read-package.tsentryPointsOf: one non-TypeScript export (e.g../styles.css) now fails the whole package withNoSourceEntry; the./legacyfixture case was removed instead of kept;TYPESCRIPT_SOURCEmatches.d.ts, so atypes: ./dist/x.d.tsfallback becomes an entry point indist. Confirm intended versus anunresolvedcoverage reason.api-model.schema.ts135-141: the coverage kindsunsupported,unresolvedandexternal-star, andCoverageReason.construct, are declared but never emitted.recordBlock: dotted namespaces (namespace A.B {}) lose anchors because the nestedTSModuleDeclarationbody is ignored.lineAt(source-index.ts:93): the mutable cursor assumes increasing offsets; a line-start table with binary search removes the order dependence.