@jsenv/test 1.8.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2515,6 +2515,7 @@ const createLog = ({
2515
2515
  const { columns = 80, rows = 24 } = stream;
2516
2516
 
2517
2517
  const log = {
2518
+ destroyed: false,
2518
2519
  onVerticalOverflow: () => {},
2519
2520
  };
2520
2521
 
@@ -2579,6 +2580,9 @@ const createLog = ({
2579
2580
  };
2580
2581
 
2581
2582
  const write = (string, outputFromOutside = streamOutputSpy()) => {
2583
+ if (log.destroyed) {
2584
+ throw new Error("Cannot write log after destroy");
2585
+ }
2582
2586
  if (!lastOutput) {
2583
2587
  doWrite(string);
2584
2588
  return;
@@ -2599,6 +2603,7 @@ const createLog = ({
2599
2603
  };
2600
2604
 
2601
2605
  const destroy = () => {
2606
+ log.destroyed = true;
2602
2607
  if (streamOutputSpy) {
2603
2608
  streamOutputSpy(); // this uninstalls the spy
2604
2609
  streamOutputSpy = null;
@@ -2724,6 +2729,46 @@ const createTeardown = () => {
2724
2729
  };
2725
2730
  };
2726
2731
 
2732
+ const createCallOrderer = () => {
2733
+ const queue = [];
2734
+ const callWhenPreviousExecutionAreDone = (executionIndex, callback) => {
2735
+ if (queue[executionIndex]) {
2736
+ throw new Error(`${executionIndex} already used`);
2737
+ }
2738
+
2739
+ let allBeforeAreDone = true;
2740
+ if (executionIndex > 0) {
2741
+ let beforeIndex = executionIndex - 1;
2742
+ do {
2743
+ const value = queue[beforeIndex];
2744
+ if (!value) {
2745
+ allBeforeAreDone = false;
2746
+ break;
2747
+ }
2748
+ } while (beforeIndex--);
2749
+ }
2750
+ if (!allBeforeAreDone) {
2751
+ queue[executionIndex] = callback;
2752
+ return;
2753
+ }
2754
+ queue[executionIndex] = true;
2755
+ callback();
2756
+ let afterIndex = executionIndex + 1;
2757
+ while (afterIndex < queue.length) {
2758
+ const value = queue[afterIndex];
2759
+ if (value === undefined) {
2760
+ break;
2761
+ }
2762
+ if (typeof value === "function") {
2763
+ queue[afterIndex] = true;
2764
+ value();
2765
+ }
2766
+ afterIndex++;
2767
+ }
2768
+ };
2769
+ return callWhenPreviousExecutionAreDone;
2770
+ };
2771
+
2727
2772
  const readNodeV8CoverageDirectory = async ({
2728
2773
  logger,
2729
2774
  signal,
@@ -4686,6 +4731,13 @@ const executeTestPlan = async ({
4686
4731
  }) => {
4687
4732
  const teardown = createTeardown();
4688
4733
 
4734
+ const beforeExecutionCallbackSet = new Set();
4735
+ const afterExecutionCallbackSet = new Set();
4736
+ const afterAllExecutionCallbackSet = new Set();
4737
+ beforeExecutionCallbackSet.add(beforeExecutionCallback);
4738
+ afterExecutionCallbackSet.add(afterExecutionCallback);
4739
+ afterAllExecutionCallbackSet.add(afterAllExecutionCallback);
4740
+
4689
4741
  const operation = Abort.startOperation();
4690
4742
  operation.addAbortSignal(signal);
4691
4743
  if (handleSIGINT) {
@@ -4943,9 +4995,7 @@ To fix this warning:
4943
4995
  checkSummary: `${executionSteps.length} files will be executed`,
4944
4996
  });
4945
4997
  const annotations = [];
4946
- const afterExecutionCallbackPrevious = afterExecutionCallback;
4947
- afterExecutionCallback = (afterExecutionInfo) => {
4948
- afterExecutionCallbackPrevious(afterExecutionInfo);
4998
+ afterExecutionCallbackSet.add((afterExecutionInfo) => {
4949
4999
  const { executionResult } = afterExecutionInfo;
4950
5000
  const { errors = [] } = executionResult;
4951
5001
  for (const error of errors) {
@@ -4955,10 +5005,8 @@ To fix this warning:
4955
5005
  });
4956
5006
  annotations.push(annotation);
4957
5007
  }
4958
- };
4959
- const afterAllExecutionCallbackPrevious = afterAllExecutionCallback;
4960
- afterAllExecutionCallback = async (returnValue) => {
4961
- afterAllExecutionCallbackPrevious(returnValue);
5008
+ });
5009
+ afterAllExecutionCallbackSet.add(async (returnValue) => {
4962
5010
  const { summary } = returnValue;
4963
5011
  const title = "Jsenv test results";
4964
5012
  const summaryText = stripAnsi(formatSummary(summary));
@@ -4975,7 +5023,7 @@ To fix this warning:
4975
5023
  summary: summaryText,
4976
5024
  annotations,
4977
5025
  });
4978
- };
5026
+ });
4979
5027
  }
4980
5028
 
4981
5029
  executionSteps = executionSteps.filter(
@@ -5059,6 +5107,7 @@ To fix this warning:
5059
5107
  const executionLogsEnabled = logger.levels.info;
5060
5108
  const executionSpinner =
5061
5109
  logRefresh &&
5110
+ maxExecutionsInParallel === 1 &&
5062
5111
  !debugLogsEnabled &&
5063
5112
  executionLogsEnabled &&
5064
5113
  process.stdout.isTTY &&
@@ -5078,6 +5127,9 @@ To fix this warning:
5078
5127
  completed: 0,
5079
5128
  done: 0,
5080
5129
  };
5130
+
5131
+ const callWhenPreviousExecutionAreDone = createCallOrderer();
5132
+
5081
5133
  await executeInParallel({
5082
5134
  multipleExecutionsOperation,
5083
5135
  maxExecutionsInParallel,
@@ -5133,8 +5185,9 @@ To fix this warning:
5133
5185
  },
5134
5186
  });
5135
5187
  }
5136
- beforeExecutionCallback(beforeExecutionInfo);
5137
-
5188
+ for (const beforeExecutionCallback of beforeExecutionCallbackSet) {
5189
+ beforeExecutionCallback(beforeExecutionInfo);
5190
+ }
5138
5191
  const fileUrl = `${rootDirectoryUrl}${fileRelativeUrl}`;
5139
5192
  let executionResult;
5140
5193
  if (existsSync(new URL(fileUrl))) {
@@ -5143,7 +5196,7 @@ To fix this warning:
5143
5196
  logger,
5144
5197
  allocatedMs: executionParams.allocatedMs,
5145
5198
  keepRunning,
5146
- mirrorConsole: false, // file are executed in parallel, log would be a mess to read
5199
+ mirrorConsole: false, // might be executed in parallel: log would be a mess to read
5147
5200
  collectConsole: executionParams.collectConsole,
5148
5201
  coverageEnabled,
5149
5202
  coverageTempDirectoryUrl,
@@ -5163,7 +5216,6 @@ To fix this warning:
5163
5216
  ],
5164
5217
  };
5165
5218
  }
