@@ -14,12 +14,11 @@ import {NoopImportRewriter, Reference, ReferenceEmitter} from '../../imports';
14
14
import { ClassDeclaration , ReflectionHost } from '../../reflection' ;
15
15
import { ImportManager } from '../../translator' ;
16
16
17
- import { TemplateSourceMapping , TypeCheckableDirectiveMeta , TypeCheckBlockMetadata , TypeCheckingConfig , TypeCtorMetadata } from './api' ;
17
+ import { TemplateId , TemplateSourceMapping , TypeCheckableDirectiveMeta , TypeCheckBlockMetadata , TypeCheckingConfig , TypeCtorMetadata } from './api' ;
18
18
import { TemplateSourceResolver } from './diagnostics' ;
19
19
import { DomSchemaChecker , RegistryDomSchemaChecker } from './dom' ;
20
20
import { Environment } from './environment' ;
21
21
import { OutOfBandDiagnosticRecorder , OutOfBandDiagnosticRecorderImpl } from './oob' ;
22
- import { TypeCheckShimGenerator } from './shim' ;
23
22
import { TemplateSourceManager } from './source' ;
24
23
import { generateTypeCheckBlock , requiresInlineTypeCheckBlock } from './type_check_block' ;
25
24
import { TypeCheckFile } from './type_check_file' ;
@@ -46,7 +45,8 @@ export interface TypeCheckRequest {
46
45
}
47
46
48
47
/**
49
- * Data for a type-checking shim which is required to support generation of diagnostics.
48
+ * Data for template type-checking related to a specific input file in the user's program (which
49
+ * contains components to be checked).
50
50
*/
51
51
export interface FileTypeCheckingData {
52
52
/**
@@ -56,25 +56,39 @@ export interface FileTypeCheckingData {
56
56
hasInlines : boolean ;
57
57
58
58
/**
59
- * Source mapping information for mapping diagnostics back to the original template.
59
+ * Source mapping information for mapping diagnostics from inlined type check blocks back to the
60
+ * original template.
60
61
*/
61
62
sourceResolver : TemplateSourceResolver ;
62
63
63
64
/**
64
- * Any `ts.Diagnostic`s which were produced during the generation of this shim .
65
+ * Data for each shim generated from this input file .
65
66
*
66
- * Some diagnostics are produced during creation time and are tracked here.
67
+ * A single input file will generate one or more shim files that actually contain template
68
+ * type-checking code.
67
69
*/
68
- genesisDiagnostics : ts . Diagnostic [ ] ;
70
+ shimData : Map < AbsoluteFsPath , ShimTypeCheckingData > ;
71
+ }
69
72
73
+ /**
74
+ * Data specific to a single shim generated from an input file.
75
+ */
76
+ export interface ShimTypeCheckingData {
70
77
/**
71
78
* Path to the shim file.
72
79
*/
73
- typeCheckFile : AbsoluteFsPath ;
80
+ path : AbsoluteFsPath ;
81
+
82
+ /**
83
+ * Any `ts.Diagnostic`s which were produced during the generation of this shim.
84
+ *
85
+ * Some diagnostics are produced during creation time and are tracked here.
86
+ */
87
+ genesisDiagnostics : ts . Diagnostic [ ] ;
74
88
}
75
89
76
90
/**
77
- * Data for a type-checking shim which is still having its code generated .
91
+ * Data for an input file which is still in the process of template type-checking code generation .
78
92
*/
79
93
export interface PendingFileTypeCheckingData {
80
94
/**
@@ -83,10 +97,18 @@ export interface PendingFileTypeCheckingData {
83
97
hasInlines : boolean ;
84
98
85
99
/**
86
- * `TemplateSourceManager` being used to track source mapping information for this shim.
100
+ * Source mapping information for mapping diagnostics from inlined type check blocks back to the
101
+ * original template.
87
102
*/
88
103
sourceManager : TemplateSourceManager ;
89
104
105
+ /**
106
+ * Map of in-progress shim data for shims generated from this input file.
107
+ */
108
+ shimData : Map < AbsoluteFsPath , PendingShimData > ;
109
+ }
110
+
111
+ export interface PendingShimData {
90
112
/**
91
113
* Recorder for out-of-band diagnostics which are raised during generation.
92
114
*/
@@ -98,9 +120,28 @@ export interface PendingFileTypeCheckingData {
98
120
domSchemaChecker : DomSchemaChecker ;
99
121
100
122
/**
101
- * Path to the shim file .
123
+ * Shim file in the process of being generated .
102
124
*/
103
- typeCheckFile : TypeCheckFile ;
125
+ file : TypeCheckFile ;
126
+ }
127
+
128
+ /**
129
+ * Abstracts the operation of determining which shim file will host a particular component's
130
+ * template type-checking code.
131
+ *
132
+ * Different consumers of the type checking infrastructure may choose different approaches to
133
+ * optimize for their specific use case (for example, the command-line compiler optimizes for
134
+ * efficient `ts.Program` reuse in watch mode).
135
+ */
136
+ export interface ComponentToShimMappingStrategy {
137
+ /**
138
+ * Given a component, determine a path to the shim file into which that component's type checking
139
+ * code will be generated.
140
+ *
141
+ * A major constraint is that components in different input files must not share the same shim
142
+ * file. The behavior of the template type-checking system is undefined if this is violated.
143
+ */
144
+ shimPathForComponent ( node : ts . ClassDeclaration ) : AbsoluteFsPath ;
104
145
}
105
146
106
147
/**
@@ -115,6 +156,7 @@ export class TypeCheckContext {
115
156
constructor (
116
157
private config : TypeCheckingConfig ,
117
158
private compilerHost : Pick < ts . CompilerHost , 'getCanonicalFileName' > ,
159
+ private componentMappingStrategy : ComponentToShimMappingStrategy ,
118
160
private refEmitter : ReferenceEmitter , private reflector : ReflectionHost ) { }
119
161
120
162
/**
@@ -160,8 +202,7 @@ export class TypeCheckContext {
160
202
schemas : SchemaMetadata [ ] , sourceMapping : TemplateSourceMapping ,
161
203
file : ParseSourceFile ) : void {
162
204
const fileData = this . dataForFile ( ref . node . getSourceFile ( ) ) ;
163
-
164
- const id = fileData . sourceManager . captureSource ( sourceMapping , file ) ;
205
+ const shimData = this . pendingShimForComponent ( ref . node ) ;
165
206
// Get all of the directives used in the template and record type constructors for all of them.
166
207
for ( const dir of boundTarget . getUsedDirectives ( ) ) {
167
208
const dirRef = dir . ref as Reference < ClassDeclaration < ts . ClassDeclaration > > ;
@@ -184,15 +225,19 @@ export class TypeCheckContext {
184
225
}
185
226
}
186
227
187
- const tcbMetadata : TypeCheckBlockMetadata = { id, boundTarget, pipes, schemas} ;
228
+ const meta = {
229
+ id : fileData . sourceManager . captureSource ( ref . node , sourceMapping , file ) ,
230
+ boundTarget,
231
+ pipes,
232
+ schemas,
233
+ } ;
188
234
if ( requiresInlineTypeCheckBlock ( ref . node ) ) {
189
235
// This class didn't meet the requirements for external type checking, so generate an inline
190
236
// TCB for the class.
191
- this . addInlineTypeCheckBlock ( fileData , ref , tcbMetadata ) ;
237
+ this . addInlineTypeCheckBlock ( fileData , shimData , ref , meta ) ;
192
238
} else {
193
239
// The class can be type-checked externally as normal.
194
- fileData . typeCheckFile . addTypeCheckBlock (
195
- ref , tcbMetadata , fileData . domSchemaChecker , fileData . oobRecorder ) ;
240
+ shimData . file . addTypeCheckBlock ( ref , meta , shimData . domSchemaChecker , shimData . oobRecorder ) ;
196
241
}
197
242
}
198
243
@@ -278,17 +323,27 @@ export class TypeCheckContext {
278
323
perFileData : new Map < AbsoluteFsPath , FileTypeCheckingData > ( ) ,
279
324
} ;
280
325
281
- for ( const [ sfPath , fileData ] of this . fileMap . entries ( ) ) {
282
- updates . set ( fileData . typeCheckFile . fileName , fileData . typeCheckFile . render ( ) ) ;
283
- results . perFileData . set ( sfPath , {
284
- genesisDiagnostics : [
285
- ...fileData . domSchemaChecker . diagnostics ,
286
- ...fileData . oobRecorder . diagnostics ,
287
- ] ,
288
- hasInlines : fileData . hasInlines ,
289
- sourceResolver : fileData . sourceManager ,
290
- typeCheckFile : fileData . typeCheckFile . fileName ,
291
- } ) ;
326
+ // Then go through each input file that has pending code generation operations.
327
+ for ( const [ sfPath , pendingFileData ] of this . fileMap ) {
328
+ const fileData : FileTypeCheckingData = {
329
+ hasInlines : pendingFileData . hasInlines ,
330
+ shimData : new Map ( ) ,
331
+ sourceResolver : pendingFileData . sourceManager ,
332
+ } ;
333
+
334
+ // For each input file, consider generation operations for each of its shims.
335
+ for ( const [ shimPath , pendingShimData ] of pendingFileData . shimData ) {
336
+ updates . set ( pendingShimData . file . fileName , pendingShimData . file . render ( ) ) ;
337
+ fileData . shimData . set ( shimPath , {
338
+ genesisDiagnostics : [
339
+ ...pendingShimData . domSchemaChecker . diagnostics ,
340
+ ...pendingShimData . oobRecorder . diagnostics ,
341
+ ] ,
342
+ path : pendingShimData . file . fileName ,
343
+ } ) ;
344
+ }
345
+
346
+ results . perFileData . set ( sfPath , fileData ) ;
292
347
}
293
348
294
349
for ( const [ sfPath , fileData ] of this . adoptedFiles . entries ( ) ) {
@@ -299,32 +354,44 @@ export class TypeCheckContext {
299
354
}
300
355
301
356
private addInlineTypeCheckBlock (
302
- fileData : PendingFileTypeCheckingData , ref : Reference < ClassDeclaration < ts . ClassDeclaration > > ,
357
+ fileData : PendingFileTypeCheckingData , shimData : PendingShimData ,
358
+ ref : Reference < ClassDeclaration < ts . ClassDeclaration > > ,
303
359
tcbMeta : TypeCheckBlockMetadata ) : void {
304
360
const sf = ref . node . getSourceFile ( ) ;
305
361
if ( ! this . opMap . has ( sf ) ) {
306
362
this . opMap . set ( sf , [ ] ) ;
307
363
}
308
364
const ops = this . opMap . get ( sf ) ! ;
309
365
ops . push ( new TcbOp (
310
- ref , tcbMeta , this . config , this . reflector , fileData . domSchemaChecker ,
311
- fileData . oobRecorder ) ) ;
366
+ ref , tcbMeta , this . config , this . reflector , shimData . domSchemaChecker ,
367
+ shimData . oobRecorder ) ) ;
312
368
fileData . hasInlines = true ;
313
369
}
314
370
371
+ private pendingShimForComponent ( node : ts . ClassDeclaration ) : PendingShimData {
372
+ const fileData = this . dataForFile ( node . getSourceFile ( ) ) ;
373
+ const shimPath = this . componentMappingStrategy . shimPathForComponent ( node ) ;
374
+ if ( ! fileData . shimData . has ( shimPath ) ) {
375
+ fileData . shimData . set ( shimPath , {
376
+ domSchemaChecker : new RegistryDomSchemaChecker ( fileData . sourceManager ) ,
377
+ oobRecorder : new OutOfBandDiagnosticRecorderImpl ( fileData . sourceManager ) ,
378
+ file : new TypeCheckFile (
379
+ shimPath , this . config , this . refEmitter , this . reflector , this . compilerHost ) ,
380
+ } ) ;
381
+ }
382
+ return fileData . shimData . get ( shimPath ) ! ;
383
+ }
384
+
315
385
private dataForFile ( sf : ts . SourceFile ) : PendingFileTypeCheckingData {
316
386
const sfPath = absoluteFromSourceFile ( sf ) ;
317
387
388
+ const sourceManager = new TemplateSourceManager ( ) ;
389
+
318
390
if ( ! this . fileMap . has ( sfPath ) ) {
319
- const sourceManager = new TemplateSourceManager ( ) ;
320
391
const data : PendingFileTypeCheckingData = {
321
- domSchemaChecker : new RegistryDomSchemaChecker ( sourceManager ) ,
322
- oobRecorder : new OutOfBandDiagnosticRecorderImpl ( sourceManager ) ,
323
- typeCheckFile : new TypeCheckFile (
324
- TypeCheckShimGenerator . shimFor ( sfPath ) , this . config , this . refEmitter , this . reflector ,
325
- this . compilerHost ) ,
326
392
hasInlines : false ,
327
393
sourceManager,
394
+ shimData : new Map ( ) ,
328
395
} ;
329
396
this . fileMap . set ( sfPath , data ) ;
330
397
}
0 commit comments