Skip to content

Commit

Permalink
refactor(cli;core): migrate inquirer to inquirer/prompts; introduce I…
Browse files Browse the repository at this point in the history
…nquirerWrapper

* refactor(cli;core): replace inquirer with inquirer/prompts

* refactor(*): enable esModuleInterop

* refactor(cli;core): migrate to inquirer/prompts

* test(cli;core): switch to default imports for mocked modules

* refactor(cli): add inquirer/types; introduce InquirerWrapper class

* test(PromptSession): update tests
  • Loading branch information
jackofdiamond5 authored Sep 16, 2024
1 parent 7db0a92 commit 7caf281
Show file tree
Hide file tree
Showing 25 changed files with 497 additions and 360 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@
"handlebars": "4.7.8"
},
"devDependencies": {
"@types/inquirer": "0.0.35",
"@types/jasmine": "3.3.9",
"@types/minimatch": "^5.1.2",
"@types/node": "^20.12.3",
"@inquirer/prompts": "^5.4.0",
"@typescript-eslint/eslint-plugin": "^7.5.0",
"@typescript-eslint/parser": "^7.5.0",
"browser-sync": "^3.0.2",
Expand All @@ -73,7 +73,6 @@
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-unicorn": "^52.0.0",
"glob": "^11.0.0",
"inquirer": "^6.4.1",
"jasmine": "3.5.0",
"jasmine-spec-reporter": "^4.2.1",
"lerna": "^3.16.4",
Expand Down
12 changes: 5 additions & 7 deletions packages/cli/lib/PromptSession.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {
BasePromptSession, GoogleAnalytics, PackageManager, ProjectConfig,
BasePromptSession, GoogleAnalytics, InquirerWrapper, PackageManager, ProjectConfig,
ProjectLibrary, PromptTaskContext, Task, Util
} from "@igniteui/cli-core";
import * as inquirer from "inquirer";
import * as path from "path";
import { default as add } from "./commands/add";
import { default as start } from "./commands/start";
Expand All @@ -16,14 +15,12 @@ export class PromptSession extends BasePromptSession {
}

public static async chooseTerm() {
const answers = await inquirer.prompt({
const answer = await InquirerWrapper.input({
default: null,
message: "Enter a search term",
name: "term",
type: "input"
});
if (answers.term) {
return answers.term;
if (answer) {
return answer;
} else {
const retProm = await this.chooseTerm();
return retProm;
Expand Down Expand Up @@ -55,6 +52,7 @@ export class PromptSession extends BasePromptSession {
name: "projectName",
message: "Enter a name for your project:",
default: Util.getAvailableName(defaultProjName, true),
choices: null,
validate: this.nameIsValid
});

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/lib/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { App, GoogleAnalytics, Util } from "@igniteui/cli-core";
import * as yargs from "yargs";
import yargs from "yargs";
import {
add,
ADD_COMMAND_NAME,
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"@types/yargs": "^17.0.33",
"chalk": "^5.3.0",
"glob": "^11.0.0",
"inquirer": "^6.4.1",
"@inquirer/prompts": "^5.4.0",
"open": "^10.1.0",
"resolve": "^1.22.8",
"through2": "^4.0.2",
Expand All @@ -94,7 +94,6 @@
"@angular-devkit/core": "~14.0.0",
"@angular-devkit/schematics": "~14.0.0",
"@schematics/angular": "~14.0.0",
"@types/inquirer": "0.0.35",
"@types/jasmine": "~3.5.0",
"@types/node": "^20.12.3",
"browser-sync": "^3.0.2",
Expand Down
9 changes: 5 additions & 4 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
"dependencies": {
"chalk": "^2.3.2",
"glob": "^7.1.2",
"inquirer": "^6.2.2",
"@inquirer/prompts": "^5.4.0",
"through2": "^2.0.3",
"typescript": "~5.4.3"
},
"devDependencies": {
"@angular-devkit/schematics": "~14.0.0"
},
"devDependencies": {
"@angular-devkit/schematics": "~14.0.0",
"@inquirer/type": "^1.5.3"
}
}
44 changes: 32 additions & 12 deletions packages/core/prompt/BasePromptSession.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as inquirer from "inquirer";
import * as path from "path";
import { Separator } from "@inquirer/prompts";
import { BaseTemplateManager } from "../templates";
import {
Component, Config, ControlExtraConfigType, ControlExtraConfiguration, Framework,
FrameworkId, ProjectLibrary, ProjectTemplate, Template
} from "../types";
import { App, ChoiceItem, GoogleAnalytics, ProjectConfig, Util } from "../util";
import { Task, TaskRunner, WIZARD_BACK_OPTION } from "./TaskRunner";
import { InquirerWrapper } from "./InquirerWrapper";

export abstract class BasePromptSession {
protected config: Config;
Expand Down Expand Up @@ -37,6 +38,7 @@ export abstract class BasePromptSession {
name: "projectName",
message: "Enter a name for your project:",
default: Util.getAvailableName(defaultProjName, true),
choices: null,
validate: this.nameIsValid
});

Expand Down Expand Up @@ -124,9 +126,12 @@ export abstract class BasePromptSession {
options.choices = this.addSeparators(options.choices);
}

const userInput = await inquirer.prompt(options);

const result = userInput[options.name] as string;
let result: string = null;
if (options.type === "list") {
result = await InquirerWrapper.select(options);
} else {
result = await InquirerWrapper.input(options);
}

// post to GA everything but 'Back' user choice
if (!withBackChoice || result !== WIZARD_BACK_OPTION) {
Expand Down Expand Up @@ -261,6 +266,7 @@ export abstract class BasePromptSession {
name: `${type === "component" ? type : "customView"}Name`,
message: `Name your ${type}:`,
default: availableDefaultName,
choices: null,
validate: (input: string) => {
// TODO: GA post?
const name = Util.nameFromPath(input);
Expand All @@ -273,10 +279,23 @@ export abstract class BasePromptSession {

/** Create prompts from template extra configuration and assign user answers to the template */
protected async customizeTemplateTask(template: Template) {
const extraPrompt: any[] = this.createQuestions(template.getExtraConfiguration());
const extraConfigAnswers = await inquirer.prompt(extraPrompt);
const extraConfig = this.parseAnswers(extraConfigAnswers);
const extraPrompt = this.createQuestions(template.getExtraConfiguration());
const extraConfigAnswers = [];
for (const question of extraPrompt) {
switch (question.type) {
case "input":
extraConfigAnswers.push(await InquirerWrapper.input(question));
break;
case "select":
extraConfigAnswers.push(await InquirerWrapper.select(question));
break;
case "checkbox":
extraConfigAnswers.push(await InquirerWrapper.checkbox(question));
break;
}
}

const extraConfig = this.parseAnswers(extraConfigAnswers);
GoogleAnalytics.post({
t: "event",
ec: "$ig wizard",
Expand All @@ -296,12 +315,12 @@ export abstract class BasePromptSession {
for (let i = 0; i < array.length; i++) {
newArray.push(array[i]);
if (i + 1 < array.length) {
newArray.push(new inquirer.Separator());
newArray.push(new Separator());
}
}
if (array.length > 4) {
// additional separator after last item for lists that wrap around
newArray.push(new inquirer.Separator(new Array(15).join("=")));
newArray.push(new Separator(new Array(15).join("=")));
}
return newArray;
}
Expand All @@ -310,13 +329,13 @@ export abstract class BasePromptSession {
* Generate questions from extra configuration array
* @param extraConfig
*/
private createQuestions(extraConfig: ControlExtraConfiguration[]): any {
private createQuestions(extraConfig: ControlExtraConfiguration[]): { type: string; name: string; message: string; choices: any[]; default: any; }[] {
const result = [];
for (const element of extraConfig) {
const currExtraConfig = {};
switch (element.type) {
case ControlExtraConfigType.Choice:
currExtraConfig["type"] = "list";
currExtraConfig["type"] = "select"; // formerly list
break;
case ControlExtraConfigType.MultiChoice:
currExtraConfig["type"] = "checkbox";
Expand Down Expand Up @@ -414,6 +433,7 @@ export abstract class BasePromptSession {
name: "port",
message: "Choose app host port:",
default: defaultPort,
choices: null,
validate: (input: string) => {
if (!Number(input)) {
Util.log(""); /* new line */
Expand Down Expand Up @@ -615,7 +635,7 @@ export interface IUserInputOptions {
type: string;
name: string;
message: string;
choices?: any[];
choices: any[];
default?: any;
validate?: (input: string) => string | boolean;
}
Expand Down
32 changes: 32 additions & 0 deletions packages/core/prompt/InquirerWrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { checkbox, input, select } from '@inquirer/prompts';
import { Context } from '@inquirer/type';

// ref - node_modules\@inquirer\input\dist\cjs\types\index.d.ts - bc for some reason this is not publicly exported
type InputConfig = {
message: string;
default?: string;
required?: boolean;
transformer?: (value: string, { isFinal }: {
isFinal: boolean;
}) => string;

// TODO: consider typing these by extracting the types from the inquirer package
validate?: any;
theme?: unknown;
};

export class InquirerWrapper {
private constructor() { }

public static async input(message: InputConfig, context?: Context): Promise<string> {
return input(message, context);
}

public static async select(message: InputConfig & { choices: string[] }, context?: Context): Promise<string> {
return select(message, context);
}

public static async checkbox(message: InputConfig & { choices: string[] }, context?: Context): Promise<string[]> {
return checkbox(message, context);
}
}
1 change: 1 addition & 0 deletions packages/core/prompt/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./TaskRunner";
export * from "./BasePromptSession";
export * from "./InquirerWrapper";
3 changes: 1 addition & 2 deletions packages/igx-templates/igx-ts-legacy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ class IgxProjectLibrary extends BaseProjectLibrary {
this.themes = ["Custom", "Default"];

const groups = require("./groups.json");
// tslint:disable-next-line:forin
for (const key in groups) {
this.groupDescriptions.set(key, groups[key]);
}
}
}
module.exports = new IgxProjectLibrary();
export = new IgxProjectLibrary() as BaseProjectLibrary;
3 changes: 1 addition & 2 deletions packages/igx-templates/igx-ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ class IgxProjectLibrary extends BaseProjectLibrary {
this.themes = ["Custom", "Default"];

const groups = require("./groups.json");
// tslint:disable-next-line:forin
for (const key in groups) {
this.groupDescriptions.set(key, groups[key]);
}
}
}
module.exports = new IgxProjectLibrary();
export = new IgxProjectLibrary() as BaseProjectLibrary;
4 changes: 2 additions & 2 deletions packages/igx-templates/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as standalone from './igx-ts';
import * as legacy from './igx-ts-legacy';
import standalone from './igx-ts';
import legacy from './igx-ts-legacy';

export * from './IgniteUIForAngularTemplate';
export * from './AngularTypeScriptFileUpdate';
Expand Down
1 change: 1 addition & 0 deletions packages/ng-schematics/src/ng-new/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export function newProject(options: OptionsSchema): Rule {
name: "projectName",
message: "Enter a name for your project:",
default: Util.getAvailableName(defaultProjName, true),
choices: null as unknown as string[],
validate: prompt.nameIsValid
});
nameProvided = false;
Expand Down
3 changes: 1 addition & 2 deletions spec/acceptance/config-spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { GoogleAnalytics, GoogleAnalyticsParameters } from "@igniteui/cli-core";
import os from "os";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as cli from "../../packages/cli/lib/cli";
import { deleteAll, resetSpy } from "../helpers/utils";

describe("Config command", () => {
let testFolder = path.parse(__filename).name;

// tslint:disable:no-console
beforeEach(() => {
spyOn(console, "log");
spyOn(console, "error");
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/generate-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Config, GoogleAnalytics, GoogleAnalyticsParameters, ProjectConfig, Template } from "@igniteui/cli-core";
import os from "os";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as cli from "../../packages/cli/lib/cli";
import { deleteAll } from "../helpers/utils";
Expand Down
1 change: 0 additions & 1 deletion spec/acceptance/new-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { deleteAll, filesDiff, resetSpy } from "../helpers/utils";
describe("New command", () => {
let testFolder;

// tslint:disable:no-console
beforeEach(() => {
spyOn(console, "log");
spyOn(console, "error");
Expand Down
6 changes: 3 additions & 3 deletions spec/unit/GoogleAnalytic-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { GoogleAnalytics, ProjectConfig } from "@igniteui/cli-core";
import * as childProcess from "child_process";
import child_process from "child_process";
import https from "https";
import * as fs from "fs";
import * as https from "https";
import * as path from "path";
import * as process from "process";
import { deleteAll } from "../helpers/utils";
Expand All @@ -20,7 +20,7 @@ describe("Unit - Google Analytic", () => {
request = jasmine.createSpyObj("request", ["on", "end"]);
spyOn(https, "request").and.returnValue(request);
serviceSpy =
spyOn(childProcess, "execSync").and.returnValue("some string which contains REG_SZ so we can get Machine Key");
spyOn(child_process, "execSync").and.returnValue("some string which contains REG_SZ so we can get Machine Key");
while (fs.existsSync(`./output/${testFolder}`)) {
testFolder += 1;
}
Expand Down
4 changes: 2 additions & 2 deletions spec/unit/ProjectConfig-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fs from "fs";
import os from "os";
import { ProjectConfig } from "@igniteui/cli-core";
import * as fs from "fs";
import * as os from "os";

describe("Unit - ProjectConfig", () => {
it("hasLocalConfig returns correct values", async done => {
Expand Down
Loading

0 comments on commit 7caf281

Please sign in to comment.