diff --git a/dts-summary.json b/dts-summary.json index f19dc6f..d428b4d 100644 --- a/dts-summary.json +++ b/dts-summary.json @@ -1,6 +1,6 @@ { "api.d.mts": { - "summary": "declare function summary(_: string, _: string, _: (_: string, _: string) => boolean): Record", + "summary": "declare function summary(_: string, _: string): Record", "#exports": [ "summary" ] diff --git a/src/api.ts b/src/api.ts index 3ef98f9..45f3283 100644 --- a/src/api.ts +++ b/src/api.ts @@ -2,7 +2,7 @@ import dprint from 'dprint-node' import { walk } from 'estree-walker' import MagicString from 'magic-string' import { parseSync } from 'rolldown/experimental' -import type { Node, Span } from '@oxc-project/types' +import type { Node, Span, TSTypeAnnotation } from '@oxc-project/types' const multilineCommentsRE = /\/\*.*?\*\//gs const singlelineCommentsRE = /\/\/.*$/gm @@ -10,7 +10,6 @@ const singlelineCommentsRE = /\/\/.*$/gm export function summary( code: string, fileName: string = 'dummy.d.ts', - filter?: (symbol: string, summary: string) => boolean, ): Record { code = code .replaceAll(multilineCommentsRE, '') @@ -44,18 +43,29 @@ export function summary( } const register = (symbol: string, node: Node) => { - const summary = format(slice(node)) - if (!filter || filter(symbol, summary)) { - result[symbol] = summary + let code: string | undefined + if (node.type === 'VariableDeclarator') { + const typeAnnotation = ( + node.id.typeAnnotation as TSTypeAnnotation | null | undefined + )?.typeAnnotation + if (typeAnnotation) { + code = s.slice(typeAnnotation.start, node.end) + } else if (node.init) { + code = slice(node.init) + } } + code ||= slice(node) + + const summary = format(code) + result[symbol] = summary } if (decl.type === 'VariableDeclaration') { for (const node of decl.declarations) { - register(slice(node.id), node) + register(nodeToString(node.id), node) } } else if ('id' in decl && decl.id) { - register(slice(decl.id), decl) + register(nodeToString(decl.id), decl) } else if ( // default export decl.type === 'ExportDefaultDeclaration' && @@ -66,7 +76,31 @@ export function summary( } } + for (const stmt of program.body) { + if ( + stmt.type === 'ExportNamedDeclaration' && + stmt.declaration === null && + stmt.specifiers.length + ) { + for (const specifier of stmt.specifiers) { + const exported = nodeToString(specifier.exported) + const local = nodeToString(specifier.local) + if (local !== exported) { + result[exported] = result[local] + } + } + } + } + return result + + function nodeToString(node: Node) { + return node.type === 'Identifier' + ? node.name + : node.type === 'Literal' + ? (node.value as string) + : slice(node) + } } function format(code: string) { diff --git a/src/index.ts b/src/index.ts index 53de62a..bbca011 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,11 +12,11 @@ export interface Options { include?: FilterPattern exclude?: FilterPattern /** - * @default false + * @default true */ excludeNonExport?: boolean /** - * @default 'dts-summary.json' + * @default '[cwd]/dts-summary.json' */ saveTo?: string } @@ -25,36 +25,43 @@ export function DtsSummary(options: Options = {}): Plugin { const { include = RE_DTS, exclude, - excludeNonExport, + excludeNonExport = true, saveTo = 'dts-summary.json', } = options const filter = createFilter(include, exclude) return { name: 'rolldown-plugin-dts-summary', - async generateBundle(_, bundle) { - const result: Record< - string, - Record - > = Object.create(null) - - for (const chunk of Object.values(bundle)) { - if (chunk.type === 'asset' || !filter(chunk.fileName)) continue - - result[chunk.fileName] = summary( - chunk.code, - chunk.fileName, - chunk.isEntry && excludeNonExport - ? (symbol) => chunk.exports.includes(symbol) - : undefined, - ) - if (chunk.isEntry) { - result[chunk.fileName]['#exports'] = chunk.exports + generateBundle: { + order: 'post', + async handler(_, bundle) { + const result: Record< + string, + Record + > = Object.create(null) + + for (const chunk of Object.values(bundle)) { + if (chunk.type === 'asset' || !filter(chunk.fileName)) continue + + const map: Record = (result[ + chunk.fileName + ] = summary(chunk.code, chunk.fileName)) + + if (chunk.isEntry) { + if (excludeNonExport) { + for (const key of Object.keys(map)) { + if (key !== '#exports' && !chunk.exports.includes(key)) { + delete map[key] + } + } + } + map['#exports'] = chunk.exports + } } - } - const code = `${JSON.stringify(result, null, 2)}\n` - await writeFile(saveTo, code) + const code = `${JSON.stringify(result, null, 2)}\n` + await writeFile(saveTo, code) + }, }, } } diff --git a/tests/index.test.ts b/tests/index.test.ts index 2259804..b749462 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -12,7 +12,8 @@ test('basic', () => { } export function format( filePath: string, - code: string, options?: Options): string;`, + code: string, options?: Options): string; + export { format as "module.exports" }`, ), ).toMatchInlineSnapshot(` { @@ -21,6 +22,7 @@ test('basic', () => { str: 'hello' }", "format": "function format(_: string, _: string, _: Options): string", + "module.exports": "function format(_: string, _: string, _: Options): string", } `) })