@@ -114,45 +114,104 @@ class Transformer {
114
114
}
115
115
116
116
// import ... from 'lit-localize' -> (removed)
117
- if ( this . isLitLocalizeImport ( node ) ) {
118
- return undefined ;
117
+ if ( ts . isImportDeclaration ( node ) ) {
118
+ const moduleSymbol = this . typeChecker . getSymbolAtLocation (
119
+ node . moduleSpecifier
120
+ ) ;
121
+ if ( moduleSymbol && this . isLitLocalizeModule ( moduleSymbol ) ) {
122
+ return undefined ;
123
+ }
119
124
}
120
125
121
- // configureTransformLocalization(...) -> {getLocale: () => "es-419"}
122
- if (
123
- this . isCallToTaggedFunction (
124
- node ,
125
- '_LIT_LOCALIZE_CONFIGURE_TRANSFORM_LOCALIZATION_'
126
- )
127
- ) {
128
- return ts . createObjectLiteral (
129
- [
130
- ts . createPropertyAssignment (
131
- ts . createIdentifier ( 'getLocale' ) ,
132
- ts . createArrowFunction (
133
- undefined ,
134
- undefined ,
135
- [ ] ,
136
- undefined ,
137
- ts . createToken ( ts . SyntaxKind . EqualsGreaterThanToken ) ,
138
- ts . createStringLiteral ( this . locale )
139
- )
140
- ) ,
141
- ] ,
142
- false
143
- ) ;
126
+ if ( ts . isCallExpression ( node ) ) {
127
+ // configureTransformLocalization(...) -> {getLocale: () => "es-419"}
128
+ if (
129
+ this . typeHasProperty (
130
+ node . expression ,
131
+ '_LIT_LOCALIZE_CONFIGURE_TRANSFORM_LOCALIZATION_'
132
+ )
133
+ ) {
134
+ return ts . createObjectLiteral (
135
+ [
136
+ ts . createPropertyAssignment (
137
+ ts . createIdentifier ( 'getLocale' ) ,
138
+ ts . createArrowFunction (
139
+ undefined ,
140
+ undefined ,
141
+ [ ] ,
142
+ undefined ,
143
+ ts . createToken ( ts . SyntaxKind . EqualsGreaterThanToken ) ,
144
+ ts . createStringLiteral ( this . locale )
145
+ )
146
+ ) ,
147
+ ] ,
148
+ false
149
+ ) ;
150
+ }
151
+
152
+ // configureLocalization(...) -> Error
153
+ if (
154
+ this . typeHasProperty (
155
+ node . expression ,
156
+ '_LIT_LOCALIZE_CONFIGURE_LOCALIZATION_'
157
+ )
158
+ ) {
159
+ // TODO(aomarks) This error is not surfaced earlier in the analysis phase
160
+ // as a nicely formatted diagnostic, but it should be.
161
+ throw new KnownError (
162
+ 'Cannot use configureLocalization in transform mode. ' +
163
+ 'Use configureTransformLocalization instead.'
164
+ ) ;
165
+ }
166
+
167
+ // Localized(LitElement) -> LitElement
168
+ if ( this . typeHasProperty ( node . expression , '_LIT_LOCALIZE_LOCALIZED_' ) ) {
169
+ if ( node . arguments . length !== 1 ) {
170
+ // TODO(aomarks) Surface as diagnostic instead.
171
+ throw new KnownError (
172
+ `Expected Localized mixin call to have one argument, ` +
173
+ `got ${ node . arguments . length } `
174
+ ) ;
175
+ }
176
+ return node . arguments [ 0 ] ;
177
+ }
144
178
}
145
179
146
- // configureLocalization(...) -> Error
147
- if (
148
- this . isCallToTaggedFunction ( node , '_LIT_LOCALIZE_CONFIGURE_LOCALIZATION_' )
149
- ) {
150
- // TODO(aomarks) This error is not surfaced earlier in the analysis phase
151
- // as a nicely formatted diagnostic, but it should be.
152
- throw new KnownError (
153
- 'Cannot use configureLocalization in transform mode. ' +
154
- 'Use configureTransformLocalization instead.'
155
- ) ;
180
+ // LOCALE_STATUS_EVENT -> "lit-localize-status"
181
+ //
182
+ // We want to replace this imported string constant with its static value so
183
+ // that we can always safely remove the 'lit-localize' module import.
184
+ //
185
+ // TODO(aomarks) Maybe we should error here instead, since lit-localize
186
+ // won't fire any of these events in transform mode? But I'm still thinking
187
+ // about the use case of an app that can run in either runtime or transform
188
+ // mode without code changes (e.g. runtime for dev, transform for
189
+ // production)...
190
+ //
191
+ // We can't tag this string const with a special property like we do with
192
+ // our exported functions, because doing so breaks lookups into
193
+ // `WindowEventMap`. So we instead identify the symbol by name, and check
194
+ // that it was declared in the lit-localize module.
195
+ let eventSymbol = this . typeChecker . getSymbolAtLocation ( node ) ;
196
+ if ( eventSymbol && eventSymbol . name === 'LOCALE_STATUS_EVENT' ) {
197
+ if ( eventSymbol . flags & ts . SymbolFlags . Alias ) {
198
+ // Symbols will be aliased in the case of
199
+ // `import {LOCALE_STATUS_EVENT} ...`
200
+ // but not in the case of `import * as ...`.
201
+ eventSymbol = this . typeChecker . getAliasedSymbol ( eventSymbol ) ;
202
+ }
203
+ for ( const decl of eventSymbol . declarations ) {
204
+ let sourceFile : ts . Node = decl ;
205
+ while ( ! ts . isSourceFile ( sourceFile ) ) {
206
+ sourceFile = sourceFile . parent ;
207
+ }
208
+ const sourceFileSymbol = this . typeChecker . getSymbolAtLocation (
209
+ sourceFile
210
+ ) ;
211
+ if ( sourceFileSymbol && this . isLitLocalizeModule ( sourceFileSymbol ) ) {
212
+ return ts . createStringLiteral ( 'lit-localize-status' ) ;
213
+ }
214
+ }
156
215
}
157
216
158
217
return ts . visitEachChild ( node , this . boundVisitNode , this . context ) ;
@@ -380,16 +439,11 @@ class Transformer {
380
439
}
381
440
382
441
/**
383
- * Return whether the given node is an import for the lit-localize module.
442
+ * Return whether the given symbol looks like one of the lit-localize modules
443
+ * (because it exports one of the special tagged functions).
384
444
*/
385
- isLitLocalizeImport ( node : ts . Node ) : node is ts . ImportDeclaration {
386
- if ( ! ts . isImportDeclaration ( node ) ) {
387
- return false ;
388
- }
389
- const moduleSymbol = this . typeChecker . getSymbolAtLocation (
390
- node . moduleSpecifier
391
- ) ;
392
- if ( ! moduleSymbol || ! moduleSymbol . exports ) {
445
+ isLitLocalizeModule ( moduleSymbol : ts . Symbol ) : boolean {
446
+ if ( ! moduleSymbol . exports ) {
393
447
return false ;
394
448
}
395
449
const exports = moduleSymbol . exports . values ( ) ;
@@ -398,27 +452,30 @@ class Transformer {
398
452
} ) {
399
453
const type = this . typeChecker . getTypeAtLocation ( xport . valueDeclaration ) ;
400
454
const props = this . typeChecker . getPropertiesOfType ( type ) ;
401
- if ( props . some ( ( prop ) => prop . escapedName === '_LIT_LOCALIZE_MSG_' ) ) {
455
+ if (
456
+ props . some (
457
+ ( prop ) =>
458
+ prop . escapedName === '_LIT_LOCALIZE_MSG_' ||
459
+ prop . escapedName === '_LIT_LOCALIZE_LOCALIZED_'
460
+ )
461
+ ) {
402
462
return true ;
403
463
}
404
464
}
405
465
return false ;
406
466
}
407
467
408
468
/**
409
- * Return whether the given node is call to a function which is is "tagged"
410
- * with the given special identifying property (e.g. "_LIT_LOCALIZE_MSG_").
469
+ * Return whether the tpe of the given node is "tagged" with the given special
470
+ * identifying property (e.g. "_LIT_LOCALIZE_MSG_").
411
471
*/
412
- isCallToTaggedFunction (
472
+ typeHasProperty (
413
473
node : ts . Node ,
414
- tagProperty : string
474
+ propertyName : string
415
475
) : node is ts . CallExpression {
416
- if ( ! ts . isCallExpression ( node ) ) {
417
- return false ;
418
- }
419
- const type = this . typeChecker . getTypeAtLocation ( node . expression ) ;
476
+ const type = this . typeChecker . getTypeAtLocation ( node ) ;
420
477
const props = this . typeChecker . getPropertiesOfType ( type ) ;
421
- return props . some ( ( prop ) => prop . escapedName === tagProperty ) ;
478
+ return props . some ( ( prop ) => prop . escapedName === propertyName ) ;
422
479
}
423
480
}
424
481
0 commit comments