1+ const fs = require ( 'fs' ) ;
2+ const chalk = require ( 'chalk' ) ;
13const pkgDir = require ( 'pkg-dir' ) ;
24const globby = require ( 'globby' ) ;
5+ const j = require ( 'jscodeshift' ) . withParser ( 'ts' ) ;
36const debug = require ( 'debug' ) ( 'tagless-ember-components-codemod' ) ;
47
58async function run ( ) {
69 let path = await pkgDir ( ) ;
7- await runForPath ( path )
10+ await runForPath ( path ) ;
811}
912
1013async function runForPath ( path , options = { } ) {
@@ -19,7 +22,79 @@ async function runForPath(path, options = {}) {
1922 debug ( 'componentPaths = %O' , componentPaths ) ;
2023
2124 for ( let componentPath of componentPaths ) {
22- // TODO skip tagless components (silent)
25+ let dimPath = chalk . dim ( componentPath ) ;
26+
27+ let source = fs . readFileSync ( componentPath , 'utf8' ) ;
28+
29+ let root = j ( source ) ;
30+
31+ // find `export default Component.extend({ ... });` AST node
32+ let exportDefaultDeclarations = root . find ( j . ExportDefaultDeclaration , {
33+ declaration : {
34+ type : 'CallExpression' ,
35+ callee : {
36+ type : 'MemberExpression' ,
37+ object : { name : 'Component' } ,
38+ property : { name : 'extend' } ,
39+ } ,
40+ } ,
41+ } ) ;
42+
43+ if ( exportDefaultDeclarations . length !== 1 ) {
44+ console . log (
45+ chalk . yellow ( `${ dimPath } : Could not find \`export default Component.extend({ ... });\`` )
46+ ) ;
47+ continue ;
48+ }
49+
50+ let exportDefaultDeclaration = exportDefaultDeclarations . get ( ) ;
51+
52+ // find first `{ ... }` inside `Component.extend()` arguments
53+ let extendObjectArgs = exportDefaultDeclaration
54+ . get ( 'declaration' , 'arguments' )
55+ . filter ( path => path . value . type === 'ObjectExpression' ) ;
56+
57+ let objectArg = extendObjectArgs [ 0 ] ;
58+ if ( ! objectArg ) {
59+ console . log (
60+ chalk . yellow (
61+ `${ dimPath } : Could not find object argument in \`export default Component.extend({ ... });\``
62+ )
63+ ) ;
64+ continue ;
65+ }
66+
67+ // find `tagName` property if it exists
68+ let properties = objectArg . get ( 'properties' ) ;
69+
70+ let tagName = 'div' ;
71+
72+ let tagNameProperty = properties . filter (
73+ ( { value : node } ) =>
74+ node . type === 'ObjectProperty' &&
75+ node . key . type === 'Identifier' &&
76+ node . key . name === 'tagName'
77+ ) [ 0 ] ;
78+ if ( tagNameProperty ) {
79+ let tagNamePath = tagNameProperty . get ( 'value' ) ;
80+ if ( tagNamePath . value . type !== 'StringLiteral' ) {
81+ console . log (
82+ chalk . yellow ( `${ dimPath } : Unexpected \`tagName\` value: ${ j ( tagNamePath ) . toSource ( ) } ` )
83+ ) ;
84+ continue ;
85+ }
86+
87+ tagName = tagNamePath . value . value ;
88+ }
89+
90+ // skip tagless components (silent)
91+ if ( tagName === '' ) {
92+ debug ( `${ componentPath } : tagName detected: %o -> skip` , tagName ) ;
93+ continue ;
94+ }
95+
96+ debug ( `${ componentPath } : tagName detected: %o` , tagName ) ;
97+
2398 // TODO skip components that use `this.element` (warn)
2499 // TODO skip components that use `click()` etc. (warn)
25100
@@ -32,6 +107,8 @@ async function runForPath(path, options = {}) {
32107 // TODO set `tagName: ''` and remove `attributeBindings`, `classNames`, ...
33108 // TODO wrap existing template with root element
34109 }
110+
111+ debug ( 'runForPath() finished' ) ;
35112}
36113
37114module . exports = { run } ;
0 commit comments