@@ -23,16 +23,14 @@ import { assert, log } from "./util.ts";
23
23
import * as util from "./util.ts" ;
24
24
import { TextDecoder , TextEncoder } from "./web/text_encoding.ts" ;
25
25
import { core } from "./core.ts" ;
26
+ import Timer from "./timer.ts" ;
26
27
27
- export function resolveModules (
28
- specifiers : string [ ] ,
29
- referrer ?: string
30
- ) : string [ ] {
28
+ function resolveModules ( specifiers : string [ ] , referrer ?: string ) : string [ ] {
31
29
util . log ( "compiler::resolveModules" , { specifiers, referrer } ) ;
32
30
return sendSync ( "op_resolve_modules" , { specifiers, referrer } ) ;
33
31
}
34
32
35
- export function fetchSourceFiles (
33
+ function fetchSourceFiles (
36
34
specifiers : string [ ] ,
37
35
referrer ?: string
38
36
) : Promise <
@@ -61,11 +59,17 @@ function getAsset(name: string): string {
61
59
return decoder . decode ( sourceCodeBytes ! ) ;
62
60
}
63
61
62
+ function getCache ( url : string ) : string {
63
+ const { content } = sendSync ( "op_get_cache" , { url } ) ;
64
+ return content ;
65
+ }
66
+
64
67
// Constants used by `normalizeString` and `resolvePath`
65
68
const CHAR_DOT = 46 ; /* . */
66
69
const CHAR_FORWARD_SLASH = 47 ; /* / */
67
70
const ASSETS = "$asset$" ;
68
71
const OUT_DIR = "$deno$" ;
72
+ const TS_BUILD_INFO = `${ OUT_DIR } /tsbuildinfo.json` ;
69
73
70
74
// TODO(Bartlomieju): this check should be done in Rust
71
75
const IGNORED_COMPILER_OPTIONS : readonly string [ ] = [
@@ -141,14 +145,17 @@ const DEFAULT_COMPILE_OPTIONS: ts.CompilerOptions = {
141
145
allowNonTsExtensions : true ,
142
146
checkJs : false ,
143
147
esModuleInterop : true ,
148
+ incremental : true ,
144
149
jsx : ts . JsxEmit . React ,
145
150
module : ts . ModuleKind . ESNext ,
151
+ moduleResolution : ts . ModuleResolutionKind . NodeJs ,
146
152
outDir : OUT_DIR ,
147
153
resolveJsonModule : true ,
148
154
sourceMap : true ,
149
155
strict : true ,
150
156
stripComments : true ,
151
157
target : ts . ScriptTarget . ESNext ,
158
+ tsBuildInfoFile : TS_BUILD_INFO ,
152
159
} ;
153
160
154
161
const DEFAULT_RUNTIME_COMPILE_OPTIONS : ts . CompilerOptions = {
@@ -174,6 +181,7 @@ interface CompilerHostOptions {
174
181
target : CompilerHostTarget ;
175
182
unstable ?: boolean ;
176
183
writeFile : WriteFileCallback ;
184
+ rootNames ?: string [ ] ;
177
185
}
178
186
179
187
interface ConfigureResponse {
@@ -370,6 +378,10 @@ class SourceFile {
370
378
}
371
379
return undefined ;
372
380
}
381
+
382
+ static urls ( ) : string [ ] {
383
+ return Array . from ( SOURCE_FILE_CACHE . keys ( ) ) . filter ( ts . pathIsAbsolute ) ;
384
+ }
373
385
}
374
386
375
387
function getAssetInternal ( filename : string ) : SourceFile {
@@ -391,10 +403,12 @@ function getAssetInternal(filename: string): SourceFile {
391
403
} ) ;
392
404
}
393
405
394
- class Host implements ts . CompilerHost {
406
+ export class Host implements ts . CompilerHost {
395
407
readonly #options = DEFAULT_COMPILE_OPTIONS ;
396
408
#target: CompilerHostTarget ;
397
409
#writeFile: WriteFileCallback ;
410
+ #rootPath = "." ;
411
+ #rootName = "" ;
398
412
399
413
/* Deno specific APIs */
400
414
@@ -403,6 +417,7 @@ class Host implements ts.CompilerHost {
403
417
target,
404
418
unstable,
405
419
writeFile,
420
+ rootNames,
406
421
} : CompilerHostOptions ) {
407
422
this . #target = target ;
408
423
this . #writeFile = writeFile ;
@@ -418,6 +433,10 @@ class Host implements ts.CompilerHost {
418
433
"lib.deno.unstable.d.ts" ,
419
434
] ;
420
435
}
436
+ if ( rootNames ) {
437
+ this . #rootName = rootNames [ 0 ] ;
438
+ this . #rootPath = this . #rootName. split ( "/" ) . slice ( 0 , - 1 ) . join ( "/" ) ;
439
+ }
421
440
}
422
441
423
442
configure (
@@ -494,6 +513,24 @@ class Host implements ts.CompilerHost {
494
513
return "\n" ;
495
514
}
496
515
516
+ getAbsolutePath ( fileName : string ) : string {
517
+ return ts . resolvePath ( this . #rootPath, fileName ) ;
518
+ }
519
+
520
+ getModuleAbsolutePath ( containingFile : string , specifier : string ) : string {
521
+ return ts . resolvePath (
522
+ ts . getDirectoryPath ( this . getAbsolutePath ( containingFile ) ) ,
523
+ specifier
524
+ ) ;
525
+ }
526
+
527
+ getRelativePath ( fileName : string ) : string {
528
+ if ( ! ts . pathIsAbsolute ( fileName ) ) {
529
+ return fileName ;
530
+ }
531
+ return ts . getRelativePathFromDirectory ( this . #rootPath, fileName , false ) ;
532
+ }
533
+
497
534
getSourceFile (
498
535
fileName : string ,
499
536
languageVersion : ts . ScriptTarget ,
@@ -505,21 +542,26 @@ class Host implements ts.CompilerHost {
505
542
assert ( ! shouldCreateNewSourceFile ) ;
506
543
const sourceFile = fileName . startsWith ( ASSETS )
507
544
? getAssetInternal ( fileName )
508
- : SourceFile . getCached ( fileName ) ;
545
+ : SourceFile . getCached ( this . getAbsolutePath ( fileName ) ) ;
509
546
assert ( sourceFile != null ) ;
510
547
if ( ! sourceFile . tsSourceFile ) {
511
548
assert ( sourceFile . sourceCode != null ) ;
512
549
const tsSourceFileName = fileName . startsWith ( ASSETS )
513
550
? sourceFile . filename
514
- : fileName ;
551
+ : this . getRelativePath ( fileName ) ;
515
552
516
553
sourceFile . tsSourceFile = ts . createSourceFile (
517
554
tsSourceFileName ,
518
555
sourceFile . sourceCode ,
519
556
languageVersion
520
557
) ;
558
+ //@ts -ignore
559
+ sourceFile . tsSourceFile . version = this . createHash (
560
+ sourceFile . tsSourceFile . text
561
+ ) ;
521
562
delete sourceFile . sourceCode ;
522
563
}
564
+
523
565
return sourceFile . tsSourceFile ;
524
566
} catch ( e ) {
525
567
if ( onError ) {
@@ -531,8 +573,18 @@ class Host implements ts.CompilerHost {
531
573
}
532
574
}
533
575
534
- readFile ( _fileName : string ) : string | undefined {
535
- return util . notImplemented ( ) ;
576
+ readFile ( fileName : string ) : string | undefined {
577
+ util . log ( "compiler::host.readFile" , fileName ) ;
578
+ if ( fileName === TS_BUILD_INFO ) {
579
+ return getCache ( `${ this . #rootName} .json` ) ;
580
+ }
581
+ return getCache (
582
+ this . getAbsolutePath ( fileName . replace ( OUT_DIR , this . #rootPath) )
583
+ ) ;
584
+ }
585
+
586
+ createHash ( data : string ) : string {
587
+ return ts . generateDjb2Hash ( data ) ;
536
588
}
537
589
538
590
resolveModuleNames (
@@ -544,22 +596,25 @@ class Host implements ts.CompilerHost {
544
596
containingFile,
545
597
} ) ;
546
598
return moduleNames . map ( ( specifier ) => {
547
- const maybeUrl = SourceFile . getResolvedUrl ( specifier , containingFile ) ;
599
+ const url = this . getModuleAbsolutePath ( containingFile , specifier ) ;
548
600
549
601
let sourceFile : SourceFile | undefined = undefined ;
550
602
551
603
if ( specifier . startsWith ( ASSETS ) ) {
552
604
sourceFile = getAssetInternal ( specifier ) ;
553
- } else if ( typeof maybeUrl !== "undefined" ) {
554
- sourceFile = SourceFile . getCached ( maybeUrl ) ;
605
+ } else {
606
+ sourceFile = SourceFile . getCached ( url ) ;
555
607
}
556
608
557
609
if ( ! sourceFile ) {
558
610
return undefined ;
559
611
}
560
612
613
+ const resolvedFileName = specifier . startsWith ( ASSETS )
614
+ ? sourceFile . url
615
+ : this . getRelativePath ( sourceFile . url ) ;
561
616
return {
562
- resolvedFileName : sourceFile . url ,
617
+ resolvedFileName,
563
618
isExternalLibraryImport : specifier . startsWith ( ASSETS ) ,
564
619
extension : sourceFile . extension ,
565
620
} ;
@@ -578,7 +633,11 @@ class Host implements ts.CompilerHost {
578
633
sourceFiles ?: readonly ts . SourceFile [ ]
579
634
) : void {
580
635
util . log ( "compiler::host.writeFile" , fileName ) ;
581
- this . #writeFile( fileName , data , sourceFiles ) ;
636
+ this . #writeFile(
637
+ this . getAbsolutePath ( fileName . replace ( OUT_DIR , this . #rootPath) ) ,
638
+ data ,
639
+ sourceFiles
640
+ ) ;
582
641
}
583
642
}
584
643
@@ -876,18 +935,26 @@ function createBundleWriteFile(state: WriteFileState): WriteFileCallback {
876
935
877
936
// TODO(bartlomieju): probably could be defined inline?
878
937
function createCompileWriteFile ( state : WriteFileState ) : WriteFileCallback {
938
+ const rootPath = state . rootNames [ 0 ] . split ( "/" ) . slice ( 0 , - 1 ) . join ( "/" ) ;
879
939
return function writeFile (
880
940
fileName : string ,
881
941
data : string ,
882
942
sourceFiles ?: readonly ts . SourceFile [ ]
883
943
) : void {
884
- assert ( sourceFiles != null ) ;
944
+ const isBuildInfo = fileName === TS_BUILD_INFO . replace ( OUT_DIR , rootPath ) ;
945
+ if ( sourceFiles != null ) {
946
+ assert ( sourceFiles . length === 1 ) ;
947
+ } else {
948
+ assert ( isBuildInfo ) ;
949
+ }
885
950
assert ( state . host ) ;
886
951
assert ( state . emitMap ) ;
887
952
assert ( ! state . bundle ) ;
888
- assert ( sourceFiles . length === 1 ) ;
953
+ const filename = isBuildInfo
954
+ ? `${ state . rootNames [ 0 ] } .json`
955
+ : ts . resolvePath ( rootPath , sourceFiles ! [ 0 ] . fileName ) ;
889
956
state . emitMap [ fileName ] = {
890
- filename : sourceFiles [ 0 ] . fileName ,
957
+ filename,
891
958
contents : data ,
892
959
} ;
893
960
} ;
@@ -1040,6 +1107,30 @@ function processConfigureResponse(
1040
1107
return diagnostics ;
1041
1108
}
1042
1109
1110
+ const getPreEmitDiagnostics = (
1111
+ program : ts . EmitAndSemanticDiagnosticsBuilderProgram
1112
+ ) : readonly ts . Diagnostic [ ] => {
1113
+ const allDiagnostics = program . getConfigFileParsingDiagnostics ( ) . slice ( ) ;
1114
+ const configFileParsingDiagnosticsLength = allDiagnostics . length ;
1115
+ allDiagnostics . push ( ...program . getSyntacticDiagnostics ( ) ) ;
1116
+
1117
+ // If we didn't have any syntactic errors, then also try getting the global and
1118
+ // semantic errors.
1119
+ if ( allDiagnostics . length === configFileParsingDiagnosticsLength ) {
1120
+ allDiagnostics . push (
1121
+ ...program . getOptionsDiagnostics ( ) ,
1122
+ ...program . getGlobalDiagnostics ( )
1123
+ ) ;
1124
+
1125
+ if ( allDiagnostics . length === configFileParsingDiagnosticsLength ) {
1126
+ allDiagnostics . push ( ...program . getSemanticDiagnostics ( ) ) ;
1127
+ }
1128
+ }
1129
+ return allDiagnostics . filter (
1130
+ ( { code } ) => ! ignoredDiagnostics . includes ( code )
1131
+ ) ;
1132
+ } ;
1133
+
1043
1134
function normalizeString ( path : string ) : string {
1044
1135
let res = "" ;
1045
1136
let lastSegmentLength = 0 ;
@@ -1246,6 +1337,7 @@ type CompilerRequest =
1246
1337
interface CompileResult {
1247
1338
emitMap ?: Record < string , EmmitedSource > ;
1248
1339
bundleOutput ?: string ;
1340
+ sources : string [ ] ;
1249
1341
diagnostics : Diagnostic ;
1250
1342
}
1251
1343
@@ -1276,6 +1368,8 @@ async function compile(
1276
1368
type : CompilerRequestType [ request . type ] ,
1277
1369
} ) ;
1278
1370
1371
+ const compileTimer = new Timer ( "Compilation" ) ;
1372
+
1279
1373
// When a programme is emitted, TypeScript will call `writeFile` with
1280
1374
// each file that needs to be emitted. The Deno compiler host delegates
1281
1375
// this, to make it easier to perform the right actions, which vary
@@ -1294,6 +1388,7 @@ async function compile(
1294
1388
writeFile = createCompileWriteFile ( state ) ;
1295
1389
}
1296
1390
const host = ( state . host = new Host ( {
1391
+ rootNames,
1297
1392
bundle,
1298
1393
target,
1299
1394
writeFile,
@@ -1323,25 +1418,29 @@ async function compile(
1323
1418
// to generate the program and possibly emit it.
1324
1419
if ( diagnostics . length === 0 ) {
1325
1420
const options = host . getCompilationSettings ( ) ;
1326
- const program = ts . createProgram ( {
1327
- rootNames,
1421
+ const relativeRootNames = rootNames . map ( host . getRelativePath . bind ( host ) ) ;
1422
+ const creationTimer = new Timer ( "Create program" ) ;
1423
+ const program = ts . createIncrementalProgram ( {
1424
+ rootNames : relativeRootNames ,
1328
1425
options,
1329
1426
host,
1330
- oldProgram : TS_SNAPSHOT_PROGRAM ,
1331
1427
} ) ;
1428
+ creationTimer . end ( ) ;
1332
1429
1333
- diagnostics = ts
1334
- . getPreEmitDiagnostics ( program )
1335
- . filter ( ( { code } ) => ! ignoredDiagnostics . includes ( code ) ) ;
1430
+ const diagnosticsTimer = new Timer ( "Pre emit diagnostics" ) ;
1431
+ diagnostics = getPreEmitDiagnostics ( program ) ;
1432
+ diagnosticsTimer . end ( ) ;
1336
1433
1337
1434
// We will only proceed with the emit if there are no diagnostics.
1338
1435
if ( diagnostics && diagnostics . length === 0 ) {
1339
1436
if ( bundle ) {
1340
1437
// we only support a single root module when bundling
1341
1438
assert ( resolvedRootModules . length === 1 ) ;
1342
- setRootExports ( program , resolvedRootModules [ 0 ] ) ;
1439
+ // setRootExports(program, resolvedRootModules[0]);
1343
1440
}
1441
+ const emitTimer = new Timer ( "Emit" ) ;
1344
1442
const emitResult = program . emit ( ) ;
1443
+ emitTimer . end ( ) ;
1345
1444
assert ( emitResult . emitSkipped === false , "Unexpected skip of the emit." ) ;
1346
1445
// emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned
1347
1446
// without casting.
@@ -1360,9 +1459,11 @@ async function compile(
1360
1459
const result : CompileResult = {
1361
1460
emitMap : state . emitMap ,
1362
1461
bundleOutput,
1462
+ sources : SourceFile . urls ( ) ,
1363
1463
diagnostics : fromTypeScriptDiagnostic ( diagnostics ) ,
1364
1464
} ;
1365
1465
1466
+ compileTimer . end ( ) ;
1366
1467
util . log ( "<<< compile end" , {
1367
1468
rootNames,
1368
1469
type : CompilerRequestType [ request . type ] ,
0 commit comments