Skip to content

Commit 044a8ec

Browse files
authored
Add rendering logic for properties elements (#38)
* Add logic to render properties in documentation * Add test coverage * Use more compact inline rendering style
1 parent c57d61d commit 044a8ec

4 files changed

Lines changed: 141 additions & 1 deletion

File tree

src/lib/reference/render.ts

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22
* Apple Developer Reference documentation rendering functionality
33
*/
44

5-
import type { AppleDocJSON, ContentItem, IndexContentItem, TopicSection, Variant } from "./types"
5+
import type {
6+
AppleDocJSON,
7+
ContentItem,
8+
IndexContentItem,
9+
PropertyItem,
10+
TopicSection,
11+
Variant,
12+
} from "./types"
613

714
interface RenderOptions {
815
externalOrigin?: string
@@ -77,6 +84,16 @@ export async function renderFromJSON(
7784
)
7885
}
7986

87+
// Add properties (used by object/dictionary pages in data docs)
88+
const propertiesSection = jsonData.primaryContentSections.find((s) => s.kind === "properties")
89+
if (propertiesSection?.items) {
90+
markdown += renderProperties(
91+
propertiesSection.items,
92+
jsonData.references,
93+
options.externalOrigin,
94+
)
95+
}
96+
8097
// Add content sections
8198
const contentSections = jsonData.primaryContentSections.filter((s) => s.kind === "content")
8299
for (const section of contentSections) {
@@ -242,6 +259,60 @@ function renderParameters(
242259
return markdown
243260
}
244261

262+
/**
263+
* Render properties section for data dictionary pages.
264+
*/
265+
function renderProperties(
266+
properties: PropertyItem[],
267+
references?: Record<string, ContentItem>,
268+
externalOrigin?: string,
269+
): string {
270+
if (properties.length === 0) return ""
271+
272+
let markdown = "## Properties\n\n"
273+
274+
for (const property of properties) {
275+
if (!property.name) continue
276+
277+
const typeText = renderPropertyType(property.type, references, externalOrigin)
278+
const requiredText = property.required === true ? "required" : "optional"
279+
const metadata = [typeText, requiredText].filter(Boolean)
280+
const headingSuffix = metadata.length > 0 ? ` *(${metadata.join(", ")})*` : ""
281+
markdown += `### \`${property.name}\`${headingSuffix}\n\n`
282+
283+
if (property.content && Array.isArray(property.content)) {
284+
markdown += `${renderContentArray(property.content, references, 0, externalOrigin)}`
285+
}
286+
287+
const allowedValues = property.attributes?.find((a) => a.kind === "allowedValues")?.values
288+
if (allowedValues && allowedValues.length > 0) {
289+
const possibleValues = allowedValues.map((value) => `\`${value}\``).join(", ")
290+
markdown += `Possible Values: ${possibleValues}\n\n`
291+
}
292+
}
293+
294+
return markdown
295+
}
296+
297+
function renderPropertyType(
298+
type: Array<{ text?: string; kind?: string; identifier?: string }> | undefined,
299+
references?: Record<string, ContentItem>,
300+
externalOrigin?: string,
301+
): string {
302+
if (!type || type.length === 0) return ""
303+
304+
return type
305+
.map((part) => {
306+
if (part.kind === "typeIdentifier" && part.identifier && part.text) {
307+
const url = convertIdentifierToURL(part.identifier, references, externalOrigin)
308+
return url ? `[${part.text}](${url})` : part.text
309+
}
310+
return part.text || ""
311+
})
312+
.join("")
313+
.trim()
314+
}
315+
245316
/**
246317
* Render main content sections
247318
*/

src/lib/reference/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type {
2121
Parameter,
2222
Platform,
2323
PrimaryContentSection,
24+
PropertyItem,
2425
SeeAlsoSection,
2526
SwiftInterfaceItem,
2627
SymbolVariant,

src/lib/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,25 @@ export interface PrimaryContentSection {
116116
content?: ContentItem[]
117117
declarations?: Declaration[]
118118
parameters?: Parameter[]
119+
items?: PropertyItem[]
120+
}
121+
122+
/**
123+
* Represents a property item used in data dictionary pages.
124+
*/
125+
export interface PropertyItem {
126+
name: string
127+
required?: boolean
128+
content?: ContentItem[]
129+
type?: Array<{
130+
text?: string
131+
kind?: string
132+
identifier?: string
133+
}>
134+
attributes?: Array<{
135+
kind?: string
136+
values?: string[]
137+
}>
119138
}
120139

121140
// ============================================================================

tests/render.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,55 @@ describe("Render Function", () => {
308308
})
309309
})
310310

311+
describe("Properties rendering", () => {
312+
it("should render primaryContentSections with kind properties", async () => {
313+
const data = {
314+
metadata: { title: "Songs.Attributes" },
315+
primaryContentSections: [
316+
{
317+
kind: "properties",
318+
items: [
319+
{
320+
name: "audioVariants",
321+
required: false,
322+
type: [{ kind: "text", text: "[string]" }],
323+
attributes: [
324+
{
325+
kind: "allowedValues",
326+
values: [
327+
"dolby-atmos",
328+
"dolby-audio",
329+
"hi-res-lossless",
330+
"lossless",
331+
"lossy-stereo",
332+
],
333+
},
334+
],
335+
content: [
336+
{
337+
type: "paragraph",
338+
inlineContent: [
339+
{ type: "strong", inlineContent: [{ type: "text", text: "(Extended)" }] },
340+
{ type: "text", text: " Indicates the specific audio variant for a song." },
341+
],
342+
},
343+
],
344+
},
345+
],
346+
},
347+
],
348+
}
349+
350+
const result = await renderFromJSON(data as any, "https://test.com")
351+
expect(result).toContain("## Properties")
352+
expect(result).toContain("### `audioVariants` *([string], optional)*")
353+
expect(result).toContain("**(Extended)** Indicates the specific audio variant for a song.")
354+
expect(result).toContain(
355+
"Possible Values: `dolby-atmos`, `dolby-audio`, `hi-res-lossless`, `lossless`, `lossy-stereo`",
356+
)
357+
})
358+
})
359+
311360
describe("Inline image rendering", () => {
312361
it("should render inline image with alt and URL from reference", async () => {
313362
const data = {

0 commit comments

Comments
 (0)