diff --git a/packages/cli/package.json b/packages/cli/package.json index d248e4a3a1..88a1c836c0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -52,7 +52,6 @@ "@types/chai-as-promised": "^7.1.3", "@types/express": "^4.17.14", "@types/fs-extra": "^8.0.1", - "@types/inquirer": "7.3.3", "@types/keytar": "^4.4.2", "@types/lodash": "^4.14.170", "@types/mocha": "^8.0.4", @@ -114,7 +113,6 @@ "express": "^4.19.2", "figures": "^3.2.0", "fs-extra": "^9.1.0", - "inquirer": "^7.3.3", "lodash": "^4.17.21", "node-machine-id": "^1.1.12", "open": "^8.2.1", diff --git a/packages/cli/pnpm-lock.yaml b/packages/cli/pnpm-lock.yaml index 42516eb633..e3d4afd659 100644 --- a/packages/cli/pnpm-lock.yaml +++ b/packages/cli/pnpm-lock.yaml @@ -59,9 +59,6 @@ dependencies: fs-extra: specifier: ^9.1.0 version: 9.1.0 - inquirer: - specifier: ^7.3.3 - version: 7.3.3 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -108,9 +105,6 @@ devDependencies: '@types/fs-extra': specifier: ^8.0.1 version: 8.0.1 - '@types/inquirer': - specifier: 7.3.3 - version: 7.3.3 '@types/keytar': specifier: ^4.4.2 version: 4.4.2 @@ -1011,13 +1005,6 @@ packages: resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} dev: true - /@types/inquirer@7.3.3: - resolution: {integrity: sha512-HhxyLejTHMfohAuhRun4csWigAMjXTmRyiJTU1Y/I1xmggikFMkOUoMQRlFm+zQcPEGHSs3io/0FAmNZf8EymQ==} - dependencies: - '@types/through': 0.0.33 - rxjs: 6.6.7 - dev: true - /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: true @@ -1116,12 +1103,6 @@ packages: resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} dev: true - /@types/through@0.0.33: - resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} - dependencies: - '@types/node': 14.14.21 - dev: true - /@types/underscore@1.11.0: resolution: {integrity: sha512-ipNAQLgRnG0EWN1cTtfdVHp5AyTW/PAMJ1PxLN4bAKSHbusSZbj48mIHiydQpN7GgQrYqwfnvZ573OVfJm5Nzg==} dev: true @@ -1911,6 +1892,7 @@ packages: engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 + dev: true /cli-spinners@2.9.2: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} @@ -1934,11 +1916,6 @@ packages: string-width: 4.2.3 dev: true - /cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - dev: false - /cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -3460,25 +3437,6 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} requiresBuild: true - /inquirer@7.3.3: - resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} - engines: {node: '>=8.0.0'} - dependencies: - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - external-editor: 3.1.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - run-async: 2.4.1 - rxjs: 6.6.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - dev: false - /internal-slot@1.0.6: resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} engines: {node: '>= 0.4'} @@ -4191,6 +4149,7 @@ packages: /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + dev: true /mimic-response@2.1.0: resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} @@ -4284,10 +4243,6 @@ packages: /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - /mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - dev: false - /mute-stream@1.0.0: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4487,6 +4442,7 @@ packages: engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 + dev: true /open@8.2.1: resolution: {integrity: sha512-rXILpcQlkF/QuFez2BJDf3GsqpjGKbkUUToAIGo9A0Q6ZkoSGogZJulrUdwRkrAsoQvoZsrjCYt8+zblOk7JQQ==} @@ -5002,6 +4958,7 @@ packages: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 + dev: true /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} @@ -5027,11 +4984,6 @@ packages: glob: 10.3.10 dev: true - /run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - dev: false - /run-async@3.0.0: resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} engines: {node: '>=0.12.0'} @@ -5043,12 +4995,6 @@ packages: queue-microtask: 1.2.3 dev: true - /rxjs@6.6.7: - resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} - engines: {npm: '>=2.0.0'} - dependencies: - tslib: 1.14.1 - /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: @@ -5633,6 +5579,7 @@ packages: /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} @@ -5705,6 +5652,7 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true /tslib@2.3.1: resolution: {integrity: sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==} diff --git a/packages/cli/src/colorize.ts b/packages/cli/src/colorize.ts index 811b3df03f..493d6317b7 100644 --- a/packages/cli/src/colorize.ts +++ b/packages/cli/src/colorize.ts @@ -14,7 +14,6 @@ export enum TextType { Important = "important", Details = "details", // secondary text Commands = "commands", // commands, parameters, system inputs - Spinner = "spinner", } export function colorize(message: string, type: TextType): string { @@ -39,8 +38,6 @@ export function colorize(message: string, type: TextType): string { return chalk.gray(message); case TextType.Commands: return chalk.blueBright(message); - case TextType.Spinner: - return chalk.yellowBright(message); } } diff --git a/packages/cli/src/spinner.ts b/packages/cli/src/spinner.ts deleted file mode 100644 index 3744fdf5c3..0000000000 --- a/packages/cli/src/spinner.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { TextType, colorize } from "./colorize"; - -const defaultSpinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; -const defaultTextType = TextType.Spinner; -const defaultRefreshInterval = 100; - -interface CustomizedSpinnerOptions { - spinnerFrames?: string[]; - textType?: TextType; - refreshInterval?: number; -} - -export class CustomizedSpinner { - public spinnerFrames: string[] = defaultSpinnerFrames; - public textType: TextType = defaultTextType; - public refreshInterval: number = defaultRefreshInterval; // refresh internal in milliseconds - private intervalId: NodeJS.Timeout | null = null; - - constructor(options: CustomizedSpinnerOptions = {}) { - if (options.spinnerFrames) { - this.spinnerFrames = options.spinnerFrames; - } - if (options.textType) { - this.textType = options.textType; - } - if (options.refreshInterval) { - this.refreshInterval = options.refreshInterval; - } - } - - public start(): void { - // hide cursor - process.stdout.write("\x1b[?25l"); - let currentFrameIndex = 0; - this.intervalId = setInterval(() => { - const frame = this.spinnerFrames[currentFrameIndex % this.spinnerFrames.length]; - const message = colorize(frame, this.textType); - process.stdout.write(`\r${message}`); - currentFrameIndex++; - }, this.refreshInterval); - } - - public stop(): void { - if (this.intervalId) { - clearInterval(this.intervalId); - this.intervalId = null; - // show cursor - process.stdout.write("\x1b[?25h"); - } - } -} diff --git a/packages/cli/src/userInteraction.ts b/packages/cli/src/userInteraction.ts index 4d35015478..ba4c32e97c 100644 --- a/packages/cli/src/userInteraction.ts +++ b/packages/cli/src/userInteraction.ts @@ -1,8 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { confirm, password } from "@inquirer/prompts"; -import { prompt } from "inquirer"; +import { confirm, password, input } from "@inquirer/prompts"; import { Colors, ConfirmConfig, @@ -46,7 +45,7 @@ import { cliSource } from "./constants"; import { CheckboxChoice, SelectChoice, checkbox, select } from "./prompts"; import { errors } from "./resource"; import { getColorizedString } from "./utils"; -import { CustomizedSpinner } from "./spinner"; + /// TODO: input can be undefined type ValidationType = (input: T) => string | boolean | Promise; @@ -114,17 +113,13 @@ class CLIUserInteraction implements UserInteraction { return ok(defaultValue || ""); } ScreenManager.pause(); - const answer = await prompt([ - { - type: "input", - name: name, - message: message, - default: defaultValue, - validate: validate, - }, - ]); + const answer = await input({ + message, + default: defaultValue, + validate, + }); ScreenManager.continue(); - return ok(answer[name]); + return ok(answer); } async password( @@ -436,8 +431,6 @@ class CLIUserInteraction implements UserInteraction { if (config.validation || config.additionalValidationOnAccept) { validationFunc = async (input: string) => { let res: string | undefined = undefined; - const spinner = new CustomizedSpinner(); - spinner.start(); if (config.validation) { res = await config.validation(input); } @@ -445,7 +438,6 @@ class CLIUserInteraction implements UserInteraction { if (!res && !!config.additionalValidationOnAccept) { res = await config.additionalValidationOnAccept(input); } - spinner.stop(); return res; }; } diff --git a/packages/cli/tests/unit/colorize.tests.ts b/packages/cli/tests/unit/colorize.tests.ts index 86c1e31d7f..6b6cb5593f 100644 --- a/packages/cli/tests/unit/colorize.tests.ts +++ b/packages/cli/tests/unit/colorize.tests.ts @@ -57,9 +57,6 @@ describe("colorize", () => { it("colorize - Commands", async () => { colorize("test", TextType.Commands); }); - it("colorize - Spinner", async () => { - colorize("test", TextType.Spinner); - }); it("replace template string", async () => { const template = "test %s"; const result = replaceTemplateString(template, "test"); diff --git a/packages/cli/tests/unit/spinner.tests.ts b/packages/cli/tests/unit/spinner.tests.ts deleted file mode 100644 index e7672b874d..0000000000 --- a/packages/cli/tests/unit/spinner.tests.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { expect } from "chai"; -import "mocha"; -import sinon from "sinon"; -import { CustomizedSpinner } from "../../src/spinner"; -import { TextType } from "../../src/colorize"; - -describe("CustomizedSpinner", function () { - let clock: sinon.SinonFakeTimers; - let writeStub: sinon.SinonStub; - - beforeEach(() => { - clock = sinon.useFakeTimers(); - writeStub = sinon.stub(process.stdout, "write"); - }); - - afterEach(() => { - clock.restore(); - writeStub.restore(); - }); - - describe("should correctly cycle through spinner frames on start", async () => { - it("", async () => { - const spinner = new CustomizedSpinner(); - spinner.start(); - - clock.tick(spinner.refreshInterval * 3); - - expect(writeStub.callCount).to.equal(4); - expect(writeStub.lastCall.args[0]).to.include(spinner.spinnerFrames[2]); - - spinner.stop(); - }); - }); - - describe("should hide and show the cursor on start and stop", async () => { - it("", async () => { - const spinner = new CustomizedSpinner(); - spinner.start(); - - expect(writeStub.firstCall.args[0]).to.equal("\x1b[?25l"); - - spinner.stop(); - - expect(writeStub.lastCall.args[0]).to.equal("\x1b[?25h"); - }); - }); - - describe("should allow custom spinner frames, text type, and refresh interval", async () => { - it("", async () => { - const customFrames = ["-", "\\", "|", "/"]; - const customTextType = TextType.Info; - const customInterval = 200; - const spinner = new CustomizedSpinner({ - spinnerFrames: customFrames, - textType: customTextType, - refreshInterval: customInterval, - }); - expect(spinner.spinnerFrames).to.deep.equal(customFrames); - expect(spinner.textType).to.equal(customTextType); - expect(spinner.refreshInterval).to.equal(customInterval); - }); - }); -}); diff --git a/packages/cli/tests/unit/ui.tests.ts b/packages/cli/tests/unit/ui.tests.ts index 3d44cd1879..9a4d19caa9 100644 --- a/packages/cli/tests/unit/ui.tests.ts +++ b/packages/cli/tests/unit/ui.tests.ts @@ -2,9 +2,9 @@ // Licensed under the MIT license. import * as prompts from "@inquirer/prompts"; -import inquirer from "inquirer"; import { Colors, + InputTextConfig, LogLevel, MultiSelectConfig, SelectFileConfig, @@ -374,7 +374,7 @@ describe("User Interaction Tests", function () { }); it("interactive", async () => { sandbox.stub(UI, "interactive").value(true); - sandbox.stub(inquirer, "prompt").resolves({ test: "abc" }); + sandbox.stub(prompts, "input").resolves("abc"); const result = await UI.input("test", "Input the password", "default string"); expect(result.isOk() ? result.value : result.error).equals("abc"); }); @@ -465,6 +465,22 @@ describe("User Interaction Tests", function () { const result = await UI.selectFolder(config); expect(result.isOk() ? result.value.result : result.error).deep.equals("./"); }); + it("Input text", async () => { + sandbox.stub(prompts, "input").resolves("abc"); + sandbox.stub(UI, "interactive").value(true); + const config: InputTextConfig = { + name: "folder", + title: "Select a folder", + validation: () => { + return undefined; + }, + additionalValidationOnAccept: () => { + return undefined; + }, + }; + const result = await UI.inputText(config); + expect(result.isOk() ? result.value.result : result.error).deep.equals("abc"); + }); }); describe("Show Message", () => { diff --git a/packages/cli/tests/unit/ui2.tests.ts b/packages/cli/tests/unit/ui2.tests.ts index b98a0fbf7c..1b4d164001 100644 --- a/packages/cli/tests/unit/ui2.tests.ts +++ b/packages/cli/tests/unit/ui2.tests.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import inquirer from "inquirer"; +import * as inquirer from "@inquirer/prompts"; import { InputTextConfig, MultiSelectConfig, @@ -213,7 +213,7 @@ describe("UserInteraction(CLI) 2", () => { describe("selectFileOrInput", () => { it("happy path", async () => { - sandbox.stub(inquirer, "prompt").resolves({ test: "somevalue" }); + sandbox.stub(inquirer, "input").resolves("somevalue"); const res = await UI.selectFileOrInput({ name: "test", title: "test",