@@ -34,6 +34,8 @@ function transform(file, api/*, options*/) {
3434 // use this at the end to generate a report.
3535 let warnings = [ ] ;
3636
37+ let pendingGlobals = { } ;
38+
3739 try {
3840 // Discover existing module imports, if any, in the file. If the user has
3941 // already imported one or more exports that we rewrite a global with, we
@@ -62,6 +64,10 @@ function transform(file, api/*, options*/) {
6264 // e.g. `const { String: { underscore } } = Ember;`.
6365 let globalAliases = findGlobalEmberAliases ( root , globalEmber , mappings ) ;
6466
67+ // Go through all of the tracked pending Ember globals. The ones that have
68+ // been marked as missing should be added to the warnings.
69+ resolvePendingGlobals ( ) ;
70+
6571 // Resolve the discovered aliases against the module registry. We intentionally do
6672 // this ahead of finding replacements for e.g. `Ember.String.underscore` usage in
6773 // order to reuse custom names for any fields referenced both ways.
@@ -72,7 +78,6 @@ function transform(file, api/*, options*/) {
7278 // mappings, save it off for replacement later.
7379 let replacements = findUsageOfEmberGlobal ( root , globalEmber )
7480 . map ( findReplacement ( mappings ) ) ;
75-
7681 // add the already found namespace replacements to our replacement array
7782 for ( let ns of namespaceUsages ) {
7883 let namespaceReplacements = ns . usages
@@ -188,9 +193,20 @@ function transform(file, api/*, options*/) {
188193 }
189194
190195 function findUsageOfDestructuredEmber ( root , globalEmber ) {
196+ // Keep track of the nested properties off of the Ember namespace,
197+ // to support multi-statement destructuring, i.e.:
198+ // const { computed } = Ember;
199+ // const { oneWay } = computed;
200+ let globalEmberWithNestedProperties = [ globalEmber ] ;
191201 let uses = root . find ( j . VariableDeclarator , ( node ) => {
192202 if ( j . Identifier . check ( node . init ) ) {
193- return node . init . name === globalEmber ;
203+ if ( includes ( globalEmberWithNestedProperties , node . init . name ) ) {
204+ // We've found an Ember global, or one of its nested properties.
205+ // Add it to the uses, and add its properties to the list of nested properties
206+ const identifierProperties = getIdentifierProperties ( node ) ;
207+ globalEmberWithNestedProperties = globalEmberWithNestedProperties . concat ( identifierProperties ) ;
208+ return true ;
209+ }
194210 } else if ( j . MemberExpression . check ( node . init ) ) {
195211 return node . init . object . name === globalEmber ;
196212 }
@@ -199,6 +215,29 @@ function transform(file, api/*, options*/) {
199215 return uses . paths ( ) ;
200216 }
201217
218+ function resolvePendingGlobals ( ) {
219+ Object . keys ( pendingGlobals ) . forEach ( ( key ) => {
220+ let pendingGlobal = pendingGlobals [ key ] ;
221+ const parentPath = pendingGlobal . pattern . parentPath ;
222+ if ( ! pendingGlobal . hasMissingGlobal ) {
223+ parentPath . prune ( ) ;
224+ } else {
225+ warnMissingGlobal ( parentPath , pendingGlobal . emberPath ) ;
226+ }
227+ } )
228+ }
229+
230+ function getIdentifierProperties ( node ) {
231+ let identifierProperties = [ ] ;
232+ node . id . properties . forEach ( ( property ) => {
233+ if ( j . Identifier . check ( property . value ) ) {
234+ identifierProperties . push ( property . key . name ) ;
235+ }
236+ } ) ;
237+
238+ return identifierProperties ;
239+ }
240+
202241 function joinEmberPath ( nodePath , globalEmber ) {
203242 if ( j . Identifier . check ( nodePath . node ) ) {
204243 if ( nodePath . node . name !== globalEmber ) {
@@ -217,16 +256,38 @@ function transform(file, api/*, options*/) {
217256
218257 // Determine aliases introduced by the given destructuring pattern, removing
219258 // items from the pattern when they're available via a module import instead.
259+ // Also tracks and flags pending globals for future patterns,
260+ // in case we have multi-statement destructuring, i.e:
261+ // const { computed } = Ember;
262+ // const { oneWay } = computed;
220263 function extractAliases ( mappings , pattern , emberPath ) {
221264 if ( j . Identifier . check ( pattern . node ) ) {
222265 if ( emberPath in mappings ) {
223266 pattern . parentPath . prune ( ) ;
267+ const pendingGlobalParent = findPendingGlobal ( emberPath ) ;
268+ if ( pendingGlobalParent ) {
269+ // A parent has been found. Mark it as no longer being missing.
270+ pendingGlobalParent . hasMissingGlobal = false ;
271+ }
272+
224273 return [ new GlobalAlias ( pattern , emberPath ) ] ;
225274 } else {
226- // skip warnings for destructured namespaces, these will be handled elsewhere
227- if ( ! includes ( EMBER_NAMESPACES , emberPath ) ) {
228- warnMissingGlobal ( pattern . parentPath , emberPath ) ;
275+ let thisPatternHasMissingGlobal = false ;
276+ const pendingGlobalParent = findPendingGlobal ( emberPath ) ;
277+ if ( pendingGlobalParent ) {
278+ // A parent has been found. Mark it as a missing global.
279+ pendingGlobalParent . hasMissingGlobal = true ;
280+ } else {
281+ // Otherwise, mark this pattern as a missing global.
282+ thisPatternHasMissingGlobal = true ;
229283 }
284+
285+ // Add this pattern to pendingGlobals
286+ pendingGlobals [ pattern . node . name ] = {
287+ pattern,
288+ emberPath,
289+ hasMissingGlobal : thisPatternHasMissingGlobal
290+ } ;
230291 }
231292 } else if ( j . ObjectPattern . check ( pattern . node ) ) {
232293 let aliases = findObjectPatternAliases ( mappings , pattern , emberPath ) ;
@@ -239,6 +300,19 @@ function transform(file, api/*, options*/) {
239300 return [ ] ;
240301 }
241302
303+ function findPendingGlobal ( emberPath ) {
304+ if ( ! emberPath ) {
305+ return ;
306+ }
307+ const paths = emberPath . split ( '.' ) ;
308+ for ( let idx = 0 ; idx < paths . length ; idx ++ ) {
309+ const path = paths [ idx ] ;
310+ if ( pendingGlobals [ path ] ) {
311+ return pendingGlobals [ path ] ;
312+ }
313+ }
314+ }
315+
242316 function findObjectPatternAliases ( mappings , objectPattern , basePath ) {
243317 let aliases = [ ] ;
244318 for ( let i = objectPattern . node . properties . length - 1 ; i >= 0 ; i -- ) {
0 commit comments