fix(site): resolve api reference slug mismatch on airplay and hlsjs pages (#1755)

This commit is contained in:
Wesley Luyten
2026-06-26 14:10:25 -07:00
committed by GitHub
parent d321522cf0
commit 29b3f0f00a
7 changed files with 62 additions and 15 deletions
@@ -1,6 +1,5 @@
---
import { getEntry } from 'astro:content';
import { kebabCase } from 'es-toolkit/string';
import ContentWidth from '@/components/frames/ContentWidth.astro';
import H2 from '@/components/typography/H2Markdown.astro';
import H3 from '@/components/typography/H3Markdown.astro';
@@ -9,6 +8,7 @@ import MarkdownCode from '@/components/typography/MarkdownCode.astro';
import P from '@/components/typography/P.astro';
import type { ComponentReference } from '@/types/component-reference';
import { isValidFramework } from '@/types/docs';
import { resolveReferenceSlug } from '@/utils/api-reference-overrides';
import { createComponentReferenceModel } from '@/utils/componentReferenceModel';
import FrameworkCase from '../FrameworkCase.astro';
import ApiCSSVarsTable from './ApiCSSVarsTable.astro';
@@ -28,7 +28,7 @@ if (!framework || !isValidFramework(framework)) {
throw new Error(`Invalid or missing framework param.`);
}
const entry = await getEntry('componentReference', kebabCase(component));
const entry = await getEntry('componentReference', resolveReferenceSlug(component));
const apiRef: ComponentReference | null = entry?.data ?? null;
if (!apiRef) return;
@@ -1,6 +1,5 @@
---
import { getEntry } from 'astro:content';
import { kebabCase } from 'es-toolkit/string';
import ContentWidth from '@/components/frames/ContentWidth.astro';
import A from '@/components/typography/A.astro';
import H2 from '@/components/typography/H2Markdown.astro';
@@ -14,6 +13,7 @@ import Th from '@/components/typography/Th.astro';
import Thead from '@/components/typography/Thead.astro';
import Tr from '@/components/typography/Tr.astro';
import type { MediaReference } from '@/types/media-reference';
import { resolveReferenceSlug } from '@/utils/api-reference-overrides';
import { createMediaReferenceModel } from '@/utils/mediaReferenceModel';
import ApiCSSVarsTable from './ApiCSSVarsTable.astro';
import CodeChip from './CodeChip.astro';
@@ -26,7 +26,7 @@ interface Props {
const { media } = Astro.props;
const framework = Astro.params.framework === 'react' ? 'react' : 'html';
const entry = await getEntry('mediaReference', kebabCase(media));
const entry = await getEntry('mediaReference', resolveReferenceSlug(media));
const ref: MediaReference | null = entry?.data ?? null;
if (!ref) return;
+25
View File
@@ -0,0 +1,25 @@
import { kebabCase } from 'es-toolkit/string';
/**
* Reference entries whose PascalCase name doesn't match a simple
* kebab-to-pascal conversion. Keyed by generated-reference file slug (a
* component's kebab directory name or a media element's tag name) →
* PascalCase name.
*
* Consumed by the api-docs-builder for component generation, and inverted
* below so reference pages can resolve a file slug from the public name.
*/
export const NAME_OVERRIDES: Record<string, string> = {
'pip-button': 'PiPButton',
'airplay-button': 'AirPlayButton',
'hlsjs-video': 'HlsJsVideo',
};
const NAME_TO_SLUG: Record<string, string> = Object.fromEntries(
Object.entries(NAME_OVERRIDES).map(([slug, name]) => [name, slug])
);
/** Resolve a reference's generated file slug from its PascalCase name. */
export function resolveReferenceSlug(name: string): string {
return NAME_TO_SLUG[name] ?? kebabCase(name);
}
+5 -5
View File
@@ -3,6 +3,7 @@ import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { kebabCase } from 'es-toolkit/string';
import GithubSlugger from 'github-slugger';
import { resolveReferenceSlug } from './api-reference-overrides';
import { buildComponentReferenceTocHeadings, createComponentReferenceModel } from './componentReferenceModel';
import { buildFeatureReferenceTocHeadings, createFeatureReferenceModel } from './featureReferenceModel';
import { buildMediaReferenceTocHeadings, createMediaReferenceModel } from './mediaReferenceModel';
@@ -14,9 +15,8 @@ const FEATURE_REF_DIR = path.resolve(__dirname, '../content/generated-feature-re
const UTIL_REF_DIR = path.resolve(__dirname, '../content/generated-util-reference');
const MEDIA_REF_DIR = path.resolve(__dirname, '../content/generated-media-reference');
function readComponentRefJson(componentName) {
const kebab = kebabCase(componentName);
const filePath = path.join(COMPONENT_REF_DIR, `${kebab}.json`);
function readComponentRefJson(slug) {
const filePath = path.join(COMPONENT_REF_DIR, `${slug}.json`);
try {
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
} catch {
@@ -142,7 +142,7 @@ function injectComponentReferenceHeadings(node, headingsWithMetadata, reservedSl
const componentName = typeof componentAttr?.value === 'string' ? componentAttr.value : null;
if (!componentName) return;
const json = readComponentRefJson(componentName);
const json = readComponentRefJson(resolveReferenceSlug(componentName));
if (!json) return;
const partOrderAttr = node.attributes?.find((a) => a.name === 'partOrder');
@@ -226,7 +226,7 @@ function injectMediaReferenceHeadings(node, headingsWithMetadata, reservedSlugs)
const mediaName = typeof mediaAttr?.value === 'string' ? mediaAttr.value : null;
if (!mediaName) return;
const json = readMediaRefJson(kebabCase(mediaName));
const json = readMediaRefJson(resolveReferenceSlug(mediaName));
if (!json) return;
const mediaModel = createMediaReferenceModel(mediaName, json);
@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest';
import { resolveReferenceSlug } from '../api-reference-overrides';
describe('resolveReferenceSlug', () => {
it('kebab-cases names without an override', () => {
expect(resolveReferenceSlug('PlayButton')).toBe('play-button');
expect(resolveReferenceSlug('VolumeSlider')).toBe('volume-slider');
expect(resolveReferenceSlug('SimpleHlsVideo')).toBe('simple-hls-video');
});
it('uses the inverted NAME_OVERRIDES map for special casing', () => {
// kebabCase('PiPButton') would be 'pi-p-button'; the override maps it back.
expect(resolveReferenceSlug('PiPButton')).toBe('pip-button');
// kebabCase('AirPlayButton') would be 'air-play-button'.
expect(resolveReferenceSlug('AirPlayButton')).toBe('airplay-button');
// kebabCase('HlsJsVideo') would be 'hls-js-video'; the tag name is 'hlsjs-video'.
expect(resolveReferenceSlug('HlsJsVideo')).toBe('hlsjs-video');
});
});