2
2
import * as fs from 'fs' ;
3
3
import * as path from 'path' ;
4
4
import * as ts from 'typescript' ;
5
+ import * as SourceMap from 'source-map' ;
5
6
6
7
const { __NGTOOLS_PRIVATE_API_2 } = require ( '@angular/compiler-cli' ) ;
7
8
const ContextElementDependency = require ( 'webpack/lib/dependencies/ContextElementDependency' ) ;
@@ -36,6 +37,9 @@ export interface AotPluginOptions {
36
37
}
37
38
38
39
40
+ const inlineSourceMapRe = / \/ \/ # s o u r c e M a p p i n g U R L = d a t a : a p p l i c a t i o n \/ j s o n ; b a s e 6 4 , ( [ \s \S ] + ) $ / ;
41
+
42
+
39
43
export class AotPlugin implements Tapable {
40
44
private _options : AotPluginOptions ;
41
45
@@ -342,16 +346,40 @@ export class AotPlugin implements Tapable {
342
346
} ) ;
343
347
}
344
348
349
+ private _translateSourceMap ( sourceText : string , fileName : string ,
350
+ { line, character} : { line : number , character : number } ) {
351
+ const match = sourceText . match ( inlineSourceMapRe ) ;
352
+
353
+ if ( ! match ) {
354
+ return { line, character, fileName} ;
355
+ }
356
+
357
+ // On any error, return line and character.
358
+ try {
359
+ const sourceMapJson = JSON . parse ( Buffer . from ( match [ 1 ] , 'base64' ) . toString ( ) ) ;
360
+ const consumer = new SourceMap . SourceMapConsumer ( sourceMapJson ) ;
361
+
362
+ const original = consumer . originalPositionFor ( { line, column : character } ) ;
363
+ return {
364
+ line : original . line ,
365
+ character : original . column ,
366
+ fileName : original . source || fileName
367
+ } ;
368
+ } catch ( e ) {
369
+ return { line, character, fileName} ;
370
+ }
371
+ }
372
+
345
373
diagnose ( fileName : string ) {
346
374
if ( this . _diagnoseFiles [ fileName ] ) {
347
375
return ;
348
376
}
349
377
this . _diagnoseFiles [ fileName ] = true ;
350
378
351
379
const sourceFile = this . _program . getSourceFile ( fileName ) ;
352
- if ( ! sourceFile ) {
353
- return ;
354
- }
380
+ if ( ! sourceFile ) {
381
+ return ;
382
+ }
355
383
356
384
const diagnostics : ts . Diagnostic [ ] = [ ]
357
385
. concat (
@@ -364,9 +392,14 @@ export class AotPlugin implements Tapable {
364
392
if ( diagnostics . length > 0 ) {
365
393
const message = diagnostics
366
394
. map ( diagnostic => {
367
- const { line, character} = diagnostic . file . getLineAndCharacterOfPosition ( diagnostic . start ) ;
395
+ const position = diagnostic . file . getLineAndCharacterOfPosition ( diagnostic . start ) ;
396
+
397
+ const sourceText = diagnostic . file . getFullText ( ) ;
398
+ let { line, character, fileName} = this . _translateSourceMap ( sourceText ,
399
+ diagnostic . file . fileName , position ) ;
400
+
368
401
const message = ts . flattenDiagnosticMessageText ( diagnostic . messageText , '\n' ) ;
369
- return `${ diagnostic . file . fileName } (${ line + 1 } ,${ character + 1 } ): ${ message } ) ` ;
402
+ return `${ fileName } (${ line + 1 } ,${ character + 1 } ): ${ message } ` ;
370
403
} )
371
404
. join ( '\n' ) ;
372
405
this . _compilation . errors . push ( message ) ;
@@ -404,6 +437,15 @@ export class AotPlugin implements Tapable {
404
437
} ) ;
405
438
} )
406
439
. then ( ( ) => {
440
+ // Get the ngfactory that were created by the previous step, and add them to the root
441
+ // file path (if those files exists).
442
+ const newRootFilePath = this . _compilerHost . getChangedFilePaths ( )
443
+ . filter ( x => x . match ( / \. n g f a c t o r y \. t s $ / ) ) ;
444
+ // Remove files that don't exist anymore, and add new files.
445
+ this . _rootFilePath = this . _rootFilePath
446
+ . filter ( x => this . _compilerHost . fileExists ( x ) )
447
+ . concat ( newRootFilePath ) ;
448
+
407
449
// Create a new Program, based on the old one. This will trigger a resolution of all
408
450
// transitive modules, which include files that might just have been generated.
409
451
// This needs to happen after the code generator has been created for generated files
0 commit comments