5166
- counters.done++;
5167
5219
  const fileReport = report[fileRelativeUrl];
5168
5220
  if (fileReport) {
5169
5221
  fileReport[executionName] = executionResult;
@@ -5180,58 +5232,65 @@ To fix this warning:
5180
5232
  executionResult,
5181
5233
  };
5182
5234
 
5183
- if (executionResult.status === "aborted") {
5184
- counters.aborted++;
5185
- } else if (executionResult.status === "timedout") {
5186
- counters.timedout++;
5187
- } else if (executionResult.status === "failed") {
5188
- counters.failed++;
5189
- } else if (executionResult.status === "completed") {
5190
- counters.completed++;
5191
- }
5192
5235
  if (gcBetweenExecutions) {
5193
5236
  global.gc();
5194
5237
  }
5195
- if (executionLogsEnabled) {
5196
- const log = createExecutionLog(afterExecutionInfo, {
5197
- logShortForCompletedExecutions,
5198
- logRuntime,
5199
- logEachDuration,
5200
- ...(logTimeUsage
5201
- ? {
5202
- timeEllapsed: Date.now() - startMs,
5203
- }
5204
- : {}),
5205
- ...(logMemoryHeapUsage
5206
- ? { memoryHeap: memoryUsage().heapUsed }
5207
- : {}),
5208
- });
5209
- // replace spinner with this execution result
5210
- if (spinner) spinner.stop();
5211
- executionLog.write(log);
5212
- rawOutput += stripAnsi(log);
5213
-
5214
- const canOverwriteLog = canOverwriteLogGetter({
5215
- logMergeForCompletedExecutions,
5216
- executionResult,
5217
- });
5218
- if (canOverwriteLog) {
5219
- // nothing to do, we reuse the current executionLog object
5220
- } else {
5221
- executionLog.destroy();
5222
- executionLog = createLog({ newLine: "" });
5238
+
5239
+ const timeEllapsed = Date.now() - startMs;
5240
+ const memoryHeap = memoryUsage().heapUsed;
5241
+ callWhenPreviousExecutionAreDone(executionIndex, () => {
5242
+ counters.done++;
5243
+ if (executionResult.status === "aborted") {
5244
+ counters.aborted++;
5245
+ } else if (executionResult.status === "timedout") {
5246
+ counters.timedout++;
5247
+ } else if (executionResult.status === "failed") {
5248
+ counters.failed++;
5249
+ } else if (executionResult.status === "completed") {
5250
+ counters.completed++;
5251
+ }
5252
+
5253
+ if (executionLogsEnabled) {
5254
+ // replace spinner with this execution result
5255
+ if (spinner) {
5256
+ spinner.stop();
5257
+ spinner = null;
5258
+ }
5259
+
5260
+ const log = createExecutionLog(afterExecutionInfo, {
5261
+ logShortForCompletedExecutions,
5262
+ logRuntime,
5263
+ logEachDuration,
5264
+ ...(logTimeUsage ? { timeEllapsed } : {}),
5265
+ ...(logMemoryHeapUsage ? { memoryHeap } : {}),
5266
+ });
5267
+
5268
+ executionLog.write(log);
5269
+ rawOutput += stripAnsi(log);
5270
+ const canOverwriteLog = canOverwriteLogGetter({
5271
+ logMergeForCompletedExecutions,
5272
+ executionResult,
5273
+ });
5274
+ if (canOverwriteLog) {
5275
+ // nothing to do, we reuse the current executionLog object
5276
+ } else {
5277
+ executionLog.destroy();
5278
+ executionLog = createLog({ newLine: "" });
5279
+ }
5280
+ const isLastExecutionLog =
5281
+ executionIndex === executionSteps.length - 1;
5282
+ if (isLastExecutionLog && logger.levels.info) {
5283
+ executionLog.write("\n");
5284
+ }
5223
5285
  }
5286
+ });
5287
+ for (const afterExecutionCallback of afterExecutionCallbackSet) {
5288
+ afterExecutionCallback(afterExecutionInfo);
5224
5289
  }
5225
- afterExecutionCallback(afterExecutionInfo);
5226
- const isLastExecutionLog = executionIndex === executionSteps.length - 1;
5227
5290
  const cancelRemaining =
5228
5291
  failFast &&
5229
5292
  executionResult.status !== "completed" &&
5230
5293
  counters.done < counters.total;
5231
- if (isLastExecutionLog && logger.levels.info) {
5232
- executionLog.write("\n");
5233
- }
5234
-
5235
5294
  if (cancelRemaining) {
5236
5295
  logger.info(`"failFast" enabled -> cancel remaining executions`);
5237
5296
  failFastAbortController.abort();
@@ -5320,7 +5379,9 @@ To fix this warning:
5320
5379
  await Promise.all(promises);
5321
5380
  }
5322
5381
 
5323
- await afterAllExecutionCallback(returnValue);
5382
+ for (const afterAllExecutionCallback of afterAllExecutionCallbackSet) {
5383
+ await afterAllExecutionCallback(returnValue);
5384
+ }
5324
5385
  return returnValue;
5325
5386
  };
5326
5387
 
@@ -5360,12 +5421,14 @@ const executeInParallel = async ({
5360
5421
  return;
5361
5422
  }
5362
5423
  const outputPromiseArray = [];
5424
+ let previousExecPromise = Promise.resolve();
5363
5425
  while (
5364
5426
  remainingExecutionCount > 0 &&
5365
5427
  outputPromiseArray.length < maxExecutionsInParallel
5366
5428
  ) {
5367
5429
  remainingExecutionCount--;
5368
- const outputPromise = executeOne(progressionIndex);
5430
+ const outputPromise = executeOne(progressionIndex, previousExecPromise);
5431
+ previousExecPromise = outputPromise;
5369
5432
  progressionIndex++;
5370
5433
  outputPromiseArray.push(outputPromise);
5371
5434
  }
@@ -5377,9 +5440,9 @@ const executeInParallel = async ({
5377
5440
  }
5378
5441
  };
5379
5442
 
5380
- const executeOne = async (index) => {
5443
+ const executeOne = async (index, previousExecPromise) => {
5381
5444
  const input = executionSteps[index];
5382
- const output = await start(input);
5445
+ const output = await start(input, previousExecPromise);
5383
5446
  if (!multipleExecutionsOperation.signal.aborted) {
5384
5447
  executionResults[index] = output;
5385
5448
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/test",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,12 +36,12 @@
36
36
  "@jsenv/abort": "4.2.4",
37
37
  "@jsenv/ast": "6.0.1",
38
38
  "@jsenv/filesystem": "4.3.2",
39
- "@jsenv/log": "3.4.2",
39
+ "@jsenv/log": "3.4.3",
40
40
  "@jsenv/plugin-supervisor": "1.3.9",
41
41
  "@jsenv/urls": "2.2.1",
42
42
  "@jsenv/url-meta": "8.1.0",
43
43
  "@jsenv/utils": "2.0.1",
44
- "@jsenv/github-check-run": "0.1.3",
44
+ "@jsenv/github-check-run": "0.1.4",
45
45
  "v8-to-istanbul": "9.1.3",
46
46
  "istanbul-lib-coverage": "3.2.2",
47
47
  "istanbul-lib-instrument": "6.0.1",
@@ -24,6 +24,7 @@ import {
24
24
  } from "@jsenv/github-check-run";
25
25
 
26
26
  import { createTeardown } from "../helpers/teardown.js";
27
+ import { createCallOrderer } from "../helpers/call_orderer.js";
27
28
  import { reportToCoverage } from "../coverage/report_to_coverage.js";
28
29
  import { generateCoverageJsonFile } from "../coverage/coverage_reporter_json_file.js";
29
30
  import { generateCoverageHtmlDirectory } from "../coverage/coverage_reporter_html_directory.js";
@@ -142,6 +143,13 @@ export const executeTestPlan = async ({
142
143
  }) => {
143
144
  const teardown = createTeardown();
144
145
 
146
+ const beforeExecutionCallbackSet = new Set();
147
+ const afterExecutionCallbackSet = new Set();
148
+ const afterAllExecutionCallbackSet = new Set();
149
+ beforeExecutionCallbackSet.add(beforeExecutionCallback);
150
+ afterExecutionCallbackSet.add(afterExecutionCallback);
151
+ afterAllExecutionCallbackSet.add(afterAllExecutionCallback);
152
+
145
153
  const operation = Abort.startOperation();
146
154
  operation.addAbortSignal(signal);
147
155
  if (handleSIGINT) {
@@ -399,9 +407,7 @@ To fix this warning:
399
407
  checkSummary: `${executionSteps.length} files will be executed`,
400
408
  });
401
409
  const annotations = [];
402
- const afterExecutionCallbackPrevious = afterExecutionCallback;
403
- afterExecutionCallback = (afterExecutionInfo) => {
404
- afterExecutionCallbackPrevious(afterExecutionInfo);
410
+ afterExecutionCallbackSet.add((afterExecutionInfo) => {
405
411
  const { executionResult } = afterExecutionInfo;
406
412
  const { errors = [] } = executionResult;
407
413
  for (const error of errors) {
@@ -411,10 +417,8 @@ To fix this warning:
411
417
  });
412
418
  annotations.push(annotation);
413
419
  }
414
- };
415
- const afterAllExecutionCallbackPrevious = afterAllExecutionCallback;
416
- afterAllExecutionCallback = async (returnValue) => {
417
- afterAllExecutionCallbackPrevious(returnValue);
420
+ });
421
+ afterAllExecutionCallbackSet.add(async (returnValue) => {
418
422
  const { summary } = returnValue;
419
423
  const title = "Jsenv test results";
420
424
  const summaryText = stripAnsi(formatSummary(summary));
@@ -431,7 +435,7 @@ To fix this warning:
431
435
  summary: summaryText,
432
436
  annotations,
433
437
  });
434
- };
438
+ });
435
439
  }
