mirror of
https://github.com/zoriya/v10.git
synced 2026-08-11 16:40:27 +00:00
fix(site): resolve api reference slug mismatch on airplay and hlsjs pages (#1755)
This commit is contained in:
@@ -28,16 +28,19 @@ The builder derives PascalCase from kebab-case using `kebabCase` from es-toolkit
|
||||
|
||||
## NAME_OVERRIDES
|
||||
|
||||
When kebab-to-pascal conversion doesn't produce the correct name, add an override in `site/scripts/api-docs-builder/src/index.ts`:
|
||||
When kebab-to-pascal conversion doesn't produce the correct name, add an override in `site/src/utils/api-reference-overrides.ts` (the shared map the builder imports and the reference pages invert for slug lookup):
|
||||
|
||||
```ts
|
||||
const NAME_OVERRIDES: Record<string, string> = {
|
||||
export const NAME_OVERRIDES: Record<string, string> = {
|
||||
'pip-button': 'PiPButton',
|
||||
'airplay-button': 'AirPlayButton',
|
||||
};
|
||||
```
|
||||
|
||||
Use overrides only when the standard conversion fails (e.g., acronyms like PiP). Prefer aligning component naming with the standard conversion when possible.
|
||||
|
||||
The same map covers media elements whose PascalCase name doesn't kebab-case to their element tag name (e.g. `'hlsjs-video': 'HlsJsVideo'`). It is keyed by the generated-reference file slug regardless of component vs. media.
|
||||
|
||||
## Multi-Part Components
|
||||
|
||||
**Detection**: Presence of `packages/react/src/ui/{name}/index.parts.ts`.
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import * as ts from 'typescript';
|
||||
import * as tae from 'typescript-api-extractor';
|
||||
import { NAME_OVERRIDES } from '../../../src/utils/api-reference-overrides.js';
|
||||
import { extractCore } from './core-handler.js';
|
||||
import { extractCSSVars } from './css-vars-handler.js';
|
||||
import { extractDataAttrs } from './data-attrs-handler.js';
|
||||
@@ -31,10 +32,9 @@ import { kebabToPascal, partKebabFromSource, sortProps } from './utils.js';
|
||||
|
||||
// ─── Overrides ─────────────────────────────────────────────────────
|
||||
|
||||
// Components whose PascalCase name doesn't match simple kebab-to-pascal conversion.
|
||||
export const NAME_OVERRIDES: Record<string, string> = {
|
||||
'pip-button': 'PiPButton',
|
||||
};
|
||||
// `NAME_OVERRIDES` is the source of truth shared with the site's reference
|
||||
// pages — re-exported here for the builder's existing import surface.
|
||||
export { NAME_OVERRIDES };
|
||||
|
||||
// Parts whose HTML element file doesn't follow the `{component}-{part}-element.ts` convention.
|
||||
// Key: `{component}/{part-kebab}`, Value: element file basename (without `.ts`).
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user