1+ import { TransformError } from '@vue-macros/common'
2+ import { err , ok , safeTry , type ResultAsync } from 'neverthrow'
3+ import type { ErrorUnknownNode } from '../error'
14import { isTSDeclaration } from './is'
25import { resolveDts } from './resolve-file'
36import {
@@ -20,124 +23,143 @@ export function isTSNamespace(val: unknown): val is TSNamespace {
2023 *
2124 *@limitation don't support non-TS declaration (e.g. class, function...)
2225 */
23- export async function resolveTSNamespace ( scope :TSScope ) :Promise < void > {
24- if ( scope . exports ) return
25-
26- const exports :TSNamespace = {
27- [ namespaceSymbol ] :true ,
28- }
29- scope . exports = exports
30-
31- const declarations :TSNamespace = {
32- [ namespaceSymbol ] :true ,
33- ...scope . declarations ,
34- }
35- scope . declarations = declarations
36-
37- const { body, file} = resolveTSScope ( scope )
38- for ( const stmt of body || [ ] ) {
39- if (
40- stmt . type === 'ExportDefaultDeclaration' &&
41- isTSDeclaration ( stmt . declaration )
42- ) {
43- exports . default = await resolveTSReferencedType ( {
44- scope,
45- type :stmt . declaration ,
46- } )
47- } else if ( stmt . type === 'ExportAllDeclaration' ) {
48- const resolved = await resolveDts ( stmt . source . value , file . filePath )
49- if ( ! resolved ) continue
50-
51- const sourceScope = await getTSFile ( resolved )
52- await resolveTSNamespace ( sourceScope )
53-
54- Object . assign ( exports , sourceScope . exports ! )
55- } else if ( stmt . type === 'ExportNamedDeclaration' ) {
56- let sourceExports :TSNamespace
57-
58- if ( stmt . source ) {
26+ export function resolveTSNamespace (
27+ scope :TSScope ,
28+ ) :ResultAsync < void , TransformError < ErrorUnknownNode > > {
29+ return safeTry ( async function * ( ) {
30+ if ( scope . exports ) return ok ( )
31+
32+ const exports :TSNamespace = {
33+ [ namespaceSymbol ] :true ,
34+ }
35+ scope . exports = exports
36+
37+ const declarations :TSNamespace = {
38+ [ namespaceSymbol ] :true ,
39+ ...scope . declarations ,
40+ }
41+ scope . declarations = declarations
42+
43+ const { body, file} = resolveTSScope ( scope )
44+ for ( const stmt of body || [ ] ) {
45+ if (
46+ stmt . type === 'ExportDefaultDeclaration' &&
47+ isTSDeclaration ( stmt . declaration )
48+ ) {
49+ exports . default = yield * resolveTSReferencedType ( {
50+ scope,
51+ type :stmt . declaration ,
52+ } )
53+ } else if ( stmt . type === 'ExportAllDeclaration' ) {
5954const resolved = await resolveDts ( stmt . source . value , file . filePath )
6055if ( ! resolved ) continue
6156
62- const scope = await getTSFile ( resolved )
63- await resolveTSNamespace ( scope )
64- sourceExports = scope . exports !
65- } else {
66- sourceExports = declarations
67- }
57+ const sourceScope = await getTSFile ( resolved )
58+ yield * resolveTSNamespace ( sourceScope )
59+
60+ Object . assign ( exports , sourceScope . exports ! )
61+ } else if ( stmt . type === 'ExportNamedDeclaration' ) {
62+ let sourceExports : TSNamespace
6863
69- for ( const specifier of stmt . specifiers ) {
70- let exported :TSNamespace [ string ]
71- if ( specifier . type === 'ExportDefaultSpecifier' ) {
72- // export x from 'xxx'
73- exported = sourceExports . default
74- } else if ( specifier . type === 'ExportNamespaceSpecifier' ) {
75- // export * as x from 'xxx'
76- exported = sourceExports
77- } else if ( specifier . type === 'ExportSpecifier' ) {
78- // export { x } from 'xxx'
79- exported = sourceExports ! [ specifier . local . name ]
64+ if ( stmt . source ) {
65+ const resolved = await resolveDts ( stmt . source . value , file . filePath )
66+ if ( ! resolved ) continue
67+
68+ const scope = await getTSFile ( resolved )
69+ yield * resolveTSNamespace ( scope )
70+ sourceExports = scope . exports !
8071} else {
81- throw new Error ( `Unknown export type: ${ ( specifier as any ) . type } ` )
72+ sourceExports = declarations
8273}
8374
84- const name =
85- specifier . exported . type === 'Identifier'
86- ?specifier . exported . name
87- :specifier . exported . value
88- exports [ name ] = exported
89- }
75+ for ( const specifier of stmt . specifiers ) {
76+ let exported :TSNamespace [ string ]
77+ if ( specifier . type === 'ExportDefaultSpecifier' ) {
78+ // export x from 'xxx'
79+ exported = sourceExports . default
80+ } else if ( specifier . type === 'ExportNamespaceSpecifier' ) {
81+ // export * as x from 'xxx'
82+ exported = sourceExports
83+ } else if ( specifier . type === 'ExportSpecifier' ) {
84+ // export { x } from 'xxx'
85+ exported = sourceExports ! [ specifier . local . name ]
86+ } else {
87+ return err (
88+ new TransformError (
89+ `Unknown export type:${
90+ //@ts -expect-error unknown type
91+ specifier . type as string
92+ } `,
93+ ) ,
94+ )
95+ }
9096
91- // export interface A {}
92- if ( isTSDeclaration ( stmt . declaration ) ) {
93- const decl = stmt . declaration
94-
95- if ( decl . id ?. type === 'Identifier' ) {
96- const exportedName = decl . id . name
97- declarations [ exportedName ] = exports [ exportedName ] =
98- await resolveTSReferencedType ( {
99- scope,
100- type :decl ,
101- } )
97+ const name =
98+ specifier . exported . type === 'Identifier'
99+ ?specifier . exported . name
100+ :specifier . exported . value
101+ exports [ name ] = exported
102+ }
103+
104+ // export interface A {}
105+ if ( isTSDeclaration ( stmt . declaration ) ) {
106+ const decl = stmt . declaration
107+
108+ if ( decl . id ?. type === 'Identifier' ) {
109+ const exportedName = decl . id . name
110+ declarations [ exportedName ] = exports [ exportedName ] =
111+ yield * resolveTSReferencedType ( {
112+ scope,
113+ type :decl ,
114+ } )
115+ }
102116}
103117}
104- }
105118
106- // declarations
107- else if ( isTSDeclaration ( stmt ) ) {
108- if ( stmt . id ?. type !== 'Identifier' ) continue
109-
110- declarations [ stmt . id . name ] = await resolveTSReferencedType ( {
111- scope,
112- type :stmt ,
113- } )
114- } else if ( stmt . type === 'ImportDeclaration' ) {
115- const resolved = await resolveDts ( stmt . source . value , file . filePath )
116- if ( ! resolved ) continue
117-
118- const importScope = await getTSFile ( resolved )
119- await resolveTSNamespace ( importScope )
120- const exports = importScope . exports !
121-
122- for ( const specifier of stmt . specifiers ) {
123- const local = specifier . local . name
124-
125- let imported :TSNamespace [ string ]
126- if ( specifier . type === 'ImportDefaultSpecifier' ) {
127- imported = exports . default
128- } else if ( specifier . type === 'ImportNamespaceSpecifier' ) {
129- imported = exports
130- } else if ( specifier . type === 'ImportSpecifier' ) {
131- const name =
132- specifier . imported . type === 'Identifier'
133- ?specifier . imported . name
134- :specifier . imported . value
135- imported = exports [ name ]
136- } else {
137- throw new Error ( `Unknown import type:${ ( specifier as any ) . type } ` )
119+ // declarations
120+ else if ( isTSDeclaration ( stmt ) ) {
121+ if ( stmt . id ?. type !== 'Identifier' ) continue
122+
123+ declarations [ stmt . id . name ] = yield * resolveTSReferencedType ( {
124+ scope,
125+ type :stmt ,
126+ } )
127+ } else if ( stmt . type === 'ImportDeclaration' ) {
128+ const resolved = await resolveDts ( stmt . source . value , file . filePath )
129+ if ( ! resolved ) continue
130+
131+ const importScope = await getTSFile ( resolved )
132+ yield * resolveTSNamespace ( importScope )
133+ const exports = importScope . exports !
134+
135+ for ( const specifier of stmt . specifiers ) {
136+ const local = specifier . local . name
137+
138+ let imported :TSNamespace [ string ]
139+ if ( specifier . type === 'ImportDefaultSpecifier' ) {
140+ imported = exports . default
141+ } else if ( specifier . type === 'ImportNamespaceSpecifier' ) {
142+ imported = exports
143+ } else if ( specifier . type === 'ImportSpecifier' ) {
144+ const name =
145+ specifier . imported . type === 'Identifier'
146+ ?specifier . imported . name
147+ :specifier . imported . value
148+ imported = exports [ name ]
149+ } else {
150+ return err (
151+ new TransformError (
152+ `Unknown import type:${
153+ //@ts -expect-error unknown type
154+ specifier . type as string
155+ } `,
156+ ) ,
157+ )
158+ }
159+ declarations [ local ] = imported
138160}
139- declarations [ local ] = imported
140161}
141162}
142- }
163+ return ok ( )
164+ } )
143165}