436
440
 
437
441
  executionSteps = executionSteps.filter(
@@ -515,6 +519,7 @@ To fix this warning:
515
519
  const executionLogsEnabled = logger.levels.info;
516
520
  const executionSpinner =
517
521
  logRefresh &&
522
+ maxExecutionsInParallel === 1 &&
518
523
  !debugLogsEnabled &&
519
524
  executionLogsEnabled &&
520
525
  process.stdout.isTTY &&
@@ -534,6 +539,9 @@ To fix this warning:
534
539
  completed: 0,
535
540
  done: 0,
536
541
  };
542
+
543
+ const callWhenPreviousExecutionAreDone = createCallOrderer();
544
+
537
545
  await executeInParallel({
538
546
  multipleExecutionsOperation,
539
547
  maxExecutionsInParallel,
@@ -589,8 +597,9 @@ To fix this warning:
589
597
  },
590
598
  });
591
599
  }
592
- beforeExecutionCallback(beforeExecutionInfo);
593
-
600
+ for (const beforeExecutionCallback of beforeExecutionCallbackSet) {
601
+ beforeExecutionCallback(beforeExecutionInfo);
602
+ }
594
603
  const fileUrl = `${rootDirectoryUrl}${fileRelativeUrl}`;
595
604
  let executionResult;
