@jsenv/test 3.0.16 → 3.0.17

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.
Files changed (2) hide show
  1. package/dist/jsenv_test.js +642 -629
  2. package/package.json +7 -7
@@ -4,8 +4,8 @@ import { readdir, chmod, stat, lstat, promises, readFile as readFile$1, writeFil
4
4
  import { takeCoverage } from "node:v8";
5
5
  import stripAnsi from "strip-ansi";
6
6
  import { URL_META, createException } from "./js/exception.js";
7
- import stringWidth from "string-width";
8
7
  import tty from "node:tty";
8
+ import stringWidth from "string-width";
9
9
  import { pathToFileURL, fileURLToPath } from "node:url";
10
10
  import { dirname } from "node:path";
11
11
  import crypto from "node:crypto";
@@ -530,599 +530,343 @@ const SIGINT_CALLBACK = {
530
530
  },
531
531
  };
532
532
 
533
- const LOG_LEVEL_OFF = "off";
534
-
535
- const LOG_LEVEL_DEBUG = "debug";
536
-
537
- const LOG_LEVEL_INFO = "info";
538
-
539
- const LOG_LEVEL_WARN = "warn";
533
+ const createDetailedMessage = (message, details = {}) => {
534
+ let string = `${message}`;
540
535
 
541
- const LOG_LEVEL_ERROR = "error";
536
+ Object.keys(details).forEach((key) => {
537
+ const value = details[key];
538
+ string += `
539
+ --- ${key} ---
540
+ ${
541
+ Array.isArray(value)
542
+ ? value.join(`
543
+ `)
544
+ : value
545
+ }`;
546
+ });
542
547
 
543
- const createLogger = ({ logLevel = LOG_LEVEL_INFO } = {}) => {
544
- if (logLevel === LOG_LEVEL_DEBUG) {
545
- return {
546
- level: "debug",
547
- levels: { debug: true, info: true, warn: true, error: true },
548
- debug,
549
- info,
550
- warn,
551
- error,
552
- };
553
- }
554
- if (logLevel === LOG_LEVEL_INFO) {
555
- return {
556
- level: "info",
557
- levels: { debug: false, info: true, warn: true, error: true },
558
- debug: debugDisabled,
559
- info,
560
- warn,
561
- error,
562
- };
563
- }
564
- if (logLevel === LOG_LEVEL_WARN) {
565
- return {
566
- level: "warn",
567
- levels: { debug: false, info: false, warn: true, error: true },
568
- debug: debugDisabled,
569
- info: infoDisabled,
570
- warn,
571
- error,
572
- };
573
- }
574
- if (logLevel === LOG_LEVEL_ERROR) {
575
- return {
576
- level: "error",
577
- levels: { debug: false, info: false, warn: false, error: true },
578
- debug: debugDisabled,
579
- info: infoDisabled,
580
- warn: warnDisabled,
581
- error,
582
- };
583
- }
584
- if (logLevel === LOG_LEVEL_OFF) {
585
- return {
586
- level: "off",
587
- levels: { debug: false, info: false, warn: false, error: false },
588
- debug: debugDisabled,
589
- info: infoDisabled,
590
- warn: warnDisabled,
591
- error: errorDisabled,
592
- };
593
- }
594
- throw new Error(`unexpected logLevel.
595
- --- logLevel ---
596
- ${logLevel}
597
- --- allowed log levels ---
598
- ${LOG_LEVEL_OFF}
599
- ${LOG_LEVEL_ERROR}
600
- ${LOG_LEVEL_WARN}
601
- ${LOG_LEVEL_INFO}
602
- ${LOG_LEVEL_DEBUG}`);
548
+ return string;
603
549
  };
604
550
 
605
- const debug = (...args) => console.debug(...args);
606
-
607
- const debugDisabled = () => {};
608
-
609
- const info = (...args) => console.info(...args);
551
+ // From: https://github.com/sindresorhus/has-flag/blob/main/index.js
552
+ /// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) {
553
+ function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process$1.argv) {
554
+ const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
555
+ const position = argv.indexOf(prefix + flag);
556
+ const terminatorPosition = argv.indexOf('--');
557
+ return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
558
+ }
610
559
 
611
- const infoDisabled = () => {};
560
+ const {env} = process$1;
612
561
 
613
- const warn = (...args) => console.warn(...args);
562
+ let flagForceColor;
563
+ if (
564
+ hasFlag('no-color')
565
+ || hasFlag('no-colors')
566
+ || hasFlag('color=false')
567
+ || hasFlag('color=never')
568
+ ) {
569
+ flagForceColor = 0;
570
+ } else if (
571
+ hasFlag('color')
572
+ || hasFlag('colors')
573
+ || hasFlag('color=true')
574
+ || hasFlag('color=always')
575
+ ) {
576
+ flagForceColor = 1;
577
+ }
614
578
 
615
- const warnDisabled = () => {};
579
+ function envForceColor() {
580
+ if ('FORCE_COLOR' in env) {
581
+ if (env.FORCE_COLOR === 'true') {
582
+ return 1;
583
+ }
616
584
 
617
- const error = (...args) => console.error(...args);
585
+ if (env.FORCE_COLOR === 'false') {
586
+ return 0;
587
+ }
618
588
 
619
- const errorDisabled = () => {};
589
+ return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
590
+ }
591
+ }
620
592
 
621
- /* globals WorkerGlobalScope, DedicatedWorkerGlobalScope, SharedWorkerGlobalScope, ServiceWorkerGlobalScope */
593
+ function translateLevel(level) {
594
+ if (level === 0) {
595
+ return false;
596
+ }
622
597
 
623
- const isBrowser = globalThis.window?.document !== undefined;
598
+ return {
599
+ level,
600
+ hasBasic: true,
601
+ has256: level >= 2,
602
+ has16m: level >= 3,
603
+ };
604
+ }
624
605
 
