import { parse, ParserPlugin } from '@babel/parser'; import traverse from '@babel/traverse'; import generate from '@babel/generator'; import template from '@babel/template'; import type { Comment } from '@babel/types'; import { program, isFunctionDeclaration } from '@babel/types'; import fs from 'fs-extra'; import _ from 'lodash'; export * from './tsgenerator'; export * from './parser'; const babelPlugins: ParserPlugin[] = ['jsx', 'typescript']; const buildNamedExport = template('export function %%name%%(): any', { plugins: babelPlugins, }); interface Options { entryPath: string; // targetPath: string; // TODO } export async function generateFunctionDeclare(options: Options) { const sourcecode = await fs.readFile(options.entryPath, 'utf8'); const exported = getSourceCodeExportedFunction(sourcecode); const astList = exported.map((e) => { return buildNamedExport({ name: e.name, }); }); const code = generate(program(_.flatten(astList))).code; return code; } interface ExportedItem { name: string; comments?: string; } function getSourceCodeExportedFunction(sourcecode: string): ExportedItem[] { const ast = parse(sourcecode, { sourceType: 'module', plugins: babelPlugins, }); const exported: ExportedItem[] = []; traverse(ast, { ExportNamedDeclaration({ node }) { if (node.declaration) { if (isFunctionDeclaration(node.declaration)) { const name = node.declaration.id?.name; if (typeof name === 'string') { exported.push({ name, comments: getCommentStr(node.leadingComments), }); } } } else { const names = node.specifiers.map((s) => { const exported = s.exported; if (exported.type === 'Identifier') { return { name: exported.name, comments: getCommentStr(node.leadingComments), }; } else { return null; } }); exported.push(...names.filter((n): n is any => !!n)); } }, }); return exported; } function getCommentStr( comments: Comment[] | null | undefined ): string | undefined { if (!comments) { return undefined; } return comments.map((c) => c.value).join('\n'); }