From 2e14aad2a4c1888a0d5bdea9a8a9ab69c93db9fb Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Tue, 13 Feb 2024 17:26:29 +0300 Subject: [PATCH 1/4] feat: pass generalAnswers to configure plugins --- src/configBuilder.test.ts | 19 +++++++++++++++---- src/configBuilder.ts | 27 +++++++++++++++++++-------- src/index.ts | 4 ++-- src/types/pluginsConfig.ts | 3 ++- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/configBuilder.test.ts b/src/configBuilder.test.ts index cf241f1..122a6fd 100644 --- a/src/configBuilder.test.ts +++ b/src/configBuilder.test.ts @@ -5,7 +5,7 @@ import defaultHermioneConfig from "./constants/defaultHermioneConfig"; import { ConfigBuilder } from "./configBuilder"; import fsUtils from "./fsUtils"; import defaultPluginsConfig from "./pluginsConfig"; -import type { GeneralPrompt, HermioneConfig } from "./types"; +import type { Answers, GeneralPrompt, HermioneConfig } from "./types"; jest.mock("inquirer"); @@ -104,14 +104,21 @@ describe("configBuilder", () => { }); it("should use default pluginsConfig, if not specified", async () => { + const generalAnswers: Answers = { _path: "/", _language: "ts" }; defaultPluginsConfig["html-reporter/hermione"] = jest.fn().mockImplementation((config: HermioneConfig) => { _.set(config, "htmlReporterIsSet", true); }); - await configBuilder.configurePlugins(["html-reporter/hermione"]); + await configBuilder.configurePlugins({ + pluginNames: ["html-reporter/hermione"], + generalAnswers, + }); expectConfig({ ...defaultHermioneConfig, htmlReporterIsSet: true }); - expect(defaultPluginsConfig["html-reporter/hermione"]).toBeCalledWith(defaultHermioneConfig); + expect(defaultPluginsConfig["html-reporter/hermione"]).toBeCalledWith( + defaultHermioneConfig, + generalAnswers, + ); }); it("should use overwrited pluginsConfig, if specified", async () => { @@ -125,7 +132,11 @@ describe("configBuilder", () => { }, }); - await configBuilder.configurePlugins(["html-reporter/hermione"], cb); + await configBuilder.configurePlugins({ + pluginNames: ["html-reporter/hermione"], + createPluginsConfig: cb, + generalAnswers: { _path: "/", _language: "ts" }, + }); expectConfig({ ...defaultHermioneConfig, foo: "bar" }); }); diff --git a/src/configBuilder.ts b/src/configBuilder.ts index 619398d..1c7fe6f 100644 --- a/src/configBuilder.ts +++ b/src/configBuilder.ts @@ -5,11 +5,17 @@ import defaultPluginsConfig from "./pluginsConfig"; import defaultToolOpts from "./constants/defaultToolOpts"; import defaultHermioneConfig from "./constants/defaultHermioneConfig"; import type { HermioneConfig, Language } from "./types/hermioneConfig"; -import type { HandleGeneralPromptsCallback } from "./types/toolOpts"; +import type { Answers, HandleGeneralPromptsCallback } from "./types/toolOpts"; import type { CreateBaseConfigCallback, CreatePluginsConfigCallback } from "."; import type { GeneralPrompt } from "./types/toolOpts"; import { getTemplate } from "./utils/configTemplates"; +type ConfigurePluginsOpts = { + pluginNames: string[]; + createPluginsConfig?: CreatePluginsConfigCallback; + generalAnswers: Answers; +}; + export class ConfigBuilder { static create(createBaseConfig?: CreateBaseConfigCallback, opts?: { language: Language }): ConfigBuilder { return new this(createBaseConfig, opts); @@ -30,9 +36,14 @@ export class ConfigBuilder { promts: GeneralPrompt[], handlers: HandleGeneralPromptsCallback[], { path, noQuestions }: { path: string; noQuestions: boolean }, - ): Promise { + ): Promise { + const answers: Answers = { + _path: path, + _language: this._config.__template!.language, + }; + if (_.isEmpty(promts) || _.isEmpty(handlers)) { - return; + return answers; } const defaults = promts.reduce((acc, prompt) => { @@ -45,23 +56,23 @@ export class ConfigBuilder { const promptsToAsk = noQuestions ? promts.filter(prompt => _.isUndefined(prompt.default)) : promts; const inquirerAnswers = await inquirer.prompt(promptsToAsk); - const answers = noQuestions ? { ...defaults, ...inquirerAnswers } : inquirerAnswers; - answers._path = path; - answers._language = this._config.__template!.language; + Object.assign(answers, defaults, inquirerAnswers, answers); for (const handler of handlers) { this._config = await handler(this._config, answers); } + + return answers; } - async configurePlugins(pluginNames: string[], createPluginsConfig?: CreatePluginsConfigCallback): Promise { + async configurePlugins({ pluginNames, createPluginsConfig, generalAnswers }: ConfigurePluginsOpts): Promise { const pluginsConfig = createPluginsConfig ? createPluginsConfig(defaultPluginsConfig) : defaultPluginsConfig; this._config.plugins ||= {}; for (const plugin of pluginNames) { - await pluginsConfig[plugin](this._config); + await pluginsConfig[plugin](this._config, generalAnswers); } } diff --git a/src/index.ts b/src/index.ts index 7887d0b..1f6358e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -65,12 +65,12 @@ export const run = async ({ ? [baseGeneralPromptsHandler, generalPromptsHandler] : [baseGeneralPromptsHandler]; - await configBuilder.handleGeneralQuestions(generalPrompts, generalPromptsHandlers, opts); + const generalAnswers = await configBuilder.handleGeneralQuestions(generalPrompts, generalPromptsHandlers, opts); const { pluginNames, configNotes } = await getPluginNames(opts); const extraPackages = getExtraPackagesToInstall ? getExtraPackagesToInstall() : { names: [], notes: [] }; - await configBuilder.configurePlugins(pluginNames, createPluginsConfig); + await configBuilder.configurePlugins({ pluginNames, createPluginsConfig, generalAnswers }); const packageNamesToInstall = pluginNames.concat(extraPackages.names); diff --git a/src/types/pluginsConfig.ts b/src/types/pluginsConfig.ts index 9c043c3..8400337 100644 --- a/src/types/pluginsConfig.ts +++ b/src/types/pluginsConfig.ts @@ -1,5 +1,6 @@ import type { HermioneConfig } from "./hermioneConfig"; +import type { Answers } from "./toolOpts"; export interface PluginsConfig { - [plugin: string]: (config: HermioneConfig) => void | Promise; + [plugin: string]: (config: HermioneConfig, generalAnswers?: Answers) => void | Promise; } From 33959ecdd8ac1799777852778dcd83bd2ee7d283 Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Tue, 13 Feb 2024 17:26:52 +0300 Subject: [PATCH 2/4] fix: get packages to install after plugins configure --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 1f6358e..b5fd78e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -68,10 +68,10 @@ export const run = async ({ const generalAnswers = await configBuilder.handleGeneralQuestions(generalPrompts, generalPromptsHandlers, opts); const { pluginNames, configNotes } = await getPluginNames(opts); - const extraPackages = getExtraPackagesToInstall ? getExtraPackagesToInstall() : { names: [], notes: [] }; await configBuilder.configurePlugins({ pluginNames, createPluginsConfig, generalAnswers }); + const extraPackages = getExtraPackagesToInstall ? getExtraPackagesToInstall() : { names: [], notes: [] }; const packageNamesToInstall = pluginNames.concat(extraPackages.names); if (opts.language === "ts") { From 6d95abb3c56422b0d363fb0774bc105a617dc374 Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Tue, 13 Feb 2024 17:31:33 +0300 Subject: [PATCH 3/4] fix: change installing packages placeholder --- src/package.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.ts b/src/package.ts index 7ead5eb..9a647a2 100644 --- a/src/package.ts +++ b/src/package.ts @@ -89,7 +89,7 @@ export const installPackages = async ( pluginsToInstall: string[], registry: string, ): Promise => { - const spinner = ora("Installing packages").start(); + const spinner = ora("Installing packages (this may take a while)").start(); const pluginsPackages = pluginsToInstall.map(packageNameFromPlugin).join(" "); From ee10ac0635ea2146307eaff5e46d75983ba13503 Mon Sep 17 00:00:00 2001 From: Roman Kuznetsov Date: Tue, 13 Feb 2024 19:16:01 +0300 Subject: [PATCH 4/4] fix: escaped slash expression transformation --- __fixtures/config/withExpressions.js | 4 +++- src/fsUtils.test.ts | 2 ++ src/fsUtils.ts | 10 +++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/__fixtures/config/withExpressions.js b/__fixtures/config/withExpressions.js index dc2d9f6..0292727 100644 --- a/__fixtures/config/withExpressions.js +++ b/__fixtures/config/withExpressions.js @@ -4,5 +4,7 @@ module.exports = { baz: '4', array: [ Boolean(100 + 500 * 1) - ] + ], + specials: /\n\t\r/g, + extraSlash: /\, \, \\/g }; diff --git a/src/fsUtils.test.ts b/src/fsUtils.test.ts index 13ce70f..06a456a 100644 --- a/src/fsUtils.test.ts +++ b/src/fsUtils.test.ts @@ -68,6 +68,8 @@ describe("fsUtils", () => { bar: 4, baz: "4", array: ["__expression: Boolean(100 + 500 * 1)"], + specials: "__expression: /\n\t\r/g", + extraSlash: "__expression: /\\, \\, \\\\/g", } as unknown as HermioneConfig; await expectConfig(withExpressionsConfig, configs["withExpressions"]); diff --git a/src/fsUtils.ts b/src/fsUtils.ts index ececde4..8f7549e 100644 --- a/src/fsUtils.ts +++ b/src/fsUtils.ts @@ -67,11 +67,15 @@ export const writeHermioneConfig = async (dirPath: string, hermioneConfig: Hermi const quote = template.quote; const expressionRegExp = new RegExp(`${quote}__expression: (.*)${quote}(,?)$`, "gm"); - // unescapes and restores double quotes in expressions - const withRestoredQuotesConfigStr = configStr.replace(expressionRegExp, match => match.replace(/\\"/g, '"')); + const repairQuotes = (match: string): string => match.replace(/\\"/g, '"'); + const repairSlash = (match: string): string => match.replace(/\\\\/g, "\\"); + + const repairedConfig = configStr + .replace(expressionRegExp, repairQuotes) // unescapes and restores double quotes in expressions + .replace(expressionRegExp, repairSlash); // restores '\\' in expressions // strings like '__expression: ' are turned into - return withRestoredQuotesConfigStr.replace(expressionRegExp, "$1$2"); + return repairedConfig.replace(expressionRegExp, "$1$2"); }; const getObjectRepr = _.flow([toIndentedJson, withComments, withReplacedQuotes, withExpressions]);