@@ -117,40 +117,74 @@ export function stringifyImports(imports: Import[], isCJS = false) {
117117 . join ( '\n' )
118118}
119119
120+ function encodeImportName ( name : string ) {
121+ return `\uFFFF${ name } \uFFFE`
122+ }
123+
120124export function dedupeImports ( imports : Import [ ] , warn : ( msg : string ) => void ) {
121- const map = new Map < string , number > ( )
122- const indexToRemove = new Set < number > ( )
125+ const deduped = new Map < string | number , Import > ( )
123126
124- imports . filter ( i => ! i . disabled ) . forEach ( ( i , idx ) => {
125- if ( i . declarationType === 'enum' || i . declarationType === 'const enum' || i . declarationType === 'class' )
126- return
127+ for ( let idx = imports . length - 1 ; idx >= 0 ; idx -- ) {
128+ const currImp = imports [ idx ]
129+ if ( currImp . disabled || currImp . declarationType === 'enum' || currImp . declarationType === 'const enum' || currImp . declarationType === 'class' ) {
130+ deduped . set ( idx , currImp )
131+ continue
132+ }
127133
128- const name = i . as ?? i . name
129- if ( ! map . has ( name ) ) {
130- map . set ( name , idx )
131- return
134+ const name = String ( currImp . as ?? currImp . name )
135+ const prevImp = deduped . get ( name )
136+ if ( ! prevImp ) {
137+ deduped . set ( name , currImp )
138+ continue
132139 }
133140
134- const other = imports [ map . get ( name ) ! ]
141+ const isSameSpecifier = ( currImp . type || prevImp . type )
142+ ? ( currImp . typeFrom || currImp . from ) === ( prevImp . typeFrom || prevImp . from )
143+ : currImp . from === prevImp . from
135144
136- if ( other . from === i . from ) {
137- indexToRemove . add ( idx )
138- return
145+ if ( isSameSpecifier ) {
146+ if ( Boolean ( currImp . type ) === Boolean ( prevImp . type ) ) {
147+ // currImp and prevImp are the same import
148+ if ( ( currImp . priority || 1 ) > ( prevImp . priority || 1 ) ) {
149+ deduped . delete ( name )
150+ deduped . set ( name , currImp )
151+ }
152+ }
153+ else {
154+ // currImp and prevImp are complementary imports; one for the value and one for the type
155+ const altName = encodeImportName ( name )
156+ const prevImpComplement = deduped . get ( altName )
157+ if ( ! prevImpComplement ) {
158+ deduped . set ( altName , currImp )
159+ }
160+ else if ( ( currImp . priority || 1 ) > ( prevImpComplement . priority || 1 ) ) {
161+ deduped . delete ( altName )
162+ deduped . set ( altName , currImp )
163+ }
164+ }
165+ continue
139166 }
140- const diff = ( other . priority || 1 ) - ( i . priority || 1 )
141- if ( diff === 0 )
142- warn ( `Duplicated imports "${ name } ", the one from "${ other . from } " has been ignored and "${ i . from } " is used` )
143167
144- if ( diff <= 0 ) {
145- indexToRemove . add ( map . get ( name ) ! )
146- map . set ( name , idx )
168+ // currImp and prevImp are duplicate imports
169+ const altName = encodeImportName ( name )
170+ const prevImpComplement = deduped . get ( altName )
171+ const priorityDiff = ( currImp . priority || 1 ) - Math . max ( prevImp . priority || 1 , prevImpComplement ?. priority || 1 )
172+ if ( priorityDiff > 0 ) {
173+ deduped . delete ( name )
174+ deduped . delete ( altName )
175+ deduped . set ( name , currImp )
147176 }
148- else {
149- indexToRemove . add ( idx )
177+ else if ( priorityDiff === 0 ) {
178+ warn ( `Duplicated imports " ${ name } ", the one from " ${ currImp . from } " has been ignored and " ${ prevImp . from } " is used` )
150179 }
151- } )
180+ }
152181
153- return imports . filter ( ( _ , idx ) => ! indexToRemove . has ( idx ) )
182+ let i = deduped . size
183+ const dedupedImports = new Array ( i ) // eslint-disable-line unicorn/no-new-array
184+ for ( const imp of deduped . values ( ) ) {
185+ dedupedImports [ -- i ] = imp
186+ }
187+ return dedupedImports
154188}
155189
156190export function toExports ( imports : Import [ ] , fileDir ?: string , includeType = false , options : ToExportsOptions = { } ) {
0 commit comments