import * as plugins from './plugins.js'; import * as paths from './paths.js'; export class TypeDoc { public smartshellInstance = new plugins.smartshell.Smartshell({ executor: 'bash', pathDirectories: [paths.binDir], }); // Static public static async isTypeDocDir(dirPathArg: string): Promise { return await plugins.fsInstance.file(plugins.path.join(dirPathArg, 'ts/index.ts')).exists() || await plugins.fsInstance.file(plugins.path.join(dirPathArg, 'ts_web/index.ts')).exists(); } // Instance public typedocDirectory: string; constructor(dirPathArg: string) { this.typedocDirectory = dirPathArg; } public async compile(options?: { publicSubdir?: string }) { const projectTsconfig = plugins.path.resolve(this.typedocDirectory, 'tsconfig.json'); const hasProjectTsconfig = await plugins.fsInstance.file(projectTsconfig).exists(); const data = { ...(hasProjectTsconfig ? { extends: projectTsconfig } : { compilerOptions: { experimentalDecorators: true, useDefineForClassFields: false, target: 'ES2022', module: 'NodeNext', moduleResolution: 'NodeNext', esModuleInterop: true, verbatimModuleSyntax: true, } }), include: [] as string[], }; let startDirectory = ''; if (await plugins.fsInstance.directory(plugins.path.join(this.typedocDirectory, './ts')).exists()) { data.include.push(plugins.path.join(this.typedocDirectory, './ts/**/*')); startDirectory = 'ts'; } if (await plugins.fsInstance.directory(plugins.path.join(this.typedocDirectory, './ts_web')).exists()) { data.include.push(plugins.path.join(this.typedocDirectory, './ts_web/**/*')); if (!startDirectory) { startDirectory = 'ts_web'; } } if (!startDirectory) { throw new Error(`No TypeDoc entrypoint found in ${this.typedocDirectory}`); } const tempDir = plugins.path.join(this.typedocDirectory, '.nogit', 'tsdoc'); const tempTsconfigFile = plugins.path.join(tempDir, 'tsconfig.json'); await plugins.fsInstance.directory(tempDir).recursive().create(); await plugins.fsInstance.file(tempTsconfigFile).encoding('utf8').write(JSON.stringify(data)); const publicDir = plugins.path.join(this.typedocDirectory, 'public'); let targetDir = publicDir; if (options?.publicSubdir) { targetDir = plugins.path.resolve(plugins.path.join(targetDir, options.publicSubdir)); const resolvedPublicDir = plugins.path.resolve(publicDir); if (!targetDir.startsWith(`${resolvedPublicDir}${plugins.path.sep}`) && targetDir !== resolvedPublicDir) { throw new Error(`Invalid publicSubdir outside public directory: ${options.publicSubdir}`); } } try { const result = await this.smartshellInstance.exec( `typedoc --tsconfig ${shellQuote(tempTsconfigFile)} --out ${shellQuote(targetDir)} ${shellQuote(plugins.path.join(this.typedocDirectory, startDirectory, 'index.ts'))}`, ); if (result.exitCode !== 0) { throw new Error('typedoc command failed.'); } } finally { if (await plugins.fsInstance.file(tempTsconfigFile).exists()) { await plugins.fsInstance.file(tempTsconfigFile).delete(); } } } } const shellQuote = (value: string): string => `'${value.replaceAll("'", "'\\''")}'`;