From 474a099d7f9f06aa748aefa233047006f43076b6 Mon Sep 17 00:00:00 2001 From: Tim Raderschad Date: Thu, 2 Nov 2023 20:58:28 +0100 Subject: [PATCH] feat(cli): bump version --- packages/cli/CHANGELOG.md | 6 +++ packages/cli/package.json | 3 +- packages/cli/src/add-flag.ts | 29 +++++------ packages/cli/src/add-remote-config.ts | 31 ++++++------ packages/cli/src/util.ts | 17 ++++++- packages/cli/tests/base.test.ts | 50 +++++++++---------- pnpm-lock.yaml | 72 +++++++++++---------------- 7 files changed, 107 insertions(+), 101 deletions(-) diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 3ef17efd..de31968a 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # @tryabby/cli +## 1.1.0 + +### Minor Changes + +- introduce new add commands + ## 1.0.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 42033eb2..429ebb86 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@tryabby/cli", - "version": "1.0.2", + "version": "1.1.0", "private": false, "main": "./dist/index.js", "bin": { @@ -27,6 +27,7 @@ "dotenv": "^16.0.3", "esbuild": "0.18.17", "figlet": "^1.6.0", + "magicast": "^0.3.2", "msw": "^1.2.2", "node-fetch": "^3.3.1", "polka": "^0.5.2", diff --git a/packages/cli/src/add-flag.ts b/packages/cli/src/add-flag.ts index 16286f0e..570d70b5 100644 --- a/packages/cli/src/add-flag.ts +++ b/packages/cli/src/add-flag.ts @@ -6,10 +6,9 @@ import { push } from "./push"; import { updateConfigFile } from "./update-config-file"; export async function addFlag(options: { apiKey: string; host?: string; configPath?: string }) { - const { config, configFilePath } = await loadLocalConfig(options.configPath); - const configFileContents = await fs.readFile(configFilePath, "utf-8"); - - const oldConfig = JSON.parse(JSON.stringify(config)); + const { mutableConfig, saveMutableConfig, restoreConfig } = await loadLocalConfig( + options.configPath + ); const { flagName } = await prompts({ type: "text", @@ -17,32 +16,30 @@ export async function addFlag(options: { apiKey: string; host?: string; configPa message: "Type the name for your new flag: ", }); - if (!config.flags) { - config.flags = []; + if (!mutableConfig.flags) { + mutableConfig.flags = []; } - if (config.flags.includes(flagName)) { + if (mutableConfig.flags.includes(flagName)) { console.log(chalk.red("A flag with that name already exists!")); return; } - config.flags.push(flagName); - - console.log(chalk.blue("Updating local config...")); - await updateConfigFile(config, configFileContents, configFilePath); - console.log(chalk.green("Local config updated successfully")); - - console.log(chalk.blue("Updating remote config...")); + mutableConfig.flags.push(flagName); try { + console.log(chalk.blue("Updating local config...")); + await saveMutableConfig(); + console.log(chalk.green("Local config updated successfully")); + + console.log(chalk.blue("Updating remote config...")); await push({ apiKey: options.apiKey, configPath: options.configPath, apiUrl: options.host }); } catch (error) { console.log(chalk.red("Pushing flag failed, restoring old config file...")); - await updateConfigFile(oldConfig, configFileContents, configFilePath); + await restoreConfig(); console.log(chalk.green("Restored old config file")); - // pass error to command handler throw error; } diff --git a/packages/cli/src/add-remote-config.ts b/packages/cli/src/add-remote-config.ts index 1eb07bd4..c325ddec 100644 --- a/packages/cli/src/add-remote-config.ts +++ b/packages/cli/src/add-remote-config.ts @@ -1,19 +1,17 @@ -import * as fs from "fs/promises"; import { loadLocalConfig } from "./util"; import chalk from "chalk"; import { push } from "./push"; import { default as prompts } from "prompts"; -import { updateConfigFile } from "./update-config-file"; +import { builders } from "magicast"; export async function addRemoteConfig(options: { apiKey: string; host?: string; configPath?: string; }) { - const { config, configFilePath } = await loadLocalConfig(options.configPath); - const configFileContents = await fs.readFile(configFilePath, "utf-8"); - - const oldConfig = JSON.parse(JSON.stringify(config)); + const { mutableConfig, saveMutableConfig, restoreConfig } = await loadLocalConfig( + options.configPath + ); const { remoteConfigName, remoteConfigType } = await prompts([ { @@ -42,27 +40,28 @@ export async function addRemoteConfig(options: { }, ]); - if (!config.remoteConfig) { - config.remoteConfig = {}; + if (!mutableConfig.remoteConfig) { + mutableConfig.remoteConfig = builders.literal({}); } - if (remoteConfigName in config.remoteConfig) { + if (remoteConfigName in mutableConfig.remoteConfig!) { console.log(chalk.red("A remote config with that name already exists!")); return; } - config.remoteConfig[remoteConfigName] = remoteConfigType; - - console.log(chalk.blue("Updating local config...")); - await updateConfigFile(config, configFileContents, configFilePath); - console.log(chalk.green("Local config updated successfully")); + mutableConfig.remoteConfig![remoteConfigName] = remoteConfigType; - console.log(chalk.blue("Updating remote config...")); try { + console.log(chalk.blue("Updating local config...")); + await saveMutableConfig(); + console.log(chalk.green("Local config updated successfully")); + + console.log(chalk.blue("Updating remote config...")); await push({ apiKey: options.apiKey, configPath: options.configPath, apiUrl: options.host }); + console.log(chalk.green("Remote config updated successfully")); } catch (error) { console.log(chalk.red("Pushing the configuration failed. Restoring old config file...")); - await updateConfigFile(oldConfig, configFileContents, configFilePath); + await restoreConfig(); console.log(chalk.green("Old config restored.")); // pass error to command handler diff --git a/packages/cli/src/util.ts b/packages/cli/src/util.ts index 9a5419aa..1be56e0e 100644 --- a/packages/cli/src/util.ts +++ b/packages/cli/src/util.ts @@ -6,6 +6,8 @@ import portFinder from "portfinder"; import polka from "polka"; import { ABBY_BASE_URL } from "./consts"; import cors from "cors"; +import fs from "fs/promises"; +import { writeFile, loadFile, parseModule } from "magicast"; export async function loadLocalConfig(configPath?: string) { loadEnv(); @@ -40,7 +42,20 @@ export async function loadLocalConfig(configPath?: string) { console.error(result.error); throw new Error("Invalid config file"); } - return { config: result.data, configFilePath: sources[0] }; + const originalConfig = await fs.readFile(sources[0], "utf-8"); + const mod = await loadFile(sources[0]); + if (mod.exports.default.$type !== "function-call") throw new Error("Invalid config file"); + + return { + config: result.data, + configFilePath: sources[0], + mutableConfig: mod.exports.default.$args[1], + saveMutableConfig: () => writeFile(mod, sources[0]), + restoreConfig: () => { + const mod = parseModule(originalConfig); + return writeFile(mod, sources[0]); + }, + }; } export function multiLineLog(...args: any[]) { diff --git a/packages/cli/tests/base.test.ts b/packages/cli/tests/base.test.ts index f0de495b..75adf51d 100644 --- a/packages/cli/tests/base.test.ts +++ b/packages/cli/tests/base.test.ts @@ -1,5 +1,6 @@ import { PullAbbyConfigResponse, defineConfig } from "@tryabby/core"; import { HttpService } from "../src/http"; +import { writeFile as mgWriteFile } from "magicast"; import { writeFile } from "fs/promises"; import prompts from "prompts"; @@ -14,7 +15,12 @@ vi.mock("prettier", () => ({ })); vi.mock("fs/promises", async () => ({ - ...((await vi.importActual("fs/promises")) as object), + ...(await vi.importActual("fs/promises")), + writeFile: vi.fn(), +})); + +vi.mock("magicast", async () => ({ + ...(await vi.importActual("magicast")), writeFile: vi.fn(), })); @@ -114,15 +120,12 @@ describe("Abby CLI", () => { it("adds a flag and pushes it", async () => { prompts.inject(["newFlag"]); - const spy = vi.spyOn(HttpService, "updateConfigOnServer"); + const httpSpy = vi.spyOn(HttpService, "updateConfigOnServer"); await addFlag({ apiKey: API_KEY, configPath: __dirname + "/abby.config.stub.ts" }); - expect(writeFile).toHaveBeenCalledWith( - __dirname + "/abby.config.stub.ts", - expect.stringContaining("newFlag") - ); - expect(spy).toHaveBeenCalledOnce(); + expect(mgWriteFile).toHaveBeenCalledWith(expect.anything(), __dirname + "/abby.config.stub.ts"); + expect(httpSpy).toHaveBeenCalledOnce(); }); it("adds a remote config and pushes it", async () => { @@ -131,10 +134,7 @@ describe("Abby CLI", () => { await addRemoteConfig({ apiKey: API_KEY, configPath: __dirname + "/abby.config.stub.ts" }); - expect(writeFile).toHaveBeenCalledWith( - __dirname + "/abby.config.stub.ts", - expect.stringContaining("newRemoteConfig") - ); + expect(mgWriteFile).toHaveBeenCalledWith(expect.anything(), __dirname + "/abby.config.stub.ts"); expect(spy).toHaveBeenCalledOnce(); }); @@ -145,21 +145,21 @@ describe("Abby CLI", () => { throw new Error("failed"); }); - let errorCatched = false; + let errorCaught = false; try { await addFlag({ apiKey: API_KEY, configPath: __dirname + "/abby.config.stub.ts" }); } catch (error) { expect(error).instanceof(Error); expect((error as Error).message).toBe("failed"); - errorCatched = true; + errorCaught = true; } - expect(errorCatched).toBe(true); + expect(errorCaught).toBe(true); - expect(writeFile).toHaveBeenCalledTimes(2); - expect(writeFile).toHaveBeenLastCalledWith( - __dirname + "/abby.config.stub.ts", - expect.not.stringContaining("newFlag") + expect(mgWriteFile).toHaveBeenCalledTimes(2); + expect(mgWriteFile).toHaveBeenLastCalledWith( + expect.anything(), + __dirname + "/abby.config.stub.ts" ); }); @@ -170,21 +170,21 @@ describe("Abby CLI", () => { throw new Error("failed"); }); - let errorCatched = false; + let errorCaught = false; try { await addRemoteConfig({ apiKey: API_KEY, configPath: __dirname + "/abby.config.stub.ts" }); } catch (error) { expect(error).instanceof(Error); expect((error as Error).message).toBe("failed"); - errorCatched = true; + errorCaught = true; } - expect(errorCatched).toBe(true); + expect(errorCaught).toBe(true); - expect(writeFile).toHaveBeenCalledTimes(2); - expect(writeFile).toHaveBeenLastCalledWith( - __dirname + "/abby.config.stub.ts", - expect.not.stringContaining("newRemoteConfig") + expect(mgWriteFile).toHaveBeenCalledTimes(2); + expect(mgWriteFile).toHaveBeenLastCalledWith( + expect.anything(), + __dirname + "/abby.config.stub.ts" ); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf021b3f..4dab4d1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -669,6 +669,9 @@ importers: figlet: specifier: ^1.6.0 version: 1.6.0 + magicast: + specifier: ^0.3.2 + version: 0.3.2 msw: specifier: ^1.2.2 version: 1.2.2(typescript@5.3.3) @@ -1729,10 +1732,10 @@ packages: '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.19.3) '@babel/helper-module-transforms': 7.22.5 '@babel/helpers': 7.22.5 - '@babel/parser': 7.22.5 + '@babel/parser': 7.23.6 '@babel/template': 7.22.5 '@babel/traverse': 7.22.5 - '@babel/types': 7.22.5 + '@babel/types': 7.23.6 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -1796,10 +1799,10 @@ packages: '@babel/helper-compilation-targets': 7.22.5(@babel/core@7.22.5) '@babel/helper-module-transforms': 7.22.5 '@babel/helpers': 7.22.5 - '@babel/parser': 7.22.5 + '@babel/parser': 7.23.6 '@babel/template': 7.22.5 '@babel/traverse': 7.22.5 - '@babel/types': 7.22.5 + '@babel/types': 7.23.6 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -1818,10 +1821,10 @@ packages: '@babel/helper-compilation-targets': 7.22.9(@babel/core@7.22.9) '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.9) '@babel/helpers': 7.22.6 - '@babel/parser': 7.22.7 + '@babel/parser': 7.23.6 '@babel/template': 7.22.5 '@babel/traverse': 7.22.8 - '@babel/types': 7.22.5 + '@babel/types': 7.23.6 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -2162,7 +2165,7 @@ packages: '@babel/helper-module-imports': 7.22.5 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 '@babel/template': 7.22.15 '@babel/traverse': 7.23.6 '@babel/types': 7.23.6 @@ -2180,7 +2183,7 @@ packages: '@babel/helper-module-imports': 7.22.5 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 dev: true /@babel/helper-module-transforms@7.23.3(@babel/core@7.20.12): @@ -2320,10 +2323,6 @@ packages: dependencies: '@babel/types': 7.23.6 - /@babel/helper-string-parser@7.22.5: - resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} - engines: {node: '>=6.9.0'} - /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} @@ -2332,10 +2331,6 @@ packages: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.22.5: - resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} - engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.22.5: resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} engines: {node: '>=6.9.0'} @@ -2390,7 +2385,7 @@ packages: resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 chalk: 2.4.2 js-tokens: 4.0.0 @@ -2414,13 +2409,6 @@ packages: resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==} engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.22.5 - - /@babel/parser@7.22.7: - resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} - engines: {node: '>=6.0.0'} - hasBin: true dependencies: '@babel/types': 7.23.6 dev: true @@ -4444,7 +4432,7 @@ packages: '@babel/helper-module-imports': 7.22.5 '@babel/helper-plugin-utils': 7.22.5 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.5) - '@babel/types': 7.22.5 + '@babel/types': 7.23.6 dev: true /@babel/plugin-transform-react-jsx@7.22.5(@babel/core@7.23.6): @@ -5251,14 +5239,6 @@ packages: to-fast-properties: 2.0.0 dev: true - /@babel/types@7.22.5: - resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 - to-fast-properties: 2.0.0 - /@babel/types@7.23.6: resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} engines: {node: '>=6.9.0'} @@ -17349,7 +17329,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.23.6 - '@babel/parser': 7.22.5 + '@babel/parser': 7.23.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.1 @@ -18448,6 +18428,14 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magicast@0.3.2: + resolution: {integrity: sha512-Fjwkl6a0syt9TFN0JSYpOybxiMCkYNEeOTnOTNRbjphirLakznZXAqrXgj/7GG3D1dvETONNwrBfinvAbpunDg==} + dependencies: + '@babel/parser': 7.23.6 + '@babel/types': 7.23.6 + source-map-js: 1.0.2 + dev: false + /make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -25644,16 +25632,16 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.4 + '@types/eslint-scope': 3.7.7 '@types/estree': 0.0.51 '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/wasm-edit': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 acorn: 8.11.2 acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.21.5 + browserslist: 4.22.2 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.14.1 + enhanced-resolve: 5.15.0 es-module-lexer: 0.9.3 eslint-scope: 5.1.1 events: 3.3.0 @@ -25663,7 +25651,7 @@ packages: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.2.0 + schema-utils: 3.3.0 tapable: 2.2.1 terser-webpack-plugin: 5.3.9(esbuild@0.17.8)(webpack@5.76.1) watchpack: 2.4.0 @@ -25683,16 +25671,16 @@ packages: webpack-cli: optional: true dependencies: - '@types/eslint-scope': 3.7.7 + '@types/eslint-scope': 3.7.4 '@types/estree': 0.0.51 '@webassemblyjs/ast': 1.11.1 '@webassemblyjs/wasm-edit': 1.11.1 '@webassemblyjs/wasm-parser': 1.11.1 acorn: 8.11.2 acorn-import-assertions: 1.9.0(acorn@8.11.2) - browserslist: 4.22.2 + browserslist: 4.21.5 chrome-trace-event: 1.0.3 - enhanced-resolve: 5.15.0 + enhanced-resolve: 5.14.1 es-module-lexer: 0.9.3 eslint-scope: 5.1.1 events: 3.3.0 @@ -25702,7 +25690,7 @@ packages: loader-runner: 4.3.0 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.3.0 + schema-utils: 3.2.0 tapable: 2.2.1 terser-webpack-plugin: 5.3.9(esbuild@0.17.8)(webpack@5.76.1) watchpack: 2.4.0