596
605
  if (existsSync(new URL(fileUrl))) {
@@ -599,7 +608,7 @@ To fix this warning:
599
608
  logger,
600
609
  allocatedMs: executionParams.allocatedMs,
601
610
  keepRunning,
602
- mirrorConsole: false, // file are executed in parallel, log would be a mess to read
611
+ mirrorConsole: false, // might be executed in parallel: log would be a mess to read
603
612
  collectConsole: executionParams.collectConsole,
604
613
  coverageEnabled,
605
614
  coverageTempDirectoryUrl,
@@ -619,7 +628,6 @@ To fix this warning:
619
628
  ],
620
629
  };
621
630
  }
622
- counters.done++;
623
631
  const fileReport = report[fileRelativeUrl];
624
632
  if (fileReport) {
625
633
  fileReport[executionName] = executionResult;
@@ -636,58 +644,65 @@ To fix this warning:
636
644
  executionResult,
637
645
  };
638
646
 
639
- if (executionResult.status === "aborted") {
640
- counters.aborted++;
641
- } else if (executionResult.status === "timedout") {
642
- counters.timedout++;
643
- } else if (executionResult.status === "failed") {
644
- counters.failed++;
645
- } else if (executionResult.status === "completed") {
646
- counters.completed++;
647
- }
648
647
  if (gcBetweenExecutions) {
649
648
  global.gc();
650
649
  }