625
- globalThis.process?.versions?.node !== undefined;
606
+ function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {
607
+ const noFlagForceColor = envForceColor();
608
+ if (noFlagForceColor !== undefined) {
609
+ flagForceColor = noFlagForceColor;
610
+ }
626
611
 
627
- globalThis.process?.versions?.bun !== undefined;
612
+ const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
628
613
 
629
- globalThis.Deno?.version?.deno !== undefined;
614
+ if (forceColor === 0) {
615
+ return 0;
616
+ }
630
617
 
631
- globalThis.process?.versions?.electron !== undefined;
618
+ if (sniffFlags) {
619
+ if (hasFlag('color=16m')
620
+ || hasFlag('color=full')
621
+ || hasFlag('color=truecolor')) {
622
+ return 3;
623
+ }
632
624
 
633
- globalThis.navigator?.userAgent?.includes('jsdom') === true;
625
+ if (hasFlag('color=256')) {
626
+ return 2;
627
+ }
628
+ }
634
629
 
635
- typeof WorkerGlobalScope !== 'undefined' && globalThis instanceof WorkerGlobalScope;
630
+ // Check for Azure DevOps pipelines.
631
+ // Has to be above the `!streamIsTTY` check.
632
+ if ('TF_BUILD' in env && 'AGENT_NAME' in env) {
633
+ return 1;
634
+ }
636
635
 
637
- typeof DedicatedWorkerGlobalScope !== 'undefined' && globalThis instanceof DedicatedWorkerGlobalScope;
636
+ if (haveStream && !streamIsTTY && forceColor === undefined) {
637
+ return 0;
638
+ }
638
639
 
639
- typeof SharedWorkerGlobalScope !== 'undefined' && globalThis instanceof SharedWorkerGlobalScope;
640
+ const min = forceColor || 0;
640
641
 
641
- typeof ServiceWorkerGlobalScope !== 'undefined' && globalThis instanceof ServiceWorkerGlobalScope;
642
+ if (env.TERM === 'dumb') {
643
+ return min;
644
+ }
642
645
 
643
- // Note: I'm intentionally not DRYing up the other variables to keep them "lazy".
644
- const platform = globalThis.navigator?.userAgentData?.platform;
646
+ if (process$1.platform === 'win32') {
647
+ // Windows 10 build 10586 is the first Windows release that supports 256 colors.
648
+ // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
649
+ const osRelease = os.release().split('.');
650
+ if (
651
+ Number(osRelease[0]) >= 10
652
+ && Number(osRelease[2]) >= 10_586
653
+ ) {
654
+ return Number(osRelease[2]) >= 14_931 ? 3 : 2;
655
+ }
645
656
 
646
- platform === 'macOS'
647
- || globalThis.navigator?.platform === 'MacIntel' // Even on Apple silicon Macs.
648
- || globalThis.navigator?.userAgent?.includes(' Mac ') === true
649
- || globalThis.process?.platform === 'darwin';
657
+ return 1;
658
+ }
650
659
 
651
- platform === 'Windows'
652
- || globalThis.navigator?.platform === 'Win32'
653
- || globalThis.process?.platform === 'win32';
660
+ if ('CI' in env) {
661
+ if ('GITHUB_ACTIONS' in env || 'GITEA_ACTIONS' in env) {
662
+ return 3;
663
+ }
654
664
 
655
- platform === 'Linux'
656
- || globalThis.navigator?.platform?.startsWith('Linux') === true
657
- || globalThis.navigator?.userAgent?.includes(' Linux ') === true
658
- || globalThis.process?.platform === 'linux';
665
+ if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
666
+ return 1;
667
+ }
659
668
 
660
- platform === 'Android'
661
- || globalThis.navigator?.platform === 'Android'
662
- || globalThis.navigator?.userAgent?.includes(' Android ') === true
663
- || globalThis.process?.platform === 'android';
669
+ return min;
670
+ }
664
671
 
665
- const ESC = '\u001B[';
672
+ if ('TEAMCITY_VERSION' in env) {
673
+ return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
674
+ }
666
675
 
667
- !isBrowser && process$1.env.TERM_PROGRAM === 'Apple_Terminal';
668
- const isWindows$3 = !isBrowser && process$1.platform === 'win32';
676
+ if (env.COLORTERM === 'truecolor') {
677
+ return 3;
678
+ }
669
679
 
670
- isBrowser ? () => {
671
- throw new Error('`process.cwd()` only works in Node.js, not the browser.');
672
- } : process$1.cwd;
680
+ if (env.TERM === 'xterm-kitty') {
681
+ return 3;
682
+ }
673
683
 
674
- const cursorUp = (count = 1) => ESC + count + 'A';
684
+ if ('TERM_PROGRAM' in env) {
685
+ const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
675
686
 
676
- const cursorLeft = ESC + 'G';
687
+ switch (env.TERM_PROGRAM) {
688
+ case 'iTerm.app': {
689
+ return version >= 3 ? 3 : 2;
690
+ }
677
691
 
678
- const eraseLines = count => {
679
- let clear = '';
692
+ case 'Apple_Terminal': {
693
+ return 2;
694
+ }
695
+ // No default
696
+ }
697
+ }
680
698
 
681
- for (let i = 0; i < count; i++) {
682
- clear += eraseLine + (i < count - 1 ? cursorUp() : '');
699
+ if (/-256(color)?$/i.test(env.TERM)) {
700
+ return 2;
683
701
  }
684
702
 
685
- if (count) {
686
- clear += cursorLeft;
703
+ if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
704
+ return 1;
687
705
  }
688
706
 
689
- return clear;
690
- };
691
- const eraseLine = ESC + '2K';
692
- const eraseScreen = ESC + '2J';
693
-
694
- const clearTerminal = isWindows$3
695
- ? `${eraseScreen}${ESC}0f`
696
- // 1. Erases the screen (Only done in case `2` is not supported)
697
- // 2. Erases the whole screen including scrollback buffer
698
- // 3. Moves cursor to the top-left position
699
- // More info: https://www.real-world-systems.com/docs/ANSIcode.html
700
- : `${eraseScreen}${ESC}3J${ESC}H`;
701
-
702
- /*
703
- * see also https://github.com/vadimdemedes/ink
704
- */
707
+ if ('COLORTERM' in env) {
708
+ return 1;
709
+ }
705
710
 
711
+ return min;
712
+ }
706
713
 
