import { formatWithPrettier , loadTSConfig , projectRoot , } from "@zwave-js/maintenance" ; import { compareStrings } from "@zwave-js/shared" ; import * as fs from "fs-extra" ; import * as path from "path" ; import ts from "typescript" ; const ccIndexFile = path . join ( projectRoot , "src/lib/commandclass/index.ts" ) ; function findExports ( ) { const tsConfig = loadTSConfig ( "zwave-js" ) ; const program = ts . createProgram ( tsConfig . fileNames , tsConfig . options ) ; const checker = program . getTypeChecker ( ) ; const ccExports = new Map < string , { name : string ; typeOnly : boolean ; } [ ] > ( ) ; function addExport ( filename , name , typeOnly ) { if ( ! ccExports . has ( filename ) ) ccExports . set ( filename , [ ] ) ; ccExports . get ( filename ) ! . push ( { name , typeOnly } ) ; } function inheritsFromCommandClass ( node ) { let type = checker . getTypeAtLocation ( node ) as ts . InterfaceType ; while ( type ) { if ( type . symbol . name === "CommandClass" ) return true ; type = checker . getBaseTypes ( type ) [ 0 ] as ts . InterfaceType | undefined ; } return false ; } for ( const sourceFile of program . getSourceFiles ( ) ) { const relativePath = path . relative ( projectRoot , sourceFile . fileName ) . replace ( /\\/g , "/" ) ; if ( relativePath . startsWith ( ".." ) ) continue ; if ( ! relativePath . includes ( "/commandclass/" ) ) { continue ; } if ( relativePath . endsWith ( ".test.ts" ) || relativePath . endsWith ( "index.ts" ) ) { continue ; } const fileFullText = sourceFile . getFullText ( ) ; ts . forEachChild ( sourceFile , ( node ) => { if ( ts . isEnumDeclaration ( node ) || ts . isTypeAliasDeclaration ( node ) || ts . isInterfaceDeclaration ( node ) || ts . isClassDeclaration ( node ) || ts . isFunctionDeclaration ( node ) || ts . isArrowFunction ( node ) ) { if ( ! node . name ) return ; if ( ts . isClassDeclaration ( node ) && node . name . text . includes ( "CC" ) && inheritsFromCommandClass ( node ) ) { addExport ( sourceFile . fileName , node . name . text , false ) ; return ; } const leadingComments = ts . getLeadingCommentRanges ( fileFullText , node . pos ) ; if ( ! leadingComments ) return ; for ( const comment of leadingComments ) { const commentText = fileFullText . slice ( comment . pos , comment . end ) ; if ( commentText . includes ( "@publicAPI" ) ) { if ( ! node . modifiers ?. some ( ( m ) => m . kind === ts . SyntaxKind . ExportKeyword ) ) { const location = ts . getLineAndCharacterOfPosition ( sourceFile , node . getStart ( sourceFile , false ) ) ; throw new Error ( `${ relativePath }:${ location . line } Found @publicAPI comment, but the node ${ node . name . text } is not exported!` ) ; } addExport ( sourceFile . fileName , node . name . text , ts . isTypeAliasDeclaration ( node ) || ts . isInterfaceDeclaration ( node ) ) ; } } } } ) ; } return ccExports ; } export async function generateCCExports ( ) { let fileContent = `
// This file is auto-generated by maintenance/generateCCExports.ts
// Do not edit it by hand or your changes will be lost!

` ; for ( const [ filename , fileExports ] of [ ... findExports ( ) . entries ( ) ] . sort ( ( [ fileA ] , [ fileB ] ) => compareStrings ( fileA , fileB ) ) ) { const relativePath = path . relative ( ccIndexFile , filename ) . replace ( /\\/g , "/" ) . replace ( /\.ts$/ , "" ) . replace ( /^\.\.\// , "./" ) ; const typeExports = fileExports . filter ( ( e ) => e . typeOnly ) ; if ( typeExports . length ) { fileContent += `export type { ${ typeExports . map ( ( e ) => e . name ) . join ( ", " ) } } from "${ relativePath }"\n` ; } const valueExports = fileExports . filter ( ( e ) => ! e . typeOnly ) ; if ( valueExports . length ) { fileContent += `export { ${ valueExports . map ( ( e ) => e . name ) . join ( ", " ) } } from "${ relativePath }"\n` ; } } const originalFileContent = await fs . readFile ( ccIndexFile , "utf8" ) ; fileContent = formatWithPrettier ( ccIndexFile , fileContent ) ; if ( fileContent !== originalFileContent ) { console . log ( "CC index file changed" ) ; await fs . writeFile ( ccIndexFile , fileContent , "utf8" ) ; } } if ( require . main === module ) void generateCCExports ( ) ;