651
- if (executionLogsEnabled) {
652
- const log = createExecutionLog(afterExecutionInfo, {
653
- logShortForCompletedExecutions,
654
- logRuntime,
655
- logEachDuration,
656
- ...(logTimeUsage
657
- ? {
658
- timeEllapsed: Date.now() - startMs,
659
- }
660
- : {}),
661
- ...(logMemoryHeapUsage
662
- ? { memoryHeap: memoryUsage().heapUsed }
663
- : {}),
664
- });
665
- // replace spinner with this execution result
666
- if (spinner) spinner.stop();
667
- executionLog.write(log);
668
- rawOutput += stripAnsi(log);
669
-
670
- const canOverwriteLog = canOverwriteLogGetter({
671
- logMergeForCompletedExecutions,
672
- executionResult,
673
- });
674
- if (canOverwriteLog) {
675
- // nothing to do, we reuse the current executionLog object
676
- } else {
677
- executionLog.destroy();
678
- executionLog = createLog({ newLine: "" });
650
+
651
+ const timeEllapsed = Date.now() - startMs;
652
+ const memoryHeap = memoryUsage().heapUsed;
653
+ callWhenPreviousExecutionAreDone(executionIndex, () => {
654
+ counters.done++;
655
+ if (executionResult.status === "aborted") {
656
+ counters.aborted++;
657
+ } else if (executionResult.status === "timedout") {
658
+ counters.timedout++;
659
+ } else if (executionResult.status === "failed") {
660
+ counters.failed++;
661
+ } else if (executionResult.status === "completed") {
662
+ counters.completed++;
679
663
  }
664
+
665
+ if (executionLogsEnabled) {
666
+ // replace spinner with this execution result
667
+ if (spinner) {
668
+ spinner.stop();
669
+ spinner = null;
670
+ }
671
+
672
+ const log = createExecutionLog(afterExecutionInfo, {
673
+ logShortForCompletedExecutions,
674
+ logRuntime,
675
+ logEachDuration,
676
+ ...(logTimeUsage ? { timeEllapsed } : {}),
677
+ ...(logMemoryHeapUsage ? { memoryHeap } : {}),
678
+ });
679
+
680
+ executionLog.write(log);
681
+ rawOutput += stripAnsi(log);
682
+ const canOverwriteLog = canOverwriteLogGetter({
683
+ logMergeForCompletedExecutions,
684
+ executionResult,
685
+ });
686
+ if (canOverwriteLog) {
687
+ // nothing to do, we reuse the current executionLog object
688
+ } else {
689
+ executionLog.destroy();
690
+ executionLog = createLog({ newLine: "" });
691
+ }
692
+ const isLastExecutionLog =
693
+ executionIndex === executionSteps.length - 1;
694
+ if (isLastExecutionLog && logger.levels.info) {
695
+ executionLog.write("\n");
696
+ }
697
+ }
698
+ });
699
+ for (const afterExecutionCallback of afterExecutionCallbackSet) {
700
+ afterExecutionCallback(afterExecutionInfo);
680
701
  }
681
- afterExecutionCallback(afterExecutionInfo);
682
- const isLastExecutionLog = executionIndex === executionSteps.length - 1;
683
702
  const cancelRemaining =
684
703
  failFast &&
685
704
  executionResult.status !== "completed" &&
686
705
  counters.done < counters.total;
687
- if (isLastExecutionLog && logger.levels.info) {
688
- executionLog.write("\n");
689
- }
690
-
691
706
  if (cancelRemaining) {
692
707
  logger.info(`"failFast" enabled -> cancel remaining executions`);
693
708
  failFastAbortController.abort();
@@ -776,7 +791,9 @@ To fix this warning:
776
791
  await Promise.all(promises);
777
792
  }
778
793
 
779
- await afterAllExecutionCallback(returnValue);
794
+ for (const afterAllExecutionCallback of afterAllExecutionCallbackSet) {
795
+ await afterAllExecutionCallback(returnValue);
796
+ }
780
797
  return returnValue;
781
798
  };
782
799
 
@@ -816,12 +833,14 @@ const executeInParallel = async ({
816
833
  return;
817
834
  }
818
835
  const outputPromiseArray = [];
836
+ let previousExecPromise = Promise.resolve();
819
837
  while (
820
838
  remainingExecutionCount > 0 &&
821
839
  outputPromiseArray.length < maxExecutionsInParallel
822
840
  ) {
823
841
  remainingExecutionCount--;
824
- const outputPromise = executeOne(progressionIndex);
842
+ const outputPromise = executeOne(progressionIndex, previousExecPromise);
843
+ previousExecPromise = outputPromise;
825
844
  progressionIndex++;
826
845
  outputPromiseArray.push(outputPromise);
827
846
  }
@@ -833,9 +852,9 @@ const executeInParallel = async ({
833
852
  }
834
853
  };
835
854
 
836
- const executeOne = async (index) => {
855
+ const executeOne = async (index, previousExecPromise) => {
837
856
  const input = executionSteps[index];
838
- const output = await start(input);
857
+ const output = await start(input, previousExecPromise);
839
858
  if (!multipleExecutionsOperation.signal.aborted) {
840
859
  executionResults[index] = output;
841
860
  }
@@ -0,0 +1,39 @@
1
+ export const createCallOrderer = () => {
2
+ const queue = [];
3
+ const callWhenPreviousExecutionAreDone = (executionIndex, callback) => {
4
+ if (queue[executionIndex]) {
5
+ throw new Error(`${executionIndex} already used`);
6
+ }
7
+
8
+ let allBeforeAreDone = true;
9
+ if (executionIndex > 0) {
10
+ let beforeIndex = executionIndex - 1;
11
+ do {
12
+ const value = queue[beforeIndex];
13
+ if (!value) {
14
+ allBeforeAreDone = false;
15
+ break;
16
+ }
17
+ } while (beforeIndex--);
18
+ }
19
+ if (!allBeforeAreDone) {
20
+ queue[executionIndex] = callback;
21
+ return;
22
+ }
23
+ queue[executionIndex] = true;
24
+ callback();
25
+ let afterIndex = executionIndex + 1;
26
+ while (afterIndex < queue.length) {
27
+ const value = queue[afterIndex];
28
+ if (value === undefined) {
29
+ break;
30
+ }
31
+ if (typeof value === "function") {
32
+ queue[afterIndex] = true;
33
+ value();
34
+ }
35
+ afterIndex++;
36
+ }
37
+ };
38
+ return callWhenPreviousExecutionAreDone;
39
+ };