From 5df56be550a5cf7dd3be81c510150fa0a474a133 Mon Sep 17 00:00:00 2001 From: RiN Date: Tue, 7 Sep 2021 11:18:50 +0800 Subject: [PATCH] feat(CLI): renew command line arguments (#3) * chore: add change-case for support case style * docs: add inputs arguments in README.md * feat(CLI): add flags and options * `-v, --version`: Print version information. * `-C, --case`: Sets case for export name declarations * refactor(CLI): remove unused and fix typo for function declarations * feat(CLI): add missing style case * feat(CLI): support multiple input directories or files --- README.md | 8 ++- index.bin.js | 141 +++++++++++++++++++++++++++++++++++++------------- lib/chakra.js | 14 ++--- package.json | 1 + yarn.lock | 135 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2988c1a..3d84b5e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ create-chakra-icons [FLAGS] [OPTIONS] [INPUT] ### Options ```console --i, --input This option for read the input from PATH from FILE or DIRECTORIES. +-i, --input This option for read the input from PATH of FILE or DIRECTORIES. [e.g.: -i some/path , -i file.svg] -o, --output Writes the output. [default: stdout] -n, --name Sets value for `displayName` properties @@ -39,6 +39,12 @@ create-chakra-icons [FLAGS] [OPTIONS] [INPUT] [e.g.: -S "Icon"] --ts, --typescript Sets output as TypeScript code. ``` + +### Input +```console +[INPUT] This option for read the input from PATH of FILE or DIRECTORIES. + [e.g.: create-chakra-icons ./MyICON.svg ~/assets] +``` ### Examples #### Pipelines command: diff --git a/index.bin.js b/index.bin.js index 9be9f5f..01edf6d 100755 --- a/index.bin.js +++ b/index.bin.js @@ -1,11 +1,27 @@ #!/usr/bin/env node +const { + name: packageName, + version: packageVersion, +} = require("./package.json"); const Fs = require("fs"); const Path = require("path"); const BabelGenerator = require("@babel/generator").default; const argv = require("minimist")(process.argv.slice(2)); const { createChakraIcon } = require("./lib/chakra"); -const { stdout: output, stdin: input, exit } = process; +const { + stdout: output, + stdin: input, + exit, + stderr: error, +} = require("process"); +const { + pascalCase: PascalCase, + camelCase, + snakeCase: snake_case, + constantCase: CONSTANT_CASE, +} = require("change-case"); const encoding = "utf-8"; + if (input.isTTY) { main(argv); } else { @@ -13,61 +29,114 @@ if (input.isTTY) { input.on("data", function (data) { if (data) { const name = argv.name || argv.n || "Unamed"; + const exportNameCase = argv.C || argv.case; const source = createCode(name, { - file: data, - displayName: name, + source: data, + displayName: stringToCase(name, exportNameCase), }); output.write(source); } }); } -function createCode(name, ...svgs) { - const icon = createChakraIcon(name, ...svgs); +function createCode(...sources) { + const icon = createChakraIcon(...sources); return BabelGenerator(icon).code; } + +function stringToCase(str, _case) { + return { + [true]: PascalCase(str), + [_case === "pascal"]: PascalCase(str), + [_case === "camel"]: camelCase(str), + [_case === "constant"]: CONSTANT_CASE(str), + [_case === "snake"]: snake_case(str), + }[true]; +} + +function stringToInput({ displayName, exportNameCase, encoding }) { + return function (acc, str) { + if (Fs.existsSync(str)) { + if (Fs.lstatSync(str).isDirectory()) { + const pathResolved = Path.resolve(str); + acc.push( + ...Fs.readdirSync(pathResolved) + .filter((f) => f.split(".")[1] === "svg") + .map((f) => Path.join(pathResolved, f)) + .map((source) => ({ + displayName: stringToCase( + Path.basename(source).split(".")[0], + exportNameCase + ), + source: Fs.readFileSync(source, encoding), + })) + ); + } else { + acc.push({ + displayName: stringToCase(displayName, exportNameCase), + source: Fs.readFileSync(str, encoding), + }); + } + } + return acc; + }; +} // :: [Object] -> () *Effect* function main(args) { - const fileOrDirs = args.i || args.input; + const inputs = (args.i && [args.i]) || (args.input && [args.input]) || args._; + const version = args.V || args.version; const outFile = args.o || args.output; const name = args.name || args.n || "Unamed"; + const exportNameCase = args.C || args.case; - // check is {file} exist - Fs.exists(fileOrDirs, (isExist) => { - if (!isExist) { - console.error("Cannot handle input") && exit(1); - } - // all the input will put in an array {_files} - let _files = []; - // handle when {-i} || {--input} is file.svg or dirs/ - if (Fs.lstatSync(fileOrDirs).isDirectory()) { - const pathResolved = Path.resolve(fileOrDirs); - _files = _files.concat( - Fs.readdirSync(pathResolved) - .filter((f) => f.split(".")[1] === "svg") - .map((f) => Path.join(pathResolved, f)) - ); - } else { - _files = _files.concat([fileOrDirs]); - } + if (inputs.length > 0) { // make code const source = createCode( - name, - ..._files.map((_file) => ({ - file: Fs.readFileSync(_file, encoding), - displayName: (() => { - const [_name] = Path.basename(_file).split(".") || [name]; - return _name; - })(), - })) + ...inputs.reduce( + stringToInput({ displayName: name, exportNameCase, encoding }), + [] + ) ); // write output in output - outFile + return outFile ? Fs.writeFile(Path.resolve(outFile), source, (err) => { if (err) { - console.log(err) && exit(1); + error.write(err, () => exit(1)); } }) - : output.write(source); - }); + : output.write(`${source}`); + } else if (version) { + return output.write(packageVersion); + } + + output.write(` +USAGE: ${packageName} [FLAGS] [OPTIONS] [INPUT] + +By default, output is written to stdout. +Stdin is read if is piped to create-chakra-icons. + +FLAGS: + -h, --help Prints help information + -V, --version Prints version information + + +OPTIONS: + -i, --input This option for read the input from PATH from FILE or DIRECTORIES. + [e.g.: -i some/path , -i file.svg] + -o, --output Writes the output. [default: stdout] + -n, --name Sets value for \`displayName\` properties + (*ONLY for pipelines command). [default: Unamed] [e.g. -n "MyIcon"] + -C, --case + Sets for case [snake|camel|constant|pascal] in export named declaration + output. [default: pascal] + -S, --suffix Sets for suffix in export named declaration. + [e.g.: -S "Icon"] + --ts, --typescript Sets output as TypeScript code. (UNAVAILABLE, SOON). + + +[INPUT]: This option for read the input from PATH of FILE or DIRECTORIES. + [e.g.: create-chakra-icons ./MyICON.svg ~/assets] + +${packageName} (version: ${packageVersion}) +`); } diff --git a/lib/chakra.js b/lib/chakra.js index 93c4546..9118e16 100644 --- a/lib/chakra.js +++ b/lib/chakra.js @@ -11,8 +11,8 @@ const ast = require("./ast"); * @param {String} displayName * @returns {Object} */ -const createChakraIcon = (displayName, ...svgs) => { - const perFileCode = ({ file: svg, displayName }) => { +const createChakraIcon = (...sources) => { + const perFileCode = ({ source: svg, displayName }) => { const hast = SvgParser.parse(svg); if (ast.hastChildrenLength(hast) > 1) { @@ -32,8 +32,7 @@ const createChakraIcon = (displayName, ...svgs) => { }); return iconAST; }; - - const svgCodes = [...svgs].map(perFileCode); + const svgCodes = [...sources].map(perFileCode); const hasVariableDeclaratorInit = (is) => @@ -48,15 +47,18 @@ const createChakraIcon = (displayName, ...svgs) => { }) => type === is; + // TODO: make it more accurate with {@babel/types}.isArrowFunctionExpression const hasArrowFunctionExpression = hasVariableDeclaratorInit( "ArrowFunctionExpression" ); + // TODO: make it more accurate with {@babel/types}.isCallExpression const hasCallExpression = hasVariableDeclaratorInit("CallExpression"); - const isNotEmptryString = (str) => str !== ""; + const isNotEmptyString = (str) => str !== ""; + // usage for generate import module const imports = [ svgCodes.some(hasArrowFunctionExpression) ? "Icon" : "", svgCodes.some(hasCallExpression) ? "createIcon" : "", - ].filter(isNotEmptryString); + ].filter(isNotEmptyString); const program = ast.toSource( ast.toImportDeclaration("@chakra-ui/react", ...imports), ...svgCodes diff --git a/package.json b/package.json index af65a41..5ffded8 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@babel/generator": "7.15.0", "@babel/types": "7.15.0", "@svgr/hast-util-to-babel-ast": "5.5.0", + "change-case": "4.1.2", "minimist": "1.2.5", "svg-parser": "2.0.4" }, diff --git a/yarn.lock b/yarn.lock index 16280e3..741451f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -559,6 +559,14 @@ call-bind@^1.0.0: function-bind "^1.1.1" get-intrinsic "^1.0.2" +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -573,6 +581,15 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + ccount@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" @@ -587,6 +604,24 @@ chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +change-case@4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== + dependencies: + camel-case "^4.1.2" + capital-case "^1.0.4" + constant-case "^3.0.4" + dot-case "^3.0.4" + header-case "^2.0.4" + no-case "^3.0.4" + param-case "^3.0.4" + pascal-case "^3.1.2" + path-case "^3.0.4" + sentence-case "^3.0.4" + snake-case "^3.0.4" + tslib "^2.0.3" + character-entities-html4@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" @@ -770,6 +805,15 @@ consolidate@^0.16.0: dependencies: bluebird "^3.7.2" +constant-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case "^2.0.2" + continuable-cache@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/continuable-cache/-/continuable-cache-0.3.1.tgz#bd727a7faed77e71ff3985ac93351a912733ad0f" @@ -1144,6 +1188,14 @@ documentation@13.2.5: "@vue/compiler-sfc" "^3.0.11" vue-template-compiler "^2.6.12" +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -1678,6 +1730,14 @@ he@^1.1.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +header-case@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== + dependencies: + capital-case "^1.0.4" + tslib "^2.0.3" + highlight.js@^10.7.2: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -2161,6 +2221,13 @@ longest-streak@^2.0.0: resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.4.tgz#b8599957da5b5dab64dee3fe316fa774597d90e4" integrity sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2562,6 +2629,14 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -2732,6 +2807,14 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + parse-entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" @@ -2791,11 +2874,27 @@ parse-url@^6.0.0: parse-path "^4.0.0" protocols "^1.4.0" +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +path-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -3288,6 +3387,15 @@ semver@^7.1.1, semver@^7.3.4: dependencies: lru-cache "^6.0.0" +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3312,6 +3420,14 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -3708,6 +3824,11 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +tslib@^2.0.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + type-fest@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" @@ -3825,6 +3946,20 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + +upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== + dependencies: + tslib "^2.0.3" + urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"