@@ -6,12 +6,15 @@ import { task } from "hereby";
6
6
import _glob from "glob" ;
7
7
import util from "util" ;
8
8
import chalk from "chalk" ;
9
- import { exec , readJson , getDiffTool , getDirSize , memoize , needsUpdate } from "./scripts/build/utils.mjs" ;
10
- import { runConsoleTests , refBaseline , localBaseline , refRwcBaseline , localRwcBaseline } from "./scripts/build/tests.mjs" ;
11
- import { buildProject as realBuildProject , cleanProject , watchProject } from "./scripts/build/projects.mjs" ;
9
+ import { Debouncer , Deferred , exec , getDiffTool , getDirSize , memoize , needsUpdate , readJson } from "./scripts/build/utils.mjs" ;
10
+ import { cleanTestDirs , localBaseline , localRwcBaseline , refBaseline , refRwcBaseline , runConsoleTests } from "./scripts/build/tests.mjs" ;
11
+ import { cleanProject , buildProject as realBuildProject , watchProject } from "./scripts/build/projects.mjs" ;
12
12
import { localizationDirectories } from "./scripts/build/localization.mjs" ;
13
13
import cmdLineOptions from "./scripts/build/options.mjs" ;
14
14
import esbuild from "esbuild" ;
15
+ import chokidar from "chokidar" ;
16
+ import { EventEmitter } from "events" ;
17
+ import { CancelToken } from "@esfx/canceltoken" ;
15
18
16
19
const glob = util . promisify ( _glob ) ;
17
20
@@ -191,6 +194,7 @@ async function runDtsBundler(entrypoint, output) {
191
194
* @property {string[] } [external]
192
195
* @property {boolean } [exportIsTsObject]
193
196
* @property {boolean } [treeShaking]
197
+ * @property {esbuild.WatchMode } [watchMode]
194
198
*/
195
199
function createBundler ( entrypoint , outfile , taskOptions = { } ) {
196
200
const getOptions = memoize ( async ( ) => {
@@ -269,7 +273,7 @@ function createBundler(entrypoint, outfile, taskOptions = {}) {
269
273
270
274
return {
271
275
build : async ( ) => esbuild . build ( await getOptions ( ) ) ,
272
- watch : async ( ) => esbuild . build ( { ...await getOptions ( ) , watch : true , logLevel : "info" } ) ,
276
+ watch : async ( ) => esbuild . build ( { ...await getOptions ( ) , watch : taskOptions . watchMode ?? true , logLevel : "info" } ) ,
273
277
} ;
274
278
}
275
279
@@ -324,10 +328,21 @@ function entrypointBuildTask(options) {
324
328
} ,
325
329
} ) ;
326
330
331
+ const mainDeps = options . mainDeps ?. slice ( 0 ) ?? [ ] ;
332
+ if ( cmdLineOptions . bundle ) {
333
+ mainDeps . push ( bundle ) ;
334
+ if ( cmdLineOptions . typecheck ) {
335
+ mainDeps . push ( build ) ;
336
+ }
337
+ }
338
+ else {
339
+ mainDeps . push ( build , shim ) ;
340
+ }
341
+
327
342
const main = task ( {
328
343
name : options . name ,
329
344
description : options . description ,
330
- dependencies : ( options . mainDeps ?? [ ] ) . concat ( cmdLineOptions . bundle ? [ bundle ] : [ build , shim ] ) ,
345
+ dependencies : mainDeps ,
331
346
} ) ;
332
347
333
348
const watch = task ( {
@@ -354,7 +369,7 @@ function entrypointBuildTask(options) {
354
369
}
355
370
356
371
357
- const { main : tsc , build : buildTsc , watch : watchTsc } = entrypointBuildTask ( {
372
+ const { main : tsc , watch : watchTsc } = entrypointBuildTask ( {
358
373
name : "tsc" ,
359
374
description : "Builds the command-line compiler" ,
360
375
buildDeps : [ generateDiagnostics ] ,
@@ -392,7 +407,7 @@ export const dtsServices = task({
392
407
} ) ;
393
408
394
409
395
- const { main : tsserver , build : buildTsserver , watch : watchTsserver } = entrypointBuildTask ( {
410
+ const { main : tsserver , watch : watchTsserver } = entrypointBuildTask ( {
396
411
name : "tsserver" ,
397
412
description : "Builds the language server" ,
398
413
buildDeps : [ generateDiagnostics ] ,
@@ -410,15 +425,10 @@ const { main: tsserver, build: buildTsserver, watch: watchTsserver } = entrypoin
410
425
export { tsserver , watchTsserver } ;
411
426
412
427
413
- const buildMin = task ( {
414
- name : "build-min" ,
415
- dependencies : [ buildTsc , buildTsserver ] ,
416
- } ) ;
417
-
418
428
export const min = task ( {
419
429
name : "min" ,
420
430
description : "Builds only tsc and tsserver" ,
421
- dependencies : [ tsc , tsserver ] . concat ( cmdLineOptions . typecheck ? [ buildMin ] : [ ] ) ,
431
+ dependencies : [ tsc , tsserver ] ,
422
432
} ) ;
423
433
424
434
export const watchMin = task ( {
@@ -461,7 +471,7 @@ export const dts = task({
461
471
462
472
463
473
const testRunner = "./built/local/run.js" ;
464
-
474
+ const watchTestsEmitter = new EventEmitter ( ) ;
465
475
const { main : tests , watch : watchTests } = entrypointBuildTask ( {
466
476
name : "tests" ,
467
477
description : "Builds the test infrastructure" ,
@@ -482,6 +492,11 @@ const { main: tests, watch: watchTests } = entrypointBuildTask({
482
492
"mocha" ,
483
493
"ms" ,
484
494
] ,
495
+ watchMode : {
496
+ onRebuild ( ) {
497
+ watchTestsEmitter . emit ( "rebuild" ) ;
498
+ }
499
+ }
485
500
} ,
486
501
} ) ;
487
502
export { tests , watchTests } ;
@@ -582,15 +597,10 @@ export const watchOtherOutputs = task({
582
597
dependencies : [ watchCancellationToken , watchTypingsInstaller , watchWatchGuard , generateTypesMap , copyBuiltLocalDiagnosticMessages ] ,
583
598
} ) ;
584
599
585
- const buildLocal = task ( {
586
- name : "build-local" ,
587
- dependencies : [ buildTsc , buildTsserver , buildServices , buildLssl ]
588
- } ) ;
589
-
590
600
export const local = task ( {
591
601
name : "local" ,
592
602
description : "Builds the full compiler and services" ,
593
- dependencies : [ localize , tsc , tsserver , services , lssl , otherOutputs , dts ] . concat ( cmdLineOptions . typecheck ? [ buildLocal ] : [ ] ) ,
603
+ dependencies : [ localize , tsc , tsserver , services , lssl , otherOutputs , dts ] ,
594
604
} ) ;
595
605
export default local ;
596
606
@@ -601,7 +611,7 @@ export const watchLocal = task({
601
611
dependencies : [ localize , watchTsc , watchTsserver , watchServices , watchLssl , watchOtherOutputs , dts , watchSrc ] ,
602
612
} ) ;
603
613
604
- const runtestsDeps = [ tests , generateLibs ] . concat ( cmdLineOptions . typecheck ? [ dts , buildSrc ] : [ ] ) ;
614
+ const runtestsDeps = [ tests , generateLibs ] . concat ( cmdLineOptions . typecheck ? [ dts ] : [ ] ) ;
605
615
606
616
export const runTests = task ( {
607
617
name : "runtests" ,
@@ -625,6 +635,117 @@ export const runTests = task({
625
635
// " --shardId": "1-based ID of this shard (default: 1)",
626
636
// };
627
637
638
+ export const runTestsAndWatch = task ( {
639
+ name : "runtests-watch" ,
640
+ dependencies : [ watchTests ] ,
641
+ run : async ( ) => {
642
+ if ( ! cmdLineOptions . tests && ! cmdLineOptions . failed ) {
643
+ console . log ( chalk . redBright ( `You must specifiy either --tests/-t or --failed to use 'runtests-watch'.` ) ) ;
644
+ return ;
645
+ }
646
+
647
+ let watching = true ;
648
+ let running = true ;
649
+ let lastTestChangeTimeMs = Date . now ( ) ;
650
+ let testsChangedDeferred = /** @type {Deferred<void> } */ ( new Deferred ( ) ) ;
651
+ let testsChangedCancelSource = CancelToken . source ( ) ;
652
+
653
+ const testsChangedDebouncer = new Debouncer ( 1_000 , endRunTests ) ;
654
+ const testCaseWatcher = chokidar . watch ( [
655
+ "tests/cases/**/*.*" ,
656
+ "tests/lib/**/*.*" ,
657
+ "tests/projects/**/*.*" ,
658
+ ] , {
659
+ ignorePermissionErrors : true ,
660
+ alwaysStat : true
661
+ } ) ;
662
+
663
+ process . on ( "SIGINT" , endWatchMode ) ;
664
+ process . on ( "SIGKILL" , endWatchMode ) ;
665
+ process . on ( "beforeExit" , endWatchMode ) ;
666
+ watchTestsEmitter . on ( "rebuild" , onRebuild ) ;
667
+ testCaseWatcher . on ( "all" , onChange ) ;
668
+
669
+ while ( watching ) {
670
+ const promise = testsChangedDeferred . promise ;
671
+ const token = testsChangedCancelSource . token ;
672
+ if ( ! token . signaled ) {
673
+ running = true ;
674
+ try {
675
+ await runConsoleTests ( testRunner , "mocha-fivemat-progress-reporter" , /*runInParallel*/ false , { token, watching : true } ) ;
676
+ }
677
+ catch {
678
+ // ignore
679
+ }
680
+ running = false ;
681
+ }
682
+ if ( watching ) {
683
+ console . log ( chalk . yellowBright ( `[watch] test run complete, waiting for changes...` ) ) ;
684
+ await promise ;
685
+ }
686
+ }
687
+
688
+ function onRebuild ( ) {
689
+ beginRunTests ( testRunner ) ;
690
+ }
691
+
692
+ /**
693
+ * @param {'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir' } eventName
694
+ * @param {string } path
695
+ * @param {fs.Stats | undefined } stats
696
+ */
697
+ function onChange ( eventName , path , stats ) {
698
+ switch ( eventName ) {
699
+ case "change" :
700
+ case "unlink" :
701
+ case "unlinkDir" :
702
+ break ;
703
+ case "add" :
704
+ case "addDir" :
705
+ // skip files that are detected as 'add' but haven't actually changed since the last time tests were
706
+ // run.
707
+ if ( stats && stats . mtimeMs <= lastTestChangeTimeMs ) {
708
+ return ;
709
+ }
710
+ break ;
711
+ }
712
+ beginRunTests ( path ) ;
713
+ }
714
+
715
+ /**
716
+ * @param {string } path
717
+ */
718
+ function beginRunTests ( path ) {
719
+ if ( testsChangedDebouncer . empty ) {
720
+ console . log ( chalk . yellowBright ( `[watch] tests changed due to '${ path } ', restarting...` ) ) ;
721
+ if ( running ) {
722
+ console . log ( chalk . yellowBright ( "[watch] aborting in-progress test run..." ) ) ;
723
+ }
724
+ testsChangedCancelSource . cancel ( ) ;
725
+ testsChangedCancelSource = CancelToken . source ( ) ;
726
+ }
727
+
728
+ testsChangedDebouncer . enqueue ( ) ;
729
+ }
730
+
731
+ function endRunTests ( ) {
732
+ lastTestChangeTimeMs = Date . now ( ) ;
733
+ testsChangedDeferred . resolve ( ) ;
734
+ testsChangedDeferred = /** @type {Deferred<void> } */ ( new Deferred ( ) ) ;
735
+ }
736
+
737
+ function endWatchMode ( ) {
738
+ if ( watching ) {
739
+ watching = false ;
740
+ console . log ( chalk . yellowBright ( "[watch] exiting watch mode..." ) ) ;
741
+ testsChangedCancelSource . cancel ( ) ;
742
+ testCaseWatcher . close ( ) ;
743
+ watchTestsEmitter . off ( "rebuild" , onRebuild ) ;
744
+ }
745
+ }
746
+ } ,
747
+ } ) ;
748
+
628
749
export const runTestsParallel = task ( {
629
750
name : "runtests-parallel" ,
630
751
description : "Runs all the tests in parallel using the built run.js file." ,
@@ -726,7 +847,7 @@ export const importDefinitelyTypedTests = task({
726
847
export const produceLKG = task ( {
727
848
name : "LKG" ,
728
849
description : "Makes a new LKG out of the built js files" ,
729
- dependencies : [ localize , tsc , tsserver , services , lssl , otherOutputs , dts ] ,
850
+ dependencies : [ local ] ,
730
851
run : async ( ) => {
731
852
if ( ! cmdLineOptions . bundle ) {
732
853
throw new Error ( "LKG cannot be created when --bundle=false" ) ;
0 commit comments