@@ -3,19 +3,20 @@ const camelCase = require("camelcase");
33const {
44 get,
55 getOptions,
6- shouldImportVolatile,
7- shouldImportReadOnly,
86 capitalizeFirstLetter,
97 startsWithUpperCaseLetter,
108 DECORATOR_PATHS ,
9+ LAYOUT_IMPORT_SPECIFIER ,
1110 METHOD_DECORATORS ,
12- META_DECORATORS
11+ META_DECORATORS ,
12+ EMBER_DECORATOR_SPECIFIERS
1313} = require ( "./util" ) ;
1414const { hasValidProps, isFileOfType } = require ( "./validation-helper" ) ;
1515const {
1616 withComments,
1717 createClass,
18- createImportDeclaration
18+ createImportDeclaration,
19+ createEmberDecoratorSpecifiers
1920} = require ( "./transform-helper" ) ;
2021const EOProp = require ( "./EOProp" ) ;
2122const logger = require ( "./log-helper" ) ;
@@ -56,6 +57,9 @@ function getEmberObjectProps(j, eoExpression, importedDecoratedProps = {}) {
5657 prop . setDecorators ( importedDecoratedProps ) ;
5758 instanceProps . push ( prop ) ;
5859 }
60+ if ( prop . isLayout ) {
61+ prop . setLayoutValue ( LAYOUT_IMPORT_SPECIFIER ) ;
62+ }
5963 } ) ;
6064
6165 // Assign decoator names to the binding props if any
@@ -204,55 +208,25 @@ function getDecoratorImports(j, root) {
204208}
205209
206210/**
207- * Create the import declarations for `readOnly` in `computed(...).readOnly()` and `volatile` in `computed(...).volatile()`.
208- * It will import from these specifiers from `@ember-decorators/object`
209- *
210- * @param {Object } j - jscodeshift lib reference
211- * @param {Boolean } param.importVolatile if true import `volatile`
212- * @param {Boolean } param.importReadOnly if true import `readOnly`
213- */
214- function createVolatileReadOnlyImportDeclarations (
215- j ,
216- { importVolatile = false , importReadOnly = false } = { }
217- ) {
218- const specifiers = [ ] ;
219- if ( importVolatile ) {
220- specifiers . push ( j . importSpecifier ( j . identifier ( "volatile" ) ) ) ;
221- }
222- if ( importReadOnly ) {
223- specifiers . push ( j . importSpecifier ( j . identifier ( "readOnly" ) ) ) ;
224- }
225- if ( specifiers . length ) {
226- return createImportDeclaration ( j , specifiers , "@ember-decorators/object" ) ;
227- }
228- }
229-
230- /**
231- * Create and insert the import declarations for `computed(...).volatile()` and `computed(...).readOnly()`
211+ * Get the map of decorators to import other than the computed props, services etc
212+ * which already have imports in the code
232213 *
233- * @param {Object } j - jscodeshift lib reference
234- * @param {File } root
235- * @param {Boolean } param.importVolatile if true import `volatile`
236- * @param {Boolean } param.importReadOnly if true import `readOnly`
214+ * @param {EOProp[] } instanceProps
215+ * @param {Object } decoratorsMap
237216 */
238- function insertVolatileReadOnlyImportDeclarations (
239- j ,
240- root ,
241- { importVolatile, importReadOnly }
242- ) {
243- const vroImportDeclaration = createVolatileReadOnlyImportDeclarations ( j , {
244- importVolatile,
245- importReadOnly
246- } ) ;
247- if ( ! vroImportDeclaration ) {
248- return ;
249- }
250- const importDeclarations = root . find ( j . ImportDeclaration ) ;
251- if ( importDeclarations . length ) {
252- importDeclarations . at ( - 1 ) . insertAfter ( vroImportDeclaration ) ;
253- } else {
254- root . get ( ) . node . program . body . unshift ( vroImportDeclaration ) ;
255- }
217+ function getDecoratorsToImport ( instanceProps , decoratorsMap = { } ) {
218+ return instanceProps . reduce ( ( specs , prop ) => {
219+ return {
220+ attribute : specs . attribute || prop . hasAttributeDecorator ,
221+ readOnly : specs . readOnly || prop . hasReadOnly ,
222+ action : specs . action || prop . isAction ,
223+ layout : specs . layout || prop . isLayout ,
224+ tagName : specs . tagName || prop . isTagName ,
225+ className : specs . className || prop . hasClassNameDecorator ,
226+ classNames : specs . classNames || prop . isClassNames ,
227+ volatile : specs . volatile || prop . hasVolatile
228+ } ;
229+ } , decoratorsMap ) ;
256230}
257231
258232/**
@@ -262,17 +236,24 @@ function insertVolatileReadOnlyImportDeclarations(
262236 * @param {File } root
263237 * @returns {Object }
264238 */
265- function createDecoratorImportDeclarations (
266- j ,
267- root ,
268- { importVolatile = false , importReadOnly = false } = { }
269- ) {
239+ function createDecoratorImportDeclarations ( j , root , decoratorsToImport = [ ] ) {
240+ // create a copy - we need to mutate the object later
241+ const edSpecifiers = Object . assign ( { } , EMBER_DECORATOR_SPECIFIERS ) ;
270242 getDecoratorImports ( j , root ) . forEach ( decoratorImport => {
271243 const { importPropDecoratorMap, decoratorPath } = DECORATOR_PATHS [
272244 get ( decoratorImport , "value.source.value" )
273245 ] ;
246+ const pathSpecifiers = edSpecifiers [ decoratorPath ] || [ ] ;
247+ if ( pathSpecifiers . length ) {
248+ // delete the visited path to avoid duplicate imports
249+ delete edSpecifiers [ decoratorPath ] ;
250+ }
251+ const decoratedSpecifiers = createEmberDecoratorSpecifiers (
252+ j ,
253+ pathSpecifiers ,
254+ decoratorsToImport
255+ ) ;
274256
275- const decoratedSpecifiers = [ ] ;
276257 const specifiers = get ( decoratorImport , "value.specifiers" ) || [ ] ;
277258
278259 for ( let i = specifiers . length - 1 ; i >= 0 ; i -= 1 ) {
@@ -311,10 +292,25 @@ function createDecoratorImportDeclarations(
311292 }
312293 } ) ;
313294
314- insertVolatileReadOnlyImportDeclarations ( j , root , {
315- importVolatile,
316- importReadOnly
317- } ) ;
295+ const edSpecifierPaths = Object . keys ( edSpecifiers ) ;
296+ if ( edSpecifierPaths . length ) {
297+ edSpecifierPaths . forEach ( path => {
298+ const specifiers = createEmberDecoratorSpecifiers (
299+ j ,
300+ edSpecifiers [ path ] ,
301+ decoratorsToImport
302+ ) ;
303+
304+ if ( specifiers . length ) {
305+ j (
306+ root
307+ . find ( j . Declaration )
308+ . at ( 0 )
309+ . get ( )
310+ ) . insertBefore ( createImportDeclaration ( j , specifiers , path ) ) ;
311+ }
312+ } ) ;
313+ }
318314}
319315
320316/**
@@ -366,6 +362,45 @@ function getEmberObjectCallExpressions(j, root) {
366362 ) ;
367363}
368364
365+ /**
366+ * Extracts the layout property name
367+ *
368+ * @param {Object } j - jscodeshift lib reference
369+ * @param {File } root
370+ * @returns {String } Name of the layout property
371+ */
372+ function getLayoutPropertyName ( j , root ) {
373+ const layoutPropCollection = root . find ( j . Property , {
374+ key : {
375+ type : "Identifier" ,
376+ name : "layout"
377+ }
378+ } ) ;
379+ if ( layoutPropCollection . length ) {
380+ const layoutProp = layoutPropCollection . get ( ) ;
381+ return get ( layoutProp , "value.value.name" ) ;
382+ }
383+ }
384+
385+ /**
386+ * Update the layout import name
387+ *
388+ * @param {Object } j - jscodeshift lib reference
389+ * @param {File } root
390+ */
391+ function updateLayoutImportDeclaration ( j , root , layoutName ) {
392+ if ( ! layoutName ) {
393+ return ;
394+ }
395+ const layoutIdentifier = root
396+ . find ( j . ImportDefaultSpecifier , { local : { name : layoutName } } )
397+ . find ( j . Identifier ) ;
398+
399+ if ( layoutIdentifier . length ) {
400+ layoutIdentifier . get ( ) . value . name = LAYOUT_IMPORT_SPECIFIER ;
401+ }
402+ }
403+
369404/**
370405 * Returns the variable name
371406 *
@@ -466,9 +501,9 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
466501 }
467502 // Parse the import statements
468503 const importedDecoratedProps = getImportedDecoratedProps ( j , root ) ;
504+ const layoutName = getLayoutPropertyName ( j , root ) ;
469505 let transformed = false ;
470- let importVolatile = false ;
471- let importReadOnly = false ;
506+ let decoratorsToImportMap = { } ;
472507
473508 getEmberObjectCallExpressions ( j , root ) . forEach ( eoCallExpression => {
474509 const { eoExpression, mixins } = parseEmberObjectCallExpression (
@@ -480,6 +515,7 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
480515 eoExpression ,
481516 importedDecoratedProps
482517 ) ;
518+
483519 const errors = hasValidProps ( eoProps , getOptions ( options ) ) ;
484520 if ( errors . length ) {
485521 logger . warn (
@@ -504,20 +540,21 @@ function replaceEmberObjectExpressions(j, root, filePath, options = {}) {
504540
505541 transformed = true ;
506542
507- importVolatile =
508- importVolatile || shouldImportVolatile ( eoProps . instanceProps ) ;
509- importReadOnly =
510- importReadOnly || shouldImportReadOnly ( eoProps . instanceProps ) ;
511-
512- logger . info ( `[${ filePath } ]: SUCCESS` ) ;
543+ decoratorsToImportMap = getDecoratorsToImport (
544+ eoProps . instanceProps ,
545+ decoratorsToImportMap
546+ ) ;
513547 } ) ;
548+
514549 // Need to find another way, as there might be a case where
515550 // one object from a file is transformed and other is not
516551 if ( transformed ) {
517- createDecoratorImportDeclarations ( j , root , {
518- importVolatile,
519- importReadOnly
520- } ) ;
552+ const decoratorsToImport = Object . keys ( decoratorsToImportMap ) . filter (
553+ key => decoratorsToImportMap [ key ]
554+ ) ;
555+ createDecoratorImportDeclarations ( j , root , decoratorsToImport ) ;
556+ updateLayoutImportDeclaration ( j , root , layoutName ) ;
557+ logger . info ( `[${ filePath } ]: SUCCESS` ) ;
521558 }
522559}
523560
0 commit comments