@@ -6,53 +6,175 @@ const { moveFile, removeDirs } = require('./utils/file')
6
6
7
7
module . exports = class Migrator {
8
8
constructor ( options ) {
9
- this . options = options ;
9
+ const { projectRoot, structure } = options ;
10
+
11
+ this . projectRoot = projectRoot ;
12
+ this . structure = structure ;
10
13
}
11
14
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 ) ;
24
58
} ) ;
25
59
}
26
60
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 ( '/-' , '/' ) ;
35
89
}
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 ) ;
38
98
} ) ;
39
99
}
40
100
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 ) ;
47
111
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 ) ;
50
115
} ) ;
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
+ }
53
151
} ) ;
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
+ }
54
176
55
- let onlyRemoveEmptyDirs = Boolean ( templatesWithLayoutName . length ) ;
56
- await removeDirs ( sourceComponentTemplatesPath , onlyRemoveEmptyDirs ) ;
177
+ // Clean up
178
+ await this . removeEmptyClassicComponentDirectories ( ) ;
57
179
}
58
180
}
0 commit comments