11import _generate from '@babel/generator'
22import { parse } from '@babel/parser'
33import * as t from '@babel/types'
4- import { isDeclarationType , isTypeOf } from 'ast-kit'
4+ import { isDeclarationType , isTypeOf , resolveString } from 'ast-kit'
55import { walk } from 'estree-walker'
66import {
77 filename_dts_to ,
@@ -45,6 +45,7 @@ export function createFakeJsPlugin({
4545 const identifierMap : Record < string , number > = Object . create ( null )
4646 const symbolMap = new Map < number /* symbol id */ , SymbolInfo > ( )
4747 const commentsMap = new Map < string /* filename */ , t . Comment [ ] > ( )
48+ const typeOnlyMap = new Map < string , string [ ] > ( )
4849
4950 return {
5051 name : 'rolldown-plugin-dts:fake-js' ,
@@ -99,6 +100,7 @@ export function createFakeJsPlugin({
99100 sourceType : 'module' ,
100101 } )
101102 const { program, comments } = file
103+ const typeOnlyIds : string [ ] = [ ]
102104
103105 if ( comments ) {
104106 const directives = collectReferenceDirectives ( comments )
@@ -110,7 +112,7 @@ export function createFakeJsPlugin({
110112
111113 for ( const [ i , stmt ] of program . body . entries ( ) ) {
112114 const setStmt = ( node : t . Node ) => ( program . body [ i ] = node as any )
113- if ( rewriteImportExport ( stmt , setStmt ) ) continue
115+ if ( rewriteImportExport ( stmt , setStmt , typeOnlyIds ) ) continue
114116
115117 const sideEffect =
116118 stmt . type === 'TSModuleDeclaration' && stmt . kind !== 'namespace'
@@ -231,6 +233,8 @@ export function createFakeJsPlugin({
231233 ...appendStmts ,
232234 ]
233235
236+ typeOnlyMap . set ( id , typeOnlyIds )
237+
234238 const result = generate ( file , {
235239 comments : false ,
236240 sourceMaps : sourcemap ,
@@ -244,6 +248,12 @@ export function createFakeJsPlugin({
244248 return
245249 }
246250
251+ const typeOnlyIds : string [ ] = [ ]
252+ for ( const module of chunk . moduleIds ) {
253+ const ids = typeOnlyMap . get ( module )
254+ if ( ids ) typeOnlyIds . push ( ...ids )
255+ }
256+
247257 const file = parse ( code , {
248258 sourceType : 'module' ,
249259 } )
@@ -254,7 +264,7 @@ export function createFakeJsPlugin({
254264 program . body = program . body
255265 . map ( ( node ) => {
256266 if ( isHelperImport ( node ) ) return null
257- if ( patchImportSource ( node ) ) return node
267+ if ( patchImportExport ( node , typeOnlyIds ) ) return node
258268 if ( node . type !== 'VariableDeclaration' ) return node
259269
260270 const [ decl ] = node . declarations
@@ -513,18 +523,31 @@ function isHelperImport(node: t.Node) {
513523}
514524
515525// patch `.d.ts` suffix in import source to `.js`
516- function patchImportSource ( node : t . Node ) {
526+ function patchImportExport ( node : t . Node , typeOnlyIds : string [ ] ) {
517527 if (
518528 isTypeOf ( node , [
519529 'ImportDeclaration' ,
520530 'ExportAllDeclaration' ,
521531 'ExportNamedDeclaration' ,
522- ] ) &&
523- node . source ?. value &&
524- RE_DTS . test ( node . source . value )
532+ ] )
525533 ) {
526- node . source . value = filename_dts_to ( node . source . value , 'js' )
527- return true
534+ if ( typeOnlyIds . length && node . type === 'ExportNamedDeclaration' ) {
535+ for ( const spec of node . specifiers ) {
536+ const name = resolveString ( spec . exported )
537+ if ( typeOnlyIds . includes ( name ) ) {
538+ if ( spec . type === 'ExportSpecifier' ) {
539+ spec . exportKind = 'type'
540+ } else {
541+ node . exportKind = 'type'
542+ }
543+ }
544+ }
545+ }
546+
547+ if ( node . source ?. value && RE_DTS . test ( node . source . value ) ) {
548+ node . source . value = filename_dts_to ( node . source . value , 'js' )
549+ return true
550+ }
528551 }
529552}
530553
@@ -595,13 +618,14 @@ function patchTsNamespace(nodes: t.Statement[]) {
595618// - import { type ... } from '...'
596619// - export type { ... }
597620// - export { type ... }
598- // - export type * as '...'
621+ // - export type * as x '...'
599622// - import Foo = require("./bar")
600623// - export = Foo
601624// - export default x
602625function rewriteImportExport (
603626 node : t . Node ,
604627 set : ( node : t . Node ) => void ,
628+ typeOnlyIds : string [ ] ,
605629) : node is
606630 | t . ImportDeclaration
607631 | t . ExportAllDeclaration
@@ -611,6 +635,22 @@ function rewriteImportExport(
611635 ( node . type === 'ExportNamedDeclaration' && ! node . declaration )
612636 ) {
613637 for ( const specifier of node . specifiers ) {
638+ if (
639+ ( 'exportKind' in specifier && specifier . exportKind === 'type' ) ||
640+ ( 'exportKind' in node && node . exportKind === 'type' )
641+ ) {
642+ typeOnlyIds . push (
643+ resolveString (
644+ (
645+ specifier as
646+ | t . ExportSpecifier
647+ | t . ExportDefaultSpecifier
648+ | t . ExportNamespaceSpecifier
649+ ) . exported ,
650+ ) ,
651+ )
652+ }
653+
614654 if ( specifier . type === 'ImportSpecifier' ) {
615655 specifier . importKind = 'value'
616656 } else if ( specifier . type === 'ExportSpecifier' ) {
0 commit comments