@@ -12,23 +12,34 @@ import config from '../typedoc.config.mjs';
1212
1313import {
1414 apiNavigationJson ,
15+ apiReferenceRoot ,
1516 docsContentRoot ,
1617 publicApiDocs ,
1718} from './api-docs-config' ;
1819
1920const repoRoot = process . cwd ( ) ;
2021const docsRoot = path . resolve ( repoRoot , docsContentRoot ) ;
2122const publicRoot = path . resolve ( repoRoot , 'docs/public' ) ;
23+ const referenceRoot = path . resolve ( repoRoot , apiReferenceRoot ) ;
2224const markdownRoots = [ 'README.md' , docsContentRoot ] ;
25+ const expectedGeneratedApiPagePaths = new Set ( [
26+ 'index.md' ,
27+ ...publicApiDocs . map ( ( entry ) =>
28+ path . relative ( apiReferenceRoot , entry . apiPagePath ) . replace ( / \\ / g, '/' ) ,
29+ ) ,
30+ ] ) ;
31+ const expectedGeneratedApiNavigationPaths = new Map (
32+ publicApiDocs . map ( ( entry ) => [
33+ path . relative ( apiReferenceRoot , entry . apiPagePath ) . replace ( / \\ / g, '/' ) ,
34+ entry . moduleName ,
35+ ] ) ,
36+ ) ;
2337const requiredApiEntryPages = [
2438 `${ docsContentRoot } /api/index.mdx` ,
2539 `${ docsContentRoot } /api/root-package.mdx` ,
26- ...publicApiDocs . map ( ( entry ) => entry . apiIndexPage ) ,
40+ ...publicApiDocs . map ( ( entry ) => entry . apiPagePath ) ,
2741 apiNavigationJson ,
2842] as const ;
29- const requiredApiModules = new Set (
30- publicApiDocs . map ( ( entry ) => entry . moduleName ) ,
31- ) ;
3243
3344const markdownLinkPattern = / ! ? \[ [ ^ \] ] * ] \( ( [ ^ ) ] + ) \) / g;
3445const linkTargetPattern = / ^ ( [ ^ \s ] + ) (?: \s + [ " ' ] [ ^ " ' ] * [ " ' ] ) ? $ / ;
@@ -82,6 +93,8 @@ const resolveLinkCandidates = (
8293 const candidates = new Set < string > ( [ absoluteTarget ] ) ;
8394
8495 if ( normalizedTarget . endsWith ( '/' ) ) {
96+ candidates . add ( `${ absoluteTarget } .md` ) ;
97+ candidates . add ( `${ absoluteTarget } .mdx` ) ;
8598 candidates . add ( path . join ( absoluteTarget , 'index.md' ) ) ;
8699 candidates . add ( path . join ( absoluteTarget , 'index.mdx' ) ) ;
87100 candidates . add ( path . join ( absoluteTarget , 'README.md' ) ) ;
@@ -210,6 +223,30 @@ const verifyBaseAwareLinks = async (): Promise<string[]> => {
210223 return failures ;
211224} ;
212225
226+ const verifyGeneratedApiLayout = async ( ) : Promise < string [ ] > => {
227+ const failures : string [ ] = [ ] ;
228+ const generatedMarkdownFiles = await collectMarkdownFiles ( apiReferenceRoot ) ;
229+ const seenGeneratedApiPagePaths = new Set (
230+ generatedMarkdownFiles . map ( ( file ) =>
231+ path . relative ( referenceRoot , file ) . replace ( / \\ / g, '/' ) ,
232+ ) ,
233+ ) ;
234+
235+ for ( const expectedPagePath of expectedGeneratedApiPagePaths ) {
236+ if ( ! seenGeneratedApiPagePaths . has ( expectedPagePath ) ) {
237+ failures . push ( `missing generated page "${ expectedPagePath } "` ) ;
238+ }
239+ }
240+
241+ for ( const seenPagePath of seenGeneratedApiPagePaths ) {
242+ if ( ! expectedGeneratedApiPagePaths . has ( seenPagePath ) ) {
243+ failures . push ( `unexpected generated page "${ seenPagePath } "` ) ;
244+ }
245+ }
246+
247+ return failures ;
248+ } ;
249+
213250const verifyApiEntryPages = async ( ) : Promise < string [ ] > => {
214251 const failures : string [ ] = [ ] ;
215252
@@ -232,7 +269,7 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
232269 title ?: string ;
233270 path ?: string ;
234271 } [ ] ;
235- const seenModules = new Set < string > ( ) ;
272+ const seenNavigationPaths = new Set < string > ( ) ;
236273
237274 const visitNavigationItems = (
238275 items : readonly {
@@ -242,11 +279,8 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
242279 } [ ] ,
243280 ) : void => {
244281 for ( const item of items ) {
245- if (
246- typeof item . path === 'string' &&
247- item . path . endsWith ( '/index.md' )
248- ) {
249- seenModules . add ( item . path . slice ( 0 , - '/index.md' . length ) ) ;
282+ if ( typeof item . path === 'string' ) {
283+ seenNavigationPaths . add ( item . path ) ;
250284 }
251285
252286 if ( Array . isArray ( item . children ) ) {
@@ -263,16 +297,19 @@ const verifyApiEntryPages = async (): Promise<string[]> => {
263297
264298 visitNavigationItems ( navigationJson ) ;
265299
266- for ( const moduleName of requiredApiModules ) {
267- if ( ! seenModules . has ( moduleName ) ) {
300+ for ( const [
301+ navigationPathValue ,
302+ moduleName ,
303+ ] of expectedGeneratedApiNavigationPaths ) {
304+ if ( ! seenNavigationPaths . has ( navigationPathValue ) ) {
268305 failures . push ( `navigation.json missing module "${ moduleName } "` ) ;
269306 }
270307 }
271308
272- for ( const moduleName of seenModules ) {
273- if ( moduleName !== undefined && ! requiredApiModules . has ( moduleName ) ) {
309+ for ( const seenNavigationPath of seenNavigationPaths ) {
310+ if ( ! expectedGeneratedApiNavigationPaths . has ( seenNavigationPath ) ) {
274311 failures . push (
275- `navigation.json contains non-exported module "${ moduleName } "` ,
312+ `navigation.json contains unexpected path "${ seenNavigationPath } "` ,
276313 ) ;
277314 }
278315 }
@@ -343,6 +380,7 @@ const verifyTypeDocSummaries = async (): Promise<string[]> => {
343380const main = async ( ) : Promise < void > => {
344381 const linkFailures = await verifyLinks ( ) ;
345382 const baseAwareFailures = await verifyBaseAwareLinks ( ) ;
383+ const generatedLayoutFailures = await verifyGeneratedApiLayout ( ) ;
346384 const apiFailures = await verifyApiEntryPages ( ) ;
347385 const summaryFailures = await verifyTypeDocSummaries ( ) ;
348386
@@ -358,6 +396,13 @@ const main = async (): Promise<void> => {
358396 failures . push ( ...baseAwareFailures . map ( ( failure ) => `- ${ failure } ` ) ) ;
359397 }
360398
399+ if ( generatedLayoutFailures . length > 0 ) {
400+ failures . push ( 'Generated API layout violations:' ) ;
401+ failures . push (
402+ ...generatedLayoutFailures . map ( ( failure ) => `- ${ failure } ` ) ,
403+ ) ;
404+ }
405+
361406 if ( apiFailures . length > 0 ) {
362407 failures . push ( 'Missing generated API entry pages:' ) ;
363408 failures . push ( ...apiFailures . map ( ( failure ) => `- ${ failure } ` ) ) ;
0 commit comments