Skip to content

Commit fc7cfe5

Browse files
committed
♻️ Consume @deno/doc v2 Symbol/Declaration natively (drop DocNode shim)
Removes the `DocNode = Declaration & { name }` compatibility adapter and the `{ ...declaration, name }` re-attachment introduced during the v2 upgrade. The renderer now consumes v2's model directly: a `DocPageSection` holds a raw `Declaration`, and the render helpers receive the owning symbol's identity via `type SymbolInfo = Omit<Symbol, "declarations">` — sourced from the real `Symbol` at build time and from `{ name: page.name }` at render time. - `extract(declaration, symbol)`, `Type({ declaration, symbol })`, `exportHash(declaration, symbol, i)`; `DocPageSection.node` → `declaration`. - Namespace members recurse over `NamespaceDef.elements` (real member Symbols) instead of synthesizing flat nodes. - `DocPage.name` is kept (many call sites read it); `SymbolInfo` flows only through the render helpers. - Both docs paths updated (`package.ts` and the `/x` `package/node.ts`). No rendered-output change (verified: API pages, experimental pages, and `/x` contrib pages render identically).
1 parent c71b8b3 commit fc7cfe5

6 files changed

Lines changed: 80 additions & 68 deletions

File tree

www/components/api/api-page.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,17 @@ export function* ApiBody({
159159
<h2
160160
class="my-0! grow"
161161
id={section.id}
162-
data-kind={section.node.kind}
163-
data-name={section.node.name}
162+
data-kind={section.declaration.kind}
163+
data-name={page.name}
164164
>
165-
{yield* Type({ node: section.node })}
165+
{yield* Type({
166+
declaration: section.declaration,
167+
symbol: { name: page.name },
168+
})}
166169
</h2>
167170
<a
168171
class="opacity-40 before:content-['View_code'] group-hover:opacity-100 before:flex before:text-xs before:mr-1 p-2 flex-none flex rounded no-underline items-center h-8"
169-
href={`${section.node.location.url}`}
172+
href={`${section.declaration.location.url}`}
170173
>
171174
<SourceCodeIcon />
172175
</a>

www/components/type/jsx.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { JSXElement } from "revolution/jsx-runtime";
22
import { type Operation } from "effection";
33
import type {
4+
Declaration,
45
ParamDef,
56
TsTypeDef,
67
TsTypeParamDef,
78
TsTypeRefDef,
89
VariableDef,
910
} from "@deno/doc";
10-
import type { DocNode } from "../../hooks/use-deno-doc.tsx";
11+
import type { SymbolInfo } from "../../hooks/use-deno-doc.tsx";
1112
import {
1213
Builtin,
1314
ClassName,
@@ -18,11 +19,13 @@ import {
1819
} from "./tokens.tsx";
1920

2021
interface TypeProps {
21-
node: DocNode;
22+
declaration: Declaration;
23+
symbol: SymbolInfo;
2224
}
2325

2426
export function* Type(props: TypeProps): Operation<JSXElement> {
25-
let { node } = props;
27+
// `node` aliases the declaration; `symbol` supplies the name.
28+
let { declaration: node, symbol } = props;
2629

2730
switch (node.kind) {
2831
case "function": {
@@ -32,7 +35,7 @@ export function* Type(props: TypeProps): Operation<JSXElement> {
3235
{node.def.isAsync ? <Punctuation>{"async "}</Punctuation> : <></>}
3336
<Keyword>{node.kind}</Keyword>
3437
{node.def.isGenerator ? <Punctuation>*</Punctuation> : <></>}{" "}
35-
<span class="token function">{node.name}</span>
38+
<span class="token function">{symbol.name}</span>
3639
{typeParams.length > 0
3740
? <InterfaceTypeParams typeParams={typeParams} />
3841
: <></>}
@@ -48,7 +51,7 @@ export function* Type(props: TypeProps): Operation<JSXElement> {
4851
let impl /* implements */ = node.def.implements ?? [];
4952
return (
5053
<span class="language-ts code-highlight inline-block">
51-
<Keyword>{node.kind}</Keyword> <ClassName>{node.name}</ClassName>
54+
<Keyword>{node.kind}</Keyword> <ClassName>{symbol.name}</ClassName>
5255
{node.def.extends
5356
? (
5457
<>
@@ -77,7 +80,7 @@ export function* Type(props: TypeProps): Operation<JSXElement> {
7780
let ext = node.def.extends ?? [];
7881
return (
7982
<span class="language-ts code-highlight inline-block">
80-
<Keyword>{node.kind}</Keyword> <ClassName>{node.name}</ClassName>
83+
<Keyword>{node.kind}</Keyword> <ClassName>{symbol.name}</ClassName>
8184
{typeParams.length > 0
8285
? <InterfaceTypeParams typeParams={typeParams} />
8386
: <></>}
@@ -99,14 +102,14 @@ export function* Type(props: TypeProps): Operation<JSXElement> {
99102
case "variable":
100103
return (
101104
<span class="inline-block">
102-
<TSVariableDef variableDef={node.def} name={node.name} />
105+
<TSVariableDef variableDef={node.def} name={symbol.name} />
103106
</span>
104107
);
105108
case "typeAlias":
106109
return (
107110
<span class="inline-block">
108111
<Keyword>{"type "}</Keyword>
109-
{node.name}
112+
{symbol.name}
110113
<Operator>{" = "}</Operator>
111114
<TypeDef typeDef={node.def.tsType} />
112115
</span>
@@ -117,7 +120,7 @@ export function* Type(props: TypeProps): Operation<JSXElement> {
117120
console.log("<Type> unimplemented", node.kind);
118121
return (
119122
<span class="inline-block">
120-
<Keyword>{node.kind}</Keyword> {node.name}
123+
<Keyword>{node.kind}</Keyword> {symbol.name}
121124
</span>
122125
);
123126
}

www/components/type/markdown.tsx

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { Operation } from "effection";
22
import type {
33
ClassMethodDef,
4+
Declaration,
45
ParamDef,
56
TsTypeDef,
67
TsTypeParamDef,
78
} from "@deno/doc";
89
import { toHtml } from "hast-util-to-html";
9-
import { type DocNode, DocPage } from "../../hooks/use-deno-doc.tsx";
10+
import { DocPage, type SymbolInfo } from "../../hooks/use-deno-doc.tsx";
1011
import { Icon } from "./icon.tsx";
1112

1213
const NEW =
@@ -19,7 +20,8 @@ const READONLY =
1920
export const NO_DOCS_AVAILABLE = "*No documentation available.*";
2021

2122
export function* extract(
22-
node: DocNode,
23+
node: Declaration,
24+
symbol: SymbolInfo,
2325
): Operation<{ markdown: string; ignore: boolean; pages: DocPage[] }> {
2426
let lines = [];
2527
let pages: DocPage[] = [];
@@ -65,7 +67,7 @@ export function* extract(
6567
lines.push(`### Constructors`, "<dl>");
6668
for (let constructor of constructors) {
6769
lines.push(
68-
`<dt>${NEW} **${node.name}**(${
70+
`<dt>${NEW} **${symbol.name}**(${
6971
constructor.params
7072
.map(Param)
7173
.join(", ")
@@ -100,37 +102,37 @@ export function* extract(
100102
}
101103

102104
if (node.kind === "namespace") {
103-
// v2 namespace elements are grouped symbols; flatten their declarations
104-
// back into flat variable nodes (re-attaching the symbol name).
105-
let variables: DocNode[] = node.def.elements.flatMap((element) =>
105+
// v2 namespace elements are member symbols; render each variable member,
106+
// passing the member's own symbol for its name/identity.
107+
let members = node.def.elements.flatMap((element) =>
106108
element.declarations
107109
.filter((declaration) => declaration.kind === "variable")
108-
.map((declaration) => ({ ...declaration, name: element.name }))
110+
.map((declaration) => ({ declaration, member: element }))
109111
);
110-
if (variables.length > 0) {
112+
if (members.length > 0) {
111113
lines.push("### Variables");
112114
lines.push("<dl>");
113-
for (let variable of variables) {
114-
let name = `${node.name}.${variable.name}`;
115-
let section = yield* extract(variable);
116-
let description = variable.jsDoc?.doc || NO_DOCS_AVAILABLE;
115+
for (let { declaration, member } of members) {
116+
let name = `${symbol.name}.${member.name}`;
117+
let section = yield* extract(declaration, member);
118+
let description = declaration.jsDoc?.doc || NO_DOCS_AVAILABLE;
117119
pages.push({
118120
name,
119-
kind: variable.kind,
121+
kind: declaration.kind,
120122
description,
121123
dependencies: [],
122124
sections: [
123125
{
124-
id: exportHash(variable, 0),
125-
node: variable,
126+
id: exportHash(declaration, member, 0),
127+
declaration,
126128
markdown: section.markdown,
127129
ignore: section.ignore,
128130
},
129131
],
130132
});
131133
lines.push(
132134
`<dt>`,
133-
toHtml(<Icon kind={variable.kind} />),
135+
toHtml(<Icon kind={declaration.kind} />),
134136
`[${name}](${name})`,
135137
`</dt>`,
136138
);
@@ -245,11 +247,15 @@ export function* extract(
245247
};
246248
}
247249

248-
export function exportHash(node: DocNode, index: number): string {
249-
return [node.kind, node.name, index].filter(Boolean).join("_");
250+
export function exportHash(
251+
declaration: Declaration,
252+
symbol: SymbolInfo,
253+
index: number,
254+
): string {
255+
return [declaration.kind, symbol.name, index].filter(Boolean).join("_");
250256
}
251257

252-
export function TypeParams(typeParams: TsTypeParamDef[], node: DocNode) {
258+
export function TypeParams(typeParams: TsTypeParamDef[], node: Declaration) {
253259
let lines = [];
254260
if (typeParams.length > 0) {
255261
lines.push("### Type Parameters");

www/hooks/use-deno-doc.tsx

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type Document,
77
LoadResponse,
88
Location,
9+
type Symbol as DocSymbol,
910
} from "@deno/doc";
1011
import { call, type Operation, until, useScope } from "effection";
1112
import { createGraph } from "@deno/graph";
@@ -22,14 +23,15 @@ export const npmSpecifierPattern = regex(
2223
);
2324

2425
/**
25-
* A single documented declaration.
26+
* A symbol's identity without its declarations.
2627
*
27-
* As of `@deno/doc` v2 (>=0.195.0), `doc()` returns a `Document` per module
28-
* whose `symbols` group declarations by name. `DocNode` re-attaches the symbol
29-
* `name` onto each `Declaration` so the rest of the rendering pipeline can keep
30-
* treating a documented symbol as a single flat node.
28+
* `@deno/doc` v2 groups a module's declarations under a `Symbol` ({ name,
29+
* isDefault?, declarations }). Renderers only need the identity (name /
30+
* isDefault), not the sibling declarations — which would duplicate the
31+
* per-section `Declaration`s — so they take a `SymbolInfo`. A full `Symbol` is
32+
* structurally assignable to it, so build-time code can pass the real Symbol.
3133
*/
32-
export type DocNode = Declaration & { name: string };
34+
export type SymbolInfo = Omit<DocSymbol, "declarations">;
3335

3436
export function* useDenoDoc(
3537
specifiers: string[],
@@ -49,7 +51,7 @@ export interface DocPage {
4951
name: string;
5052
sections: DocPageSection[];
5153
description: string;
52-
kind: DocNode["kind"];
54+
kind: Declaration["kind"];
5355
dependencies: Dependency[];
5456
/**
5557
* True when this symbol is exported from the package's `./experimental`
@@ -61,7 +63,7 @@ export interface DocPage {
6163
export interface DocPageSection {
6264
id: string;
6365

64-
node: DocNode;
66+
declaration: Declaration;
6567

6668
markdown?: string;
6769

@@ -142,19 +144,17 @@ export function* useDocPages(
142144
for (let [url, document] of Object.entries(docs)) {
143145
let pages: DocPage[] = [];
144146
for (let symbol of document.symbols) {
145-
// v2 groups declarations by symbol name; re-attach the name to each
146-
// declaration so downstream rendering can treat it as a flat node.
147-
let nodes: DocNode[] = symbol.declarations.map((declaration) => ({
148-
...declaration,
149-
name: symbol.name,
150-
}));
151-
147+
// v2 groups declarations under a symbol; render each declaration as a
148+
// section, passing the owning symbol for its name/identity.
152149
let sections: DocPageSection[] = [];
153-
for (let node of nodes) {
154-
let { markdown, ignore, pages: _pages } = yield* extract(node);
150+
for (let declaration of symbol.declarations) {
151+
let { markdown, ignore, pages: _pages } = yield* extract(
152+
declaration,
153+
symbol,
154+
);
155155
sections.push({
156-
id: exportHash(node, sections.length),
157-
node,
156+
id: exportHash(declaration, symbol, sections.length),
157+
declaration,
158158
markdown,
159159
ignore,
160160
});
@@ -175,7 +175,7 @@ export function* useDocPages(
175175

176176
pages.push({
177177
name: symbol.name,
178-
kind: nodes.at(0)?.kind!,
178+
kind: symbol.declarations.at(0)?.kind!,
179179
description,
180180
sections,
181181
dependencies: externalDependencies,
@@ -281,8 +281,8 @@ function isDocPageSection(value: unknown): value is DocPageSection {
281281

282282
return (
283283
typeof section.id === "string" &&
284-
typeof section.node === "object" &&
285-
section.node !== null && // You might need a guard for DocNode if it's complex
284+
typeof section.declaration === "object" &&
285+
section.declaration !== null &&
286286
(typeof section.markdown === "undefined" ||
287287
typeof section.markdown === "string") &&
288288
typeof section.ignore === "boolean"
@@ -318,19 +318,19 @@ function* extractImports(
318318
}
319319

320320
/**
321-
* LocalDocsPages are DocNodes that are stored locally
321+
* LocalDocsPages are declarations that are stored locally
322322
* but they represent symbols hosted on GitHub. They
323-
* have LocalDocNode locations that include URLs to GitHub.
323+
* have LocalDeclaration locations that include URLs to GitHub.
324324
*/
325325
export type LocalDocsPages = Record<string, LocalDocPage[]>;
326326

327327
export type LocalDocPage = DocPage & { sections: LocalDocPageSection[] };
328328

329329
export type LocalDocPageSection = DocPageSection & {
330-
node: LocalDocNode;
330+
declaration: LocalDeclaration;
331331
};
332332

333-
export type LocalDocNode = DocNode & {
333+
export type LocalDeclaration = Declaration & {
334334
location: LocalLocation;
335335
};
336336

www/lib/package.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,17 +169,17 @@ function* initPackage(
169169
experimental: entrypoint === "./experimental",
170170
sections: page.sections.map((section) => ({
171171
...section,
172-
node: {
173-
...section.node,
172+
declaration: {
173+
...section.declaration,
174174
location: {
175-
...section.node.location,
175+
...section.declaration.location,
176176
url: new URL(
177177
`${
178178
relative(
179179
path,
180-
fileURLToPath(section.node.location.filename),
180+
fileURLToPath(section.declaration.location.filename),
181181
)
182-
}#L${section.node.location.line + 1}`,
182+
}#L${section.declaration.location.line + 1}`,
183183
`${ref.url}/`,
184184
),
185185
},

www/lib/package/node.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,17 +258,17 @@ export function createNodePackage(
258258
...page,
259259
sections: page.sections.map((section) => ({
260260
...section,
261-
node: {
262-
...section.node,
261+
declaration: {
262+
...section.declaration,
263263
location: {
264-
...section.node.location,
264+
...section.declaration.location,
265265
url: new URL(
266266
`${
267267
relative(
268268
path,
269-
fileURLToPath(section.node.location.filename),
269+
fileURLToPath(section.declaration.location.filename),
270270
)
271-
}#L${section.node.location.line + 1}`,
271+
}#L${section.declaration.location.line + 1}`,
272272
`${ref.url}/`,
273273
),
274274
},

0 commit comments

Comments
 (0)