@@ -6,53 +6,175 @@ const { moveFile, removeDirs } = require('./utils/file')
66
77module . exports = class Migrator {
88 constructor ( options ) {
9- this . options = options ;
9+ const { projectRoot, structure } = options ;
10+
11+ this . projectRoot = projectRoot ;
12+ this . structure = structure ;
1013 }
1114
12- async execute ( ) {
13- let sourceComponentTemplatesPath = path . join ( this . options . projectRoot , 'app/templates/components' ) ;
14- var sourceComponentTemplateFilePaths = glob . sync ( `${ sourceComponentTemplatesPath } /**/*.hbs` ) ;
15-
16- let sourceComponentPath = path . join ( this . options . projectRoot , 'app/components' ) ;
17- let sourceComponentFilePaths = glob . sync ( `${ sourceComponentPath } /**/*.js` ) ;
18- let templatesWithLayoutName = getLayoutNameTemplates ( sourceComponentFilePaths ) ;
19- if ( templatesWithLayoutName . length ) {
20- sourceComponentTemplateFilePaths = sourceComponentTemplateFilePaths . filter ( sourceTemplateFilePath => {
21- let sourceTemplatePathInApp = sourceTemplateFilePath . slice ( this . options . projectRoot . length ) ; // '/app/templates/components/nested1/nested-component.hbs'
22- let templatePath = sourceTemplatePathInApp . slice ( 'app/templates/' . length ) ; // '/nested1/nested-component.hbs'
23- return ! templatesWithLayoutName . includes ( templatePath . slice ( 1 ) . replace ( '.hbs' , '' ) ) ;
15+ findClassicComponentTemplates ( ) {
16+ const templateFolderPath = path . join ( this . projectRoot , 'app/templates/components' ) ;
17+ const templateFilePaths = glob . sync ( `${ templateFolderPath } /**/*.hbs` ) ;
18+
19+ return templateFilePaths ;
20+ }
21+
22+ findClassicComponentClasses ( ) {
23+ const classFolderPath = path . join ( this . projectRoot , 'app/components' ) ;
24+ const classFilePaths = glob . sync ( `${ classFolderPath } /**/*.{js,ts}` ) ;
25+
26+ return classFilePaths ;
27+ }
28+
29+ findTemplates ( ) {
30+ const templateFolderPath = path . join ( this . projectRoot , 'app/templates' ) ;
31+ const templateFilePaths = glob . sync ( `${ templateFolderPath } /**/*.hbs` ) ;
32+
33+ return templateFilePaths ;
34+ }
35+
36+ skipTemplatesUsedAsLayoutName ( templateFilePaths ) {
37+ console . info ( `\nChecking if any component templates are used as templates of other components using \`layoutName\`` ) ;
38+
39+ const classFilePaths = this . findClassicComponentClasses ( ) ;
40+ const componentsWithLayoutName = getLayoutNameTemplates ( classFilePaths ) ;
41+
42+ if ( componentsWithLayoutName . length ) {
43+ componentsWithLayoutName . sort ( ) . forEach ( component => {
44+ console . info ( `❌ Did not move '${ component } ' due to usage as "layoutName" in a component` ) ;
45+ } ) ;
46+
47+ templateFilePaths = templateFilePaths . filter ( templateFilePath => {
48+ // Extract '/app/templates/components/nested1/nested-component.hbs'
49+ const filePathFromApp = templateFilePath . slice ( this . projectRoot . length ) ;
50+
51+ // Extract '/components/nested1/nested-component.hbs'
52+ const filePathFromAppTemplates = filePathFromApp . slice ( 'app/templates/' . length ) ;
53+
54+ // Extract 'components/nested1/nested-component'
55+ const classFilePath = filePathFromAppTemplates . slice ( 1 ) . replace ( '.hbs' , '' ) ;
56+
57+ return ! componentsWithLayoutName . includes ( classFilePath ) ;
2458 } ) ;
2559 }
2660
27- let sourceTemplatesPath = path . join ( this . options . projectRoot , 'app/templates' ) ;
28- var sourceTemplateFilePaths = glob . sync ( `${ sourceTemplatesPath } /**/*.hbs` ) ;
29- let templatesInPartials = getPartialTemplates ( sourceTemplateFilePaths ) ;
30- if ( templatesInPartials . length ) {
31- sourceComponentTemplateFilePaths = sourceComponentTemplateFilePaths . filter ( sourceTemplateFilePath => {
32- let sourceTemplatePathInApp = sourceTemplateFilePath . slice ( this . options . projectRoot . length ) ; // '/app/templates/components/nested1/nested-component.hbs'
33- if ( / \/ \- [ \w \- ] + \. h b s / . test ( sourceTemplatePathInApp ) ) {
34- sourceTemplatePathInApp = sourceTemplatePathInApp . replace ( '/-' , '/' ) ;
61+ return templateFilePaths ;
62+ }
63+
64+ skipTemplatesUsedAsPartial ( templateFilePaths ) {
65+ console . info ( `\nChecking if any component templates are used as partials` ) ;
66+
67+ const componentsWithPartial = getPartialTemplates ( this . findTemplates ( ) ) ;
68+
69+ if ( componentsWithPartial . length ) {
70+ componentsWithPartial . sort ( ) . forEach ( component => {
71+ console . info ( `❌ Did not move '${ component } ' due to usage as a "partial"` ) ;
72+ } ) ;
73+
74+ templateFilePaths = templateFilePaths . filter ( templateFilePath => {
75+ // Extract '/app/templates/components/nested1/nested-component.hbs'
76+ let filePathFromApp = templateFilePath . slice ( this . projectRoot . length ) ;
77+
78+ /*
79+ When Ember sees `{{partial "foo"}}`, it will look for the template in
80+ two locations:
81+
82+ - `app/templates/foo.hbs`
83+ - `app/templates/-foo.hbs`
84+
85+ If `filePathFromApp` matches the latter pattern, we remove the hyphen.
86+ */
87+ if ( / \/ \- [ \w \- ] + \. h b s / . test ( filePathFromApp ) ) {
88+ filePathFromApp = filePathFromApp . replace ( '/-' , '/' ) ;
3589 }
36- let templatePath = sourceTemplatePathInApp . slice ( 'app/templates/' . length ) ; // '/nested1/nested-component.hbs'
37- return ! templatesInPartials . includes ( templatePath . slice ( 1 ) . replace ( '.hbs' , '' ) ) ;
90+
91+ // Extract '/components/nested1/nested-component.hbs'
92+ const filePathFromAppTemplates = filePathFromApp . slice ( 'app/templates/' . length ) ;
93+
94+ // Extract 'components/nested1/nested-component'
95+ const classFilePath = filePathFromAppTemplates . slice ( 1 ) . replace ( '.hbs' , '' ) ;
96+
97+ return ! componentsWithPartial . includes ( classFilePath ) ;
3898 } ) ;
3999 }
40100
41- sourceComponentTemplateFilePaths . forEach ( sourceTemplateFilePath => {
42- let sourceTemplatePathInApp = sourceTemplateFilePath . slice ( this . options . projectRoot . length ) ; // '/app/templates/components/nested1/nested-component.hbs'
43- let templatePath = sourceTemplatePathInApp . slice ( 'app/templates/components/' . length ) ; // '/nested1/nested-component.hbs'
44- let targetTemplateFilePath = path . join ( this . options . projectRoot , 'app/components' , templatePath ) ; // '[APP_PATH]/app/components/nested1/nested-component.hbs'
45- moveFile ( sourceTemplateFilePath , targetTemplateFilePath ) ;
46- } ) ;
101+ return templateFilePaths ;
102+ }
103+
104+ changeComponentStructureToFlat ( templateFilePaths ) {
105+ templateFilePaths . forEach ( templateFilePath => {
106+ // Extract '/app/templates/components/nested1/nested-component.hbs'
107+ const filePathFromApp = templateFilePath . slice ( this . projectRoot . length ) ;
108+
109+ // Extract '/nested1/nested-component.hbs'
110+ const filePathFromAppTemplatesComponents = filePathFromApp . slice ( 'app/templates/components/' . length ) ;
47111
48- templatesWithLayoutName . sort ( ) . forEach ( template => {
49- console . info ( `❌ Did not move '${ template } ' due to usage as "layoutName" in a component` ) ;
112+ // '[APP_PATH]/app/components/nested1/nested-component.hbs'
113+ const newTemplateFilePath = path . join ( this . projectRoot , 'app/components' , filePathFromAppTemplatesComponents ) ;
114+ moveFile ( templateFilePath , newTemplateFilePath ) ;
50115 } ) ;
51- templatesInPartials . sort ( ) . forEach ( template => {
52- console . info ( `❌ Did not move '${ template } ' due to usage as a "partial"` ) ;
116+ }
117+
118+ changeComponentStructureToNested ( templateFilePaths ) {
119+ const classFilePaths = this . findClassicComponentClasses ( ) ;
120+
121+ templateFilePaths . forEach ( templateFilePath => {
122+ // Extract '/app/templates/components/nested1/nested-component.hbs'
123+ const filePathFromApp = templateFilePath . slice ( this . projectRoot . length ) ;
124+
125+ // Extract '/nested1/nested-component.hbs'
126+ const filePathFromAppTemplatesComponents = filePathFromApp . slice ( 'app/templates/components/' . length ) ;
127+ const fileExtension = path . extname ( filePathFromAppTemplatesComponents ) ;
128+
129+ // Extract '/nested1/nested-component'
130+ const targetPath = filePathFromAppTemplatesComponents . slice ( 0 , - fileExtension . length ) ;
131+
132+ // Build '[APP_PATH]/app/components/nested1/nested-component/index.hbs'
133+ const newTemplateFilePath = path . join ( this . projectRoot , 'app/components' , targetPath , 'index.hbs' ) ;
134+ moveFile ( templateFilePath , newTemplateFilePath ) ;
135+
136+ // Build '[APP_PATH]/app/components/nested1/nested-component/index.js'
137+ const classFilePath = {
138+ js : path . join ( this . projectRoot , 'app/components' , `${ targetPath } .js` ) ,
139+ ts : path . join ( this . projectRoot , 'app/components' , `${ targetPath } .ts` )
140+ } ;
141+
142+ if ( classFilePaths . includes ( classFilePath . js ) ) {
143+ const newClassFilePath = path . join ( this . projectRoot , 'app/components' , targetPath , 'index.js' ) ;
144+ moveFile ( classFilePath . js , newClassFilePath ) ;
145+
146+ } else if ( classFilePaths . includes ( classFilePath . ts ) ) {
147+ const newClassFilePath = path . join ( this . projectRoot , 'app/components' , targetPath , 'index.ts' ) ;
148+ moveFile ( classFilePath . ts , newClassFilePath ) ;
149+
150+ }
53151 } ) ;
152+ }
153+
154+ async removeEmptyClassicComponentDirectories ( ) {
155+ const templateFolderPath = path . join ( this . projectRoot , 'app/templates/components' ) ;
156+
157+ const classFilePaths = this . findClassicComponentClasses ( ) ;
158+ const templatesWithLayoutName = getLayoutNameTemplates ( classFilePaths ) ;
159+ const removeOnlyEmptyDirectories = Boolean ( templatesWithLayoutName . length ) ;
160+
161+ await removeDirs ( templateFolderPath , removeOnlyEmptyDirectories ) ;
162+ }
163+
164+ async execute ( ) {
165+ let templateFilePaths = this . findClassicComponentTemplates ( ) ;
166+ templateFilePaths = this . skipTemplatesUsedAsLayoutName ( templateFilePaths ) ;
167+ templateFilePaths = this . skipTemplatesUsedAsPartial ( templateFilePaths ) ;
168+
169+ if ( this . structure === 'flat' ) {
170+ this . changeComponentStructureToFlat ( templateFilePaths ) ;
171+
172+ } else if ( this . structure === 'nested' ) {
173+ this . changeComponentStructureToNested ( templateFilePaths ) ;
174+
175+ }
54176
55- let onlyRemoveEmptyDirs = Boolean ( templatesWithLayoutName . length ) ;
56- await removeDirs ( sourceComponentTemplatesPath , onlyRemoveEmptyDirs ) ;
177+ // Clean up
178+ await this . removeEmptyClassicComponentDirectories ( ) ;
57179 }
58180}
0 commit comments