import fs from 'fs-extra' import glob from 'glob' import path from 'path' import process from 'process' import { Application, DeclarationReflection, DefaultTheme, DefaultThemeRenderContext, JSX, Logger, Options, ProjectReflection, ReferenceReflection, ReferenceType, Reflection, ReflectionKind, Renderer, SignatureReflection, TSConfigReader, TypeDocReader, } from 'typedoc' interface PropertyDoc { defaultValue?: string deprecated?: string deprecatedSince?: string description?: string descriptionHtml?: string name: string required?: boolean sourceFile?: string sourceUrl?: string type?: string typeDetail?: string } interface InterfaceDoc { deprecated?: string deprecatedSince?: string description?: string name: string properties: PropertyDoc[] } interface ModuleDoc { [key: string]: InterfaceDoc } interface ProjectDoc { [filename: string]: ModuleDoc } interface Deprecation { interface: string property: string deprecatedSince: string message: string } function isInterface(r: Reflection): r is DeclarationReflection { return r.kindOf(ReflectionKind.Interface) } function isProperty(r: Reflection): r is DeclarationReflection { return ( r.kindOf(ReflectionKind.Property) || r.kindOf(ReflectionKind.FunctionOrMethod) ) } function getCommentTag(r: Reflection, tag: string) { return r.comment?.getTag(tag)?.text } function toProjectDoc(r: ProjectReflection) { const doc: ProjectDoc = {} r.traverse((child) => { const filename = child.sources?.[0]?.fileName if (filename) { const value = doc[filename] || {} if (isInterface(child)) { value[child.name] = toInterfaceDoc(child) } doc[filename] = value } }) return doc } function toInterfaceDoc(r: DeclarationReflection): InterfaceDoc { const doc: InterfaceDoc = { name: r.name, properties: [], description: JSX.renderElement(ctx.comment(r)), deprecated: getCommentTag(r, 'deprecated'), deprecatedSince: getCommentTag(r, 'deprecatedsince'), } r.traverse((child) => { if (isProperty(child)) { doc.properties.push(toPropertyDoc(child)) } }) return doc } function getTypeDetail(r: DeclarationReflection): string { if ( r.type instanceof ReferenceType && r.type.reflection instanceof DeclarationReflection ) { return getTypeDetail(r.type.reflection) } return JSX.renderElement(ctx.type(r.type)) } function toPropertyDoc(r: DeclarationReflection): PropertyDoc { let type = JSX.renderElement(ctx.type(r.type)) let typeDetail = getTypeDetail(r) let description = r.comment?.shortText || '' let descriptionHtml = JSX.renderElement(ctx.comment(r)) if (r.signatures?.[0] instanceof SignatureReflection) { type = 'Function' typeDetail = JSX.renderElement(ctx.memberSignatureTitle(r.signatures[0])) description = r.signatures[0].comment?.shortText || '' descriptionHtml = JSX.renderElement(ctx.comment(r.signatures[0])) } else if (r instanceof ReferenceReflection) { typeDetail = JSX.renderElement(ctx.memberReference(r)) } return { name: r.name, required: !r.flags.isOptional, type, typeDetail, description, descriptionHtml, defaultValue: getCommentTag(r, 'defaultvalue'), deprecated: getCommentTag(r, 'deprecated'), deprecatedSince: getCommentTag(r, 'deprecatedsince'), sourceFile: r.sources?.[0].file?.fileName, sourceUrl: r.sources?.[0].url, } } const app = new Application() const logger = new Logger() const renderer = new Renderer(app) const ctx = new DefaultThemeRenderContext( new DefaultTheme(renderer), new Options(logger) ) async function buildTypes() { app.options.addReader(new TSConfigReader()) app.options.addReader(new TypeDocReader()) app.bootstrap({ tsconfig: 'tsconfig.prod.json', excludeExternals: true, entryPoints: ['./src/utils/propsTypedoc.ts'], }) const project = app.convert() if (project) { const outputDir = path.join(process.cwd(), 'dist/typedoc') const projectDoc = toProjectDoc(project) const writes = Object.entries(projectDoc).map(([key, value]) => { const filename = key .replace('packages/core-react/src', '') .replace(/\.tsx?/, '.json') const filepath = path.join(outputDir, filename) const filedir = path.dirname(filepath) const content = JSON.stringify(value, null, 2) return fs.ensureDir(filedir).then(() => fs.writeFile(filepath, content)) }) await Promise.all(writes) } } async function buildDeprecations() { glob('dist/typedoc/**/*.json', async (err, matches) => { const deprecations: Deprecation[] = [] const reads = matches.map( async (match) => (await fs.readJson(match)) as ModuleDoc ) const json = await Promise.all(reads) json.forEach((doc) => { Object.values(doc).forEach((interfaceDoc) => { interfaceDoc.properties?.forEach((propertyDoc) => { if (propertyDoc.deprecated) { deprecations.push({ interface: interfaceDoc.name, property: propertyDoc.name, deprecatedSince: propertyDoc.deprecatedSince || '', message: propertyDoc.deprecated, }) } }) }) }) fs.writeJsonSync('dist/typedoc/deprecations.json', deprecations) }) } buildTypes() .then(() => buildDeprecations()) .catch(console.error)