From fbe2ed9a252cd1c6ea24b413abf1f54f199d5fce Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Tue, 5 Jul 2022 17:03:32 +0900 Subject: [PATCH 1/4] Add: progress log --- build.js | 2 +- package.json | 1 + src/EjectEnum.ts | 25 ++++++-- src/ProgressLogger.ts | 41 +++++++++++++ yarn.lock | 132 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 src/ProgressLogger.ts diff --git a/build.js b/build.js index 1f93b55..e504407 100644 --- a/build.js +++ b/build.js @@ -8,7 +8,7 @@ const BUILD_TS_CONFIG_PATH = "./tsconfig.build.json"; const sharedBuildOptions = { outdir: DIST_DIR, bundle: true, - external: ["ts-morph", "yargs"], + external: ["ts-morph", "yargs", "ora"], platform: "node", }; diff --git a/package.json b/package.json index 5e2d1a0..f0b0635 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "docs": "typedoc src/index.ts" }, "dependencies": { + "ora": "^6.1.2", "ts-morph": "^15.1.0", "yargs": "^17.5.1" }, diff --git a/src/EjectEnum.ts b/src/EjectEnum.ts index 47f9302..156258c 100644 --- a/src/EjectEnum.ts +++ b/src/EjectEnum.ts @@ -12,6 +12,7 @@ import { SyntaxKind, VariableDeclarationKind, } from "ts-morph"; +import { ProgressLogger } from "./ProgressLogger"; /** * Target of the conversion. You can specify one of: @@ -107,17 +108,27 @@ export function ejectEnum(target: EjectEnumTarget) { const project = new Project(); addSourceFilesInTarget(project, target); + const progLogger = new ProgressLogger(project.getSourceFiles().length); + progLogger.start(); + for (const srcFile of project.getSourceFiles()) { - ejectEnumFromSourceFile(srcFile); + ejectEnumFromSourceFile(srcFile, progLogger); } + + progLogger.finish(); + project.saveSync(); } // Ejects enums from single source file. It is exported for the purpose of testing. -export function ejectEnumFromSourceFile(srcFile: SourceFile) { +export function ejectEnumFromSourceFile( + srcFile: SourceFile, + progLogger?: ProgressLogger +) { const ctx: EjectionContext = { rootSrcFile: srcFile, probe: new EjectionProbe(), + progLogger, }; // convert top-level statements @@ -129,6 +140,7 @@ export function ejectEnumFromSourceFile(srcFile: SourceFile) { if (ctx.probe.ejected) { srcFile.formatText(); } + ctx.progLogger?.notifyFinishFile(); } function statementedNodesVisitor(ctx: EjectionContext): (node: Node) => void { @@ -151,20 +163,20 @@ function ejectEnumFromStatementedNode( node: StatementedNode, ctx: EjectionContext ) { - node.getEnums().forEach((enumDecl) => { + for (const enumDecl of node.getEnums()) { if (!isEjectableEnum(enumDecl)) { - console.error( + ctx.progLogger?.log( `${path.relative( process.cwd(), ctx.rootSrcFile.getFilePath() )} > ${enumDecl.getName()}: it has a member whose value can't be known at compile-time. skipped.` ); - return; + continue; } convertEnumDeclaration(node, enumDecl, enumDecl.getChildIndex()); ctx.probe.setEjected(); - }); + } } function isEjectableEnum(enumDecl: EnumDeclaration): boolean { @@ -303,4 +315,5 @@ class EjectionProbe { type EjectionContext = { rootSrcFile: SourceFile; probe: EjectionProbe; + progLogger: ProgressLogger | undefined; }; diff --git a/src/ProgressLogger.ts b/src/ProgressLogger.ts new file mode 100644 index 0000000..3912c93 --- /dev/null +++ b/src/ProgressLogger.ts @@ -0,0 +1,41 @@ +import ora, { Ora } from "ora"; + +const progressText = (numFinished: number, numFiles: number): string => + `Ejecting... ${numFinished}/${numFiles} (${( + (numFinished / numFiles) * + 100 + ).toFixed(0)}%)`; + +export class ProgressLogger { + #spinner: Ora; + + #numFiles: number; + #numFinished = 0; + + constructor(numFiles: number) { + this.#numFiles = numFiles; + this.#spinner = ora(progressText(this.#numFinished, this.#numFiles)); + } + + public start() { + this.#spinner.render(); + } + + public finish() { + this.#spinner.succeed("Ejection finished"); + } + + public notifyFinishFile() { + this.#numFinished++; + this.#spinner.text = progressText(this.#numFinished, this.#numFiles); + this.#spinner.render(); + } + + public log(l: string) { + this.#spinner.clear(); + + console.log(l); + + this.#spinner.render(); + } +} diff --git a/yarn.lock b/yarn.lock index f4abbcb..d127deb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -343,11 +343,25 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2" + integrity sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + boxen@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -384,6 +398,14 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -445,6 +467,11 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.0.1.tgz#ca57d71e82bb534a296df63bbacc4a1c22b2a4b6" + integrity sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w== + check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -487,6 +514,18 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -519,6 +558,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + code-block-writer@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/code-block-writer/-/code-block-writer-11.0.0.tgz#5956fb186617f6740e2c3257757fea79315dd7d4" @@ -640,6 +684,13 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + dependencies: + clone "^1.0.2" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -1319,6 +1370,11 @@ husky@^8.0.1: resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9" integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw== +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" @@ -1360,7 +1416,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1467,6 +1523,11 @@ is-installed-globally@^0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -1538,6 +1599,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== +is-unicode-supported@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.2.0.tgz#f4f54f34d8ebc84a46b93559a036763b6d3e1014" + integrity sha512-wH+U77omcRzevfIG8dDhTS0V9zZyweakfD01FULl97+0EHiJTTZtJqxPSkIIo/SDPv/i07k/C9jAPY+jwLLeUQ== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -1676,6 +1742,14 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" @@ -1932,6 +2006,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.2.tgz#7b3c1356b42fd90fb1dad043d5dbe649388a0bf5" + integrity sha512-EJQ3NiP5Xo94wJXIzAyOtSb0QEIAUu7m8t6UZ9krbz0vAJqr92JpcK/lEXg91q6B9pEGqrykkd2EQplnifDSBw== + dependencies: + bl "^5.0.0" + chalk "^5.0.0" + cli-cursor "^4.0.0" + cli-spinners "^2.6.1" + is-interactive "^2.0.0" + is-unicode-supported "^1.1.0" + log-symbols "^5.1.0" + strip-ansi "^7.0.1" + wcwidth "^1.0.1" + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -2114,6 +2203,15 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2183,6 +2281,14 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -2221,6 +2327,11 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -2409,6 +2520,13 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2636,6 +2754,11 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -2686,6 +2809,13 @@ vscode-textmate@5.2.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" From efa29255f0df1cf8d9e7086edb9c5b9a6ce866c2 Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Tue, 5 Jul 2022 17:59:39 +0900 Subject: [PATCH 2/4] Fix: make ora bundled in bundles --- build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.js b/build.js index e504407..1f93b55 100644 --- a/build.js +++ b/build.js @@ -8,7 +8,7 @@ const BUILD_TS_CONFIG_PATH = "./tsconfig.build.json"; const sharedBuildOptions = { outdir: DIST_DIR, bundle: true, - external: ["ts-morph", "yargs", "ora"], + external: ["ts-morph", "yargs"], platform: "node", }; From c8bfb19fd76c41b325f787d41206ee0571b2c544 Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Tue, 5 Jul 2022 18:00:52 +0900 Subject: [PATCH 3/4] Add: show number of enums ejected per file --- src/EjectEnum.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/EjectEnum.ts b/src/EjectEnum.ts index 156258c..a8052e1 100644 --- a/src/EjectEnum.ts +++ b/src/EjectEnum.ts @@ -136,8 +136,16 @@ export function ejectEnumFromSourceFile( // convert nested statements srcFile.forEachDescendant(statementedNodesVisitor(ctx)); - // format file only if at least one ejection happened. if (ctx.probe.ejected) { + const n = ctx.probe.numEjected; + ctx.progLogger?.log( + `${path.relative( + process.cwd(), + ctx.rootSrcFile.getFilePath() + )}: ejected ${n} enum${n >= 2 ? "s" : ""}.` + ); + + // format file only if at least one ejection happened. srcFile.formatText(); } ctx.progLogger?.notifyFinishFile(); @@ -175,7 +183,7 @@ function ejectEnumFromStatementedNode( } convertEnumDeclaration(node, enumDecl, enumDecl.getChildIndex()); - ctx.probe.setEjected(); + ctx.probe.notifyEjected(); } } @@ -296,18 +304,22 @@ function getLeadingCommentsAssociatedWithDecl( // Object to detect an ejection of enum. class EjectionProbe { - #ejected: boolean; + #numEjected: number; constructor() { - this.#ejected = false; + this.#numEjected = 0; } get ejected(): boolean { - return this.#ejected; + return this.#numEjected > 0; + } + + get numEjected(): number { + return this.#numEjected; } - setEjected() { - this.#ejected = true; + notifyEjected() { + this.#numEjected++; } } From 39810e3cbc3dfab80646494379ff87c7161768b0 Mon Sep 17 00:00:00 2001 From: jiftechnify Date: Tue, 5 Jul 2022 18:45:24 +0900 Subject: [PATCH 4/4] Add: --silent option to suppress outputs --- src/EjectEnum.ts | 24 ++++++++++-- src/ProgressLogger.ts | 39 +++++++++++++++++--- src/index.ts | 1 + src/main.ts | 85 ++++++++++++++++++++++++------------------- test/main.test.ts | 12 +++--- 5 files changed, 108 insertions(+), 53 deletions(-) diff --git a/src/EjectEnum.ts b/src/EjectEnum.ts index a8052e1..11f1c3d 100644 --- a/src/EjectEnum.ts +++ b/src/EjectEnum.ts @@ -12,7 +12,7 @@ import { SyntaxKind, VariableDeclarationKind, } from "ts-morph"; -import { ProgressLogger } from "./ProgressLogger"; +import { initProgressLogger, ProgressLogger } from "./ProgressLogger"; /** * Target of the conversion. You can specify one of: @@ -81,6 +81,18 @@ function addSourceFilesInTarget(project: Project, target: EjectEnumTarget) { } } +/** + * Additional options for the conversion. + */ +export type EjectEnumOptions = { + /** + * If `true`, all outputs are suppressed. + * + * @defaultValue `false` + */ + silent?: boolean; +}; + /** * Ejects enums from all files specified by `target`. * @@ -103,13 +115,17 @@ function addSourceFilesInTarget(project: Project, target: EjectEnumTarget) { * ``` * * @param target Target specification of the conversion. + * @param options Additional options for the conversion. */ -export function ejectEnum(target: EjectEnumTarget) { +export function ejectEnum( + target: EjectEnumTarget, + { silent = false }: EjectEnumOptions = {} +) { const project = new Project(); addSourceFilesInTarget(project, target); - const progLogger = new ProgressLogger(project.getSourceFiles().length); - progLogger.start(); + const progLogger = initProgressLogger(silent); + progLogger.start(project.getSourceFiles().length); for (const srcFile of project.getSourceFiles()) { ejectEnumFromSourceFile(srcFile, progLogger); diff --git a/src/ProgressLogger.ts b/src/ProgressLogger.ts index 3912c93..9e6d575 100644 --- a/src/ProgressLogger.ts +++ b/src/ProgressLogger.ts @@ -1,23 +1,35 @@ import ora, { Ora } from "ora"; +export interface ProgressLogger { + start(numFiles: number): void; + finish(): void; + notifyFinishFile(): void; + log(l: string): void; +} + +export const initProgressLogger = (silent: boolean): ProgressLogger => + silent ? noopProgressLogger : new DefaultProgressLogger(); + const progressText = (numFinished: number, numFiles: number): string => `Ejecting... ${numFinished}/${numFiles} (${( (numFinished / numFiles) * 100 ).toFixed(0)}%)`; -export class ProgressLogger { +class DefaultProgressLogger implements ProgressLogger { #spinner: Ora; - #numFiles: number; + #numFiles = 0; #numFinished = 0; - constructor(numFiles: number) { - this.#numFiles = numFiles; - this.#spinner = ora(progressText(this.#numFinished, this.#numFiles)); + constructor() { + this.#spinner = ora(); } - public start() { + public start(numFiles: number) { + this.#numFiles = numFiles; + this.#spinner.text = progressText(0, this.#numFiles); + this.#spinner.render(); } @@ -39,3 +51,18 @@ export class ProgressLogger { this.#spinner.render(); } } + +const noopProgressLogger: ProgressLogger = { + start() { + /* no-op */ + }, + finish() { + /* no-op */ + }, + notifyFinishFile() { + /* no-op */ + }, + log() { + /* no-op */ + }, +}; diff --git a/src/index.ts b/src/index.ts index c7d9168..88205a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,2 @@ export { ejectEnum, EjectEnumTarget } from "./EjectEnum"; +export type { EjectEnumOptions } from "./EjectEnum"; diff --git a/src/main.ts b/src/main.ts index 022f029..a832f8d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,60 +1,65 @@ +import type { Argv as YargsArgv } from "yargs"; import { hideBin } from "yargs/helpers"; import yargs from "yargs/yargs"; +import type { EjectEnumOptions } from "./EjectEnum"; import { ejectEnum, EjectEnumTarget } from "./EjectEnum"; +const argvParser = yargs(hideBin(process.argv)) + .option("project", { + alias: "p", + type: "string", + array: true, + description: "Paths to TS config files", + default: [] as string[], + }) + .option("include", { + alias: "i", + type: "string", + array: true, + description: "Paths to include in the conversion target", + default: [] as string[], + }) + .option("exclude", { + alias: "e", + type: "string", + array: true, + description: + "Paths to exclude from the conversion target.\nYou CAN'T exclude paths included by TS configs of --project by this option!", + default: [] as string[], + }) + .option("silent", { + type: "boolean", + description: "Suppress outputs", + default: false, + }) + .usage( + "usage: $0 [--project path/to/tsconfig.json] [--include path/to/include [--exclude path/to/exclude]]" + ) + .help(); + // entrypoint of the CLI. export function main() { - const argvParser = yargs(hideBin(process.argv)) - .option("project", { - alias: "p", - type: "string", - array: true, - description: "Paths to TS config files", - default: [] as string[], - }) - .option("include", { - alias: "i", - type: "string", - array: true, - description: "Paths to include in the conversion target", - default: [] as string[], - }) - .option("exclude", { - alias: "e", - type: "string", - array: true, - description: - "Paths to exclude from the conversion target.\nYou CAN'T exclude paths included by TS configs of --project by this option!", - default: [] as string[], - }) - .usage( - "usage: $0 [--project path/to/tsconfig.json] [--include path/to/include [--exclude path/to/exclude]]" - ) - .help(); - const argv = argvParser.parseSync(); let target: EjectEnumTarget; try { - target = optionsFromArgv(argv); + target = targetFromArgv(argv); } catch (e) { console.error(`${e}`); argvParser.showHelp(); process.exit(1); } - ejectEnum(target); + ejectEnum(target, optionsFromArgv(argv)); } -type ParsedArgv = { - project: string[]; - include: string[]; - exclude: string[]; -}; +type ParsedArgv = typeof argvParser extends YargsArgv ? T : never; +type KeysAboutTarget = "project" | "include" | "exclude"; -// gets EjectEnumOptions from parsed command arguments. // throws if pre-conditions about the arguments are not satisfied. -export function optionsFromArgv(argv: ParsedArgv): EjectEnumTarget { +export function targetFromArgv( + argv: Pick +): EjectEnumTarget { if (argv.project.length === 0 && argv.include.length === 0) { throw Error("specify at least one of --project or --include"); } @@ -66,3 +71,9 @@ export function optionsFromArgv(argv: ParsedArgv): EjectEnumTarget { exclude: argv.exclude, }); } + +export function optionsFromArgv( + argv: Omit +): EjectEnumOptions { + return { silent: argv.silent }; +} diff --git a/test/main.test.ts b/test/main.test.ts index 9aedb64..bc3159a 100644 --- a/test/main.test.ts +++ b/test/main.test.ts @@ -1,15 +1,15 @@ import { describe, expect, test } from "vitest"; import { EjectEnumTarget } from "../src/EjectEnum"; -import { optionsFromArgv } from "../src/main"; +import { targetFromArgv } from "../src/main"; -describe("optionsFromArgv", () => { +describe("targetFromArgv", () => { test("--project -> EjectTarget.tsConfig", () => { const argv = { project: ["tsconfig.json", "tsconfig2.json"], include: [], exclude: [], }; - expect(optionsFromArgv(argv)).toEqual( + expect(targetFromArgv(argv)).toEqual( EjectEnumTarget.tsConfig(argv.project) ); }); @@ -20,7 +20,7 @@ describe("optionsFromArgv", () => { include: ["src/**/*", "test/**/*"], exclude: ["src/hoge/*.ts", "src/fuga.ts"], }; - expect(optionsFromArgv(argv)).toEqual( + expect(targetFromArgv(argv)).toEqual( EjectEnumTarget.paths({ include: argv.include, exclude: argv.exclude, @@ -34,7 +34,7 @@ describe("optionsFromArgv", () => { include: ["src/**/*", "test/**/*"], exclude: ["src/hoge/*.ts", "src/fuga.ts"], }; - expect(optionsFromArgv(argv)).toEqual( + expect(targetFromArgv(argv)).toEqual( EjectEnumTarget.tsConfig(argv.project) ); }); @@ -46,7 +46,7 @@ describe("optionsFromArgv", () => { exclude: ["hoge"], }; expect(() => { - optionsFromArgv(argv); + targetFromArgv(argv); }).toThrow(); }); });