diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 0b9a800..42d8e32 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -23,7 +23,7 @@ jobs: cache: 'npm' - name: ๐Ÿ’ฝ Install deps - run: npm install + run: npm ci - name: ๐Ÿงช Coverage Tests run: npm run test:coverage diff --git a/.gitignore b/.gitignore index 9c0c0e1..97cc807 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,5 @@ /build /lib /coverage - -/.clingon - lcov.info - .env \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index ecdf3e0..270e3da 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,6 +3,6 @@ "semi": false, "tabWidth": 2, "singleQuote": true, - "printWidth": 100, + "printWidth": 80, "trailingComma": "none" } diff --git a/README.md b/README.md index 9537a96..eb8a605 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@

- Clingon CLI logo + + Clingon CLI logo +

[![Build CI](https://github.com/ipetinate/clingon/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/ipetinate/clingon/actions/workflows/node.js.yml) @@ -27,7 +29,10 @@ Let's simplify all of this, execute a command, answer some questions, or select ## Links -- Official website: [clingon.dev](https://clingon.dev) +

+ ๐Ÿ”— Official website ๐Ÿ”— +

+ - Releases - [CHANGELOG](https://github.com/ipetinate/clingon/blob/main/CHANGELOG.md) - Documentation diff --git a/clingon.json b/clingon.json deleted file mode 100644 index 0967ef4..0000000 --- a/clingon.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/package-lock.json b/package-lock.json index 94d9be0..899b81c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -161,26 +161,11 @@ "node": ">=8" } }, - "node_modules/@auto-it/core/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@auto-it/core/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -200,12 +185,6 @@ "node": ">=8" } }, - "node_modules/@auto-it/core/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@auto-it/npm": { "version": "11.1.6", "resolved": "https://registry.npmjs.org/@auto-it/npm/-/npm-11.1.6.tgz", @@ -228,26 +207,11 @@ "user-home": "^2.0.0" } }, - "node_modules/@auto-it/npm/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@auto-it/npm/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -255,12 +219,6 @@ "node": ">=10" } }, - "node_modules/@auto-it/npm/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@auto-it/package-json-utils": { "version": "11.1.6", "resolved": "https://registry.npmjs.org/@auto-it/package-json-utils/-/package-json-utils-11.1.6.tgz", @@ -301,26 +259,11 @@ "tslib": "1.10.0" } }, - "node_modules/@auto-it/version-file/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@auto-it/version-file/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -334,12 +277,6 @@ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, - "node_modules/@auto-it/version-file/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -711,11 +648,11 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.3.2.tgz", - "integrity": "sha512-lUXKA/5PhPBXz6SVDE+EbBmV3Wi3X77SPRet6Mc1pn6fSXAIivvu1OWpHDpVUxc+RiFflbrDjXUgLfCQeofrWg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.3.3.tgz", + "integrity": "sha512-R64X8RVjVrMLg9wmCB5WTy8R97a/zAYrPdjY1tOybadg4zwx7mk+0Fy69H1pT0x4PRdcMO/CyPmDI0gLooakmw==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "ansi-escapes": "^4.3.2", @@ -726,14 +663,14 @@ } }, "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -813,11 +750,11 @@ } }, "node_modules/@inquirer/confirm": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.6.tgz", - "integrity": "sha512-Mj4TU29g6Uy+37UtpA8UpEOI2icBfpCwSW1QDtfx60wRhUy90s/kHPif2OXSSvuwDQT1lhAYRWUfkNf9Tecxvg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.7.tgz", + "integrity": "sha512-BZjjj19W8gnh5UGFTdP5ZxpgMNRjy03Dzq3k28sB2MDlEUFrcyTkMEoGgvBmGpUw0vNBoCJkTcbHZ3e9tb+d+w==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1" }, "engines": { @@ -825,14 +762,14 @@ } }, "node_modules/@inquirer/confirm/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -999,11 +936,11 @@ } }, "node_modules/@inquirer/editor": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.6.tgz", - "integrity": "sha512-CWmp6XhfQye6xwH6/XV1HGvY95rUfzw7EXyNDHzj5s5Qr1t/X3t6c7uRkfK7OD91y+sbSy7aL6MJv2bbNrMoew==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.7.tgz", + "integrity": "sha512-CGZk//rg57zgXqMp8q8tE2HCc5/rwCC0IwIEtZeb1BF/GJIFlijp4wvN9PeXHsEQ+ul2qRz/0dEk1JqmZzbSbA==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1", "external-editor": "^3.1.0" }, @@ -1012,14 +949,14 @@ } }, "node_modules/@inquirer/editor/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -1099,11 +1036,11 @@ } }, "node_modules/@inquirer/expand": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.6.tgz", - "integrity": "sha512-mFW/vU6mSut0UjmvxPdLC81Sz+5b4t7sMZeF7RlHki1PJkZVZIQoT91MCvoJJN2S7lDqSAV/TxeYqF41RNkY2g==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.7.tgz", + "integrity": "sha512-zwdd5Zur3kdlpthXwk3O4kH5/bXAdZA/Qfl9v7MFf2Un68Cq8XLATp/gH3iMkHcQtyyBemPFgzD9pHNq0piToQ==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1", "chalk": "^4.1.2" }, @@ -1112,14 +1049,14 @@ } }, "node_modules/@inquirer/expand/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -1207,11 +1144,11 @@ } }, "node_modules/@inquirer/input": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.1.6.tgz", - "integrity": "sha512-M8bUFOlcn/kQcVYskl4kkB6dYrHtymJJ1S4nSg/khXT3W3l71u2qhSzfo6PdBG3jUe6ILJZ0gUh4Kef2uJ5pxw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.1.7.tgz", + "integrity": "sha512-eRdwlHJI4bpYsi4icIthsz1rZGIrlfufzRZdCf2i1qfQZ8d3vLTWcILIWV7cnjD4v/nrZ81RthRaQog/uxlcGA==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1" }, "engines": { @@ -1219,14 +1156,14 @@ } }, "node_modules/@inquirer/input/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -1306,11 +1243,11 @@ } }, "node_modules/@inquirer/password": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.1.6.tgz", - "integrity": "sha512-fkiTIijBRxotoMw0/ljA2BaSsz6PlGoiav9QyAjBXCZoyFsYoItstDKvJXbWwS9NrN42fXYvXn1ljBpldnJaeA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.1.7.tgz", + "integrity": "sha512-RshkS0CRJYJO4Yxbl6MqkC3OQlU4Dmv4mNxxvoYYfRcPtC/UBLYcddm+lIDHi3zegkto9kmSNYXTCQKYNxinvg==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1", "ansi-escapes": "^4.3.2" }, @@ -1319,14 +1256,14 @@ } }, "node_modules/@inquirer/password/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -1425,11 +1362,11 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.1.6.tgz", - "integrity": "sha512-xnGBfjatdUqyBMqHi1kHHBh4ggQGZz42vYH0kFdQDnOtx4Ouo7baqVZhBRuQfZTL8tAXuOYI9X6r6BXBl8cnqw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.1.7.tgz", + "integrity": "sha512-r4tsdYWsYanwEl7MBqmf8GaZTbUAh51C3tMwozOYrAl2wT9YEQVSMDlkcMToFsisRCSq6mQ6zppv92masx4WRQ==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/type": "^1.3.1", "chalk": "^4.1.2" }, @@ -1438,14 +1375,14 @@ } }, "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -1525,11 +1462,11 @@ } }, "node_modules/@inquirer/select": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.3.2.tgz", - "integrity": "sha512-VzLHVpaobBpI3o/CWSG2sCDqrjHZEYAfT1bowbR8Q72fEi0WfBO3Fnh595QqBit9kQhI1uJbVHaaovg1I7eE7Q==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.3.3.tgz", + "integrity": "sha512-0ptHMogTnyTNKIJVEfCl4fFDQSzIR2/SjgBoD1MLXDszP3UbkYroZ9ii3e6x7dMCWrPGkGWZPyxpy3Rs55vWLw==", "dependencies": { - "@inquirer/core": "^8.1.0", + "@inquirer/core": "^8.2.0", "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "ansi-escapes": "^4.3.2", @@ -1540,14 +1477,14 @@ } }, "node_modules/@inquirer/select/node_modules/@inquirer/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.1.0.tgz", - "integrity": "sha512-kfx0SU9nWgGe1f03ao/uXc85SFH1v2w3vQVH7QDGjKxdtJz+7vPitFtG++BTyJMYyYgH8MpXigutcXJeiQwVRw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.0.tgz", + "integrity": "sha512-pexNF9j2orvMMTgoQ/uKOw8V6/R7x/sIDwRwXRhl4i0pPSh6paRzFehpFKpfMbqix1/+gzCekhYTmVbQpWkVjQ==", "dependencies": { "@inquirer/figures": "^1.0.1", "@inquirer/type": "^1.3.1", "@types/mute-stream": "^0.0.4", - "@types/node": "^20.12.7", + "@types/node": "^20.12.11", "@types/wrap-ansi": "^3.0.0", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", @@ -2266,9 +2203,9 @@ } }, "node_modules/@types/node": { - "version": "20.12.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz", - "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==", + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", "dependencies": { "undici-types": "~5.26.4" } @@ -2599,9 +2536,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001615", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001615.tgz", - "integrity": "sha512-1IpazM5G3r38meiae0bHRnPhz+CBQ3ZLqbQMtrg+AsTPKAXgW38JNsXkyZ+v8waCsDmPq87lmfun5Q2AGysNEQ==", + "version": "1.0.30001620", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz", + "integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==", "dev": true, "funding": [ { @@ -2895,9 +2832,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.756", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.756.tgz", - "integrity": "sha512-RJKZ9+vEBMeiPAvKNWyZjuYyUqMndcP1f335oHqn3BEQbs2NFtVrnK5+6Xg5wSM9TknNNpWghGDUCKGYF+xWXw==", + "version": "1.4.774", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.774.tgz", + "integrity": "sha512-132O1XCd7zcTkzS3FgkAzKmnBuNJjK8WjcTtNuoylj7MYbqw5eXehjQ5OK91g0zm7OTKIPeaAG4CPoRfD9M1Mg==", "dev": true, "peer": true }, @@ -3087,18 +3024,6 @@ "node": ">=4.0.0" } }, - "node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fp-ts": { "version": "2.16.5", "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.5.tgz", @@ -3582,19 +3507,6 @@ "node": ">=4" } }, - "node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -3891,39 +3803,6 @@ "node": ">=0.10.0" } }, - "node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3987,15 +3866,6 @@ "node": ">=6" } }, - "node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -4030,9 +3900,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -4069,6 +3939,73 @@ "node": ">=4" } }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/pretty-ms": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", @@ -4795,9 +4732,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.15.tgz", - "integrity": "sha512-K9HWH62x3/EalU1U6sjSZiylm9C8tgq2mSvshZpqc7QE69RaA2qjhkW2HlNA0tFpEbtyFz7HTqbSdN4MSwUodA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, "funding": [ { @@ -4816,7 +4753,7 @@ "peer": true, "dependencies": { "escalade": "^3.1.2", - "picocolors": "^1.0.0" + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" diff --git a/src/actions/guided.js b/src/actions/guided.js index 46699ef..a164295 100644 --- a/src/actions/guided.js +++ b/src/actions/guided.js @@ -26,7 +26,7 @@ export async function guidedAction(resourceName) { /** * Selected preset * - * @type {string } + * @type { string | boolean } */ let preset = false @@ -71,14 +71,14 @@ export async function guidedAction(resourceName) { message: 'Do you want to save the answers as a preset to use later?' }) - /** - * Save answers as new preset - * - * @type {boolean} - */ - const presetName = await presetNamePrompt() - if (savePreset) { + /** + * Save answers as new preset + * + * @type {boolean} + */ + const presetName = await presetNamePrompt() + const { success, path } = saveAnswersAsPreset(presetName, answers) if (success) { diff --git a/src/actions/init.js b/src/actions/init.js new file mode 100644 index 0000000..7bd3bd2 --- /dev/null +++ b/src/actions/init.js @@ -0,0 +1,22 @@ +import { compose } from '../utils/compose.js' +import { + checkIfPresetFolderAlreadyExists, + createFileIfNotExists, + createPresetFolderIfNotExists, + getConfigContent, + getConfigFilePath +} from '../utils/init-action.js' + +export async function initAction() { + /* + * Global Config + */ + + compose(getConfigFilePath, createFileIfNotExists, getConfigContent) + + /* + * Preset Folder + */ + + compose(checkIfPresetFolderAlreadyExists, createPresetFolderIfNotExists) +} diff --git a/src/constants/config.js b/src/constants/config.js new file mode 100644 index 0000000..6c0056f --- /dev/null +++ b/src/constants/config.js @@ -0,0 +1,18 @@ +export const defaultConfig = { + /** + * Alias for text ocurrences replacement + * + * ๐Ÿšจ Be careful, do not replace "resourcePath" or "ResourceName", to avoid generating strange behavior in the templates, + * causing auto-completion to be unconfigured + */ + alias: { + /** + * Will replace all `src` occurrences on templates to `@`, Example: `src/components/...` become `@/components/...` + */ + src: '@' + }, + /** + * If `true` will default export functions, components, pages, etc. Example: + */ + exportDefault: false +} diff --git a/src/flows/coldStart.js b/src/flows/coldStart.js new file mode 100644 index 0000000..e3c7bec --- /dev/null +++ b/src/flows/coldStart.js @@ -0,0 +1,25 @@ +import { join } from 'node:path' +import { readFileContent } from '../utils/file.js' +import { getConfigContent } from '../utils/init-action.js' + +export async function coldStart() { + const configPath = join(process.cwd(), 'clingon.config.json') + + /** + * App data + */ + const data = { + /** + * @type {import('../types').GlobalConfig | undefined} + */ + globalConfig: undefined + } + + try { + data.globalConfig = getConfigContent(configPath) + } catch (error) { + console.error(error) + } + + return data +} diff --git a/src/generators/components.js b/src/generators/components.js index be92458..f1bee67 100644 --- a/src/generators/components.js +++ b/src/generators/components.js @@ -2,7 +2,8 @@ import path, { join } from 'node:path' import { CssFrameworkEnum, FrameworkEnum } from '../enums/frameworks.js' -import { localDirname } from '../main.js' +import { globalConfig, localDirname } from '../main.js' + import { frameworkTemplates } from '../constants/templates.js' import { compose } from '../utils/compose.js' @@ -16,6 +17,10 @@ import { checkDirectoriesTree, createDir } from '../utils/directory.js' * @typedef {Record<2 | 3, "options" | "setup">} VueApi - Vue API variant options or setup (composition) */ +/* + * Data Variables + */ + /** * Component generator * @@ -32,8 +37,7 @@ export function generateComponent(answers) { ) if (success) { - console.info('\n') - console.info('Component created successfully: ' + path) + console.info('\nComponent created successfully: ' + path) } else { console.error('Error on create component, try again') } @@ -77,12 +81,18 @@ export function defineComponentTemplate(data) { switch (data.framework) { case FrameworkEnum.react: { - templatePath = frameworkTemplates.react[variant].component.functional[data.cssFramework] + templatePath = + frameworkTemplates.react[variant].component.functional[ + data.cssFramework + ] break } case FrameworkEnum.vue: { - templatePath = frameworkTemplates.vue[vueVersion][variant].component[api][data.cssFramework] + templatePath = + frameworkTemplates.vue[vueVersion][variant].component[api][ + data.cssFramework + ] break } @@ -133,7 +143,9 @@ export function makeFolderWrapperOrBypass(data) { data.name = convertCase('PascalCase', data.name) const folderWrapperPath = join(data.resourcePath, data.name) - const folderWrapperExists = checkDirectoriesTree(splitPathString(folderWrapperPath)) + const folderWrapperExists = checkDirectoriesTree( + splitPathString(folderWrapperPath) + ) if (data.folderWrapper && !folderWrapperExists) { const created = createDir(folderWrapperPath) @@ -197,6 +209,17 @@ export function makePathWithExtension(data) { export function replaceAllComponentTextOccurrences(data) { switch (data.framework) { case FrameworkEnum.react: { + if (!data.folderWrapper) { + if (globalConfig?.exportDefault) { + console.log({ globalConfig }) + + data.fileContent = data.fileContent.replace( + /export function/g, + 'export default function' + ) + } + } + data.fileContent = data.fileContent.replace(/ResourceName/g, data.name) return data @@ -239,9 +262,20 @@ export function generateComponentFile(data) { const success = createFileWithContent(data.pathWithFileName, data.fileContent) if (data.folderWrapper) { - const filePath = join(data.resourcePath, data.name, 'index.' + data.extension) - - createFileWithContent(filePath, `export * from './${data.name}'`) + const filePath = join( + data.resourcePath, + data.name, + 'index.' + data.extension + ) + + if (globalConfig?.exportDefault) { + createFileWithContent( + filePath, + `import { ${data.name} } from './${data.name}'\n\nexport * from './${data.name}'\nexport default ${data.name}\n` + ) + } else { + createFileWithContent(filePath, `export * from './${data.name}'`) + } } return { success, path } diff --git a/src/generators/functions.js b/src/generators/functions.js index e6c735f..aa1d706 100644 --- a/src/generators/functions.js +++ b/src/generators/functions.js @@ -1,6 +1,6 @@ import path from 'node:path' -import { localDirname } from '../main.js' +import { globalConfig, localDirname } from '../main.js' import { functionTemplates } from '../constants/templates.js' import { compose } from '../utils/compose.js' @@ -83,6 +83,15 @@ export function getTemplateContent(data) { * @returns {() => data} */ export function replaceAllFunctionTextOccurrences(data) { + if (!data.folderWrapper) { + if (globalConfig?.exportDefault) { + data.fileContent = data.fileContent.replace( + /export function/g, + 'export default function' + ) + } + } + data.name = convertCase('camelCase', data.name) data.fileContent = data.fileContent.replace('FunctionName', data.name) diff --git a/src/generators/storybook-story.js b/src/generators/storybook-story.js index cc66464..0951c0a 100644 --- a/src/generators/storybook-story.js +++ b/src/generators/storybook-story.js @@ -2,12 +2,15 @@ import path from 'node:path' import { FrameworkEnum } from '../enums/frameworks.js' -import { localDirname } from '../main.js' +import { globalConfig, localDirname } from '../main.js' import { storiesTemplates } from '../constants/templates.js' import { compose } from '../utils/compose.js' import { convertCase } from '../utils/string.js' -import { getFileExtension, removePostfixAndExt } from '../utils/file-extension.js' +import { + getFileExtension, + removePostfixAndExt +} from '../utils/file-extension.js' import { createFileWithContent, readFileContent } from '../utils/file.js' /** @@ -157,6 +160,15 @@ export function makePathWithExtension(data) { * }} */ export function replaceAllTestTextOccurrences(data) { + if (globalConfig?.exportDefault) { + const regex = new RegExp(`import { ResourceName } from`) + + data.fileContent = data.fileContent.replace( + regex, + `import ResourceName from` + ) + } + data.fileContent = data.fileContent.replace(/ResourceName/g, data.name) if (data.framework === FrameworkEnum.vue) { @@ -171,6 +183,10 @@ export function replaceAllTestTextOccurrences(data) { data.fileContent = data.fileContent.replace(/resourcePath/g, resourcePath) } + if (globalConfig?.alias?.src) { + data.fileContent = data.fileContent.replace(/src/g, globalConfig.alias.src) + } + return data } diff --git a/src/generators/tests.js b/src/generators/tests.js index e2de7e5..6176004 100644 --- a/src/generators/tests.js +++ b/src/generators/tests.js @@ -2,11 +2,14 @@ import path from 'node:path' import { FrameworkEnum } from '../enums/frameworks.js' -import { localDirname } from '../main.js' +import { globalConfig, localDirname } from '../main.js' import { unitTestTemplates } from '../constants/templates.js' import { compose } from '../utils/compose.js' -import { getFileExtension, removePostfixAndExt } from '../utils/file-extension.js' +import { + getFileExtension, + removePostfixAndExt +} from '../utils/file-extension.js' import { capitalizeLetter, convertCase } from '../utils/string.js' import { createFileWithContent, readFileContent } from '../utils/file.js' @@ -29,7 +32,9 @@ export function generateTests(answers) { ) if (success) { - console.info(capitalizeLetter(answers.testPostfix) + ' created successfully: ' + path) + console.info( + capitalizeLetter(answers.testPostfix) + ' created successfully: ' + path + ) } else { console.error(`Error on create ${answers.testPostfix}, try again`) } @@ -70,7 +75,8 @@ export function defineTestTemplate(data) { if (data.testFramework === 'vitest') { if (data.withTestingLibrary) { - templatePath = unitTestTemplates.react[variant].vitestTestingLibrary + templatePath = + unitTestTemplates.react[variant].vitestTestingLibrary } else { templatePath = unitTestTemplates.react[variant].vitest } @@ -197,6 +203,15 @@ export function makePathWithExtension(data) { * }} */ export function replaceAllTestTextOccurrences(data) { + if (globalConfig?.exportDefault) { + const regex = new RegExp(`import { ResourceName } from`) + + data.fileContent = data.fileContent.replace( + regex, + `import ResourceName from` + ) + } + if (['function'].includes(data.type)) { data.name = convertCase('camelCase', data.name) } @@ -231,6 +246,10 @@ export function replaceAllTestTextOccurrences(data) { } } + if (globalConfig?.alias?.src) { + data.fileContent = data.fileContent.replace(/src/g, globalConfig.alias.src) + } + return data } diff --git a/src/main.js b/src/main.js index 83be976..c3df857 100755 --- a/src/main.js +++ b/src/main.js @@ -2,17 +2,26 @@ import { Command } from 'commander' +import { coldStart } from './flows/coldStart.js' + +import { initAction } from './actions/init.js' import { guidedAction } from './actions/guided.js' import { createAction } from './actions/create.js' -import { getLocalLibDirname } from './utils/directory.js' import { TestFrameworkEnum } from './enums/frameworks.js' +import { getLocalLibDirname } from './utils/directory.js' /* * Global Variables */ -export const localDirname = getLocalLibDirname() +const localDirname = getLocalLibDirname() + +/* + * Start app + */ + +const { globalConfig } = await coldStart() /* * Resources @@ -27,7 +36,7 @@ const program = new Command() program .name('clingon') .description('CLI to generate files based on templates') - .version('0.9.4', '-v, --version', 'Current version') + .version('0.9.5', '-v, --version', 'Current version') /* * Guided flow - generate components based on prompt answers @@ -37,7 +46,9 @@ program .command('gen') .argument('[name]', 'Resource name') .action(guidedAction) - .description('Start a guided flow to generate resources (components, functions, pages, etc)') + .description( + 'Start a guided flow to generate resources (components, functions, pages, etc)' + ) /* * Preset flow - create instantly resources with presets @@ -46,39 +57,53 @@ program program .command('create') .argument('', 'Resource name') - .option('--preset [preset]', 'Preset name') - .option('--type [resourceType]', 'Resource type: "function" | "page" | "component"') - .option('--vue-version [vueVersion]', 'Vue version: "2" | "3" (default: 3))', '3') - .option('--framework [frameworkName]', 'Framework name for default preset: vue or react') + .option('-p, --preset [preset]', 'Preset name') + .option( + '-t, --type [resourceType]', + 'Resource type: "function" | "page" | "component"' + ) + .option( + '-vv, --vue-version [vueVersion]', + 'Vue version: "2" | "3" (default: 3))', + '3' + ) + .option( + '-f, --framework [frameworkName]', + 'Framework name for default preset: vue or react' + ) .option( - '--css-framework [cssFramework]', + '-cs, --css-framework [cssFramework]', 'Style approach: "css_modules" | "tailwind_inline" | "tailwind_file" | "css_vanilla" | "scss" (default: no_style)', 'no_style' ) .option( - '--test-framework [testFrameworkName]', + '-tf, --test-framework [testFrameworkName]', 'Test framework: jest or vitest (default: vitest)', TestFrameworkEnum.vitest ) .option( - '--path [resourcePath]', + '-rp, --path [resourcePath]', 'Path to resource, use dot (".") to current dir where command is executed' ) .option( - '--test-path [testPath]', + '-tp, --test-path [testPath]', 'Path to test, use dot (".") to current dir where command is executed, if ommited, and --spec is present, will use the same path to resource' ) .option( - '--story-path [storyPath]', + '-sp, --story-path [storyPath]', 'Path to story, use dot (".") to current dir where command is executed, if ommited, and --spec is present, will use the same path to resource' ) - .option('--typescript', 'With TypeScript (default: false)', false) - .option('--testing-library', 'With Testing Library (default: false)', false) + .option('-ts, --typescript', 'With TypeScript (default: false)', false) + .option( + '-tl, --testing-library', + 'With Testing Library (default: false)', + false + ) .option('--test', 'Add test file (default: false)', false) .option('--spec', 'Add spec file (default: false)', false) .option('--story', 'Add story file (default: false)', false) .option( - '--folder-wrapper', + '-fw, --folder-wrapper', 'Creates a folder with the name of the resource, with the files inside it', false ) @@ -91,8 +116,26 @@ program 'Creates the resources with a local preset in non-verbose mode (preview and ask to confirm are not shown, resources will be created immediately), if the preset folder is empty, it will call the guided flow (the same as the `gen` command executes)' ) +/* + * Init tool assets, generate clingon.config.json + */ + +program + .command('init') + .action(initAction) + .usage('init') + .description( + 'Init all needed setup, generate files and create folders to store assets.' + ) + /* * Parse program to execution */ program.parse() + +/* + * Exports + */ + +export { globalConfig, localDirname } diff --git a/src/types.js b/src/types.js index 724e09f..ad87db9 100644 --- a/src/types.js +++ b/src/types.js @@ -23,42 +23,49 @@ import { StoryPostfixEnum, TestPostfixEnum, StylePostfixEnum } from 'enums/postf * @typedef {TestPostfix | StoryPostfix | StylePostfix} Postfix - All files postfixes * * @typedef {{ - * name: string; - * type: Resource; - * framework: Framework; - * version: string | number - * typescript: boolean; - * withTest: boolean; - * withStory: boolean; - * withTestingLibrary: boolean - * folderWrapper: boolean - * testPostfix: TestPostfix; - * storyPostfix: StoryPostfix; - * testFramework: TestFramework - * cssFramework: CssFramework - * resourcePath: string; - * testPath: string - * storyPath: string + * name: string; + * type: Resource; + * framework: Framework; + * version: string | number + * typescript: boolean; + * withTest: boolean; + * withStory: boolean; + * withTestingLibrary: boolean + * folderWrapper: boolean + * testPostfix: TestPostfix; + * storyPostfix: StoryPostfix; + * testFramework: TestFramework + * cssFramework: CssFramework + * resourcePath: string; + * testPath: string + * storyPath: string * }} Answers - Users prompted answers * * @typedef {{ - * preset: string, - * type: Resource, - * framework: Framework, - * testFramework: TestFramework, - * cssFramework: CssFramework - * path: string, - * testPath: string, - * storyPath: string, - * test: boolean, - * spec: boolean, - * story: boolean, - * typescript: boolean, - * testingLibrary: boolean, - * folderWrapper: boolean, - * vueVersion: VueVersion + * preset: string, + * type: Resource, + * framework: Framework, + * testFramework: TestFramework, + * cssFramework: CssFramework + * path: string, + * testPath: string, + * storyPath: string, + * test: boolean, + * spec: boolean, + * story: boolean, + * typescript: boolean, + * testingLibrary: boolean, + * folderWrapper: boolean, + * vueVersion: VueVersion * }} CommanderOptions - Commander command line options * + * @typedef {{ + * alias: { + * src: string + * }, + * exportDefault: boolean + * }} GlobalConfig - Clingon global config object + * * @typedef {"Page" | "Component" | "Function" | "Type" | "Model" | "Enum" | "Test" | "Spec" | "Cypress Spec" | "Storybook Story"} TypeNames - Resource type names * * @typedef {"Vue" | "React" | "Vanilla"} FrameworkNames - Framework names diff --git a/src/utils/file.js b/src/utils/file.js index 8cc06c6..99dff49 100644 --- a/src/utils/file.js +++ b/src/utils/file.js @@ -46,3 +46,19 @@ export function createFileWithContent(filename, content) { return false } } + +/** + * Check if file exists + * + * @param {string} fullPath File path + * @returns {boolean} + */ +export function checkFileExists(fullPath) { + try { + fs.accessSync(fullPath, fs.constants.F_OK) + + return true + } catch { + return false + } +} diff --git a/src/utils/file.test.js b/src/utils/file.test.js index d27737b..da9df01 100644 --- a/src/utils/file.test.js +++ b/src/utils/file.test.js @@ -3,16 +3,22 @@ import fs from 'node:fs' import assert from 'node:assert/strict' import { describe, it, todo, mock } from 'node:test' -import { createFileWithContent, getFiles, readFileContent } from './file.js' - +import { + checkFileExists, + createFileWithContent, + getFiles, + readFileContent +} from './file.js' + +const accesSync = mock.method(fs, 'accessSync') const mockFsReadDirSync = mock.method(fs, 'readdirSync') const mockFsReadFileSync = mock.method(fs, 'readFileSync') -const mockFsWrtieFileSync = mock.method(fs, 'writeFileSync') +const mockFsWriteFileSync = mock.method(fs, 'writeFileSync') const mockFolderFiles = ['mockFile.json', 'mocks.js', 'fakeFile.ts'] describe('File Util', () => { - describe('getFiles util', () => { + describe('getFiles', () => { it('get files from root dir', () => { mockFsReadDirSync.mock.mockImplementation(() => mockFolderFiles) @@ -32,7 +38,7 @@ describe('File Util', () => { }) }) - describe('readFileContent util', () => { + describe('readFileContent', () => { it('get file content', () => { mockFsReadFileSync.mock.mockImplementation((fileName) => { const files = { @@ -57,9 +63,9 @@ describe('File Util', () => { }) }) - describe('createFileWithContent util', () => { + describe('createFileWithContent', () => { it('create a file with content', () => { - mockFsWrtieFileSync.mock.mockImplementation(() => true) + mockFsWriteFileSync.mock.mockImplementation(() => true) const success = createFileWithContent('test.json', 'content') @@ -67,7 +73,7 @@ describe('File Util', () => { }) it('create a file with content should throw error', () => { - mockFsWrtieFileSync.mock.mockImplementation(() => { + mockFsWriteFileSync.mock.mockImplementation(() => { throw new Error('wrong file format') }) @@ -78,4 +84,40 @@ describe('File Util', () => { assert.strictEqual(success, false) }) }) + + describe('checkFileExists', () => { + it('return true if the file exists', () => { + accesSync.mock.mockImplementation(() => true) + + const fileExists = checkFileExists('./myDirectory/myFile.txt') + + assert.strictEqual(fileExists, true) + }) + + it.skip('return false if the file does not exist', () => { + accesSync.mock.mockImplementation(() => false) + + const fileExists = checkFileExists('./myDirectory/nonExistentFile.txt') + + assert.strictEqual(fileExists, false) + }) + + it('return true for an existing nested file', () => { + accesSync.mock.mockImplementation(() => true) + + const fileExists = checkFileExists('./myDirectory/nested/nestedFile.txt') + + assert.strictEqual(fileExists, true) + }) + + it.skip('return false for a non-existing nested file', () => { + accesSync.mock.mockImplementation(() => false) + + const fileExists = checkFileExists( + './myDirectory/nested/nonExistentFile.txt' + ) + + assert.strictEqual(fileExists, false) + }) + }) }) diff --git a/src/utils/init-action.js b/src/utils/init-action.js new file mode 100644 index 0000000..7f68d63 --- /dev/null +++ b/src/utils/init-action.js @@ -0,0 +1,114 @@ +import { join } from 'node:path' + +import { + checkFileExists, + createFileWithContent, + readFileContent +} from './file.js' +import { defaultConfig } from '../constants/config.js' +import { createPresetsFolder } from './preset.js' +import { checkDirectoriesTree } from './directory.js' + +/* + * ---------------------------------------- + * Global Config + * ---------------------------------------- + */ + +/** + * Get config file path + * + * @returns {string | undefined} + */ +export function getConfigFilePath() { + const fullPath = join(process.cwd(), 'clingon.config.json') + const fileExists = checkFileExists(fullPath) + + if (!fileExists) return undefined + + return fullPath +} + +/** + * Create the config file if it does not exist + * + * @param {ReturnType} filePath + */ +export function createFileIfNotExists(filePath) { + if (filePath) { + console.info('\nโœ… You already have config at: ', filePath) + + return filePath + } + + const success = createFileWithContent( + 'clingon.config.json', + JSON.stringify(defaultConfig, null, 2) + ) + const fullPath = join(process.cwd(), 'clingon.config.json') + + if (success) { + console.info('๐ŸŒŽ Global config file created at: ', fullPath) + } else { + console.error('โŒ Error: Cannot create global config file, try again.') + } + + return filePath +} + +/** + * Get config file content + * + * @param {ReturnType} filePath Config file path + * @returns {import('../types.js').GlobalConfig | string} + */ +export function getConfigContent(filePath) { + const exists = checkFileExists(filePath) + + if (!exists) return null + + const fileContent = readFileContent(filePath) + const fileContentParsed = JSON.parse(fileContent) + + return fileContentParsed +} + +/* + * ---------------------------------------- + * Preset Folder + * ---------------------------------------- + */ + +const dotClingonDir = '.clingon' +const presetsDir = 'presets' +const presetFullDir = join(process.cwd(), dotClingonDir, presetsDir) + +/** + * Check if `.clingon/prests` folde exists + * + * @returns {boolean} + */ +export function checkIfPresetFolderAlreadyExists() { + return checkDirectoriesTree([dotClingonDir, presetsDir]) +} + +/** + * Create `.clingon/prests` if not exists + * + * @param {boolean} exists Folder exists? + */ +export function createPresetFolderIfNotExists(exists) { + if (exists) { + return console.info( + '\nโœ… You already have presets folder at: ', + presetFullDir + ) + } + + const created = createPresetsFolder() + + if (!created) + console.error('\nโŒ Error: cannot create presets dir, try again') + + console.info('\nโœ… Presets folder created at: ', presetFullDir) +} diff --git a/src/utils/init-action.test.js b/src/utils/init-action.test.js new file mode 100644 index 0000000..ee52293 --- /dev/null +++ b/src/utils/init-action.test.js @@ -0,0 +1,75 @@ +import fs from 'node:fs' +import assert from 'node:assert/strict' + +import { describe, it, mock } from 'node:test' + +import { + checkIfPresetFolderAlreadyExists, + createFileIfNotExists, + getConfigContent, + getConfigFilePath +} from './init-action.js' + +const configFileName = 'clingon.config.json' + +const mockFsAccessSync = mock.method(fs, 'accessSync') +const mockExistsSync = mock.method(fs, 'existsSync') +const mockFsReadFileSync = mock.method(fs, 'readFileSync') +const mockFsWriteFileSync = mock.method(fs, 'writeFileSync') + +describe('Init Action Utils', () => { + describe('Global Config composing methods', () => { + it('get config file path', () => { + mockFsAccessSync.mock.mockImplementation(() => true) + + const result = getConfigFilePath() + + const expectedPath = result.includes('clingon.config.json') + + assert.strictEqual(expectedPath, true) + }) + + it('create file if not exists', () => { + mockFsAccessSync.mock.mockImplementation(() => true) + mockFsWriteFileSync.mock.mockImplementation(() => true) + + const result = createFileIfNotExists(getConfigFilePath()) + + const expectedPath = result.includes('clingon.config.json') + + assert.strictEqual(expectedPath, true) + }) + + it('get config content', () => { + mockFsAccessSync.mock.mockImplementation(() => true) + mockFsReadFileSync.mock.mockImplementation((fileName) => { + fileName = fileName.split('clingon/')[1] + + const files = { + [configFileName]: '{"exportDefault":false,"alias":{"src":"@"}}' + } + + return files[fileName] + }) + + const result = getConfigContent(getConfigFilePath()) + + assert.deepEqual(result, { + exportDefault: false, + alias: { src: '@' } + }) + }) + }) + + describe('Presets Folder', () => { + it('check if folders exists', () => { + mockExistsSync.mock.mockImplementation((value) => + '.clingon/presets'.search(value) + ) + + const exists = checkIfPresetFolderAlreadyExists(['.clingon', 'presets']) + + assert.strictEqual(exists, true) + }) + }) +}) diff --git a/src/utils/preset.js b/src/utils/preset.js index 707577a..79cfc86 100644 --- a/src/utils/preset.js +++ b/src/utils/preset.js @@ -9,35 +9,43 @@ export const dotClingon = '.clingon' export const presetsDir = 'presets' export const presetsExtension = '.json' +export function createPresetsFolder() { + let createdRootDir + let createdPresetDir + + if (!checkDirectoriesTree([dotClingon])) { + createdRootDir = createDir(join(rootDir, dotClingon)) + } + + if (!checkDirectoriesTree([dotClingon, presetsDir])) { + createdPresetDir = createDir(join(rootDir, dotClingon, presetsDir)) + } + + if (!createdRootDir || !createdPresetDir) return false + + return true +} + export function getPresetFiles() { const hasDotClingonPath = checkDirectoriesTree([dotClingon, presetsDir]) - if (hasDotClingonPath) { - const presetFilesPath = join(rootDir, dotClingon, presetsDir) - - const files = getFiles(presetFilesPath) + if (!hasDotClingonPath) { + const created = createPresetsFolder() - const presets = files.map((path) => { - const pieces = path.split('/') + if (created) return getPresetFiles() + } - return pieces[pieces.length - 1] - }) + const presetFilesPath = join(rootDir, dotClingon, presetsDir) - return presets - } else { - let createdRootDir - let createdPresetDir + const files = getFiles(presetFilesPath) - if (!checkDirectoriesTree([dotClingon])) { - createdRootDir = createDir(join(rootDir, dotClingon)) - } + const presets = files.map((path) => { + const pieces = path.split('/') - if (!checkDirectoriesTree([dotClingon, presetsDir])) { - createdPresetDir = createDir(join(rootDir, dotClingon, presetsDir)) - } + return pieces[pieces.length - 1] + }) - if (createdRootDir && createdPresetDir) getPresetFiles() - } + return presets } export function getPresetFileContent(fileName) { diff --git a/src/utils/preset.test.js b/src/utils/preset.test.js index ad16307..4331c55 100644 --- a/src/utils/preset.test.js +++ b/src/utils/preset.test.js @@ -1,5 +1,5 @@ import fs from 'node:fs' -import assert from 'node:assert' +import assert from 'node:assert/strict' import { describe, it, mock } from 'node:test' @@ -44,7 +44,9 @@ const mockFolderFilesName = [ describe('preset', () => { it('get preset files', () => { mockFsReadDirSync.mock.mockImplementation(() => mockFolderFiles) - mockExistsSync.mock.mockImplementation((value) => nestedStrucuture.search(value)) + mockExistsSync.mock.mockImplementation((value) => + nestedStrucuture.search(value) + ) mockFsMkdirSync.mock.mockImplementation(() => true) mockStatSync.mock.mockImplementation((value) => ({ @@ -62,7 +64,9 @@ describe('preset', () => { it('get preset file name to show as object', () => { mockFsReadDirSync.mock.mockImplementation(() => mockFolderFiles) - mockExistsSync.mock.mockImplementation((value) => nestedStrucuture.search(value)) + mockExistsSync.mock.mockImplementation((value) => + nestedStrucuture.search(value) + ) mockFsMkdirSync.mock.mockImplementation(() => true) const presets = getPresetFiles() @@ -73,7 +77,9 @@ describe('preset', () => { it('get preset as inquirer choice { name: string, value: string }[]', () => { mockFsReadDirSync.mock.mockImplementation(() => mockFolderFiles) - mockExistsSync.mock.mockImplementation((value) => nestedStrucuture.search(value)) + mockExistsSync.mock.mockImplementation((value) => + nestedStrucuture.search(value) + ) mockFsMkdirSync.mock.mockImplementation(() => true) const presets = getPresetFiles() @@ -100,7 +106,9 @@ describe('preset', () => { mockFsReadDirSync.mock.mockImplementation(() => srcDirs) mockFsMkdirSync.mock.mockImplementation(() => true) - mockExistsSync.mock.mockImplementation((value) => nestedStrucuture.search(value)) + mockExistsSync.mock.mockImplementation((value) => + nestedStrucuture.search(value) + ) mockFsReadFileSync.mock.mockImplementation((fileName) => { if (fileName.includes('.clingon/presets/test.json')) { return '{"name":"test"}'