@@ -2,7 +2,12 @@ import * as fs from "fs";
22import * as path from "path" ;
33
44import loadConfigFiles from "./src/config" ;
5- import { buildSidebar , buildLinterSidebar , addToSidebar } from "./src/sidebar" ;
5+ import {
6+ buildSidebar ,
7+ buildLinterSidebar ,
8+ buildOpenAPILinterSidebar ,
9+ addToSidebar ,
10+ } from "./src/sidebar" ;
611import {
712 type AEP ,
813 type ConsolidatedLinterRule ,
@@ -15,6 +20,7 @@ import { load, dump } from "js-yaml";
1520
1621const AEP_LOC = process . env . AEP_LOCATION || "" ;
1722const AEP_LINTER_LOC = process . env . AEP_LINTER_LOC || "" ;
23+ const AEP_OPENAPI_LINTER_LOC = process . env . AEP_OPENAPI_LINTER_LOC || "" ;
1824const AEP_COMPONENTS = process . env . AEP_COMPONENTS_LOC || "" ;
1925
2026// Logging functions
@@ -47,6 +53,19 @@ function logFolderDetection() {
4753 ) ;
4854 }
4955
56+ if ( AEP_OPENAPI_LINTER_LOC ) {
57+ console . log ( `✓ OpenAPI Linter folder found: ${ AEP_OPENAPI_LINTER_LOC } ` ) ;
58+ if ( fs . existsSync ( AEP_OPENAPI_LINTER_LOC ) ) {
59+ console . log ( ` - Path exists and is accessible` ) ;
60+ } else {
61+ console . log ( ` - ⚠️ Path does not exist` ) ;
62+ }
63+ } else {
64+ console . log (
65+ `✗ OpenAPI Linter folder not configured (AEP_OPENAPI_LINTER_LOC environment variable)` ,
66+ ) ;
67+ }
68+
5069 if ( AEP_COMPONENTS ) {
5170 console . log ( `✓ Components folder found: ${ AEP_COMPONENTS } ` ) ;
5271 if ( fs . existsSync ( AEP_COMPONENTS ) ) {
@@ -277,6 +296,64 @@ async function assembleLinterRules(): Promise<LinterRule[]> {
277296 return linterRules ;
278297}
279298
299+ /**
300+ * Assembles OpenAPI linter rules from the aep-openapi-linter repository.
301+ *
302+ * Structure difference from api-linter:
303+ * - api-linter: docs/rules/XXXX/*.md (multiple files per AEP)
304+ * - openapi-linter: docs/XXXX.md (one file per AEP)
305+ *
306+ * @returns Promise<LinterRule[]> Array of linter rules, empty if repo not found
307+ */
308+ async function assembleOpenAPILinterRules ( ) : Promise < LinterRule [ ] > {
309+ // Defensive check: Verify repository exists
310+ if ( ! fs . existsSync ( AEP_OPENAPI_LINTER_LOC ) ) {
311+ console . log ( "ℹ️ OpenAPI linter repository not found, skipping..." ) ;
312+ console . log ( ` Expected location: ${ AEP_OPENAPI_LINTER_LOC } ` ) ;
313+ return [ ] ;
314+ }
315+
316+ const docsPath = path . join ( AEP_OPENAPI_LINTER_LOC , "docs" ) ;
317+
318+ // Defensive check: Verify docs directory exists
319+ if ( ! fs . existsSync ( docsPath ) ) {
320+ console . warn ( "⚠️ OpenAPI linter docs directory not found" ) ;
321+ console . warn ( ` Expected: ${ docsPath } ` ) ;
322+ return [ ] ;
323+ }
324+
325+ let linterRules : LinterRule [ ] = [ ] ;
326+
327+ try {
328+ const files = await fs . promises . readdir ( docsPath ) ;
329+
330+ for ( const file of files ) {
331+ // Process only numbered AEP files (e.g., 0131.md, 0132.md)
332+ // Skip index files like rules.md
333+ if ( file . match ( / ^ \d { 4 } \. m d $ / ) ) {
334+ const filePath = path . join ( docsPath , file ) ;
335+ const aepNumber = file . split ( "." ) [ 0 ] ; // Extract "0131" from "0131.md"
336+
337+ try {
338+ const rule = buildLinterRule ( filePath , aepNumber ) ;
339+ linterRules . push ( rule ) ;
340+ console . log ( `✓ Processed OpenAPI linter rule: AEP-${ aepNumber } ` ) ;
341+ } catch ( error ) {
342+ console . error ( `❌ Failed to process ${ file } :` , error ) ;
343+ // Continue processing other files
344+ }
345+ }
346+ }
347+
348+ console . log ( `✓ Assembled ${ linterRules . length } OpenAPI linter rules` ) ;
349+ } catch ( error ) {
350+ console . error ( "❌ Error reading OpenAPI linter docs directory:" , error ) ;
351+ return [ ] ;
352+ }
353+
354+ return linterRules ;
355+ }
356+
280357function buildLinterRule ( rulePath : string , aep : string ) : LinterRule {
281358 logFileRead ( rulePath , "Linter rule" ) ;
282359 let contents = fs . readFileSync ( rulePath , "utf-8" ) ;
@@ -326,11 +403,10 @@ ${rules_contents.join("\n")}
326403 return consolidated_rules ;
327404}
328405
329- function writeRule ( rule : ConsolidatedLinterRule ) {
330- const filePath = path . join (
331- `src/content/docs/tooling/linter/rules/` ,
332- `${ rule . aep } .md` ,
333- ) ;
406+ function writeRule ( rule : ConsolidatedLinterRule , outputPath ?: string ) {
407+ const filePath =
408+ outputPath ||
409+ path . join ( `src/content/docs/tooling/linter/rules/` , `${ rule . aep } .md` ) ;
334410 writeFile ( filePath , rule . contents ) ;
335411}
336412
@@ -506,6 +582,44 @@ if (AEP_LINTER_LOC != "") {
506582 console . warn ( "Proto linter repo is not found." ) ;
507583}
508584
585+ if ( AEP_OPENAPI_LINTER_LOC != "" ) {
586+ console . log ( "=== Processing OpenAPI Linter Repository ===" ) ;
587+
588+ // Process OpenAPI linter rules
589+ const openapiLinterRules = await assembleOpenAPILinterRules ( ) ;
590+
591+ if ( openapiLinterRules . length > 0 ) {
592+ // Write OpenAPI linter overview page
593+ await writePage (
594+ AEP_OPENAPI_LINTER_LOC ,
595+ "README.md" ,
596+ "src/content/docs/tooling/openapi-linter/index.md" ,
597+ "OpenAPI Linter" ,
598+ ) ;
599+
600+ // Consolidate rules (groups by AEP number)
601+ const consolidatedOpenAPIRules = consolidateLinterRule ( openapiLinterRules ) ;
602+
603+ // Write rule markdown files
604+ for ( const rule of consolidatedOpenAPIRules ) {
605+ const outputPath = path . join (
606+ "src/content/docs/tooling/openapi-linter/rules" ,
607+ `${ rule . aep } .md` ,
608+ ) ;
609+ writeRule ( rule , outputPath ) ;
610+ }
611+
612+ // Update sidebar navigation
613+ sidebar = buildOpenAPILinterSidebar ( consolidatedOpenAPIRules , sidebar ) ;
614+
615+ console . log ( "✅ OpenAPI linter integration complete\n" ) ;
616+ } else {
617+ console . log ( "ℹ️ No OpenAPI linter rules found, skipping integration\n" ) ;
618+ }
619+ } else {
620+ console . log ( "ℹ️ OpenAPI linter repo not configured, skipping...\n" ) ;
621+ }
622+
509623if ( AEP_COMPONENTS != "" ) {
510624 console . log ( "=== Processing Components Repository ===" ) ;
511625 // Read all YAML component files.
0 commit comments