707
- const createDynamicLog = ({
708
- stream = process.stdout,
709
- clearTerminalAllowed,
710
- onVerticalOverflow = () => {},
711
- onWriteFromOutside = () => {},
712
- } = {}) => {
713
- const { columns = 80, rows = 24 } = stream;
714
- const dynamicLog = {
715
- destroyed: false,
716
- onVerticalOverflow,
717
- onWriteFromOutside,
718
- };
714
+ function createSupportsColor(stream, options = {}) {
715
+ const level = _supportsColor(stream, {
716
+ streamIsTTY: stream && stream.isTTY,
717
+ ...options,
718
+ });
719
719
 
720
- let lastOutput = "";
721
- let lastOutputFromOutside = "";
722
- let clearAttemptResult;
723
- let writing = false;
720
+ return translateLevel(level);
721
+ }
724
722
 
725
- const getErasePreviousOutput = () => {
726
- // nothing to clear
727
- if (!lastOutput) {
728
- return "";
729
- }
730
- if (clearAttemptResult !== undefined) {
731
- return "";
732
- }
723
+ const supportsColor = {
724
+ stdout: createSupportsColor({isTTY: tty.isatty(1)}),
725
+ stderr: createSupportsColor({isTTY: tty.isatty(2)}),
726
+ };
733
727
 
734
- const logLines = lastOutput.split(/\r\n|\r|\n/);
735
- let visualLineCount = 0;
736
- for (const logLine of logLines) {
737
- const width = stringWidth(logLine);
738
- if (width === 0) {
739
- visualLineCount++;
740
- } else {
741
- visualLineCount += Math.ceil(width / columns);
742
- }
743
- }
728
+ // https://github.com/Marak/colors.js/blob/master/lib/styles.js
729
+ // https://stackoverflow.com/a/75985833/2634179
730
+ const RESET = "\x1b[0m";
744
731
 
745
- if (visualLineCount > rows) {
746
- if (clearTerminalAllowed) {
747
- clearAttemptResult = true;
748
- return clearTerminal;
749
- }
750
- // the whole log cannot be cleared because it's vertically to long
751
- // (longer than terminal height)
752
- // readline.moveCursor cannot move cursor higher than screen height
753
- // it means we would only clear the visible part of the log
754
- // better keep the log untouched
755
- clearAttemptResult = false;
756
- dynamicLog.onVerticalOverflow();
757
- return "";
758
- }
732
+ const createAnsi = ({ supported }) => {
733
+ const ANSI = {
734
+ supported,
735
+
736
+ RED: "\x1b[31m",
737
+ GREEN: "\x1b[32m",
738
+ YELLOW: "\x1b[33m",
739
+ BLUE: "\x1b[34m",
740
+ MAGENTA: "\x1b[35m",
741
+ CYAN: "\x1b[36m",
742
+ GREY: "\x1b[90m",
743
+ color: (text, ANSI_COLOR) => {
744
+ return ANSI.supported && ANSI_COLOR
745
+ ? `${ANSI_COLOR}${text}${RESET}`
746
+ : text;
747
+ },
759
748
 
760
- clearAttemptResult = true;
761
- return eraseLines(visualLineCount);
749
+ BOLD: "\x1b[1m",
750
+ UNDERLINE: "\x1b[4m",
751
+ STRIKE: "\x1b[9m",
752
+ effect: (text, ANSI_EFFECT) => {
753
+ return ANSI.supported && ANSI_EFFECT
754
+ ? `${ANSI_EFFECT}${text}${RESET}`
755
+ : text;
756
+ },
762
757
  };
763
758
 
764
- const update = (string) => {
765
- if (dynamicLog.destroyed) {
766
- throw new Error("Cannot write log after destroy");
767
- }
768
- let stringToWrite = string;
769
- if (lastOutput) {
770
- if (lastOutputFromOutside) {
771
- // We don't want to clear logs written by other code,
772
- // it makes output unreadable and might erase precious information
773
- // To detect this we put a spy on the stream.
774
- // The spy is required only if we actually wrote something in the stream
775
- // something else than this code has written in the stream
776
- // so we just write without clearing (append instead of replacing)
777
- lastOutput = "";
778
- lastOutputFromOutside = "";
779
- } else {
780
- stringToWrite = `${getErasePreviousOutput()}${string}`;
781
- }
782
- }
783
- writing = true;
784
- stream.write(stringToWrite);
785
- lastOutput = string;
786
- writing = false;
787
- clearAttemptResult = undefined;
788
- };
759
+ return ANSI;
760
+ };
789
761
 
790
- const clearDuringFunctionCall = (
791
- callback,
792
- ouputAfterCallback = lastOutput,
793
- ) => {
794
- // 1. Erase the current log
795
- // 2. Call callback (expect to write something on stdout)
796
- // 3. Restore the current log
797
- // During step 2. we expect a "write from outside" so we uninstall
798
- // the stream spy during function call
799
- update("");
762
+ const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic;
800
763
 
801
- writing = true;
802
- callback();
803
- writing = false;
764
+ const ANSI = createAnsi({
765
+ supported:
766
+ processSupportsBasicColor ||
767
+ // GitHub workflow does support ANSI but "supports-color" returns false
768
+ // because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
769
+ (process.env.GITHUB_WORKFLOW &&
770
+ // Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
771
+ // in unit test we use process.env.FORCE_COLOR = 'false' to fake
772
+ // that colors are not supported. Let it have priority
773
+ process.env.FORCE_COLOR !== "false"),
774
+ });
804
775
 
805
- update(ouputAfterCallback);
806
- };
776
+ function isUnicodeSupported() {
777
+ if (process$1.platform !== 'win32') {
778
+ return process$1.env.TERM !== 'linux'; // Linux console (kernel)
779
+ }
807
780
 
808
- const writeFromOutsideEffect = (value) => {
809
- if (!lastOutput) {
810
- // we don't care if the log never wrote anything
811
- // or if last update() wrote an empty string
812
- return;
813
- }
814
- if (writing) {
815
- return;
816
- }
817
- lastOutputFromOutside = value;
818
- dynamicLog.onWriteFromOutside(value);
819
- };
781
+ return Boolean(process$1.env.WT_SESSION) // Windows Terminal
782
+ || Boolean(process$1.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
783
+ || process$1.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
784
+ || process$1.env.TERM_PROGRAM === 'Terminus-Sublime'
785
+ || process$1.env.TERM_PROGRAM === 'vscode'
786
+ || process$1.env.TERM === 'xterm-256color'
787
+ || process$1.env.TERM === 'alacritty'
788
+ || process$1.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
789
+ }
820
790
 
821
- let removeStreamSpy;
822
- if (stream === process.stdout) {
823
- const removeStdoutSpy = spyStreamOutput(
824
- process.stdout,
825
- writeFromOutsideEffect,
826
- );
827
- const removeStderrSpy = spyStreamOutput(
828
- process.stderr,
829
- writeFromOutsideEffect,
830
- );
831
- removeStreamSpy = () => {
832
- removeStdoutSpy();
833
- removeStderrSpy();
834
- };
835
- } else {
836
- removeStreamSpy = spyStreamOutput(stream, writeFromOutsideEffect);
837
- }
791
+ // see also https://github.com/sindresorhus/figures
838
792
 
839
- const destroy = () => {
840
- dynamicLog.destroyed = true;
841
- if (removeStreamSpy) {
842
- removeStreamSpy();
843
- removeStreamSpy = null;
844
- lastOutput = "";
845
- lastOutputFromOutside = "";
846
- }
793
+ const createUnicode = ({ supported, ANSI }) => {
794
+ const UNICODE = {
795
+ supported,
796
+ get COMMAND_RAW() {
797
+ return UNICODE.supported ? `❯` : `>`;
798
+ },
799
+ get OK_RAW() {
800
+ return UNICODE.supported ? `✔` : `√`;
801
+ },
802
+ get FAILURE_RAW() {
803
+ return UNICODE.supported ? `✖` : `×`;
804
+ },
805
+ get DEBUG_RAW() {
806
+ return UNICODE.supported ? `◆` : `♦`;
807
+ },
808
+ get INFO_RAW() {
809
+ return UNICODE.supported ? `ℹ` : `i`;
810
+ },
811
+ get WARNING_RAW() {
812
+ return UNICODE.supported ? `⚠` : `‼`;
813
+ },
814
+ get CIRCLE_CROSS_RAW() {
815
+ return UNICODE.supported ? `ⓧ` : `(×)`;
816
+ },
817
+ get COMMAND() {
818
+ return ANSI.color(UNICODE.COMMAND_RAW, ANSI.GREY); // ANSI_MAGENTA)
819
+ },
820
+ get OK() {
821
+ return ANSI.color(UNICODE.OK_RAW, ANSI.GREEN);
822
+ },
823
+ get FAILURE() {
824
+ return ANSI.color(UNICODE.FAILURE_RAW, ANSI.RED);
825
+ },
826
+ get DEBUG() {
827
+ return ANSI.color(UNICODE.DEBUG_RAW, ANSI.GREY);
828
+ },
829
+ get INFO() {
830
+ return ANSI.color(UNICODE.INFO_RAW, ANSI.BLUE);
831
+ },
832
+ get WARNING() {
833
+ return ANSI.color(UNICODE.WARNING_RAW, ANSI.YELLOW);
834
+ },
835
+ get CIRCLE_CROSS() {
836
+ return ANSI.color(UNICODE.CIRCLE_CROSS_RAW, ANSI.RED);
837
+ },
838
+ get ELLIPSIS() {
839
+ return UNICODE.supported ? `…` : `...`;
840
+ },
847
841
  };
848
-
849
- Object.assign(dynamicLog, {
850
- update,
851
- destroy,
852
- stream,
853
- clearDuringFunctionCall,
854
- });
855
- return dynamicLog;
842
+ return UNICODE;
856
843
  };
857
844
 
858
- // maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
859
- // the problem with listening data on stdout
860
- // is that node.js will later throw error if stream gets closed
861
- // while something listening data on it
862
- const spyStreamOutput = (stream, callback) => {
863
- const originalWrite = stream.write;
864
-
865
- let output = "";
866
- let installed = true;
867
-
868
- stream.write = function (...args /* chunk, encoding, callback */) {
869
- output += args;
870
- callback(output);
871
- return originalWrite.call(stream, ...args);
872
- };
845
+ const UNICODE = createUnicode({
846
+ supported: isUnicodeSupported(),
847
+ ANSI,
848
+ });
873
849
 
874
- const uninstall = () => {
875
- if (!installed) {
876
- return;
877
- }
878
- stream.write = originalWrite;
879
- installed = false;
880
- };
850
+ const setRoundedPrecision = (
851
+ number,
852
+ { decimals = 1, decimalsWhenSmall = decimals } = {},
853
+ ) => {
854
+ return setDecimalsPrecision(number, {
855
+ decimals,
856
+ decimalsWhenSmall,
857
+ transform: Math.round,
858
+ });
859
+ };
881
860
 
882
- return () => {
883
- uninstall();
884
- return output;
885
- };
886
- };
887
-
888
- // From: https://github.com/sindresorhus/has-flag/blob/main/index.js
889
- /// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) {
890
- function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process$1.argv) {
891
- const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
892
- const position = argv.indexOf(prefix + flag);
893
- const terminatorPosition = argv.indexOf('--');
894
- return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
895
- }
896
-
897
- const {env} = process$1;
898
-
899
- let flagForceColor;
900
- if (
901
- hasFlag('no-color')
902
- || hasFlag('no-colors')
903
- || hasFlag('color=false')
904
- || hasFlag('color=never')
905
- ) {
906
- flagForceColor = 0;
907
- } else if (
908
- hasFlag('color')
909
- || hasFlag('colors')
910
- || hasFlag('color=true')
911
- || hasFlag('color=always')
912
- ) {
913
- flagForceColor = 1;
914
- }
915
-
916
- function envForceColor() {
917
- if ('FORCE_COLOR' in env) {
918
- if (env.FORCE_COLOR === 'true') {
919
- return 1;
920
- }
921
-
922
- if (env.FORCE_COLOR === 'false') {
923
- return 0;
924
- }
925
-
926
- return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
927
- }
928
- }
929
-
930
- function translateLevel(level) {
931
- if (level === 0) {
932
- return false;
933
- }
934
-
935
- return {
936
- level,
937
- hasBasic: true,
938
- has256: level >= 2,
939
- has16m: level >= 3,
940
- };
941
- }
942
-
943
- function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {
944
- const noFlagForceColor = envForceColor();
945
- if (noFlagForceColor !== undefined) {
946
- flagForceColor = noFlagForceColor;
947
- }
948
-
949
- const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
950
-
951
- if (forceColor === 0) {
952
- return 0;
953
- }
954
-
955
- if (sniffFlags) {
956
- if (hasFlag('color=16m')
957
- || hasFlag('color=full')
958
- || hasFlag('color=truecolor')) {
959
- return 3;
960
- }
961
-
962
- if (hasFlag('color=256')) {
963
- return 2;
964
- }
965
- }
966
-
967
- // Check for Azure DevOps pipelines.
968
- // Has to be above the `!streamIsTTY` check.
969
- if ('TF_BUILD' in env && 'AGENT_NAME' in env) {
970
- return 1;
971
- }
972
-
973
- if (haveStream && !streamIsTTY && forceColor === undefined) {
974
- return 0;
975
- }
976
-
977
- const min = forceColor || 0;
978
-
979
- if (env.TERM === 'dumb') {
980
- return min;
981
- }
982
-
983
- if (process$1.platform === 'win32') {
984
- // Windows 10 build 10586 is the first Windows release that supports 256 colors.
985
- // Windows 10 build 14931 is the first release that supports 16m/TrueColor.
986
- const osRelease = os.release().split('.');
987
- if (
988
- Number(osRelease[0]) >= 10
989
- && Number(osRelease[2]) >= 10_586
990
- ) {
991
- return Number(osRelease[2]) >= 14_931 ? 3 : 2;
992
- }
993
-
994
- return 1;
995
- }
996
-
997
- if ('CI' in env) {
998
- if ('GITHUB_ACTIONS' in env || 'GITEA_ACTIONS' in env) {
999
- return 3;
1000
- }
1001
-
1002
- if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
1003
- return 1;
1004
- }
1005
-
1006
- return min;
1007
- }
1008
-
1009
- if ('TEAMCITY_VERSION' in env) {
1010
- return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
1011
- }
1012
-
1013
- if (env.COLORTERM === 'truecolor') {
1014
- return 3;
1015
- }
1016
-
1017
- if (env.TERM === 'xterm-kitty') {
1018
- return 3;
1019
- }
1020
-
1021
- if ('TERM_PROGRAM' in env) {
1022
- const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
1023
-
1024
- switch (env.TERM_PROGRAM) {
1025
- case 'iTerm.app': {
1026
- return version >= 3 ? 3 : 2;
1027
- }
1028
-
1029
- case 'Apple_Terminal': {
1030
- return 2;
1031
- }
1032
- // No default
1033
- }
1034
- }
1035
-
1036
- if (/-256(color)?$/i.test(env.TERM)) {
1037
- return 2;
1038
- }
1039
-
1040
- if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
1041
- return 1;
1042
- }
1043
-
1044
- if ('COLORTERM' in env) {
1045
- return 1;
1046
- }
1047
-
1048
- return min;
1049
- }
1050
-
1051
- function createSupportsColor(stream, options = {}) {
1052
- const level = _supportsColor(stream, {
1053
- streamIsTTY: stream && stream.isTTY,
1054
- ...options,
1055
- });
1056
-
1057
- return translateLevel(level);
1058
- }
1059
-
1060
- const supportsColor = {
1061
- stdout: createSupportsColor({isTTY: tty.isatty(1)}),
1062
- stderr: createSupportsColor({isTTY: tty.isatty(2)}),
1063
- };
1064
-
1065
- const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic;
1066
- // https://github.com/Marak/colors.js/blob/master/lib/styles.js
1067
- // https://stackoverflow.com/a/75985833/2634179
1068
- const RESET = "\x1b[0m";
1069
-
1070
- const ANSI = {
1071
- supported: processSupportsBasicColor,
1072
-
1073
- RED: "\x1b[31m",
1074
- GREEN: "\x1b[32m",
1075
- YELLOW: "\x1b[33m",
1076
- BLUE: "\x1b[34m",
1077
- MAGENTA: "\x1b[35m",
1078
- CYAN: "\x1b[36m",
1079
- GREY: "\x1b[90m",
1080
- color: (text, ANSI_COLOR) => {
1081
- return ANSI.supported && ANSI_COLOR ? `${ANSI_COLOR}${text}${RESET}` : text;
1082
- },
1083
-
1084
- BOLD: "\x1b[1m",
1085
- UNDERLINE: "\x1b[4m",
1086
- STRIKE: "\x1b[9m",
1087
- effect: (text, ANSI_EFFECT) => {
1088
- return ANSI.supported && ANSI_EFFECT
1089
- ? `${ANSI_EFFECT}${text}${RESET}`
1090
- : text;
1091
- },
1092
- };
1093
-
1094
- // GitHub workflow does support ANSI but "supports-color" returns false
1095
- // because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
1096
- if (
1097
- process.env.GITHUB_WORKFLOW &&
1098
- // Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
1099
- // in unit test we use process.env.FORCE_COLOR = 'false' to fake
1100
- // that colors are not supported. Let it have priority
1101
- process.env.FORCE_COLOR !== "false"
1102
- ) {
1103
- ANSI.supported = true;
1104
- }
1105
-
1106
- const setRoundedPrecision = (
1107
- number,
1108
- { decimals = 1, decimalsWhenSmall = decimals } = {},
1109
- ) => {
1110
- return setDecimalsPrecision(number, {
1111
- decimals,
1112
- decimalsWhenSmall,
1113
- transform: Math.round,
1114
- });
1115
- };
1116
-
1117
- const setPrecision = (
1118
- number,
1119
- { decimals = 1, decimalsWhenSmall = decimals } = {},
1120
- ) => {
1121
- return setDecimalsPrecision(number, {
1122
- decimals,
1123
- decimalsWhenSmall,
1124
- transform: parseInt,
1125
- });
861
+ const setPrecision = (
862
+ number,
863
+ { decimals = 1, decimalsWhenSmall = decimals } = {},
864
+ ) => {
865
+ return setDecimalsPrecision(number, {
866
+ decimals,
867
+ decimalsWhenSmall,
868
+ transform: parseInt,
869
+ });
1126
870
  };
1127
871
 
1128
872
  const setDecimalsPrecision = (
@@ -1292,99 +1036,13 @@ const parseMs = (ms) => {
1292
1036
  };
1293
1037
  };
1294
1038
 
1295
- function isUnicodeSupported() {
1296
- if (process$1.platform !== 'win32') {
1297
- return process$1.env.TERM !== 'linux'; // Linux console (kernel)
1298
- }
1299
-
1300
- return Boolean(process$1.env.WT_SESSION) // Windows Terminal
1301
- || Boolean(process$1.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
1302
- || process$1.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
1303
- || process$1.env.TERM_PROGRAM === 'Terminus-Sublime'
1304
- || process$1.env.TERM_PROGRAM === 'vscode'
1305
- || process$1.env.TERM === 'xterm-256color'
1306
- || process$1.env.TERM === 'alacritty'
1307
- || process$1.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
1308
- }
1309
-
1310
- // see also https://github.com/sindresorhus/figures
1311
-
1312
-
1313
- const UNICODE = {
1314
- supported: isUnicodeSupported(),
1315
-
1316
- get COMMAND_RAW() {
1317
- return UNICODE.supported ? `❯` : `>`;
1318
- },
1319
- get OK_RAW() {
1320
- return UNICODE.supported ? `✔` : `√`;
1321
- },
1322
- get FAILURE_RAW() {
1323
- return UNICODE.supported ? `✖` : `×`;
1324
- },
1325
- get DEBUG_RAW() {
1326
- return UNICODE.supported ? `◆` : `♦`;
1327
- },
1328
- get INFO_RAW() {
1329
- return UNICODE.supported ? `ℹ` : `i`;
1330
- },
1331
- get WARNING_RAW() {
1332
- return UNICODE.supported ? `⚠` : `‼`;
1333
- },
1334
- get CIRCLE_CROSS_RAW() {
1335
- return UNICODE.supported ? `ⓧ` : `(×)`;
1336
- },
1337
- get COMMAND() {
1338
- return ANSI.color(UNICODE.COMMAND_RAW, ANSI.GREY); // ANSI_MAGENTA)
1339
- },
1340
- get OK() {
1341
- return ANSI.color(UNICODE.OK_RAW, ANSI.GREEN);
1342
- },
1343
- get FAILURE() {
1344
- return ANSI.color(UNICODE.FAILURE_RAW, ANSI.RED);
1345
- },
1346
- get DEBUG() {
1347
- return ANSI.color(UNICODE.DEBUG_RAW, ANSI.GREY);
1348
- },
1349
- get INFO() {
1350
- return ANSI.color(UNICODE.INFO_RAW, ANSI.BLUE);
1351
- },
1352
- get WARNING() {
1353
- return ANSI.color(UNICODE.WARNING_RAW, ANSI.YELLOW);
1354
- },
1355
- get CIRCLE_CROSS() {
1356
- return ANSI.color(UNICODE.CIRCLE_CROSS_RAW, ANSI.RED);
1357
- },
1358
- get ELLIPSIS() {
1359
- return UNICODE.supported ? `…` : `...`;
1360
- },
1039
+ const humanizeFileSize = (numberOfBytes, { decimals, short } = {}) => {
1040
+ return inspectBytes(numberOfBytes, { decimals, short });
1361
1041
  };
1362
1042
 
1363
- const createDetailedMessage = (message, details = {}) => {
1364
- let string = `${message}`;
1365
-
1366
- Object.keys(details).forEach((key) => {
1367
- const value = details[key];
1368
- string += `
1369
- --- ${key} ---
1370
- ${
1371
- Array.isArray(value)
1372
- ? value.join(`
1373
- `)
1374
- : value
1375
- }`;
1376
- });
1377
-
1378
- return string;
1379
- };
1380
-
1381
- const humanizeFileSize = (numberOfBytes, { decimals, short } = {}) => {
1382
- return inspectBytes(numberOfBytes, { decimals, short });
1383
- };
1384
-
1385
- const humanizeMemory = (metricValue, { decimals, short } = {}) => {
1386
- return inspectBytes(metricValue, { decimals, fixedDecimals: true, short });
1387
- };
1043
+ const humanizeMemory = (metricValue, { decimals, short } = {}) => {
1044
+ return inspectBytes(metricValue, { decimals, fixedDecimals: true, short });
1045
+ };
1388
1046
 
1389
1047
  const inspectBytes = (
1390
1048
  number,
@@ -1579,6 +1237,361 @@ const fillLeft$1 = (value, biggestValue, char = " ") => {
1579
1237
  return padded;
1580
1238
  };
1581
1239
 
1240
+ const LOG_LEVEL_OFF = "off";
1241
+
1242
+ const LOG_LEVEL_DEBUG = "debug";
1243
+
1244
+ const LOG_LEVEL_INFO = "info";
1245
+
1246
+ const LOG_LEVEL_WARN = "warn";
1247
+
1248
+ const LOG_LEVEL_ERROR = "error";
1249
+
1250
+ const createLogger = ({ logLevel = LOG_LEVEL_INFO } = {}) => {
1251
+ if (logLevel === LOG_LEVEL_DEBUG) {
1252
+ return {
1253
+ level: "debug",
1254
+ levels: { debug: true, info: true, warn: true, error: true },
1255
+ debug,
1256
+ info,
1257
+ warn,
1258
+ error,
1259
+ };
1260
+ }
1261
+ if (logLevel === LOG_LEVEL_INFO) {
1262
+ return {
1263
+ level: "info",
1264
+ levels: { debug: false, info: true, warn: true, error: true },
1265
+ debug: debugDisabled,
1266
+ info,
1267
+ warn,
1268
+ error,
1269
+ };
1270
+ }
1271
+ if (logLevel === LOG_LEVEL_WARN) {
1272
+ return {
1273
+ level: "warn",
1274
+ levels: { debug: false, info: false, warn: true, error: true },
1275
+ debug: debugDisabled,
1276
+ info: infoDisabled,
1277
+ warn,
1278
+ error,
1279
+ };
1280
+ }
1281
+ if (logLevel === LOG_LEVEL_ERROR) {
1282
+ return {
1283
+ level: "error",
1284
+ levels: { debug: false, info: false, warn: false, error: true },
1285
+ debug: debugDisabled,
1286
+ info: infoDisabled,
1287
+ warn: warnDisabled,
1288
+ error,
1289
+ };
1290
+ }
1291
+ if (logLevel === LOG_LEVEL_OFF) {
1292
+ return {
1293
+ level: "off",
1294
+ levels: { debug: false, info: false, warn: false, error: false },
1295
+ debug: debugDisabled,
1296
+ info: infoDisabled,
1297
+ warn: warnDisabled,
1298
+ error: errorDisabled,
1299
+ };
1300
+ }
1301
+ throw new Error(`unexpected logLevel.
1302
+ --- logLevel ---
1303
+ ${logLevel}
1304
+ --- allowed log levels ---
1305
+ ${LOG_LEVEL_OFF}
1306
+ ${LOG_LEVEL_ERROR}
1307
+ ${LOG_LEVEL_WARN}
1308
+ ${LOG_LEVEL_INFO}
1309
+ ${LOG_LEVEL_DEBUG}`);
1310
+ };
1311
+
1312
+ const debug = (...args) => console.debug(...args);
1313
+
1314
+ const debugDisabled = () => {};
1315
+
1316
+ const info = (...args) => console.info(...args);
1317
+
1318
+ const infoDisabled = () => {};
1319
+
1320
+ const warn = (...args) => console.warn(...args);
1321
+
1322
+ const warnDisabled = () => {};
1323
+
1324
+ const error = (...args) => console.error(...args);
1325
+
1326
+ const errorDisabled = () => {};
1327
+
1328
+ /* globals WorkerGlobalScope, DedicatedWorkerGlobalScope, SharedWorkerGlobalScope, ServiceWorkerGlobalScope */
1329
+
1330
+ const isBrowser = globalThis.window?.document !== undefined;
1331
+
1332
+ globalThis.process?.versions?.node !== undefined;
1333
+
1334
+ globalThis.process?.versions?.bun !== undefined;
1335
+
1336
+ globalThis.Deno?.version?.deno !== undefined;
1337
+
1338
+ globalThis.process?.versions?.electron !== undefined;
1339
+
1340
+ globalThis.navigator?.userAgent?.includes('jsdom') === true;
1341
+
1342
+ typeof WorkerGlobalScope !== 'undefined' && globalThis instanceof WorkerGlobalScope;
1343
+
1344
+ typeof DedicatedWorkerGlobalScope !== 'undefined' && globalThis instanceof DedicatedWorkerGlobalScope;
1345
+
1346
+ typeof SharedWorkerGlobalScope !== 'undefined' && globalThis instanceof SharedWorkerGlobalScope;
1347
+
1348
+ typeof ServiceWorkerGlobalScope !== 'undefined' && globalThis instanceof ServiceWorkerGlobalScope;
1349
+
1350
+ // Note: I'm intentionally not DRYing up the other variables to keep them "lazy".
1351
+ const platform = globalThis.navigator?.userAgentData?.platform;
1352
+
1353
+ platform === 'macOS'
1354
+ || globalThis.navigator?.platform === 'MacIntel' // Even on Apple silicon Macs.
1355
+ || globalThis.navigator?.userAgent?.includes(' Mac ') === true
1356
+ || globalThis.process?.platform === 'darwin';
1357
+
1358
+ platform === 'Windows'
1359
+ || globalThis.navigator?.platform === 'Win32'
1360
+ || globalThis.process?.platform === 'win32';
1361
+
1362
+ platform === 'Linux'
1363
+ || globalThis.navigator?.platform?.startsWith('Linux') === true
1364
+ || globalThis.navigator?.userAgent?.includes(' Linux ') === true
1365
+ || globalThis.process?.platform === 'linux';
1366
+
1367
+ platform === 'Android'
1368
+ || globalThis.navigator?.platform === 'Android'
1369
+ || globalThis.navigator?.userAgent?.includes(' Android ') === true
1370
+ || globalThis.process?.platform === 'android';
1371
+
1372
+ const ESC = '\u001B[';
1373
+
1374
+ !isBrowser && process$1.env.TERM_PROGRAM === 'Apple_Terminal';
1375
+ const isWindows$3 = !isBrowser && process$1.platform === 'win32';
1376
+
1377
+ isBrowser ? () => {
1378
+ throw new Error('`process.cwd()` only works in Node.js, not the browser.');
1379
+ } : process$1.cwd;
1380
+
1381
+ const cursorUp = (count = 1) => ESC + count + 'A';
1382
+
1383
+ const cursorLeft = ESC + 'G';
1384
+
1385
+ const eraseLines = count => {
1386
+ let clear = '';
1387
+
1388
+ for (let i = 0; i < count; i++) {
1389
+ clear += eraseLine + (i < count - 1 ? cursorUp() : '');
1390
+ }
1391
+
1392
+ if (count) {
1393
+ clear += cursorLeft;
1394
+ }
1395
+
1396
+ return clear;
1397
+ };
1398
+ const eraseLine = ESC + '2K';
1399
+ const eraseScreen = ESC + '2J';
1400
+
1401
+ const clearTerminal = isWindows$3
1402
+ ? `${eraseScreen}${ESC}0f`
1403
+ // 1. Erases the screen (Only done in case `2` is not supported)
1404
+ // 2. Erases the whole screen including scrollback buffer
1405
+ // 3. Moves cursor to the top-left position
1406
+ // More info: https://www.real-world-systems.com/docs/ANSIcode.html
1407
+ : `${eraseScreen}${ESC}3J${ESC}H`;
1408
+
1409
+ /*
1410
+ * see also https://github.com/vadimdemedes/ink
1411
+ */
1412
+
1413
+
1414
+ const createDynamicLog = ({
1415
+ stream = process.stdout,
1416
+ clearTerminalAllowed,
1417
+ onVerticalOverflow = () => {},
1418
+ onWriteFromOutside = () => {},
1419
+ } = {}) => {
1420
+ const { columns = 80, rows = 24 } = stream;
1421
+ const dynamicLog = {
1422
+ destroyed: false,
1423
+ onVerticalOverflow,
1424
+ onWriteFromOutside,
1425
+ };
1426
+
1427
+ let lastOutput = "";
1428
+ let lastOutputFromOutside = "";
1429
+ let clearAttemptResult;
1430
+ let writing = false;
1431
+
1432
+ const getErasePreviousOutput = () => {
1433
+ // nothing to clear
1434
+ if (!lastOutput) {
1435
+ return "";
1436
+ }
1437
+ if (clearAttemptResult !== undefined) {
1438
+ return "";
1439
+ }
1440
+
1441
+ const logLines = lastOutput.split(/\r\n|\r|\n/);
1442
+ let visualLineCount = 0;
1443
+ for (const logLine of logLines) {
1444
+ const width = stringWidth(logLine);
1445
+ if (width === 0) {
1446
+ visualLineCount++;
1447
+ } else {
1448
+ visualLineCount += Math.ceil(width / columns);
1449
+ }
1450
+ }
1451
+
1452
+ if (visualLineCount > rows) {
1453
+ if (clearTerminalAllowed) {
1454
+ clearAttemptResult = true;
1455
+ return clearTerminal;
1456
+ }
1457
+ // the whole log cannot be cleared because it's vertically to long
1458
+ // (longer than terminal height)
1459
+ // readline.moveCursor cannot move cursor higher than screen height
1460
+ // it means we would only clear the visible part of the log
1461
+ // better keep the log untouched
1462
+ clearAttemptResult = false;
1463
+ dynamicLog.onVerticalOverflow();
1464
+ return "";
1465
+ }
1466
+
1467
+ clearAttemptResult = true;
1468
+ return eraseLines(visualLineCount);
1469
+ };
1470
+
1471
+ const update = (string) => {
1472
+ if (dynamicLog.destroyed) {
1473
+ throw new Error("Cannot write log after destroy");
1474
+ }
1475
+ let stringToWrite = string;
1476
+ if (lastOutput) {
1477
+ if (lastOutputFromOutside) {
1478
+ // We don't want to clear logs written by other code,
1479
+ // it makes output unreadable and might erase precious information
1480
+ // To detect this we put a spy on the stream.
1481
+ // The spy is required only if we actually wrote something in the stream
1482
+ // something else than this code has written in the stream
1483
+ // so we just write without clearing (append instead of replacing)
1484
+ lastOutput = "";
1485
+ lastOutputFromOutside = "";
1486
+ } else {
1487
+ stringToWrite = `${getErasePreviousOutput()}${string}`;
1488
+ }
1489
+ }
1490
+ writing = true;
1491
+ stream.write(stringToWrite);
1492
+ lastOutput = string;
1493
+ writing = false;
1494
+ clearAttemptResult = undefined;
1495
+ };
1496
+
1497
+ const clearDuringFunctionCall = (
1498
+ callback,
1499
+ ouputAfterCallback = lastOutput,
1500
+ ) => {
1501
+ // 1. Erase the current log
1502
+ // 2. Call callback (expect to write something on stdout)
1503
+ // 3. Restore the current log
1504
+ // During step 2. we expect a "write from outside" so we uninstall
1505
+ // the stream spy during function call
1506
+ update("");
1507
+
1508
+ writing = true;
1509
+ callback();
1510
+ writing = false;
1511
+
1512
+ update(ouputAfterCallback);
1513
+ };
1514
+
1515
+ const writeFromOutsideEffect = (value) => {
1516
+ if (!lastOutput) {
1517
+ // we don't care if the log never wrote anything
1518
+ // or if last update() wrote an empty string
1519
+ return;
1520
+ }
1521
+ if (writing) {
1522
+ return;
1523
+ }
1524
+ lastOutputFromOutside = value;
1525
+ dynamicLog.onWriteFromOutside(value);
1526
+ };
1527
+
1528
+ let removeStreamSpy;
1529
+ if (stream === process.stdout) {
1530
+ const removeStdoutSpy = spyStreamOutput(
1531
+ process.stdout,
1532
+ writeFromOutsideEffect,
1533
+ );
1534
+ const removeStderrSpy = spyStreamOutput(
1535
+ process.stderr,
1536
+ writeFromOutsideEffect,
1537
+ );
1538
+ removeStreamSpy = () => {
1539
+ removeStdoutSpy();
1540
+ removeStderrSpy();
1541
+ };
1542
+ } else {
1543
+ removeStreamSpy = spyStreamOutput(stream, writeFromOutsideEffect);
1544
+ }
1545
+
1546
+ const destroy = () => {
1547
+ dynamicLog.destroyed = true;
1548
+ if (removeStreamSpy) {
1549
+ removeStreamSpy();
1550
+ removeStreamSpy = null;
1551
+ lastOutput = "";
1552
+ lastOutputFromOutside = "";
1553
+ }
1554
+ };
1555
+
1556
+ Object.assign(dynamicLog, {
1557
+ update,
1558
+ destroy,
1559
+ stream,
1560
+ clearDuringFunctionCall,
1561
+ });
1562
+ return dynamicLog;
1563
+ };
1564
+
1565
+ // maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
1566
+ // the problem with listening data on stdout
1567
+ // is that node.js will later throw error if stream gets closed
1568
+ // while something listening data on it
1569
+ const spyStreamOutput = (stream, callback) => {
1570
+ const originalWrite = stream.write;
1571
+
1572
+ let output = "";
1573
+ let installed = true;
1574
+
1575
+ stream.write = function (...args /* chunk, encoding, callback */) {
1576
+ output += args;
1577
+ callback(output);
1578
+ return originalWrite.call(stream, ...args);
1579
+ };
1580
+
1581
+ const uninstall = () => {
1582
+ if (!installed) {
1583
+ return;
1584
+ }
1585
+ stream.write = originalWrite;
1586
+ installed = false;
1587
+ };
1588
+
1589
+ return () => {
1590
+ uninstall();
1591
+ return output;
1592
+ };
1593
+ };
1594
+
1582
1595
  const urlToScheme = (url) => {
1583
1596
  const urlString = String(url);
1584
1597
  const colonIndex = urlString.indexOf(":");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/test",
3
- "version": "3.0.16",
3
+ "version": "3.0.17",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,13 +38,13 @@
38
38
  "dependencies": {
39
39
  "@c88/v8-coverage": "0.1.1",
40
40
  "@jsenv/abort": "4.3.0",
41
- "@jsenv/ast": "6.0.4",
42
- "@jsenv/filesystem": "4.6.6",
43
- "@jsenv/github-check-run": "0.1.11",
44
- "@jsenv/humanize": "1.1.0",
45
- "@jsenv/plugin-supervisor": "1.4.4",
41
+ "@jsenv/ast": "6.0.5",
42
+ "@jsenv/filesystem": "4.6.7",
43
+ "@jsenv/github-check-run": "0.1.12",
44
+ "@jsenv/humanize": "1.1.1",
45
+ "@jsenv/plugin-supervisor": "1.4.5",
46
46
  "@jsenv/url-meta": "8.4.0",
47
- "@jsenv/urls": "2.2.4",
47
+ "@jsenv/urls": "2.2.5",
48
48
  "@jsenv/utils": "2.1.1",
49
49
  "istanbul-lib-coverage": "3.2.2",
50
50
  "istanbul-lib-instrument": "6.0.3",