@@ -11,6 +11,14 @@ import { ResolvedFile } from "./Template";
11
11
12
12
const DEBUG = debugGenerator ( "css-blocks:glimmer:resolver" ) ;
13
13
14
+ /**
15
+ * Convert constituent template path parts to an Ember Classic style Block path.
16
+ *
17
+ * @param base The base path of the project to discover files relative from.
18
+ * @param templatePath The template's filesystem path relative to `base`.
19
+ * @param ext The extension to look for.
20
+ * @return Where the associated Block should be located on the filesystem.
21
+ */
14
22
function toClassicPath ( base : string , templatePath : string , ext : string ) : string {
15
23
// TODO: There is a more robust way to do all this path munging!
16
24
let classic = path . parse ( templatePath . replace ( "templates/" , "styles/" ) ) ;
@@ -19,6 +27,14 @@ function toClassicPath(base: string, templatePath: string, ext: string): string
19
27
return path . join ( base , path . format ( classic ) ) ;
20
28
}
21
29
30
+ /**
31
+ * Convert constituent template path parts to an Ember Pods style Block path.
32
+ *
33
+ * @param base The base path of the project to discover files relative from.
34
+ * @param templatePath The template's filesystem path relative to `base`.
35
+ * @param ext The extension to look for.
36
+ * @return Where the associated Block should be located on the filesystem.
37
+ */
22
38
function toPodsPath ( base : string , templatePath : string , ext : string ) : string {
23
39
let pods = path . parse ( templatePath ) ;
24
40
delete pods . base ; // Required for path.format to pick up new extension.
@@ -28,18 +44,29 @@ function toPodsPath(base: string, templatePath: string, ext: string): string {
28
44
}
29
45
30
46
/**
31
- * The Glimmer CSS Blocks Resolver deals in three
47
+ * The Glimmer CSS Blocks Resolver currently supports three
32
48
* kinds of project structure modes:
33
49
* - Component Names as Paths (Module Config)
34
50
* - Relative Template Paths (Classic Ember)
35
51
* - Relative Template Paths (Ember Pods)
52
+ *
53
+ * It provides abstractions for interacting with the three project
54
+ * structures, so the rest of the Glimmer analyzer code can operate
55
+ * independently of the filesystem structure.
56
+ *
36
57
*/
37
58
export class Resolver {
38
59
39
60
private depAnalyzers : Map < string , DependencyAnalyzer > = new Map ( ) ;
40
61
private moduleConfig ?: ResolverConfiguration ;
41
62
private fileEndings : Set < string > ;
42
63
64
+ /**
65
+ * Creates a new Resolver for this project.
66
+ *
67
+ * @param cssBlocksConfig The CSS Blocks configuration object.
68
+ * @param moduleConfig If applicable, the Glimmer module config for static analysis.
69
+ */
43
70
constructor ( cssBlocksConfig : ResolvedConfiguration , moduleConfig ?: ResolverConfiguration ) {
44
71
if ( moduleConfig ) {
45
72
this . moduleConfig = moduleConfig ;
@@ -48,23 +75,39 @@ export class Resolver {
48
75
DEBUG ( `Discovering all Block files that end with ("${ [ ...this . fileEndings ] . join ( `|` ) } ")` ) ;
49
76
}
50
77
51
- private dependencyAnalyzerFor ( dir : string ) : DependencyAnalyzer | undefined {
78
+ /**
79
+ * If appropriate, returns the `DependencyAnalyzer` for a given project.
80
+ * If no module config exists for the project (aka: is not a full, statically
81
+ * analyzable Glimmer app) it returns undefined.
82
+ *
83
+ * @param base The base path of the project to analyze.
84
+ * @return The DependencyAnalyzer, or undefined.
85
+ */
86
+ private dependencyAnalyzerFor ( base : string ) : DependencyAnalyzer | undefined {
52
87
if ( ! this . moduleConfig ) { return undefined ; }
53
- if ( this . depAnalyzers . has ( dir ) ) {
54
- return this . depAnalyzers . get ( dir ) ! ;
88
+ if ( this . depAnalyzers . has ( base ) ) {
89
+ return this . depAnalyzers . get ( base ) ! ;
55
90
}
56
91
let src = this . moduleConfig . app && this . moduleConfig . app . mainPath || "src" ;
57
- let depAnalyzer = new DependencyAnalyzer ( dir , {
92
+ let depAnalyzer = new DependencyAnalyzer ( base , {
58
93
config : { moduleConfiguration : this . moduleConfig } ,
59
94
paths : {
60
95
src,
61
96
} ,
62
97
} ) ;
63
- this . depAnalyzers . set ( dir , depAnalyzer ) ;
98
+ this . depAnalyzers . set ( base , depAnalyzer ) ;
64
99
return depAnalyzer ;
65
100
}
66
101
67
- // TODO: We need to automatically discover the file ending here – its not guaranteed to be a css file.
102
+ /**
103
+ * Converts a template file path to its associated Block filepath, if present.
104
+ * All file endings associated with a preprocessor are attempted.
105
+ * If no block exists, returns undefined.
106
+ *
107
+ * @param base The base path of the project to lookup Block files relative to.
108
+ * @param template The template name we're attempting to discover block files for.
109
+ * @return A promise that resolves with the discover Block path, or undefined.
110
+ */
68
111
private async tmplPathToStylesheetPath ( base : string , template : string ) : Promise < string | undefined > {
69
112
let triedPaths = [ ] ;
70
113
// For every supported block extension:
@@ -75,13 +118,16 @@ export class Resolver {
75
118
DEBUG ( `Discovered classic Block for template ${ template } : ${ classic } ` ) ;
76
119
return classic ;
77
120
}
121
+ // Else attempt to fetch at the pods path.
78
122
let podsPath = toPodsPath ( base , template , ext ) ;
79
123
if ( fs . pathExistsSync ( podsPath ) ) {
80
124
DEBUG ( `Discovered pods Block for template ${ template } : ${ podsPath } ` ) ;
81
125
return podsPath ;
82
126
}
83
127
triedPaths . push ( path . relative ( base , classic ) , path . relative ( base , podsPath ) ) ;
84
128
}
129
+
130
+ // If we get here, there is no Block file at any standard Ember location, for any support file ending.
85
131
DEBUG ( `No Block discovered for template ${ template } . Attempted at:${ triedPaths . join ( `\n - ` ) } ` ) ;
86
132
return undefined ;
87
133
}
@@ -92,22 +138,30 @@ export class Resolver {
92
138
* "Module Map" mode where we can statically analyze application
93
139
* dependencies. In "Classic Ember" or "Ember Pods" mode we just
94
140
* return the same ident.
141
+ *
142
+ * @param base The base of the project to analyze.
143
+ * @param identifier The Glimmer identifier to discover recursive dependencies for.
144
+ * @return The list of recursive dependencies for this identifier.
95
145
*/
96
- recursiveDependenciesForTemplate ( dir : string , identifier : string ) : string [ ] {
97
- let depAnalyzer = this . dependencyAnalyzerFor ( dir ) ;
146
+ recursiveDependenciesForTemplate ( base : string , identifier : string ) : string [ ] {
147
+ let depAnalyzer = this . dependencyAnalyzerFor ( base ) ;
98
148
if ( ! depAnalyzer ) { return [ identifier ] ; }
99
149
return depAnalyzer . recursiveDependenciesForTemplate ( identifier ) . components ;
100
150
}
101
151
102
152
/**
103
153
* Given a template identifier, resolve the Block file associated
104
154
* with the template, if any.
155
+ *
156
+ * @param base The base of the project to analyze.
157
+ * @param identifier The Glimmer identifier to discover recursive dependencies for.
158
+ * @return The resolved Block file if present, or undefined.
105
159
*/
106
- async stylesheetFor ( dir : string , identifier : string ) : Promise < ResolvedFile | undefined > {
107
- let depAnalyzer = this . dependencyAnalyzerFor ( dir ) ;
160
+ async stylesheetFor ( base : string , identifier : string ) : Promise < ResolvedFile | undefined > {
161
+ let depAnalyzer = this . dependencyAnalyzerFor ( base ) ;
108
162
109
163
if ( ! depAnalyzer ) {
110
- let stylesheet = await this . tmplPathToStylesheetPath ( dir , identifier ) ;
164
+ let stylesheet = await this . tmplPathToStylesheetPath ( base , identifier ) ;
111
165
if ( ! stylesheet ) { return undefined ; }
112
166
return new ResolvedFile (
113
167
( fs . readFileSync ( stylesheet ) ) . toString ( ) ,
@@ -120,7 +174,7 @@ export class Resolver {
120
174
identifier = depAnalyzer . project . resolver . identify ( `stylesheet:${ identifier } ` ) ;
121
175
let file : string = depAnalyzer . project . resolver . resolve ( identifier ) ;
122
176
if ( ! file ) { return undefined ; }
123
- file = path . join ( dir , depAnalyzer . project . paths . src , file ) ;
177
+ file = path . join ( base , depAnalyzer . project . paths . src , file ) ;
124
178
if ( ! fs . existsSync ( file ) ) { return undefined ; }
125
179
let content = ( fs . readFileSync ( file ) ) . toString ( ) ;
126
180
return new ResolvedFile ( content , identifier , file ) ;
@@ -129,12 +183,15 @@ export class Resolver {
129
183
/**
130
184
* Given a template identifier, resolve the actual template file associated
131
185
* with it, if any.
186
+ * @param base The base of the project to analyze.
187
+ * @param identifier The template identifier to discover recursive dependencies for.
188
+ * @return The resolved Block file if present, or undefined.
132
189
*/
133
- async templateFor ( dir : string , identifier : string ) : Promise < ResolvedFile | undefined > {
134
- let depAnalyzer = this . dependencyAnalyzerFor ( dir ) ;
190
+ async templateFor ( base : string , identifier : string ) : Promise < ResolvedFile | undefined > {
191
+ let depAnalyzer = this . dependencyAnalyzerFor ( base ) ;
135
192
136
193
if ( ! depAnalyzer ) {
137
- let template = path . join ( dir , identifier ) ;
194
+ let template = path . join ( base , identifier ) ;
138
195
return new ResolvedFile (
139
196
( fs . readFileSync ( template ) ) . toString ( ) ,
140
197
identifier ,
0 commit comments