1
- import { NodePath } from '@babel/traverse' ;
1
+ import type { NodePath } from '@babel/traverse' ;
2
2
import type * as Babel from '@babel/core' ;
3
3
import type { types as t } from '@babel/core' ;
4
4
import { ImportUtil } from 'babel-import-util' ;
@@ -147,6 +147,7 @@ interface State<EnvSpecificOptions> {
147
147
lastInsertedPath : NodePath < t . Statement > | undefined ;
148
148
filename : string ;
149
149
recursionGuard : Set < unknown > ;
150
+ originalImportedNames : Map < string , [ string , string ] > ;
150
151
}
151
152
152
153
export function makePlugin < EnvSpecificOptions > ( loadOptions : ( opts : EnvSpecificOptions ) => Options ) {
@@ -156,29 +157,21 @@ export function makePlugin<EnvSpecificOptions>(loadOptions: (opts: EnvSpecificOp
156
157
let t = babel . types ;
157
158
158
159
return {
159
- pre ( state ) {
160
- const imports = state . ast . program . body . filter (
161
- ( b ) => b . type === 'ImportDeclaration'
162
- ) as t . ImportDeclaration [ ] ;
163
- const templateCompilerImport = imports . find (
164
- ( i ) => i . source . value === '@ember/template-compiler'
165
- ) ;
166
-
167
- if ( templateCompilerImport ) {
168
- const program = NodePath . get ( {
169
- hub : state . hub ,
170
- key : 'program' ,
171
- parent : state . ast ,
172
- parentPath : null ,
173
- container : state . ast ,
174
- } ) ;
175
- for ( const i of imports ) {
176
- const specifiers = i . specifiers ;
177
- for ( const specifier of specifiers ) {
178
- const local = specifier . local ;
179
- if ( ! state . scope . getBinding ( local . name ) ?. referencePaths . length ) {
180
- state . scope . getBinding ( local . name ) ?. referencePaths . push ( program ) ;
181
- }
160
+ pre ( this : State < EnvSpecificOptions > , file ) {
161
+ // Remember the available set of imported names very early here in <pre>
162
+ // so that when other plugins (particularly
163
+ // @babel /plugin-transform-typescript) drop "unused" imports in their
164
+ // own Program.enter we still know about them. If we want to use them
165
+ // from inside a template, they weren't really unused and we can ensure
166
+ // they continue to exist.
167
+ this . originalImportedNames = new Map ( ) ;
168
+ for ( let statement of file . ast . program . body ) {
169
+ if ( statement . type === 'ImportDeclaration' ) {
170
+ for ( let specifier of statement . specifiers ) {
171
+ this . originalImportedNames . set ( specifier . local . name , [
172
+ statement . source . value ,
173
+ importedName ( specifier ) ,
174
+ ] ) ;
182
175
}
183
176
}
184
177
}
@@ -520,6 +513,7 @@ function insertCompiledTemplate<EnvSpecificOptions>(
520
513
configFile : false ,
521
514
} ) as t . File ;
522
515
516
+ ensureImportedNames ( target , scopeLocals , state . util , state . originalImportedNames ) ;
523
517
remapIdentifiers ( precompileResultAST , babel , scopeLocals ) ;
524
518
525
519
let templateExpression = ( precompileResultAST . program . body [ 0 ] as t . VariableDeclaration )
@@ -585,6 +579,7 @@ function insertTransformedTemplate<EnvSpecificOptions>(
585
579
maybePruneImport ( state . util , target . get ( 'callee' ) ) ;
586
580
target . set ( 'callee' , precompileTemplate ( state . util , target ) ) ;
587
581
}
582
+ ensureImportedNames ( target , scopeLocals , state . util , state . originalImportedNames ) ;
588
583
updateScope ( babel , target , scopeLocals ) ;
589
584
}
590
585
@@ -619,6 +614,7 @@ function insertTransformedTemplate<EnvSpecificOptions>(
619
614
let newCall = target . replaceWith (
620
615
t . callExpression ( precompileTemplate ( state . util , target ) , [ t . stringLiteral ( transformed ) ] )
621
616
) [ 0 ] ;
617
+ ensureImportedNames ( newCall , scopeLocals , state . util , state . originalImportedNames ) ;
622
618
updateScope ( babel , newCall , scopeLocals ) ;
623
619
} else {
624
620
( target . get ( 'quasi' ) . get ( 'quasis.0' ) as NodePath < t . TemplateElement > ) . replaceWith (
@@ -756,4 +752,31 @@ function name(node: t.StringLiteral | t.Identifier) {
756
752
}
757
753
}
758
754
755
+ function ensureImportedNames (
756
+ target : NodePath < t . Node > ,
757
+ scopeLocals : ScopeLocals ,
758
+ util : ImportUtil ,
759
+ originalImportedNames : Map < string , [ string , string ] >
760
+ ) {
761
+ for ( let [ nameInTemplate , identifier ] of scopeLocals . entries ( ) ) {
762
+ if ( ! target . scope . getBinding ( identifier ) ) {
763
+ let available = originalImportedNames . get ( identifier ) ;
764
+ if ( available ) {
765
+ let newIdent = util . import ( target , available [ 0 ] , available [ 1 ] , identifier ) ;
766
+ scopeLocals . add ( nameInTemplate , newIdent . name ) ;
767
+ }
768
+ }
769
+ }
770
+ }
771
+
772
+ function importedName ( node : t . ImportDeclaration [ 'specifiers' ] [ number ] ) : string {
773
+ if ( node . type === 'ImportDefaultSpecifier' ) {
774
+ return 'default' ;
775
+ } else if ( node . type === 'ImportNamespaceSpecifier' ) {
776
+ return '*' ;
777
+ } else {
778
+ return name ( node . imported ) ;
779
+ }
780
+ }
781
+
759
782
export default makePlugin < Options > ( ( options ) => options ) ;
0 commit comments