From 30e828c55fb38574c27efef782ba38d265d64858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:38:20 +0200 Subject: [PATCH 01/28] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0145d94..cceadbf 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@types/lodash-es": "^4.17.12", "@types/node": "^20.19.0", "@types/yargs": "^17.0.33", - "prettier": "^2.8.1", + "prettier": "^3.0.0", "typescript": "^5.4.5" }, "dependencies": { From 965373807c883c5d11f5b49f94c9887965d3fdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:44:16 +0200 Subject: [PATCH 02/28] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20styleguide?= =?UTF-8?q?-config=20to=201.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cceadbf..eeef310 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "homepage": "https://github.com/AckeeCZ/create-node-app#readme", "devDependencies": { - "@ackee/styleguide-backend-config": "^1.0.0", + "@ackee/styleguide-backend-config": "^1.0.1", "@types/lodash-es": "^4.17.12", "@types/node": "^20.19.0", "@types/yargs": "^17.0.33", From 039c1f68237b68e8e65ab0fbcd6df123adf440f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:45:39 +0200 Subject: [PATCH 03/28] =?UTF-8?q?=F0=9F=94=A8=20Add=20jsonc=20prettier=20v?= =?UTF-8?q?alidation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index eeef310..8e4b17f 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "type": "module", "scripts": { "build": "tsc", - "prettier:check": "prettier --ignore-path .gitignore --check '**/*.{ts,js,json,md}'", - "prettier:fix": "npm run prettier:check -- --write '**/*.{ts,js,json,md}'", + "prettier:check": "prettier --ignore-path .gitignore --check '**/*.{ts,js,json,jsonc,md}'", + "prettier:fix": "npm run prettier:check -- --write '**/*.{ts,js,json,jsonc,md}'", "lint:check": "eslint --ignore-path .gitignore 'src/**/*.ts' -f codeframe", "lint:fix": "npm run lint:check -- --fix" }, From 2ea4cbf2ab9b771d8295582de42c9957d3cbc5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:46:23 +0200 Subject: [PATCH 04/28] =?UTF-8?q?=E2=9E=95=20Add=20inquirer=20ora=20and=20?= =?UTF-8?q?fast-glob?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 3029 +++++++++++++++++++-------------------------- package.json | 3 + 2 files changed, 1252 insertions(+), 1780 deletions(-) diff --git a/package-lock.json b/package-lock.json index f591b33..f40b378 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "version": "1.0.1", "license": "MIT", "dependencies": { + "fast-glob": "^3.3.3", + "inquirer": "^12.9.0", "lodash-es": "^4.17.21", + "ora": "^8.2.0", "source-map-support": "^0.5.21", "yargs": "^18.0.0" }, @@ -17,11 +20,11 @@ "create-node-app": "bin/create-node-app.js" }, "devDependencies": { - "@ackee/styleguide-backend-config": "^1.0.0", + "@ackee/styleguide-backend-config": "^1.0.1", "@types/lodash-es": "^4.17.12", "@types/node": "^20.19.0", "@types/yargs": "^17.0.33", - "prettier": "^2.8.1", + "prettier": "^3.0.0", "typescript": "^5.4.5" }, "engines": { @@ -29,15 +32,14 @@ } }, "node_modules/@ackee/styleguide-backend-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ackee/styleguide-backend-config/-/styleguide-backend-config-1.0.0.tgz", - "integrity": "sha512-zVfzNsNL68cwOKQWA6+Yzj3O1gYCw/CTLj1x9u3MK++Yu7VHp52AsuEAWh8FAHMFbXntKENJlBjlwFwq463qJQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ackee/styleguide-backend-config/-/styleguide-backend-config-1.0.1.tgz", + "integrity": "sha512-ntCyV2E3g7673wY4p0QoygLwKVyh+8MdOtGd/PFXsQSo0RV+jTa+2pRA2wi/QovTN68MF6ogIMWK6kiUQQlCnA==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "^8.16.0", "@typescript-eslint/parser": "^8.16.0", - "cspell-lib": "^6.17.0", "eslint": "^8.57.0", "eslint-config-love": "^84.1.1", "eslint-formatter-codeframe": "^7.32.1", @@ -52,22 +54,6 @@ "node": ">=18.0.0" } }, - "node_modules/@ackee/styleguide-backend-config/node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2112,368 +2098,6 @@ "node": ">=6.9.0" } }, - "node_modules/@cspell/cspell-bundled-dicts": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-6.18.1.tgz", - "integrity": "sha512-3rGSZ5brzt9KFCoa1QVna8SiYnYzB8hqQyrWjtoJhV5SWjD4MpBtNt2xm5JtfNONWHeiHvkpPHasXjJvYXwDNg==", - "dev": true, - "dependencies": { - "@cspell/dict-ada": "^4.0.1", - "@cspell/dict-aws": "^3.0.0", - "@cspell/dict-bash": "^4.1.1", - "@cspell/dict-companies": "^3.0.5", - "@cspell/dict-cpp": "^4.0.1", - "@cspell/dict-cryptocurrencies": "^3.0.1", - "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.1", - "@cspell/dict-dart": "^2.0.1", - "@cspell/dict-django": "^4.0.1", - "@cspell/dict-docker": "^1.1.4", - "@cspell/dict-dotnet": "^4.0.1", - "@cspell/dict-elixir": "^4.0.1", - "@cspell/dict-en_us": "^4.1.2", - "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.0", - "@cspell/dict-fonts": "^3.0.0", - "@cspell/dict-fullstack": "^3.0.0", - "@cspell/dict-gaming-terms": "^1.0.3", - "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^5.0.1", - "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.2", - "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.3", - "@cspell/dict-k8s": "^1.0.0", - "@cspell/dict-latex": "^3.1.0", - "@cspell/dict-lorem-ipsum": "^3.0.0", - "@cspell/dict-lua": "^3.0.0", - "@cspell/dict-node": "^4.0.2", - "@cspell/dict-npm": "^5.0.2", - "@cspell/dict-php": "^3.0.4", - "@cspell/dict-powershell": "^3.0.0", - "@cspell/dict-public-licenses": "^2.0.1", - "@cspell/dict-python": "^4.0.1", - "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^3.0.0", - "@cspell/dict-rust": "^3.0.0", - "@cspell/dict-scala": "^3.0.0", - "@cspell/dict-software-terms": "^3.0.7", - "@cspell/dict-sql": "^2.0.1", - "@cspell/dict-svelte": "^1.0.1", - "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-typescript": "^3.1.0", - "@cspell/dict-vue": "^3.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@cspell/cspell-pipe": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-6.18.1.tgz", - "integrity": "sha512-IFtZBae5BCBIPZuRhEs0U0emFrh5hmN0N4+WR5paP4UurV5Ql9n2JsSj1Bmdx79aSFAw4mGpJnhZZtGQcFDnPQ==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@cspell/cspell-service-bus": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-6.18.1.tgz", - "integrity": "sha512-QVbVA8Ube+Z4ghywzsTQLxqdiCubYi7L/+KeFRatzh3bZ5K5pVcYHEbDhAlFdUj6FhXw0EP2n/Xb+8ZLye4LLg==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@cspell/cspell-types": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-6.18.1.tgz", - "integrity": "sha512-5X+ABUMPrCoCjQvbqb/HeCoNiSgUrJhR9O4tSlMU5/z0NRNLFSyjf+3LE6ZU2+kdwNU7tmYCr+cbCpb3UKpvQQ==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@cspell/dict-ada": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.1.tgz", - "integrity": "sha512-/E9o3nHrXOhYmQE43deKbxZcR3MIJAsa+66IzP9TXGHheKEx8b9dVMVVqydDDH8oom1H0U20NRPtu6KRVbT9xw==", - "dev": true - }, - "node_modules/@cspell/dict-aws": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-3.0.0.tgz", - "integrity": "sha512-O1W6nd5y3Z00AMXQMzfiYrIJ1sTd9fB1oLr+xf/UD7b3xeHeMeYE2OtcWbt9uyeHim4tk+vkSTcmYEBKJgS5bQ==", - "dev": true - }, - "node_modules/@cspell/dict-bash": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.1.tgz", - "integrity": "sha512-8czAa/Mh96wu2xr0RXQEGMTBUGkTvYn/Pb0o+gqOO1YW+poXGQc3gx0YPqILDryP/KCERrNvkWUJz3iGbvwC2A==", - "dev": true - }, - "node_modules/@cspell/dict-companies": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.5.tgz", - "integrity": "sha512-f5lVcL/dG2kUHh8QFLakU722lgMwqXSjZUdmW6QdOMmqcE8cgl+oN9qk/qYlCSBMsYA7uexwn3hIr4h0naoPlw==", - "dev": true - }, - "node_modules/@cspell/dict-cpp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-4.0.1.tgz", - "integrity": "sha512-mD6mn0XFCqHCz2j6p/7OQm3yNFn1dlQq6vip1pLynvNWDRz5yKYDVRUQCTEORT7ThS0dLpI4BjCX84YUKNhibA==", - "dev": true - }, - "node_modules/@cspell/dict-cryptocurrencies": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-3.0.1.tgz", - "integrity": "sha512-Tdlr0Ahpp5yxtwM0ukC13V6+uYCI0p9fCRGMGZt36rWv8JQZHIuHfehNl7FB/Qc09NCF7p5ep0GXbL+sVTd/+w==", - "dev": true - }, - "node_modules/@cspell/dict-csharp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.2.tgz", - "integrity": "sha512-1JMofhLK+4p4KairF75D3A924m5ERMgd1GvzhwK2geuYgd2ZKuGW72gvXpIV7aGf52E3Uu1kDXxxGAiZ5uVG7g==", - "dev": true - }, - "node_modules/@cspell/dict-css": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.1.tgz", - "integrity": "sha512-jxsncdeiN/wkZGqU8iLtn24n3e0Fwugj6T48rjWUItn/i3C9j2W7RXOVqd7ZIeWeV8ibyq0WWiwA8Ajg6XaKpA==", - "dev": true - }, - "node_modules/@cspell/dict-dart": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.1.tgz", - "integrity": "sha512-YRuDX9k2qPSWDEsM26j8o7KMvaZ0DXc74ijK/VRwaksm1CBRPBW289pe2TE2K7y4SJjTKXgQ9urOVlozeQDpuA==", - "dev": true - }, - "node_modules/@cspell/dict-django": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.0.1.tgz", - "integrity": "sha512-q3l7OH39qzeN2Y64jpY39SEAqki5BUzPTypnhzM40yT+LOGSWqSh9Ix5UecejtXPDVrD8vML+m7Bp5070h52HQ==", - "dev": true - }, - "node_modules/@cspell/dict-docker": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.5.tgz", - "integrity": "sha512-SNEohOScQ+0+y9dp/jKTx60OOJQrf5es5BJ32gh5Ck3jKXNo4wd9KLgPOmQMUpencb5SGjrBsC4rr1fyfCwytg==", - "dev": true - }, - "node_modules/@cspell/dict-dotnet": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-4.0.1.tgz", - "integrity": "sha512-l11TqlUX8cDgsE/1Zrea1PqLn63s20MY3jKWMbQVB5DMDPDO2f8Pukckkwxq5p/cxDABEjuGzfF1kTX3pAakBw==", - "dev": true - }, - "node_modules/@cspell/dict-elixir": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.1.tgz", - "integrity": "sha512-IejBqiTTWSXpvBm6yg4qUfnJR0LwbUUCJcK5wXOMKEJitu3yDfrT9GPc6NQJXgokbg9nBjEyxVIzNcLgx2x3/Q==", - "dev": true - }, - "node_modules/@cspell/dict-en_us": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.1.4.tgz", - "integrity": "sha512-smRT8Rx38+z1kiNl3kBvadoPdYgxCovxw2rsuO4/XtLRlSEcGPQgYJ0CCdcXMd9bhMY5roXPCcvYkBsyUVvg4A==", - "dev": true - }, - "node_modules/@cspell/dict-en-gb": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", - "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", - "dev": true - }, - "node_modules/@cspell/dict-filetypes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.0.tgz", - "integrity": "sha512-Fiyp0z5uWaK0d2TfR9GMUGDKmUMAsOhGD5A0kHoqnNGswL2iw0KB0mFBONEquxU65fEnQv4R+jdM2d9oucujuA==", - "dev": true - }, - "node_modules/@cspell/dict-fonts": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-3.0.0.tgz", - "integrity": "sha512-zTZni0AbwBVG1MKA0WpwPyIJPVF+gp6neXDQzHcu4RUnuQ4uDu0PVEuZjGHCJWwwFoR5JmkqZxVSg1y3ufJODA==", - "dev": true - }, - "node_modules/@cspell/dict-fullstack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.0.1.tgz", - "integrity": "sha512-r077HcbxGQ0gSjs4eqryvb9cu8/Noe7pzl9QksxFIEaMgyP180DEaCLAOnat4KHl7X0wntipY+naY5PVRQUI9A==", - "dev": true - }, - "node_modules/@cspell/dict-gaming-terms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.4.tgz", - "integrity": "sha512-hbDduNXlk4AOY0wFxcDMWBPpm34rpqJBeqaySeoUH70eKxpxm+dvjpoRLJgyu0TmymEICCQSl6lAHTHSDiWKZg==", - "dev": true - }, - "node_modules/@cspell/dict-git": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-2.0.0.tgz", - "integrity": "sha512-n1AxyX5Kgxij/sZFkxFJlzn3K9y/sCcgVPg/vz4WNJ4K9YeTsUmyGLA2OQI7d10GJeiuAo2AP1iZf2A8j9aj2w==", - "dev": true - }, - "node_modules/@cspell/dict-golang": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-5.0.1.tgz", - "integrity": "sha512-djsJC7OVKUpFdRm/aqBJEUSGP3kw/MDhAt7udYegnyQt2WjL3ZnVoG7r5eOEhPEEKzWVBYoi6UKSNpdQEodlbg==", - "dev": true - }, - "node_modules/@cspell/dict-haskell": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.1.tgz", - "integrity": "sha512-uRrl65mGrOmwT7NxspB4xKXFUenNC7IikmpRZW8Uzqbqcu7ZRCUfstuVH7T1rmjRgRkjcIjE4PC11luDou4wEQ==", - "dev": true - }, - "node_modules/@cspell/dict-html": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.2.tgz", - "integrity": "sha512-BskOE2K3AtGLkcjdJmo+H6/fjdfDP4XYAsEGXpB26rvdnXAnGEstE/Q8Do6UfJCvgOVYCpdUZLcMIEpoTy7QhQ==", - "dev": true - }, - "node_modules/@cspell/dict-html-symbol-entities": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.0.tgz", - "integrity": "sha512-HGRu+48ErJjoweR5IbcixxETRewrBb0uxQBd6xFGcxbEYCX8CnQFTAmKI5xNaIt2PKaZiJH3ijodGSqbKdsxhw==", - "dev": true - }, - "node_modules/@cspell/dict-java": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.4.tgz", - "integrity": "sha512-43VrLOLcBxavv6eyL4BpsnHrhVOgyYYeJqQRJG5XKObcpWy3+Lpadj58CfTVOr7M/Je3pUpd4tvsUhf/lWXMVA==", - "dev": true - }, - "node_modules/@cspell/dict-k8s": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.0.tgz", - "integrity": "sha512-XqIql+nd2DiuPuL+qPc24bN/L1mZY75kAYcuMBMW5iYgBoivkiVOg7br/aofX3ApajvHDln6tNkPZhmhsOg6Ww==", - "dev": true - }, - "node_modules/@cspell/dict-latex": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-3.1.0.tgz", - "integrity": "sha512-XD5S3FY0DrYiun2vm/KKOkeaD30LXp9v5EzVTVQvmxqQrQh0HvOT3TFD7lgKbyzZaG7E+l3wS94uwwm80cOmuw==", - "dev": true - }, - "node_modules/@cspell/dict-lorem-ipsum": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-3.0.0.tgz", - "integrity": "sha512-msEV24qEpzWZs2kcEicqYlhyBpR0amfDkJOs+iffC07si9ftqtQ+yP3lf1VFLpgqw3SQh1M1vtU7RD4sPrNlcQ==", - "dev": true - }, - "node_modules/@cspell/dict-lua": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-3.0.0.tgz", - "integrity": "sha512-WOhSCgS5wMxkGQJ8siB90iTB9ElquJB7FeqYSbJqqs6cUwH8G7MM/CEDPL6h7vCo0+v3GuxQ8yKWDSUcUhz9Lg==", - "dev": true - }, - "node_modules/@cspell/dict-node": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-4.0.2.tgz", - "integrity": "sha512-FEQJ4TnMcXEFslqBQkXa5HposMoCGsiBv2ux4IZuIXgadXeHKHUHk60iarWpjhzNzQLyN2GD7NoRMd12bK3Llw==", - "dev": true - }, - "node_modules/@cspell/dict-npm": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.3.tgz", - "integrity": "sha512-fEX67zIJISbS3gXVk/y/ZUvDIVtjc/CYJK7Mz0iTVrmlCKnLiD41lApe8v4g/12eE7hLfx/sfCXDrUWyzXVq1A==", - "dev": true - }, - "node_modules/@cspell/dict-php": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-3.0.4.tgz", - "integrity": "sha512-QX6zE/ZfnT3O5lSwV8EPVh8Va39ds34gSNNR8I4GWiuDpKcTkZPFi4OLoP3Tlhbl/3G0Ha35OkSDLvZfu8mnkA==", - "dev": true - }, - "node_modules/@cspell/dict-powershell": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-3.0.0.tgz", - "integrity": "sha512-pkztY9Ak4oc33q+Qxcn9/CTOKo4N8YIRRE6v67WwQOncA5QIJfcOPUrjfR3Z8SpzElXhu3s9qtWWSqbCy6qmcA==", - "dev": true - }, - "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.1.tgz", - "integrity": "sha512-NZNwzkL5BqKddepDxvX/Qbji378Mso1TdnV4RFAN8hJoo6dSR0fv2TTI/Y0i/YWBmfmQGyTpEztBXtAw4qgjiA==", - "dev": true - }, - "node_modules/@cspell/dict-python": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.0.1.tgz", - "integrity": "sha512-1wtUgyaTqRiQY0/fryk0oW22lcxNUnZ5DwteTzfatMdbgR0OHXTlHbI8vYxpHLWalSoch7EpLsnaymG+fOrt8g==", - "dev": true - }, - "node_modules/@cspell/dict-r": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.0.1.tgz", - "integrity": "sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA==", - "dev": true - }, - "node_modules/@cspell/dict-ruby": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-3.0.0.tgz", - "integrity": "sha512-sA98T8Y1Pmq3RStVkO14E8vTWkq6JUn8c8PldiMyYgV0yfQgwhQfFAzlSfF3Gg2B0VkIdqt2et2SPN7f9wp7fQ==", - "dev": true - }, - "node_modules/@cspell/dict-rust": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-3.0.0.tgz", - "integrity": "sha512-L1T1IBsYJZVDmfOGAbVLcpc6arWxRRCSJYvHSwEDBGrNuMyJ4jx/NvBEz5crcKf4vVKgwVlXgzQlJJZ8AVxU9w==", - "dev": true - }, - "node_modules/@cspell/dict-scala": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-3.0.0.tgz", - "integrity": "sha512-sIiCQDIMMnNns/fzD61z5npbh5pypaKq07Orqe0+eRfdQpika8iRSGUGFHVbtdd1JzB1DyTCV2e8OwdaQiXqJQ==", - "dev": true - }, - "node_modules/@cspell/dict-software-terms": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.0.8.tgz", - "integrity": "sha512-otq0yIcG19rNXkmE/EGWgUK7ClLrn/BE4n5Di3HKLw6XEp0sNBp1DKf88bg0LvbWh15uCAJ5xKAzF1sVPy2Y3w==", - "dev": true - }, - "node_modules/@cspell/dict-sql": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.0.1.tgz", - "integrity": "sha512-7fvVcvy751cl31KMD5j04yMGq2UKj018/1hx3FNtdUI9UuUTMvhBrTAqHEEemR3ZeIC9i/5p5SQjwQ13bn04qw==", - "dev": true - }, - "node_modules/@cspell/dict-svelte": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.2.tgz", - "integrity": "sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q==", - "dev": true - }, - "node_modules/@cspell/dict-swift": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.1.tgz", - "integrity": "sha512-gxrCMUOndOk7xZFmXNtkCEeroZRnS2VbeaIPiymGRHj5H+qfTAzAKxtv7jJbVA3YYvEzWcVE2oKDP4wcbhIERw==", - "dev": true - }, - "node_modules/@cspell/dict-typescript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.0.tgz", - "integrity": "sha512-4hdLlQMOYrUbGfJg2cWnbsBUevObwgL76TLVC0rwnrkSwzOxAxiGaG39VtRMvgAAe2lX6L+jka3fy0MmxzFOHw==", - "dev": true - }, - "node_modules/@cspell/dict-vue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.0.tgz", - "integrity": "sha512-niiEMPWPV9IeRBRzZ0TBZmNnkK3olkOPYxC1Ny2AX4TGlYRajcW0WUtoSHmvvjZNfWLSg2L6ruiBeuPSbjnG6A==", - "dev": true - }, - "node_modules/@cspell/strong-weak-map": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-6.18.1.tgz", - "integrity": "sha512-etyMLISiDzgFf1BSGpUBD62cHp9NrCyrOi+iT7WrJ+My0l6IPRIhANuAVp2JcsXxe28en4X3Bp/egd46Q5Rpkg==", - "dev": true, - "engines": { - "node": ">=14.6" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -2633,84 +2257,487 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, + "node_modules/@inquirer/checkbox": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", + "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, + "node_modules/@inquirer/confirm": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, + "node_modules/@inquirer/core": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", "license": "MIT", "dependencies": { - "eslint-scope": "5.1.1" + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=8.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/@inquirer/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=4.0" + "node": ">=7.0.0" + } + }, + "node_modules/@inquirer/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", + "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/external-editor": "^1.0.1", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.0.tgz", + "integrity": "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.0.tgz", + "integrity": "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -2724,7 +2751,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -2734,7 +2760,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -2779,7 +2804,7 @@ "version": "20.19.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3080,15 +3105,41 @@ "uri-js": "^4.2.2" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3162,12 +3213,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -3424,7 +3469,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -3588,22 +3632,48 @@ "node": ">=4" } }, - "node_modules/clear-module": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", - "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", - "dev": true, + "node_modules/chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "license": "MIT", "dependencies": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, "node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -3657,22 +3727,6 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", - "dev": true, - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3680,23 +3734,6 @@ "dev": true, "license": "MIT" }, - "node_modules/configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3718,27 +3755,6 @@ "url": "https://opencollective.com/core-js" } }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3754,120 +3770,6 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cspell-dictionary": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-6.18.1.tgz", - "integrity": "sha512-q+tK+MWvJs9xL8wv79YlGPddUFb3Usuqh+VB8D0Zs7Xlsa/cw9bljRluHkpQrNr8APdZijGlgQP8L0cEr0/rEw==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "cspell-trie-lib": "6.18.1", - "fast-equals": "^4.0.3", - "gensequence": "^4.0.3" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/cspell-glob": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-6.18.1.tgz", - "integrity": "sha512-6dBBtQ1lRnVPoM13GOv7mJflkIvEr93TN96saQPWoaQqX8jwmklcMmDUndIkLcA7TnyxBbi3Z3X+s68zj/YGqw==", - "dev": true, - "dependencies": { - "micromatch": "^4.0.5" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/cspell-grammar": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-6.18.1.tgz", - "integrity": "sha512-QPjOA9xwDPb3aoJXUOdL2aWX2wt8lPD7CoDROo8uruOXHAQzIY56q12EBy3jLIkxJFl9KAwtlEHkbLaJfTpIpg==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1" - }, - "bin": { - "cspell-grammar": "bin.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/cspell-io": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-6.18.1.tgz", - "integrity": "sha512-WIfiDdG/7235CbkrjKPYIkP9oT8VvWXVTAeq6JkJPH7bm2A/CoE8ClieVsbbJnPyJnetnCuOuuz/zmuheVD02g==", - "dev": true, - "dependencies": { - "@cspell/cspell-service-bus": "6.18.1", - "node-fetch": "^2.6.7" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/cspell-lib": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-6.18.1.tgz", - "integrity": "sha512-4MGjp51Ed8BbMPGXgqLGgUiWyb2DbOxgVEuWm8nxumxu7UmAWDBdMiD3QlY+ZYmfOJEVSa/kG7DTMrLQoeFwnQ==", - "dev": true, - "dependencies": { - "@cspell/cspell-bundled-dicts": "6.18.1", - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "@cspell/strong-weak-map": "6.18.1", - "clear-module": "^4.1.2", - "comment-json": "^4.2.3", - "configstore": "^5.0.1", - "cosmiconfig": "^8.0.0", - "cspell-dictionary": "6.18.1", - "cspell-glob": "6.18.1", - "cspell-grammar": "6.18.1", - "cspell-io": "6.18.1", - "cspell-trie-lib": "6.18.1", - "fast-equals": "^4.0.3", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "gensequence": "^4.0.3", - "import-fresh": "^3.3.0", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "vscode-languageserver-textdocument": "^1.0.8", - "vscode-uri": "^3.0.7" - }, - "engines": { - "node": ">=14.6" - } - }, - "node_modules/cspell-trie-lib": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-6.18.1.tgz", - "integrity": "sha512-rV32bqchz0uYdK6uafaw5QnYImRWQMcT2RNbBo0LXN6XoYoTSgpnPWTxQauNLxOm1m+dfb3GdasoAsjgWkPGnQ==", - "dev": true, - "dependencies": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "fs-extra": "^11.1.0", - "gensequence": "^4.0.3" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4049,18 +3951,6 @@ "node": ">=6.0.0" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4102,15 +3992,6 @@ "node": ">=10.13.0" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -5773,19 +5654,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5839,17 +5707,10 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-equals": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", - "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", - "dev": true - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5866,7 +5727,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -5893,7 +5753,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -5916,7 +5775,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -5979,20 +5837,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6048,15 +5892,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensequence": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-4.0.3.tgz", - "integrity": "sha512-izr+MKqJKjexkvLiPGhW96elQX8TuUR/su/xzILxjqzU1RDz1n1ZbqwDUnNFaRcq0gFR3oQfNH2JOH4Je1x/QA==", - "dev": true, - "engines": { - "node": ">=14" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6215,18 +6050,6 @@ "node": "*" } }, - "node_modules/global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "dependencies": { - "ini": "^1.3.4" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -6339,15 +6162,6 @@ "node": ">=4" } }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -6419,6 +6233,18 @@ "node": ">= 0.4" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -6494,11 +6320,31 @@ "dev": true, "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "node_modules/inquirer": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.0.tgz", + "integrity": "sha512-LlFVmvWVCun7uEgPB3vups9NzBrjJn48kRNtFGw3xU1H5UXExTEz/oF1JGLaB0fvlkUB+W6JfgLcSEaSdH7RPA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.15", + "@inquirer/prompts": "^7.8.0", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "mute-stream": "^2.0.0", + "run-async": "^4.0.5", + "rxjs": "^7.8.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } }, "node_modules/internal-slot": { "version": "1.1.0", @@ -6550,12 +6396,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -6677,7 +6517,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6699,6 +6538,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", @@ -6722,7 +6570,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -6731,6 +6578,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -6761,7 +6620,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -6784,15 +6642,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -6902,11 +6751,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/is-weakmap": { "version": "2.0.2", @@ -7024,12 +6879,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -7057,18 +6906,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -7129,12 +6966,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -7169,12 +7000,52 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/loose-envify": { "version": "1.4.0", @@ -7199,31 +7070,6 @@ "yallist": "^3.0.2" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -7238,7 +7084,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -7248,7 +7093,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -7258,6 +7102,18 @@ "node": ">=8.6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7291,6 +7147,15 @@ "dev": true, "license": "MIT" }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7298,26 +7163,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -7475,6 +7320,21 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7493,6 +7353,68 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -7541,36 +7463,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", - "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", - "dev": true, - "dependencies": { - "callsites": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7627,7 +7519,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -7656,15 +7547,16 @@ } }, "node_modules/prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" @@ -7696,7 +7588,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -7871,15 +7762,6 @@ "node": ">=6" } }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -7901,42 +7783,48 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "license": "MIT", "dependencies": { - "global-dirs": "^0.1.1" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -7960,11 +7848,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/run-async": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.5.tgz", + "integrity": "sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -7984,6 +7880,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -8048,6 +7953,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, "node_modules/scslre": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", @@ -8224,12 +8135,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8257,6 +8162,18 @@ "source-map": "^0.6.0" } }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8429,7 +8346,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -8507,7 +8423,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -8516,12 +8431,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -8597,6 +8506,12 @@ "json5": "lib/cli.js" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8701,15 +8616,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", @@ -8771,7 +8677,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -8818,27 +8724,6 @@ "node": ">=4" } }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -8880,18 +8765,6 @@ "punycode": "^2.1.0" } }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz", - "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz", - "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==", - "dev": true - }, "node_modules/vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", @@ -8917,22 +8790,6 @@ "eslint": ">=6.0.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9103,30 +8960,9 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "ISC" }, "node_modules/y18n": { "version": "5.0.8", @@ -9178,18 +9014,29 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } }, "dependencies": { "@ackee/styleguide-backend-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ackee/styleguide-backend-config/-/styleguide-backend-config-1.0.0.tgz", - "integrity": "sha512-zVfzNsNL68cwOKQWA6+Yzj3O1gYCw/CTLj1x9u3MK++Yu7VHp52AsuEAWh8FAHMFbXntKENJlBjlwFwq463qJQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@ackee/styleguide-backend-config/-/styleguide-backend-config-1.0.1.tgz", + "integrity": "sha512-ntCyV2E3g7673wY4p0QoygLwKVyh+8MdOtGd/PFXsQSo0RV+jTa+2pRA2wi/QovTN68MF6ogIMWK6kiUQQlCnA==", "dev": true, "requires": { "@typescript-eslint/eslint-plugin": "^8.16.0", "@typescript-eslint/parser": "^8.16.0", - "cspell-lib": "^6.17.0", "eslint": "^8.57.0", "eslint-config-love": "^84.1.1", "eslint-formatter-codeframe": "^7.32.1", @@ -9199,14 +9046,6 @@ "eslint-plugin-security": "^3.0.1", "eslint-plugin-sonarjs": "^2.0.4", "prettier": "^3.4.2" - }, - "dependencies": { - "prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true - } } }, "@ampproject/remapping": { @@ -10526,353 +10365,6 @@ "@babel/helper-validator-identifier": "^7.27.1" } }, - "@cspell/cspell-bundled-dicts": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-6.18.1.tgz", - "integrity": "sha512-3rGSZ5brzt9KFCoa1QVna8SiYnYzB8hqQyrWjtoJhV5SWjD4MpBtNt2xm5JtfNONWHeiHvkpPHasXjJvYXwDNg==", - "dev": true, - "requires": { - "@cspell/dict-ada": "^4.0.1", - "@cspell/dict-aws": "^3.0.0", - "@cspell/dict-bash": "^4.1.1", - "@cspell/dict-companies": "^3.0.5", - "@cspell/dict-cpp": "^4.0.1", - "@cspell/dict-cryptocurrencies": "^3.0.1", - "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.1", - "@cspell/dict-dart": "^2.0.1", - "@cspell/dict-django": "^4.0.1", - "@cspell/dict-docker": "^1.1.4", - "@cspell/dict-dotnet": "^4.0.1", - "@cspell/dict-elixir": "^4.0.1", - "@cspell/dict-en_us": "^4.1.2", - "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.0", - "@cspell/dict-fonts": "^3.0.0", - "@cspell/dict-fullstack": "^3.0.0", - "@cspell/dict-gaming-terms": "^1.0.3", - "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^5.0.1", - "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.2", - "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.3", - "@cspell/dict-k8s": "^1.0.0", - "@cspell/dict-latex": "^3.1.0", - "@cspell/dict-lorem-ipsum": "^3.0.0", - "@cspell/dict-lua": "^3.0.0", - "@cspell/dict-node": "^4.0.2", - "@cspell/dict-npm": "^5.0.2", - "@cspell/dict-php": "^3.0.4", - "@cspell/dict-powershell": "^3.0.0", - "@cspell/dict-public-licenses": "^2.0.1", - "@cspell/dict-python": "^4.0.1", - "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^3.0.0", - "@cspell/dict-rust": "^3.0.0", - "@cspell/dict-scala": "^3.0.0", - "@cspell/dict-software-terms": "^3.0.7", - "@cspell/dict-sql": "^2.0.1", - "@cspell/dict-svelte": "^1.0.1", - "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-typescript": "^3.1.0", - "@cspell/dict-vue": "^3.0.0" - } - }, - "@cspell/cspell-pipe": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-6.18.1.tgz", - "integrity": "sha512-IFtZBae5BCBIPZuRhEs0U0emFrh5hmN0N4+WR5paP4UurV5Ql9n2JsSj1Bmdx79aSFAw4mGpJnhZZtGQcFDnPQ==", - "dev": true - }, - "@cspell/cspell-service-bus": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-6.18.1.tgz", - "integrity": "sha512-QVbVA8Ube+Z4ghywzsTQLxqdiCubYi7L/+KeFRatzh3bZ5K5pVcYHEbDhAlFdUj6FhXw0EP2n/Xb+8ZLye4LLg==", - "dev": true - }, - "@cspell/cspell-types": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-6.18.1.tgz", - "integrity": "sha512-5X+ABUMPrCoCjQvbqb/HeCoNiSgUrJhR9O4tSlMU5/z0NRNLFSyjf+3LE6ZU2+kdwNU7tmYCr+cbCpb3UKpvQQ==", - "dev": true - }, - "@cspell/dict-ada": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.1.tgz", - "integrity": "sha512-/E9o3nHrXOhYmQE43deKbxZcR3MIJAsa+66IzP9TXGHheKEx8b9dVMVVqydDDH8oom1H0U20NRPtu6KRVbT9xw==", - "dev": true - }, - "@cspell/dict-aws": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-3.0.0.tgz", - "integrity": "sha512-O1W6nd5y3Z00AMXQMzfiYrIJ1sTd9fB1oLr+xf/UD7b3xeHeMeYE2OtcWbt9uyeHim4tk+vkSTcmYEBKJgS5bQ==", - "dev": true - }, - "@cspell/dict-bash": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.1.tgz", - "integrity": "sha512-8czAa/Mh96wu2xr0RXQEGMTBUGkTvYn/Pb0o+gqOO1YW+poXGQc3gx0YPqILDryP/KCERrNvkWUJz3iGbvwC2A==", - "dev": true - }, - "@cspell/dict-companies": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.5.tgz", - "integrity": "sha512-f5lVcL/dG2kUHh8QFLakU722lgMwqXSjZUdmW6QdOMmqcE8cgl+oN9qk/qYlCSBMsYA7uexwn3hIr4h0naoPlw==", - "dev": true - }, - "@cspell/dict-cpp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-4.0.1.tgz", - "integrity": "sha512-mD6mn0XFCqHCz2j6p/7OQm3yNFn1dlQq6vip1pLynvNWDRz5yKYDVRUQCTEORT7ThS0dLpI4BjCX84YUKNhibA==", - "dev": true - }, - "@cspell/dict-cryptocurrencies": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-3.0.1.tgz", - "integrity": "sha512-Tdlr0Ahpp5yxtwM0ukC13V6+uYCI0p9fCRGMGZt36rWv8JQZHIuHfehNl7FB/Qc09NCF7p5ep0GXbL+sVTd/+w==", - "dev": true - }, - "@cspell/dict-csharp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.2.tgz", - "integrity": "sha512-1JMofhLK+4p4KairF75D3A924m5ERMgd1GvzhwK2geuYgd2ZKuGW72gvXpIV7aGf52E3Uu1kDXxxGAiZ5uVG7g==", - "dev": true - }, - "@cspell/dict-css": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.1.tgz", - "integrity": "sha512-jxsncdeiN/wkZGqU8iLtn24n3e0Fwugj6T48rjWUItn/i3C9j2W7RXOVqd7ZIeWeV8ibyq0WWiwA8Ajg6XaKpA==", - "dev": true - }, - "@cspell/dict-dart": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.1.tgz", - "integrity": "sha512-YRuDX9k2qPSWDEsM26j8o7KMvaZ0DXc74ijK/VRwaksm1CBRPBW289pe2TE2K7y4SJjTKXgQ9urOVlozeQDpuA==", - "dev": true - }, - "@cspell/dict-django": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.0.1.tgz", - "integrity": "sha512-q3l7OH39qzeN2Y64jpY39SEAqki5BUzPTypnhzM40yT+LOGSWqSh9Ix5UecejtXPDVrD8vML+m7Bp5070h52HQ==", - "dev": true - }, - "@cspell/dict-docker": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.5.tgz", - "integrity": "sha512-SNEohOScQ+0+y9dp/jKTx60OOJQrf5es5BJ32gh5Ck3jKXNo4wd9KLgPOmQMUpencb5SGjrBsC4rr1fyfCwytg==", - "dev": true - }, - "@cspell/dict-dotnet": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-4.0.1.tgz", - "integrity": "sha512-l11TqlUX8cDgsE/1Zrea1PqLn63s20MY3jKWMbQVB5DMDPDO2f8Pukckkwxq5p/cxDABEjuGzfF1kTX3pAakBw==", - "dev": true - }, - "@cspell/dict-elixir": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.1.tgz", - "integrity": "sha512-IejBqiTTWSXpvBm6yg4qUfnJR0LwbUUCJcK5wXOMKEJitu3yDfrT9GPc6NQJXgokbg9nBjEyxVIzNcLgx2x3/Q==", - "dev": true - }, - "@cspell/dict-en_us": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.1.4.tgz", - "integrity": "sha512-smRT8Rx38+z1kiNl3kBvadoPdYgxCovxw2rsuO4/XtLRlSEcGPQgYJ0CCdcXMd9bhMY5roXPCcvYkBsyUVvg4A==", - "dev": true - }, - "@cspell/dict-en-gb": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", - "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", - "dev": true - }, - "@cspell/dict-filetypes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.0.tgz", - "integrity": "sha512-Fiyp0z5uWaK0d2TfR9GMUGDKmUMAsOhGD5A0kHoqnNGswL2iw0KB0mFBONEquxU65fEnQv4R+jdM2d9oucujuA==", - "dev": true - }, - "@cspell/dict-fonts": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-3.0.0.tgz", - "integrity": "sha512-zTZni0AbwBVG1MKA0WpwPyIJPVF+gp6neXDQzHcu4RUnuQ4uDu0PVEuZjGHCJWwwFoR5JmkqZxVSg1y3ufJODA==", - "dev": true - }, - "@cspell/dict-fullstack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.0.1.tgz", - "integrity": "sha512-r077HcbxGQ0gSjs4eqryvb9cu8/Noe7pzl9QksxFIEaMgyP180DEaCLAOnat4KHl7X0wntipY+naY5PVRQUI9A==", - "dev": true - }, - "@cspell/dict-gaming-terms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.4.tgz", - "integrity": "sha512-hbDduNXlk4AOY0wFxcDMWBPpm34rpqJBeqaySeoUH70eKxpxm+dvjpoRLJgyu0TmymEICCQSl6lAHTHSDiWKZg==", - "dev": true - }, - "@cspell/dict-git": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-2.0.0.tgz", - "integrity": "sha512-n1AxyX5Kgxij/sZFkxFJlzn3K9y/sCcgVPg/vz4WNJ4K9YeTsUmyGLA2OQI7d10GJeiuAo2AP1iZf2A8j9aj2w==", - "dev": true - }, - "@cspell/dict-golang": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-5.0.1.tgz", - "integrity": "sha512-djsJC7OVKUpFdRm/aqBJEUSGP3kw/MDhAt7udYegnyQt2WjL3ZnVoG7r5eOEhPEEKzWVBYoi6UKSNpdQEodlbg==", - "dev": true - }, - "@cspell/dict-haskell": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.1.tgz", - "integrity": "sha512-uRrl65mGrOmwT7NxspB4xKXFUenNC7IikmpRZW8Uzqbqcu7ZRCUfstuVH7T1rmjRgRkjcIjE4PC11luDou4wEQ==", - "dev": true - }, - "@cspell/dict-html": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.2.tgz", - "integrity": "sha512-BskOE2K3AtGLkcjdJmo+H6/fjdfDP4XYAsEGXpB26rvdnXAnGEstE/Q8Do6UfJCvgOVYCpdUZLcMIEpoTy7QhQ==", - "dev": true - }, - "@cspell/dict-html-symbol-entities": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.0.tgz", - "integrity": "sha512-HGRu+48ErJjoweR5IbcixxETRewrBb0uxQBd6xFGcxbEYCX8CnQFTAmKI5xNaIt2PKaZiJH3ijodGSqbKdsxhw==", - "dev": true - }, - "@cspell/dict-java": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.4.tgz", - "integrity": "sha512-43VrLOLcBxavv6eyL4BpsnHrhVOgyYYeJqQRJG5XKObcpWy3+Lpadj58CfTVOr7M/Je3pUpd4tvsUhf/lWXMVA==", - "dev": true - }, - "@cspell/dict-k8s": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.0.tgz", - "integrity": "sha512-XqIql+nd2DiuPuL+qPc24bN/L1mZY75kAYcuMBMW5iYgBoivkiVOg7br/aofX3ApajvHDln6tNkPZhmhsOg6Ww==", - "dev": true - }, - "@cspell/dict-latex": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-3.1.0.tgz", - "integrity": "sha512-XD5S3FY0DrYiun2vm/KKOkeaD30LXp9v5EzVTVQvmxqQrQh0HvOT3TFD7lgKbyzZaG7E+l3wS94uwwm80cOmuw==", - "dev": true - }, - "@cspell/dict-lorem-ipsum": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-3.0.0.tgz", - "integrity": "sha512-msEV24qEpzWZs2kcEicqYlhyBpR0amfDkJOs+iffC07si9ftqtQ+yP3lf1VFLpgqw3SQh1M1vtU7RD4sPrNlcQ==", - "dev": true - }, - "@cspell/dict-lua": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-3.0.0.tgz", - "integrity": "sha512-WOhSCgS5wMxkGQJ8siB90iTB9ElquJB7FeqYSbJqqs6cUwH8G7MM/CEDPL6h7vCo0+v3GuxQ8yKWDSUcUhz9Lg==", - "dev": true - }, - "@cspell/dict-node": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-4.0.2.tgz", - "integrity": "sha512-FEQJ4TnMcXEFslqBQkXa5HposMoCGsiBv2ux4IZuIXgadXeHKHUHk60iarWpjhzNzQLyN2GD7NoRMd12bK3Llw==", - "dev": true - }, - "@cspell/dict-npm": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.3.tgz", - "integrity": "sha512-fEX67zIJISbS3gXVk/y/ZUvDIVtjc/CYJK7Mz0iTVrmlCKnLiD41lApe8v4g/12eE7hLfx/sfCXDrUWyzXVq1A==", - "dev": true - }, - "@cspell/dict-php": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-3.0.4.tgz", - "integrity": "sha512-QX6zE/ZfnT3O5lSwV8EPVh8Va39ds34gSNNR8I4GWiuDpKcTkZPFi4OLoP3Tlhbl/3G0Ha35OkSDLvZfu8mnkA==", - "dev": true - }, - "@cspell/dict-powershell": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-3.0.0.tgz", - "integrity": "sha512-pkztY9Ak4oc33q+Qxcn9/CTOKo4N8YIRRE6v67WwQOncA5QIJfcOPUrjfR3Z8SpzElXhu3s9qtWWSqbCy6qmcA==", - "dev": true - }, - "@cspell/dict-public-licenses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.1.tgz", - "integrity": "sha512-NZNwzkL5BqKddepDxvX/Qbji378Mso1TdnV4RFAN8hJoo6dSR0fv2TTI/Y0i/YWBmfmQGyTpEztBXtAw4qgjiA==", - "dev": true - }, - "@cspell/dict-python": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.0.1.tgz", - "integrity": "sha512-1wtUgyaTqRiQY0/fryk0oW22lcxNUnZ5DwteTzfatMdbgR0OHXTlHbI8vYxpHLWalSoch7EpLsnaymG+fOrt8g==", - "dev": true - }, - "@cspell/dict-r": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.0.1.tgz", - "integrity": "sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA==", - "dev": true - }, - "@cspell/dict-ruby": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-3.0.0.tgz", - "integrity": "sha512-sA98T8Y1Pmq3RStVkO14E8vTWkq6JUn8c8PldiMyYgV0yfQgwhQfFAzlSfF3Gg2B0VkIdqt2et2SPN7f9wp7fQ==", - "dev": true - }, - "@cspell/dict-rust": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-3.0.0.tgz", - "integrity": "sha512-L1T1IBsYJZVDmfOGAbVLcpc6arWxRRCSJYvHSwEDBGrNuMyJ4jx/NvBEz5crcKf4vVKgwVlXgzQlJJZ8AVxU9w==", - "dev": true - }, - "@cspell/dict-scala": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-3.0.0.tgz", - "integrity": "sha512-sIiCQDIMMnNns/fzD61z5npbh5pypaKq07Orqe0+eRfdQpika8iRSGUGFHVbtdd1JzB1DyTCV2e8OwdaQiXqJQ==", - "dev": true - }, - "@cspell/dict-software-terms": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.0.8.tgz", - "integrity": "sha512-otq0yIcG19rNXkmE/EGWgUK7ClLrn/BE4n5Di3HKLw6XEp0sNBp1DKf88bg0LvbWh15uCAJ5xKAzF1sVPy2Y3w==", - "dev": true - }, - "@cspell/dict-sql": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.0.1.tgz", - "integrity": "sha512-7fvVcvy751cl31KMD5j04yMGq2UKj018/1hx3FNtdUI9UuUTMvhBrTAqHEEemR3ZeIC9i/5p5SQjwQ13bn04qw==", - "dev": true - }, - "@cspell/dict-svelte": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.2.tgz", - "integrity": "sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q==", - "dev": true - }, - "@cspell/dict-swift": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.1.tgz", - "integrity": "sha512-gxrCMUOndOk7xZFmXNtkCEeroZRnS2VbeaIPiymGRHj5H+qfTAzAKxtv7jJbVA3YYvEzWcVE2oKDP4wcbhIERw==", - "dev": true - }, - "@cspell/dict-typescript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.0.tgz", - "integrity": "sha512-4hdLlQMOYrUbGfJg2cWnbsBUevObwgL76TLVC0rwnrkSwzOxAxiGaG39VtRMvgAAe2lX6L+jka3fy0MmxzFOHw==", - "dev": true - }, - "@cspell/dict-vue": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.0.tgz", - "integrity": "sha512-niiEMPWPV9IeRBRzZ0TBZmNnkK3olkOPYxC1Ny2AX4TGlYRajcW0WUtoSHmvvjZNfWLSg2L6ruiBeuPSbjnG6A==", - "dev": true - }, - "@cspell/strong-weak-map": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-6.18.1.tgz", - "integrity": "sha512-etyMLISiDzgFf1BSGpUBD62cHp9NrCyrOi+iT7WrJ+My0l6IPRIhANuAVp2JcsXxe28en4X3Bp/egd46Q5Rpkg==", - "dev": true - }, "@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -10982,6 +10474,213 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "@inquirer/checkbox": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", + "integrity": "sha512-fdSw07FLJEU5vbpOPzXo5c6xmMGDzbZE2+niuDHX5N6mc6V0Ebso/q3xiHra4D73+PMsC8MJmcaZKuAAoaQsSA==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/confirm": { + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/core": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", + "requires": { + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "@inquirer/editor": { + "version": "4.2.18", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz", + "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==", + "requires": { + "@inquirer/core": "^10.2.0", + "@inquirer/external-editor": "^1.0.1", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/expand": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/external-editor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", + "requires": { + "chardet": "^2.1.0", + "iconv-lite": "^0.6.3" + } + }, + "@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==" + }, + "@inquirer/input": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/number": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" + } + }, + "@inquirer/password": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2" + } + }, + "@inquirer/prompts": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.0.tgz", + "integrity": "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==", + "requires": { + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" + } + }, + "@inquirer/rawlist": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/search": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.0.tgz", + "integrity": "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/select": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "requires": {} + }, "@jridgewell/gen-mapping": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", @@ -11045,7 +10744,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -11054,14 +10752,12 @@ "@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" }, "@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -11098,7 +10794,7 @@ "version": "20.19.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, + "devOptional": true, "requires": { "undici-types": "~6.21.0" } @@ -11274,11 +10970,25 @@ "uri-js": "^4.2.2" } }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + } + } + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -11330,12 +11040,6 @@ "math-intrinsics": "^1.1.0" } }, - "array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -11513,7 +11217,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "requires": { "fill-range": "^7.1.1" } @@ -11602,16 +11305,29 @@ "supports-color": "^5.3.0" } }, - "clear-module": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", - "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", - "dev": true, + "chardet": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==" + }, + "cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "requires": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" + "restore-cursor": "^5.0.0" } }, + "cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==" + }, + "cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==" + }, "cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", @@ -11650,20 +11366,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", - "dev": true, - "requires": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - } + "dev": true }, "concat-map": { "version": "0.0.1", @@ -11671,20 +11374,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -11700,24 +11389,6 @@ "browserslist": "^4.25.1" } }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "cosmiconfig": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", - "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -11729,96 +11400,6 @@ "which": "^2.0.1" } }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "cspell-dictionary": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-6.18.1.tgz", - "integrity": "sha512-q+tK+MWvJs9xL8wv79YlGPddUFb3Usuqh+VB8D0Zs7Xlsa/cw9bljRluHkpQrNr8APdZijGlgQP8L0cEr0/rEw==", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "cspell-trie-lib": "6.18.1", - "fast-equals": "^4.0.3", - "gensequence": "^4.0.3" - } - }, - "cspell-glob": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-6.18.1.tgz", - "integrity": "sha512-6dBBtQ1lRnVPoM13GOv7mJflkIvEr93TN96saQPWoaQqX8jwmklcMmDUndIkLcA7TnyxBbi3Z3X+s68zj/YGqw==", - "dev": true, - "requires": { - "micromatch": "^4.0.5" - } - }, - "cspell-grammar": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-6.18.1.tgz", - "integrity": "sha512-QPjOA9xwDPb3aoJXUOdL2aWX2wt8lPD7CoDROo8uruOXHAQzIY56q12EBy3jLIkxJFl9KAwtlEHkbLaJfTpIpg==", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1" - } - }, - "cspell-io": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-6.18.1.tgz", - "integrity": "sha512-WIfiDdG/7235CbkrjKPYIkP9oT8VvWXVTAeq6JkJPH7bm2A/CoE8ClieVsbbJnPyJnetnCuOuuz/zmuheVD02g==", - "dev": true, - "requires": { - "@cspell/cspell-service-bus": "6.18.1", - "node-fetch": "^2.6.7" - } - }, - "cspell-lib": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-6.18.1.tgz", - "integrity": "sha512-4MGjp51Ed8BbMPGXgqLGgUiWyb2DbOxgVEuWm8nxumxu7UmAWDBdMiD3QlY+ZYmfOJEVSa/kG7DTMrLQoeFwnQ==", - "dev": true, - "requires": { - "@cspell/cspell-bundled-dicts": "6.18.1", - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "@cspell/strong-weak-map": "6.18.1", - "clear-module": "^4.1.2", - "comment-json": "^4.2.3", - "configstore": "^5.0.1", - "cosmiconfig": "^8.0.0", - "cspell-dictionary": "6.18.1", - "cspell-glob": "6.18.1", - "cspell-grammar": "6.18.1", - "cspell-io": "6.18.1", - "cspell-trie-lib": "6.18.1", - "fast-equals": "^4.0.3", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "gensequence": "^4.0.3", - "import-fresh": "^3.3.0", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "vscode-languageserver-textdocument": "^1.0.8", - "vscode-uri": "^3.0.7" - } - }, - "cspell-trie-lib": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-6.18.1.tgz", - "integrity": "sha512-rV32bqchz0uYdK6uafaw5QnYImRWQMcT2RNbBo0LXN6XoYoTSgpnPWTxQauNLxOm1m+dfb3GdasoAsjgWkPGnQ==", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.18.1", - "@cspell/cspell-types": "6.18.1", - "fs-extra": "^11.1.0", - "gensequence": "^4.0.3" - } - }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -11939,15 +11520,6 @@ "esutils": "^2.0.2" } }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, "dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -11980,15 +11552,6 @@ "tapable": "^2.2.0" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -13107,12 +12670,6 @@ "eslint-visitor-keys": "^3.4.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -13149,17 +12706,10 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "fast-equals": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz", - "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==", - "dev": true - }, "fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -13172,7 +12722,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -13195,7 +12744,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, "requires": { "reusify": "^1.0.4" } @@ -13213,7 +12761,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -13254,17 +12801,6 @@ "is-callable": "^1.2.7" } }, - "fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -13303,12 +12839,6 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, - "gensequence": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-4.0.3.tgz", - "integrity": "sha512-izr+MKqJKjexkvLiPGhW96elQX8TuUR/su/xzILxjqzU1RDz1n1ZbqwDUnNFaRcq0gFR3oQfNH2JOH4Je1x/QA==", - "dev": true - }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -13417,15 +12947,6 @@ "is-glob": "^4.0.3" } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, "globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -13497,12 +13018,6 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, - "has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true - }, "has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -13545,6 +13060,14 @@ "function-bind": "^1.1.2" } }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -13600,11 +13123,19 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "inquirer": { + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.0.tgz", + "integrity": "sha512-LlFVmvWVCun7uEgPB3vups9NzBrjJn48kRNtFGw3xU1H5UXExTEz/oF1JGLaB0fvlkUB+W6JfgLcSEaSdH7RPA==", + "requires": { + "@inquirer/core": "^10.1.15", + "@inquirer/prompts": "^7.8.0", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "mute-stream": "^2.0.0", + "run-async": "^4.0.5", + "rxjs": "^7.8.2" + } }, "internal-slot": { "version": "1.1.0", @@ -13638,12 +13169,6 @@ "get-intrinsic": "^1.2.6" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -13715,8 +13240,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-finalizationregistry": { "version": "1.1.1", @@ -13727,6 +13251,11 @@ "call-bound": "^1.0.3" } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, "is-generator-function": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", @@ -13743,11 +13272,15 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } }, + "is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" + }, "is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -13763,8 +13296,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-number-object": { "version": "1.1.1", @@ -13776,12 +13308,6 @@ "has-tostringtag": "^1.0.2" } }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, "is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -13845,11 +13371,10 @@ "which-typed-array": "^1.1.16" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==" }, "is-weakmap": { "version": "2.0.2", @@ -13929,12 +13454,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -13953,16 +13472,6 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, "jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -14009,12 +13518,6 @@ "type-check": "~0.4.0" } }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -14047,6 +13550,27 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "requires": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "dependencies": { + "chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==" + }, + "is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -14065,23 +13589,6 @@ "yallist": "^3.0.2" } }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, "math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -14091,19 +13598,22 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, "micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "requires": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, + "mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==" + }, "minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -14125,21 +13635,17 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, "node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -14244,6 +13750,14 @@ "wrappy": "1" } }, + "onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "requires": { + "mimic-function": "^5.0.0" + } + }, "optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -14258,6 +13772,42 @@ "word-wrap": "^1.2.5" } }, + "ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "requires": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" + }, + "chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==" + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -14289,27 +13839,6 @@ } } }, - "parent-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", - "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", - "dev": true, - "requires": { - "callsites": "^3.1.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -14349,8 +13878,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "possible-typed-array-names": { "version": "1.1.0", @@ -14365,9 +13893,9 @@ "dev": true }, "prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true }, "prop-types": { @@ -14390,8 +13918,7 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, "react-is": { "version": "16.13.1", @@ -14506,12 +14033,6 @@ } } }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true - }, "resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -14523,32 +14044,32 @@ "supports-preserve-symlinks-flag": "^1.0.0" } }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, - "requires": { - "global-dirs": "^0.1.1" - } - }, "resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true }, + "restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "requires": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + } + } + }, "reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" }, "rimraf": { "version": "3.0.2", @@ -14559,15 +14080,27 @@ "glob": "^7.1.3" } }, + "run-async": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.5.tgz", + "integrity": "sha512-oN9GTgxUNDBumHTTDmQ8dep6VIJbgj9S3dPP+9XylVLIK4xB9XTXtKWROd5pnhdXR9k0EgO1JRcNh0T+Ny2FsA==" + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "requires": { "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "requires": { + "tslib": "^2.1.0" + } + }, "safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -14611,6 +14144,11 @@ "is-regex": "^1.2.1" } }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "scslre": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/scslre/-/scslre-0.3.0.tgz", @@ -14728,12 +14266,6 @@ "side-channel-map": "^1.0.1" } }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -14754,6 +14286,11 @@ "source-map": "^0.6.0" } }, + "stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==" + }, "stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -14873,7 +14410,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -14921,17 +14457,10 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -14979,6 +14508,11 @@ } } }, + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -15047,15 +14581,6 @@ "reflect.getprototypeof": "^1.0.6" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, "typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", @@ -15090,7 +14615,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true + "devOptional": true }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.1", @@ -15120,21 +14645,6 @@ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, "update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -15154,18 +14664,6 @@ "punycode": "^2.1.0" } }, - "vscode-languageserver-textdocument": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz", - "integrity": "sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q==", - "dev": true - }, - "vscode-uri": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.7.tgz", - "integrity": "sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==", - "dev": true - }, "vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", @@ -15181,22 +14679,6 @@ "semver": "^7.3.6" } }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -15309,24 +14791,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -15361,6 +14825,11 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true + }, + "yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==" } } } diff --git a/package.json b/package.json index 8e4b17f..c8bb07d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "typescript": "^5.4.5" }, "dependencies": { + "fast-glob": "^3.3.3", + "inquirer": "^12.9.0", + "ora": "^8.2.0", "source-map-support": "^0.5.21", "yargs": "^18.0.0", "lodash-es": "^4.17.21" From 349a651495d164f8c23f9e4d028a928381b88b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:53:09 +0200 Subject: [PATCH 05/28] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Rework=20starter?= =?UTF-8?q?=20to=20copy-paste=20pattern=20with=20inquirer=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 178 +++++----- lib/Bootstrap.js.map | 2 +- lib/Builder.js | 111 +++++++ lib/Builder.js.map | 1 + lib/Files.js | 12 + lib/Files.js.map | 1 + lib/Logger.js | 40 ++- lib/Logger.js.map | 2 +- lib/Mergers/Merger.js | 37 +++ lib/Mergers/Merger.js.map | 1 + lib/Npm.js | 88 +++-- lib/Npm.js.map | 2 +- lib/PackageJson.js | 75 ++--- lib/PackageJson.js.map | 2 +- lib/Starter.js | 4 +- lib/StarterLoader.js | 71 ++++ lib/StarterLoader.js.map | 1 + lib/cloudrun-graphql/GraphQLStarter.js | 168 ---------- lib/cloudrun-graphql/GraphQLStarter.js.map | 1 - lib/cloudrun/CloudRunStarter.js | 167 ---------- lib/cloudrun/CloudRunStarter.js.map | 1 - lib/types.js | 4 +- src/Bootstrap.ts | 166 +++++----- src/Builder.ts | 172 ++++++++++ src/Files.ts | 13 + src/Logger.ts | 33 +- src/Mergers/Merger.ts | 52 +++ src/Npm.ts | 75 ++++- src/PackageJson.ts | 5 +- src/Starter.ts | 4 +- src/StarterLoader.ts | 123 +++++++ src/Toolbelt.ts | 132 -------- src/cloudrun-graphql/GraphQLStarter.ts | 182 ---------- src/cloudrun/CloudRunStarter.ts | 182 ---------- starter/cloudrun-graphql/.env.jsonc | 12 - .../cloudrun-graphql/.eslint.tsconfig.json | 4 - starter/cloudrun-graphql/.eslintrc.cjs | 43 --- starter/cloudrun-graphql/README.md | 53 --- starter/cloudrun-graphql/codegen.yml | 11 - .../src/adapters/pino.logger.ts | 44 --- starter/cloudrun-graphql/src/config.ts | 21 -- starter/cloudrun-graphql/src/container.ts | 15 - starter/cloudrun-graphql/src/context.ts | 39 --- .../src/domain/errors/codes.ts | 9 - .../src/domain/errors/errors.ts | 25 -- .../src/domain/ports/logger.d.ts | 21 -- starter/cloudrun-graphql/src/index.ts | 11 - .../src/test/helloWorld.test.ts | 23 -- .../cloudrun-graphql/src/view/controller.ts | 42 --- .../src/view/graphql/resolvers.ts | 6 - .../graphql/resolvers/greeting.resolver.ts | 5 - .../src/view/graphql/schema.ts | 7 - .../src/view/graphql/schema/schema.graphql | 6 - starter/cloudrun-graphql/src/view/server.ts | 45 --- starter/cloudrun/.env.jsonc | 10 - starter/cloudrun/.eslint.tsconfig.json | 4 - starter/cloudrun/.eslintrc.cjs | 8 - starter/cloudrun/README.md | 69 ---- starter/cloudrun/src/adapters/pino.logger.ts | 44 --- starter/cloudrun/src/config.ts | 22 -- starter/cloudrun/src/container.ts | 18 - starter/cloudrun/src/context.ts | 39 --- starter/cloudrun/src/domain/errors/codes.ts | 9 - starter/cloudrun/src/domain/errors/errors.ts | 25 -- .../src/domain/health-check.service.ts | 15 - starter/cloudrun/src/domain/ports/logger.d.ts | 21 -- starter/cloudrun/src/index.ts | 17 - .../cloudrun/src/test/health-check.test.ts | 25 -- .../src/test/util/openapi-test.util.ts | 71 ---- starter/cloudrun/src/view/cli/README.md | 17 - starter/cloudrun/src/view/cli/cli.ts | 94 ------ .../cloudrun/src/view/cli/openapi/generate.ts | 64 ---- .../controller/health-check.controller.ts | 33 -- .../rest/middleware/context-middleware.ts | 28 -- .../src/view/rest/middleware/error-handler.ts | 60 ---- .../view/rest/middleware/request-logger.ts | 37 --- starter/cloudrun/src/view/rest/request.d.ts | 9 - starter/cloudrun/src/view/rest/routes.ts | 15 - .../cloudrun/src/view/rest/spec/openapi.yml | 65 ---- .../src/view/rest/util/openapi.util.ts | 310 ------------------ starter/cloudrun/src/view/server.ts | 25 -- starter/shared/.dockerignore | 19 -- starter/shared/.gitignore_ | 5 - starter/shared/.gitlab-ci.yml | 199 ----------- starter/shared/.mocha-junit-config.json | 6 - starter/shared/.mocharc.json | 8 - starter/shared/.nvmrc | 1 - starter/shared/Dockerfile | 40 --- starter/shared/ci-branch-config/common.env | 7 - .../shared/ci-branch-config/development.env | 7 - starter/shared/ci-branch-config/master.env | 7 - starter/shared/ci-branch-config/stage.env | 7 - .../docker-compose-entrypoint.sh | 7 - .../docker-compose/docker-compose.ci.yml | 19 -- .../docker-compose/docker-compose.local.yml | 5 - .../docker-compose.override.yml | 5 - .../shared/docker-compose/docker-compose.yml | 8 - starter/shared/jest.config.js | 12 - starter/shared/prettier.config.cjs | 1 - starter/shared/src/test/setup.ts | 1 - starter/shared/tsconfig.json | 22 -- 101 files changed, 989 insertions(+), 3101 deletions(-) create mode 100644 lib/Builder.js create mode 100644 lib/Builder.js.map create mode 100644 lib/Files.js create mode 100644 lib/Files.js.map create mode 100644 lib/Mergers/Merger.js create mode 100644 lib/Mergers/Merger.js.map create mode 100644 lib/StarterLoader.js create mode 100644 lib/StarterLoader.js.map delete mode 100644 lib/cloudrun-graphql/GraphQLStarter.js delete mode 100644 lib/cloudrun-graphql/GraphQLStarter.js.map delete mode 100644 lib/cloudrun/CloudRunStarter.js delete mode 100644 lib/cloudrun/CloudRunStarter.js.map create mode 100644 src/Builder.ts create mode 100644 src/Files.ts create mode 100644 src/Mergers/Merger.ts create mode 100644 src/StarterLoader.ts delete mode 100644 src/Toolbelt.ts delete mode 100644 src/cloudrun-graphql/GraphQLStarter.ts delete mode 100644 src/cloudrun/CloudRunStarter.ts delete mode 100644 starter/cloudrun-graphql/.env.jsonc delete mode 100644 starter/cloudrun-graphql/.eslint.tsconfig.json delete mode 100644 starter/cloudrun-graphql/.eslintrc.cjs delete mode 100644 starter/cloudrun-graphql/README.md delete mode 100644 starter/cloudrun-graphql/codegen.yml delete mode 100644 starter/cloudrun-graphql/src/adapters/pino.logger.ts delete mode 100644 starter/cloudrun-graphql/src/config.ts delete mode 100644 starter/cloudrun-graphql/src/container.ts delete mode 100644 starter/cloudrun-graphql/src/context.ts delete mode 100644 starter/cloudrun-graphql/src/domain/errors/codes.ts delete mode 100644 starter/cloudrun-graphql/src/domain/errors/errors.ts delete mode 100644 starter/cloudrun-graphql/src/domain/ports/logger.d.ts delete mode 100644 starter/cloudrun-graphql/src/index.ts delete mode 100644 starter/cloudrun-graphql/src/test/helloWorld.test.ts delete mode 100644 starter/cloudrun-graphql/src/view/controller.ts delete mode 100644 starter/cloudrun-graphql/src/view/graphql/resolvers.ts delete mode 100644 starter/cloudrun-graphql/src/view/graphql/resolvers/greeting.resolver.ts delete mode 100644 starter/cloudrun-graphql/src/view/graphql/schema.ts delete mode 100644 starter/cloudrun-graphql/src/view/graphql/schema/schema.graphql delete mode 100644 starter/cloudrun-graphql/src/view/server.ts delete mode 100644 starter/cloudrun/.env.jsonc delete mode 100644 starter/cloudrun/.eslint.tsconfig.json delete mode 100644 starter/cloudrun/.eslintrc.cjs delete mode 100644 starter/cloudrun/README.md delete mode 100644 starter/cloudrun/src/adapters/pino.logger.ts delete mode 100644 starter/cloudrun/src/config.ts delete mode 100644 starter/cloudrun/src/container.ts delete mode 100644 starter/cloudrun/src/context.ts delete mode 100644 starter/cloudrun/src/domain/errors/codes.ts delete mode 100644 starter/cloudrun/src/domain/errors/errors.ts delete mode 100644 starter/cloudrun/src/domain/health-check.service.ts delete mode 100644 starter/cloudrun/src/domain/ports/logger.d.ts delete mode 100644 starter/cloudrun/src/index.ts delete mode 100644 starter/cloudrun/src/test/health-check.test.ts delete mode 100644 starter/cloudrun/src/test/util/openapi-test.util.ts delete mode 100644 starter/cloudrun/src/view/cli/README.md delete mode 100644 starter/cloudrun/src/view/cli/cli.ts delete mode 100644 starter/cloudrun/src/view/cli/openapi/generate.ts delete mode 100644 starter/cloudrun/src/view/rest/controller/health-check.controller.ts delete mode 100644 starter/cloudrun/src/view/rest/middleware/context-middleware.ts delete mode 100644 starter/cloudrun/src/view/rest/middleware/error-handler.ts delete mode 100644 starter/cloudrun/src/view/rest/middleware/request-logger.ts delete mode 100644 starter/cloudrun/src/view/rest/request.d.ts delete mode 100644 starter/cloudrun/src/view/rest/routes.ts delete mode 100644 starter/cloudrun/src/view/rest/spec/openapi.yml delete mode 100644 starter/cloudrun/src/view/rest/util/openapi.util.ts delete mode 100644 starter/cloudrun/src/view/server.ts delete mode 100644 starter/shared/.dockerignore delete mode 100644 starter/shared/.gitignore_ delete mode 100644 starter/shared/.gitlab-ci.yml delete mode 100644 starter/shared/.mocha-junit-config.json delete mode 100644 starter/shared/.mocharc.json delete mode 100644 starter/shared/.nvmrc delete mode 100644 starter/shared/Dockerfile delete mode 100644 starter/shared/ci-branch-config/common.env delete mode 100644 starter/shared/ci-branch-config/development.env delete mode 100644 starter/shared/ci-branch-config/master.env delete mode 100644 starter/shared/ci-branch-config/stage.env delete mode 100755 starter/shared/docker-compose/docker-compose-entrypoint.sh delete mode 100644 starter/shared/docker-compose/docker-compose.ci.yml delete mode 100644 starter/shared/docker-compose/docker-compose.local.yml delete mode 100644 starter/shared/docker-compose/docker-compose.override.yml delete mode 100644 starter/shared/docker-compose/docker-compose.yml delete mode 100644 starter/shared/jest.config.js delete mode 100644 starter/shared/prettier.config.cjs delete mode 100644 starter/shared/src/test/setup.ts delete mode 100644 starter/shared/tsconfig.json diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index ab80336..228f4f5 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -1,91 +1,95 @@ -import { CloudRunStarter } from './cloudrun/CloudRunStarter.js' -import { Npm } from './Npm.js' -import { Toolbelt } from './Toolbelt.js' -import * as path from 'path' -import { PackageJson } from './PackageJson.js' -import { GraphQLStarter } from './cloudrun-graphql/GraphQLStarter.js' -import yargs from 'yargs' -import { hideBin } from 'yargs/helpers' -import { logger } from './Logger.js' +import inquirer from 'inquirer'; +import * as path from 'path'; +import * as fs from 'fs'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { Builder } from './Builder.js'; +import { Logger } from './Logger.js'; +import { Npm } from './Npm.js'; +import { PackageJson } from './PackageJson.js'; +import { StarterLoader } from './StarterLoader.js'; export class Bootstrap { - constructor() { - this.starters = [new CloudRunStarter(), new GraphQLStarter()] - } - runCLI(args) { - const cli = yargs(hideBin(args)) - .usage('create-node-app [options]') - .positional('starter', { - name: 'starter', - type: 'string', - required: true, - description: 'Which template to setup (required)', - choices: this.starters.map(starter => starter.name), - }) - .option('dir', { - type: 'string', - alias: 'd', - default: './node-app', - description: 'Destination directory', - }) - .option('project-name', { - type: 'string', - alias: 'n', - default: 'node-app', - description: 'Google Cloud project name', - }) - .option('force', { - type: 'boolean', - alias: 'f', - default: false, - description: - "Overwrite existing destination directory if it's not empty", - }) - .version('1.0.0') - .help() - const parsedArgs = cli.parseSync() - const starterArg = parsedArgs._[0] - const starter = this.starters.find(x => x.name === starterArg) - const destination = path.normalize(parsedArgs.dir) - if (!starter) { - logger.info('Invalid starter') - cli.showHelp() - process.exit(1) + constructor() { + this.starterLoader = new StarterLoader(); } - logger.info(`starter=${starter.name}, destination=${destination}`) - const npm = new Npm({ dir: destination }) - const packageJson = new PackageJson(npm) - const toolbelt = new Toolbelt({ - npm, - packageJson, - assetDirectory: path.join( - import.meta.dirname, - '..', - 'starter', - starter.name - ), - sharedDirectory: path.join( - import.meta.dirname, - '..', - 'starter', - 'shared' - ), - destination: destination, - projectName: parsedArgs.projectName, - }) - starter.setToolbelt(toolbelt) - if (!toolbelt.isDirectoryEmpty(destination)) { - if (!parsedArgs.force) { - logger.info( - `Directory '${destination}' already exists and is not empty. Use --force or -f flag to overwrite the existing directory.` - ) - process.exit(1) - } else { - logger.info(`Overwriting existing directory '${destination}'`) - } + async runCLI(args) { + try { + const cli = yargs(hideBin(args)) + .usage('create-node-app [options]') + .option('dir', { + type: 'string', + alias: 'd', + default: './node-app', + description: 'Destination directory', + }) + .option('debug', { + type: 'boolean', + alias: 'D', + default: false, + description: 'Enables debug logs', + }) + .option('project-name', { + type: 'string', + alias: 'n', + default: 'node-app', + description: 'Google Cloud project name', + }) + .option('force', { + type: 'boolean', + alias: 'f', + default: false, + description: "Overwrite existing destination directory if it's not empty", + }) + .version('1.0.0') + .help(); + const parsedArgs = cli.parseSync(); + const destination = path.normalize(parsedArgs.dir); + const logger = new Logger(parsedArgs.debug); + const npm = new Npm({ dir: destination, logger }); + const packageJson = new PackageJson(npm); + const starters = []; + if (fs.existsSync(destination) && !parsedArgs.force) { + if (!parsedArgs.force) { + const answer = await inquirer.prompt({ + type: 'confirm', + name: 'force', + message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, + }); + if (!answer.force) { + process.exit(0); + } + } + } + for (const module of this.starterLoader.getOptions()) { + const answer = await inquirer.prompt({ + type: 'list', + name: 'starter', + message: `Which ${module.name} would you like to use?`, + choices: [...module.starters, 'none'], + }); + if (answer.starter === 'none') { + continue; + } + starters.push(this.starterLoader.getStarter(answer.starter)); + } + const builder = new Builder({ + npm, + logger, + packageJson, + starters, + destination: destination, + projectName: parsedArgs.projectName, + }); + await builder.build(); + } + catch (error) { + if (error instanceof Error && error.name === 'ExitPromptError') { + process.exit(0); + } + else { + throw error; + } + } } - toolbelt.mkdir(destination, { overwrite: parsedArgs.force }) - toolbelt.npm.init() - starter.install() - } } -//# sourceMappingURL=Bootstrap.js.map +//# sourceMappingURL=Bootstrap.js.map \ No newline at end of file diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 1a35f02..2776275 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Builder.js b/lib/Builder.js new file mode 100644 index 0000000..3b692fc --- /dev/null +++ b/lib/Builder.js @@ -0,0 +1,111 @@ +import glob from 'fast-glob'; +import * as fs from 'fs/promises'; +import * as path from 'path'; +// import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' +// import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' +// import { ConfigMerger } from './Mergers/ConfigMerger.js' +// import { ContainerMerger } from './Mergers/ContainerMerger.js' +import { Files } from './Files.js'; +export class Builder { + constructor(params) { + this.npm = params.npm; + this.logger = params.logger; + this.starters = params.starters; + this.destination = params.destination; + this.projectName = params.projectName; + this.replacements = { + '{{PROJECT_NAME}}': this.projectName, + }; + this.fileMergers = [ + // new PackageJsonMerger(this.projectName, this.destination, 'package.json'), + // new EnvJsoncMerger(this.destination, '.env.jsonc'), + // new ConfigMerger(this.destination, 'src/config.ts'), + // new ContainerMerger(this.destination, 'src/container.ts'), + ]; + } + async prepareFolder() { + if (await Files.existsAndIsDir(this.destination)) { + await fs.rm(this.destination, { recursive: true }); + } + await fs.mkdir(this.destination, { recursive: true }); + } + async build() { + try { + await this.logger.loader(`Preparing clean folder`, this.prepareFolder()); + await this.logger.loader(`Preparing folder structure`, this.buildStarter(Builder.BASE_STARTER_DIR)); + for (const starter of this.starters) { + await this.logger.loader(`Adding ${starter.config.name} ${starter.config.module}`, this.buildStarter(starter.path, starter.config)); + } + await this.logger.loader(`npm install`, this.npm.run(['install'])); + const prebuildScripts = this.starters + .map(starter => starter.config.prebuild) + .filter(script => script !== undefined); + for (const script of prebuildScripts) { + await this.logger.loader(`npm run ${script.join(' ')}`, this.npm.run(['run', ...script])); + } + await this.logger.loader(`npm run build`, this.npm.run(['run', 'build'])); + this.logger.info(`Your app is ready in ${path.relative(process.cwd(), this.destination)}! 🚀`); + } + catch (error) { + this.logger.error(error); + process.exit(1); + } + } + async buildStarter(starterDir, config) { + const destDir = path.normalize(path.join(process.cwd(), this.destination)); + const files = await glob(`${starterDir}/*`, { + cwd: starterDir, + dot: true, + onlyFiles: false, + }); + const ignoredFiles = Builder.INGORED_FILES.map(file => path.join(starterDir, file)); + const mergedFiles = await Promise.all(this.fileMergers.map(async (merger) => { + return { + path: merger.getDestPath(), + content: await merger.merge(starterDir), + }; + })); + await Promise.all(files.map(async (filePath) => { + if (ignoredFiles.includes(filePath)) { + return; + } + const destFilePath = path.join(destDir, path.basename(filePath)); + if (await Files.existsAndIsDir(filePath)) { + await fs.cp(filePath, destFilePath, { recursive: true }); + return; + } + await fs.cp(filePath, destFilePath); + })); + await Promise.all(mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content))); + if (config?.replace) { + await Promise.all(config.replace.map(async (filePath) => this.replaceInFile(filePath))); + } + } + async replaceInFile(filePath) { + filePath = path.normalize(path.join(this.destination, filePath)); + let content = await fs.readFile(filePath, 'utf8'); + content = Object.keys(this.replacements).reduce((acc, key) => { + return acc.replaceAll(key, this.replacements[key]); + }, content); + return fs.writeFile(filePath, content); + } + async symlink(linkName, linkedFile) { + linkName = path.normalize(linkName); + linkedFile = path.normalize(linkedFile); + this.logger.info(`> ln -s ${linkName} ${linkedFile}`); + try { + await fs.symlink(linkedFile, linkName); + } + catch (error) { + if ('code' in error && error.code === 'EEXIST') { + // OK + } + else { + throw error; + } + } + } +} +Builder.BASE_STARTER_DIR = path.normalize(path.join(import.meta.dirname, '..', 'starter', '_base')); +Builder.INGORED_FILES = ['node-app.jsonc']; +//# sourceMappingURL=Builder.js.map \ No newline at end of file diff --git a/lib/Builder.js.map b/lib/Builder.js.map new file mode 100644 index 0000000..d966101 --- /dev/null +++ b/lib/Builder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Files.js b/lib/Files.js new file mode 100644 index 0000000..6690b8a --- /dev/null +++ b/lib/Files.js @@ -0,0 +1,12 @@ +import * as fsp from 'fs/promises'; +export class Files { + static async exists(path) { + const stat = await fsp.stat(path).catch(() => undefined); + return Boolean(stat); + } + static async existsAndIsDir(path) { + const stat = await fsp.stat(path).catch(() => undefined); + return Boolean(stat?.isDirectory()); + } +} +//# sourceMappingURL=Files.js.map \ No newline at end of file diff --git a/lib/Files.js.map b/lib/Files.js.map new file mode 100644 index 0000000..a94edd1 --- /dev/null +++ b/lib/Files.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAElC,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAY;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAY;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Logger.js b/lib/Logger.js index 904fa13..df8b3e8 100644 --- a/lib/Logger.js +++ b/lib/Logger.js @@ -1,12 +1,30 @@ -export const logger = { - info: message => { - console.log(message) - }, - verbose: message => { - console.log(message) - }, - error: message => { - console.log(message) - }, +import { oraPromise } from 'ora'; +export class Logger { + constructor(enableDebug = false) { + this.enableDebug = enableDebug; + } + info(message) { + console.log(message); + } + verbose(message) { + console.log(message); + } + error(message) { + console.log(message); + } + debug(message) { + if (this.enableDebug) { + console.log(message); + } + } + loader(message, promise) { + if (this.enableDebug) { + this.info(message); + return promise; + } + return oraPromise(promise, { + text: message, + }); + } } -//# sourceMappingURL=Logger.js.map +//# sourceMappingURL=Logger.js.map \ No newline at end of file diff --git a/lib/Logger.js.map b/lib/Logger.js.map index 43a092d..997c60d 100644 --- a/lib/Logger.js.map +++ b/lib/Logger.js.map @@ -1 +1 @@ -{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE;QACxB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,EAAE,CAAC,OAAe,EAAE,EAAE;QAC3B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,KAAK,EAAE,CAAC,OAAe,EAAE,EAAE;QACzB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;CACF,CAAA"} \ No newline at end of file +{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAEhC,MAAM,OAAO,MAAM;IACjB,YAA4B,cAAuB,KAAK;QAA5B,gBAAW,GAAX,WAAW,CAAiB;IAAG,CAAC;IAE5D,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,KAAK,CAAC,OAAe;QACnB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IACD,MAAM,CAAC,OAAe,EAAE,OAAqB;QAC3C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAClB,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,OAAO,UAAU,CAAC,OAAO,EAAE;YACzB,IAAI,EAAE,OAAO;SACd,CAAC,CAAA;IACJ,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/Merger.js b/lib/Mergers/Merger.js new file mode 100644 index 0000000..2a26ff0 --- /dev/null +++ b/lib/Mergers/Merger.js @@ -0,0 +1,37 @@ +import fsp from 'fs/promises'; +import path from 'path'; +import { Files } from '../Files.js'; +export class Merger { + constructor(destDir, pathToFile) { + this.destDir = destDir; + this.pathToFile = pathToFile; + this.destPath = path.join(this.destDir, this.pathToFile); + } + getDestPath() { + return this.destPath; + } + getPaths(originDir) { + return { + originPath: path.join(originDir, this.pathToFile), + destPath: this.getDestPath(), + }; + } + async getWhichExistsOrNull(originDir) { + const { originPath, destPath } = this.getPaths(originDir); + const [originExists, destExists] = await Promise.all([ + Files.exists(originPath), + Files.exists(destPath), + ]); + if (!originExists && destExists) { + return fsp.readFile(destPath, 'utf8'); + } + if (!destExists && originExists) { + return fsp.readFile(originPath, 'utf8'); + } + if (!originExists && !destExists) { + throw new Error(`No file found to merge: ${this.pathToFile}`); + } + return null; + } +} +//# sourceMappingURL=Merger.js.map \ No newline at end of file diff --git a/lib/Mergers/Merger.js.map b/lib/Mergers/Merger.js.map new file mode 100644 index 0000000..49d0a38 --- /dev/null +++ b/lib/Mergers/Merger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Merger.js","sourceRoot":"","sources":["../../src/Mergers/Merger.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAgB,MAAM;IAK1B,YACqB,OAAe,EACf,UAAkB;QADlB,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAQ;QAErC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1D,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAES,QAAQ,CAAC,SAAiB;QAClC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;YACjD,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE;SAC7B,CAAA;IACH,CAAC;IAES,KAAK,CAAC,oBAAoB,CAClC,SAAiB;QAEjB,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACvC,CAAC;QAED,IAAI,CAAC,UAAU,IAAI,YAAY,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QACzC,CAAC;QAED,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Npm.js b/lib/Npm.js index cb79e3b..6ed1975 100644 --- a/lib/Npm.js +++ b/lib/Npm.js @@ -1,35 +1,61 @@ -import * as childProcess from 'child_process' -import { logger } from './Logger.js' +import * as childProcess from 'child_process'; +import { Logger } from './Logger.js'; +export class NpmError extends Error { + constructor(message, code) { + super(message); + this.code = code; + this.name = 'NpmError'; + } +} export class Npm { - constructor(settings) { - this.dir = settings?.dir - } - run(args) { - logger.info(`> npm ${args.join(' ')}`) - const result = this.dir - ? childProcess.spawnSync('npm', args, { - cwd: this.dir, - }) - : childProcess.spawnSync('npm', args) - if ((result?.status ?? 0) > 0) { - logger.info( - `Failed npm command: npm ${args.join(' ')}. ${String(result.output)}` - ) + constructor(settings) { + this.logger = settings?.logger ?? new Logger(); + this.dir = settings?.dir; + } + spawn(cmd, args, options = {}) { + return new Promise((resolve, reject) => { + const cp = childProcess.spawn(cmd, args, options); + const error = []; + const stdout = []; + cp.stdout?.on('data', data => { + stdout.push(data.toString()); + }); + cp.on('error', e => { + error.push(e.toString()); + }); + cp.on('close', code => { + if (error.length || (code !== null && code > 0)) { + reject(new NpmError(error.length ? error.join('') : stdout.join(''), code ?? null)); + } + else { + resolve(undefined); + } + }); + }); + } + run(args) { + this.logger.debug(`> npm ${args.join(' ')}`); + const options = this.dir + ? { + cwd: this.dir, + stdio: this.logger.enableDebug ? 'inherit' : 'pipe', + } + : { stdio: this.logger.enableDebug ? 'inherit' : 'pipe' }; + return this.spawn('npm', args, options); + } + init() { + return this.run(['init', '--yes']); + } + i(module) { + if (!module) { + return this.run(['i']); + } + const args = ['i', module]; + return this.run(args); } - } - init() { - this.run(['init', '--yes']) - } - i(module) { - if (!module) { - return this.run(['i']) + iDev(module) { + const args = ['i', '-D', module]; + return this.run(args); } - const args = ['i', module] - this.run(args) - } - iDev(module) { - const args = ['i', '-D', module] - this.run(args) - } } -//# sourceMappingURL=Npm.js.map +//# sourceMappingURL=Npm.js.map \ No newline at end of file diff --git a/lib/Npm.js.map b/lib/Npm.js.map index aecb5fe..b32f1dd 100644 --- a/lib/Npm.js.map +++ b/lib/Npm.js.map @@ -1 +1 @@ -{"version":3,"file":"Npm.js","sourceRoot":"","sources":["../src/Npm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,MAAM,OAAO,GAAG;IAEd,YAAY,QAAyB;QACnC,IAAI,CAAC,GAAG,GAAG,QAAQ,EAAE,GAAW,CAAA;IAClC,CAAC;IACM,GAAG,CAAC,IAAc;QACvB,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG;YACrB,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;gBAClC,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC;YACJ,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,IAAI,CACT,2BAA2B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CACtE,CAAA;QACH,CAAC;IACH,CAAC;IACM,IAAI;QACT,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC7B,CAAC;IAEM,CAAC,CAAC,MAAe;QACtB,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACxB,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;QAC1B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC;IACM,IAAI,CAAC,MAAc;QACxB,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,CAAA;QAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IAChB,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"Npm.js","sourceRoot":"","sources":["../src/Npm.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/PackageJson.js b/lib/PackageJson.js index 40779a6..807e077 100644 --- a/lib/PackageJson.js +++ b/lib/PackageJson.js @@ -1,43 +1,38 @@ -import * as path from 'path' -import * as fs from 'fs' -import * as lodash from 'lodash-es' -import { logger } from './Logger.js' +import * as path from 'path'; +import * as fs from 'fs'; +import * as lodash from 'lodash-es'; export class PackageJson { - constructor(npm) { - let packagejsonPath = './package.json' - if (npm.dir) { - packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`) + constructor(npm) { + let packagejsonPath = './package.json'; + if (npm.dir) { + packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`); + } + this.path = packagejsonPath; + this.npm = npm; + } + setType(type) { + this.mergeWith({ + type, + }); + } + toJSON() { + return JSON.parse(fs.readFileSync(this.path, 'utf-8')); + } + runScript(name) { + return this.npm.run(['run', name]); + } + addNpmScript(name, command) { + this.mergeWith({ + scripts: { + [name]: command, + }, + }); + } + // Updated package json using merge with given object + mergeWith(partialWith) { + const json = lodash.merge(this.toJSON(), partialWith); + // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) + fs.writeFileSync(path.join(this.path), JSON.stringify(json, null, 2), 'utf-8'); } - this.path = packagejsonPath - this.npm = npm - } - setType(type) { - this.mergeWith({ - type, - }) - } - toJSON() { - return JSON.parse(fs.readFileSync(this.path, 'utf-8')) - } - runScript(name) { - this.npm.run(['run', name]) - } - addNpmScript(name, command) { - this.mergeWith({ - scripts: { - [name]: command, - }, - }) - } - // Updated package json using merge with given object - mergeWith(partialWith) { - const json = lodash.merge(this.toJSON(), partialWith) - logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) - fs.writeFileSync( - path.join(this.path), - JSON.stringify(json, null, 2), - 'utf-8' - ) - } } -//# sourceMappingURL=PackageJson.js.map +//# sourceMappingURL=PackageJson.js.map \ No newline at end of file diff --git a/lib/PackageJson.js.map b/lib/PackageJson.js.map index 01d3424..c9cbfff 100644 --- a/lib/PackageJson.js.map +++ b/lib/PackageJson.js.map @@ -1 +1 @@ -{"version":3,"file":"PackageJson.js","sourceRoot":"","sources":["../src/PackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAGpC,MAAM,OAAO,WAAW;IAGtB,YAAY,GAAQ;QAClB,IAAI,eAAe,GAAG,gBAAwB,CAAA;QAC9C,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,eAAe,EAAE,CAAS,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;IAChB,CAAC;IACM,OAAO,CAAC,IAA2B;QACxC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IACM,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IACM,SAAS,CAAC,IAAY;QAC3B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IAC7B,CAAC;IACM,YAAY,CAAC,IAAY,EAAE,OAAe;QAC/C,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE;gBACP,CAAC,IAAI,CAAC,EAAE,OAAO;aAChB;SACF,CAAC,CAAA;IACJ,CAAC;IACD,qDAAqD;IAC9C,SAAS,CAAC,WAAgB;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;QACrD,MAAM,CAAC,IAAI,CAAC,0BAA0B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QACpE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACpB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAA;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"PackageJson.js","sourceRoot":"","sources":["../src/PackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AAInC,MAAM,OAAO,WAAW;IAGtB,YAAY,GAAQ;QAClB,IAAI,eAAe,GAAG,gBAAwB,CAAA;QAC9C,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,eAAe,EAAE,CAAS,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;IAChB,CAAC;IACM,OAAO,CAAC,IAA2B;QACxC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IACM,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IACM,SAAS,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IACpC,CAAC;IACM,YAAY,CAAC,IAAY,EAAE,OAAe;QAC/C,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE;gBACP,CAAC,IAAI,CAAC,EAAE,OAAO;aAChB;SACF,CAAC,CAAA;IACJ,CAAC;IACD,qDAAqD;IAC9C,SAAS,CAAC,WAAgB;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;QACrD,uEAAuE;QACvE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACpB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Starter.js b/lib/Starter.js index 26460e8..9ace139 100644 --- a/lib/Starter.js +++ b/lib/Starter.js @@ -1,2 +1,2 @@ -export {} -//# sourceMappingURL=Starter.js.map +export {}; +//# sourceMappingURL=Starter.js.map \ No newline at end of file diff --git a/lib/StarterLoader.js b/lib/StarterLoader.js new file mode 100644 index 0000000..2d15e8c --- /dev/null +++ b/lib/StarterLoader.js @@ -0,0 +1,71 @@ +import glob from 'fast-glob'; +import fs from 'node:fs'; +import path from 'node:path'; +export class StarterLoader { + constructor() { + this.starters = new Map(); + this.modules = []; + const configFiles = glob.sync(`${StarterLoader.starterPath}/**/node-app.jsonc`); + for (const configFile of configFiles) { + const config = StarterLoader.validateConfig(configFile, JSON.parse(fs.readFileSync(configFile, 'utf8'))); + const original = this.starters.get(config.name); + if (original) { + throw new Error(`Duplicate starter: ${config.name}\n` + + `> Starter 1: ${original.path}\n` + + `> Starter 2: ${path.dirname(configFile)}`); + } + this.starters.set(config.name, { + name: config.name, + config, + path: path.dirname(configFile), + configPath: configFile, + }); + const module = this.modules.find(module => module.name === config.module); + if (module) { + module.starters.push(config.name); + continue; + } + this.modules.push({ + name: config.module, + starters: [config.name], + }); + } + this.modules.sort((a, b) => a.name.localeCompare(b.name)); + } + getOptions() { + return this.modules; + } + getStarter(name) { + const starter = this.starters.get(name); + if (!starter) { + throw new Error(`Starter ${name} not found`); + } + return starter; + } + static validateConfig(path, config) { + if (!config.module) { + throw new Error(`Invalid config at ${path}: module key is required`); + } + if (!config.name) { + throw new Error(`Invalid config at ${path}: name key is required`); + } + if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { + throw new Error(`Invalid config at ${path}: "prebuild" must be array of npm script names or empty`); + } + if (!StarterLoader.isValidOptionalStringArray(config.replace)) { + throw new Error(`Invalid config at ${path}: "replace" must be array of files where strings should be replaced`); + } + const invalidKeys = Object.keys(config).filter(key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key)); + if (invalidKeys.length > 0) { + throw new Error(`Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}`); + } + return config; + } + static isValidOptionalStringArray(array) { + return (!array || + (Array.isArray(array) && + array.every((item) => typeof item === 'string'))); + } +} +StarterLoader.starterPath = path.normalize(path.join(import.meta.dirname, '..', 'starter')); +//# sourceMappingURL=StarterLoader.js.map \ No newline at end of file diff --git a/lib/StarterLoader.js.map b/lib/StarterLoader.js.map new file mode 100644 index 0000000..994eb88 --- /dev/null +++ b/lib/StarterLoader.js.map @@ -0,0 +1 @@ +{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAuB5B,MAAM,OAAO,aAAa;IAOxB;QAHiB,aAAQ,GAA+B,IAAI,GAAG,EAAE,CAAA;QAChD,YAAO,GAAoB,EAAE,CAAA;QAG5C,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAC3B,GAAG,aAAa,CAAC,WAAW,oBAAoB,CACjD,CAAA;QAED,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,aAAa,CAAC,cAAc,CACzC,UAAU,EACV,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAChD,CAAA;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAC/C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CACb,sBAAsB,MAAM,CAAC,IAAI,IAAI;oBACnC,gBAAgB,QAAQ,CAAC,IAAI,IAAI;oBACjC,gBAAgB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAC7C,CAAA;YACH,CAAC;YAED,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;gBAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM;gBACN,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;gBAC9B,UAAU,EAAE,UAAU;aACvB,CAAC,CAAA;YAEF,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,CAAA;YACzE,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;gBACjC,SAAQ;YACV,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;gBAChB,IAAI,EAAE,MAAM,CAAC,MAAM;gBACnB,QAAQ,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;aACxB,CAAC,CAAA;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAA;IACrB,CAAC;IAED,UAAU,CAAC,IAAY;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,YAAY,CAAC,CAAA;QAC9C,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAEO,MAAM,CAAC,cAAc,CAAC,IAAY,EAAE,MAAW;QACrD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,0BAA0B,CAAC,CAAA;QACtE,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,wBAAwB,CAAC,CAAA;QACpE,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,0BAA0B,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,yDAAyD,CACnF,CAAA;QACH,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,0BAA0B,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,qEAAqE,CAC/F,CAAA;QACH,CAAC;QAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAC5C,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CACzE,CAAA;QACD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,qBAAqB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACvE,CAAA;QACH,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC;IAES,MAAM,CAAC,0BAA0B,CAAC,KAAU;QACpD,OAAO,CACL,CAAC,KAAK;YACN,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACnB,KAAK,CAAC,KAAK,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CACxD,CAAA;IACH,CAAC;;AA/FuB,yBAAW,GAAW,IAAI,CAAC,SAAS,CAC1D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAChD,AAFkC,CAElC"} \ No newline at end of file diff --git a/lib/cloudrun-graphql/GraphQLStarter.js b/lib/cloudrun-graphql/GraphQLStarter.js deleted file mode 100644 index 166840e..0000000 --- a/lib/cloudrun-graphql/GraphQLStarter.js +++ /dev/null @@ -1,168 +0,0 @@ -export class GraphQLStarter { - constructor() { - this.name = 'cloudrun-graphql' - } - setToolbelt(toolbelt) { - this.toolbelt = toolbelt - return this - } - install() { - if (this.toolbelt == null) { - throw new Error('No toolbelt') - } - const tb = this.toolbelt - tb.copySharedAsset('.gitignore') - tb.copySharedAsset('.gitlab-ci.yml') - tb.copySharedAsset('.nvmrc') - tb.copySharedAsset('Dockerfile') - tb.copySharedAsset('.dockerignore') - tb.mkdir(tb.stringToPath(`${tb.destination}/ci-branch-config`)) - tb.copySharedAsset('ci-branch-config/common.env') - tb.replaceInFile( - `ci-branch-config/common.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/development.env') - tb.replaceInFile( - `ci-branch-config/development.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/stage.env') - tb.replaceInFile( - `ci-branch-config/stage.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/master.env') - tb.replaceInFile( - `ci-branch-config/master.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/docker-compose`)) - tb.copySharedAsset('docker-compose/docker-compose-entrypoint.sh') - tb.copySharedAsset('docker-compose/docker-compose.ci.yml') - tb.copySharedAsset('docker-compose/docker-compose.local.yml') - tb.copySharedAsset('docker-compose/docker-compose.override.yml') - tb.symlink( - `${tb.destination}/docker-compose/docker-compose.override.yml`, - `${tb.destination}/docker-compose/docker-compose.local.yml` - ) - tb.copySharedAsset('docker-compose/docker-compose.yml') - tb.replaceInFile( - `docker-compose/docker-compose.yml`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.npm.iDev('typescript') - tb.npm.iDev('@types/node') - tb.npm.iDev('ts-node') - tb.npm.i('source-map-support') - tb.copySharedAsset('tsconfig.json') - tb.packageJson.addNpmScript( - 'build:graphql-types', - 'graphql-codegen --config codegen.yml' - ) - tb.packageJson.addNpmScript( - 'build:copy-schema', - 'mkdir -p ./dist/view/graphql/schema && cp -r ./src/view/graphql/schema ./dist/view/graphql/schema' - ) - tb.packageJson.addNpmScript( - 'build', - 'npm run build:graphql-types && npm run build:copy-schema && tsc' - ) - tb.packageJson.addNpmScript( - 'start', - 'node -r source-map-support/register dist/index.js' - ) - tb.npm.i('configuru') - tb.npm.i('pino') - tb.npm.iDev('pino-pretty') - tb.copyAsset('.env.jsonc') - tb.mkdir(tb.stringToPath(`${tb.destination}/src`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/errors`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/ports`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/utils`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/domain/errors/codes.ts') - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/ports/logger.d.ts') - tb.copyAsset('src/adapters/pino.logger.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql/resolvers`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql/schema`)) - tb.copyAsset('src/config.ts') - tb.copyAsset('src/index.ts') - tb.copyAsset('src/container.ts') - tb.copyAsset('src/context.ts') - tb.copyAsset('codegen.yml') - tb.copyAsset('src/view/controller.ts') - tb.copyAsset('src/view/server.ts') - tb.copyAsset('src/view/graphql/schema.ts') - tb.copyAsset('src/view/graphql/resolvers.ts') - tb.copyAsset('src/view/graphql/resolvers/greeting.resolver.ts') - tb.copyAsset('src/view/graphql/schema/schema.graphql') - tb.npm.iDev('mocha') - tb.npm.iDev('mocha-junit-reporter') - tb.npm.iDev('mocha-multi-reporters') - tb.npm.iDev('nyc') - tb.npm.iDev('tsx') - tb.npm.iDev('@types/mocha') - tb.npm.iDev('@istanbuljs/nyc-config-typescript') - tb.copySharedAsset('.mocharc.json', tb.destination) - tb.copySharedAsset('.mocha-junit-config.json', tb.destination) - tb.packageJson.setType('module') - tb.packageJson.addNpmScript('test', 'mocha') - tb.packageJson.addNpmScript( - 'ci-test:no-coverage', - 'npm run test -- --parallel=false -R mocha-multi-reporters --reporter-options configFile=.mocha-junit-config.json' - ) - tb.packageJson.addNpmScript( - 'ci-test', - 'nyc -a -r cobertura --report-dir output npm run ci-test:no-coverage' - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test`)) - tb.copySharedAsset('src/test/setup.ts') - tb.copyAsset('src/test/helloWorld.test.ts') - tb.npm.i('@as-integrations/express5') - tb.npm.iDev('@ackee/styleguide-backend-config') - tb.npm.iDev('prettier') - tb.npm.iDev('eslint') - tb.npm.iDev('eslint-formatter-gitlab@^5.0.0') - tb.copyAsset('.eslint.tsconfig.json') - tb.copyAsset('.eslintrc.cjs') - tb.copySharedAsset('prettier.config.cjs') - tb.packageJson.addNpmScript( - 'prettier', - "prettier --check --write '**/*.{ts,js,json,md}'" - ) - tb.packageJson.addNpmScript( - 'lint', - 'eslint --ext .ts --ext .graphql src -f codeframe --fix' - ) - tb.packageJson.addNpmScript('codestyle', 'npm run prettier && npm run lint') - tb.packageJson.addNpmScript( - 'ci-lint', - 'npm run lint -- -f checkstyle -o ./output/checkstyle-result.xml' - ) - tb.npm.i('@apollo/server') - tb.npm.iDev('@graphql-codegen/cli') - tb.npm.iDev('lodash') - tb.npm.iDev('@graphql-eslint/eslint-plugin') - tb.npm.iDev('@graphql-codegen/typescript') - tb.npm.iDev('@graphql-codegen/typescript-resolvers') - tb.npm.i('@graphql-tools/load-files') - tb.npm.i('@graphql-tools/merge') - tb.npm.i('@graphql-tools/schema') - tb.npm.i('graphql') - tb.npm.i('express') - tb.npm.iDev('@types/express') - tb.packageJson.runScript('build') - } -} -//# sourceMappingURL=GraphQLStarter.js.map diff --git a/lib/cloudrun-graphql/GraphQLStarter.js.map b/lib/cloudrun-graphql/GraphQLStarter.js.map deleted file mode 100644 index e887577..0000000 --- a/lib/cloudrun-graphql/GraphQLStarter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"GraphQLStarter.js","sourceRoot":"","sources":["../../src/cloudrun-graphql/GraphQLStarter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/cloudrun/CloudRunStarter.js b/lib/cloudrun/CloudRunStarter.js deleted file mode 100644 index 73480cc..0000000 --- a/lib/cloudrun/CloudRunStarter.js +++ /dev/null @@ -1,167 +0,0 @@ -export class CloudRunStarter { - constructor() { - this.name = 'cloudrun' - } - setToolbelt(toolbelt) { - this.toolbelt = toolbelt - return this - } - install() { - if (this.toolbelt == null) { - throw new Error('No toolbelt') - } - const tb = this.toolbelt - tb.copySharedAsset('.gitignore') - tb.copySharedAsset('.gitlab-ci.yml') - tb.copySharedAsset('.nvmrc') - tb.copySharedAsset('Dockerfile') - tb.copySharedAsset('.dockerignore') - tb.mkdir(tb.stringToPath(`${tb.destination}/ci-branch-config`)) - tb.copySharedAsset('ci-branch-config/common.env') - tb.replaceInFile( - `ci-branch-config/common.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/development.env') - tb.replaceInFile( - `ci-branch-config/development.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/stage.env') - tb.replaceInFile( - `ci-branch-config/stage.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/master.env') - tb.replaceInFile( - `ci-branch-config/master.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/docker-compose`)) - tb.copySharedAsset('docker-compose/docker-compose-entrypoint.sh') - tb.copySharedAsset('docker-compose/docker-compose.ci.yml') - tb.copySharedAsset('docker-compose/docker-compose.local.yml') - tb.symlink( - `${tb.destination}/docker-compose/docker-compose.override.yml`, - `${tb.destination}/docker-compose/docker-compose.local.yml` - ) - tb.copySharedAsset('docker-compose/docker-compose.yml') - tb.replaceInFile( - `docker-compose/docker-compose.yml`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.npm.iDev('typescript') - tb.npm.iDev('@types/node') - tb.npm.iDev('ts-node') - tb.npm.i('source-map-support') - tb.copySharedAsset('tsconfig.json') - tb.packageJson.setType('module') - tb.packageJson.addNpmScript('build', 'tsc') - tb.packageJson.addNpmScript( - 'start', - 'node -r source-map-support/register dist/index.js' - ) - tb.npm.i('configuru') - tb.npm.i('pino') - tb.npm.i('pino-http') - tb.npm.iDev('pino-pretty') - tb.copyAsset('.env.jsonc') - tb.mkdir(tb.stringToPath(`${tb.destination}/src`)) - tb.copyAsset('src/config.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/container.ts') - tb.copyAsset('src/context.ts') - tb.copyAsset('src/index.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view`)) - tb.copyAsset(`/src/view/server.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest`)) - tb.copyAsset(`/src/view/rest/request.d.ts`) - tb.copyAsset(`/src/view/rest/routes.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/controller`)) - tb.copyAsset(`/src/view/rest/controller/health-check.controller.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/middleware`)) - tb.copyAsset(`/src/view/rest/middleware/context-middleware.ts`) - tb.copyAsset(`/src/view/rest/middleware/error-handler.ts`) - tb.copyAsset(`/src/view/rest/middleware/request-logger.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/util`)) - tb.copyAsset(`/src/view/rest/util/openapi.util.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/spec`)) - tb.copyAsset(`/src/view/rest/spec/openapi.yml`) - tb.npm.i('node-healthz') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain`)) - tb.copyAsset('src/domain/health-check.service.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/errors`)) - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/errors/codes.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/util`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/ports`)) - tb.copyAsset('src/domain/ports/logger.d.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/adapters/pino.logger.ts') - tb.npm.i('express') - tb.npm.iDev('@types/express') - tb.npm.iDev('mocha') - tb.npm.iDev('mocha-junit-reporter') - tb.npm.iDev('mocha-multi-reporters') - tb.npm.iDev('nyc') - tb.npm.iDev('@types/mocha') - tb.npm.iDev('@istanbuljs/nyc-config-typescript') - tb.npm.iDev('supertest') - tb.npm.iDev('@types/supertest') - tb.copySharedAsset('.mocharc.json', tb.destination) - tb.copySharedAsset('.mocha-junit-config.json', tb.destination) - tb.packageJson.addNpmScript('test', 'mocha') - tb.packageJson.addNpmScript( - 'ci-test:no-coverage', - 'npm run test -- --parallel=false -R mocha-multi-reporters --reporter-options configFile=.mocha-junit-config.json' - ) - tb.packageJson.addNpmScript( - 'ci-test', - 'nyc -a -r cobertura --report-dir output npm run ci-test:no-coverage' - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test`)) - tb.copySharedAsset('src/test/setup.ts') - tb.copyAsset('src/test/health-check.test.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test/util`)) - tb.copyAsset('src/test/util/openapi-test.util.ts') - tb.npm.iDev('@ackee/styleguide-backend-config') - tb.npm.iDev('prettier') - tb.npm.iDev('eslint') - tb.npm.iDev('eslint-formatter-gitlab@^5.0.0') - tb.copyAsset('.eslint.tsconfig.json') - tb.copyAsset('.eslintrc.cjs') - tb.copySharedAsset('prettier.config.cjs') - tb.packageJson.addNpmScript( - 'prettier', - "prettier --check --write '**/*.{ts,js,json,md}'" - ) - tb.packageJson.addNpmScript('lint', "eslint '**/*.ts' -f codeframe --fix") - tb.packageJson.addNpmScript('codestyle', 'npm run prettier && npm run lint') - tb.packageJson.addNpmScript( - 'ci-lint', - 'npm run lint -- -f checkstyle -o ./output/checkstyle-result.xml' - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/cli`)) - tb.copyAsset('src/view/cli/cli.ts') - tb.copyAsset('src/view/cli/README.md') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/cli/openapi`)) - tb.copyAsset('src/view/cli/openapi/generate.ts') - tb.npm.iDev('yargs') - tb.npm.iDev('yaml') - tb.packageJson.addNpmScript('cli', 'tsx ./src/view/cli/cli.ts') - tb.npm.iDev('openapi-typescript') - tb.npm.iDev('tsx') - tb.packageJson.addNpmScript( - 'generate:api', - 'npm run cli openapi generate src/view/rest/spec/openapi.yml && npm run codestyle' - ) - tb.packageJson.runScript('generate:api') - tb.packageJson.runScript('build') - } -} -//# sourceMappingURL=CloudRunStarter.js.map diff --git a/lib/cloudrun/CloudRunStarter.js.map b/lib/cloudrun/CloudRunStarter.js.map deleted file mode 100644 index fb99caf..0000000 --- a/lib/cloudrun/CloudRunStarter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CloudRunStarter.js","sourceRoot":"","sources":["../../src/cloudrun/CloudRunStarter.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/types.js b/lib/types.js index 4ef431e..718fd38 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1,2 +1,2 @@ -export {} -//# sourceMappingURL=types.js.map +export {}; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/src/Bootstrap.ts b/src/Bootstrap.ts index 80895de..0b0ecf9 100644 --- a/src/Bootstrap.ts +++ b/src/Bootstrap.ts @@ -1,99 +1,101 @@ -import { CloudRunStarter } from './cloudrun/CloudRunStarter.js' -import { Npm } from './Npm.js' -import { Toolbelt } from './Toolbelt.js' +import inquirer from 'inquirer' import * as path from 'path' -import { PackageJson } from './PackageJson.js' -import { GraphQLStarter } from './cloudrun-graphql/GraphQLStarter.js' -import { Path } from './types.js' +import * as fs from 'fs' import yargs from 'yargs' import { hideBin } from 'yargs/helpers' -import { logger } from './Logger.js' -import { Starter } from './Starter.js' +import { Builder } from './Builder.js' +import { Logger } from './Logger.js' +import { Npm } from './Npm.js' +import { PackageJson } from './PackageJson.js' +import { LoadedStarter, StarterLoader } from './StarterLoader.js' +import { Path } from './types.js' export class Bootstrap { - protected starters: Starter[] = [new CloudRunStarter(), new GraphQLStarter()] + protected starterLoader = new StarterLoader() - public runCLI(args: string[]) { - const cli = yargs(hideBin(args)) - .usage('create-node-app [options]') - .positional('starter', { - name: 'starter', - type: 'string', - required: true, - description: 'Which template to setup (required)', - choices: this.starters.map(starter => starter.name), - }) - .option('dir', { - type: 'string', - alias: 'd', - default: './node-app', - description: 'Destination directory', - }) - .option('project-name', { - type: 'string', - alias: 'n', - default: 'node-app', - description: 'Google Cloud project name', - }) - .option('force', { - type: 'boolean', - alias: 'f', - default: false, - description: - "Overwrite existing destination directory if it's not empty", - }) - .version('1.0.0') - .help() + public async runCLI(args: string[]) { + try { + const cli = yargs(hideBin(args)) + .usage('create-node-app [options]') + .option('dir', { + type: 'string', + alias: 'd', + default: './node-app', + description: 'Destination directory', + }) + .option('debug', { + type: 'boolean', + alias: 'D', + default: false, + description: 'Enables debug logs', + }) + .option('project-name', { + type: 'string', + alias: 'n', + default: 'node-app', + description: 'Google Cloud project name', + }) + .option('force', { + type: 'boolean', + alias: 'f', + default: false, + description: + "Overwrite existing destination directory if it's not empty", + }) + .version('1.0.0') + .help() - const parsedArgs = cli.parseSync() - const starterArg = parsedArgs._[0] + const parsedArgs = cli.parseSync() - const starter = this.starters.find(x => x.name === starterArg) - const destination = path.normalize(parsedArgs.dir) as Path + const destination = path.normalize(parsedArgs.dir) as Path - if (!starter) { - logger.info('Invalid starter') - cli.showHelp() - process.exit(1) - } + const logger = new Logger(parsedArgs.debug) + const npm = new Npm({ dir: destination, logger }) + const packageJson = new PackageJson(npm) - logger.info(`starter=${starter.name}, destination=${destination}`) + const starters: LoadedStarter[] = [] - const npm = new Npm({ dir: destination }) - const packageJson = new PackageJson(npm) - const toolbelt = new Toolbelt({ - npm, - packageJson, - assetDirectory: path.join( - import.meta.dirname, - '..', - 'starter', - starter.name - ), - sharedDirectory: path.join( - import.meta.dirname, - '..', - 'starter', - 'shared' - ), - destination: destination, - projectName: parsedArgs.projectName, - }) - starter.setToolbelt(toolbelt) + if (fs.existsSync(destination) && !parsedArgs.force) { + const answer = await inquirer.prompt<{ force: boolean }>({ + type: 'confirm', + name: 'force', + message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, + }) + if (!answer.force) { + process.exit(0) + } + } - if (!toolbelt.isDirectoryEmpty(destination)) { - if (!parsedArgs.force) { - logger.info( - `Directory '${destination}' already exists and is not empty. Use --force or -f flag to overwrite the existing directory.` - ) - process.exit(1) + for (const module of this.starterLoader.getOptions()) { + const answer = await inquirer.prompt<{ starter: string }>({ + type: 'list', + name: 'starter', + message: `Which ${module.name} would you like to use?`, + choices: [...module.starters, 'none'], + }) + + if (answer.starter === 'none') { + continue + } + starters.push(this.starterLoader.getStarter(answer.starter)) + } + + const builder = new Builder({ + npm, + logger, + packageJson, + starters, + destination: destination, + projectName: parsedArgs.projectName, + }) + + await builder.build() + } catch (error) { + if (error instanceof Error && error.name === 'ExitPromptError') { + process.exit(0) } else { - logger.info(`Overwriting existing directory '${destination}'`) + throw error } } - - toolbelt.mkdir(destination, { overwrite: parsedArgs.force }) - toolbelt.npm.init() - starter.install() } } diff --git a/src/Builder.ts b/src/Builder.ts new file mode 100644 index 0000000..5551a39 --- /dev/null +++ b/src/Builder.ts @@ -0,0 +1,172 @@ +import glob from 'fast-glob' +import * as fs from 'fs/promises' +import * as path from 'path' +import { Logger } from './Logger.js' +import { Npm } from './Npm.js' +import { PackageJson } from './PackageJson.js' +import { LoadedStarter, StarterConfig } from './StarterLoader.js' +import { Merger } from './Mergers/Merger.js' +// import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' +// import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' +// import { ConfigMerger } from './Mergers/ConfigMerger.js' +// import { ContainerMerger } from './Mergers/ContainerMerger.js' +import { Files } from './Files.js' + +export class Builder { + public static readonly BASE_STARTER_DIR = path.normalize( + path.join(import.meta.dirname, '..', 'starter', '_base') + ) + public static readonly INGORED_FILES = ['node-app.jsonc'] + + public readonly npm: Npm + protected readonly logger: Logger + protected readonly starters: LoadedStarter[] + protected readonly fileMergers: Merger[] + protected readonly destination: string + protected readonly projectName: string + protected readonly replacements: Record + + constructor(params: { + npm: Npm + logger: Logger + packageJson: PackageJson + starters: LoadedStarter[] + destination: string + projectName: string + }) { + this.npm = params.npm + this.logger = params.logger + this.starters = params.starters + this.destination = params.destination + this.projectName = params.projectName + this.replacements = { + '{{PROJECT_NAME}}': this.projectName, + } + this.fileMergers = [ + // new PackageJsonMerger(this.projectName, this.destination, 'package.json'), + // new EnvJsoncMerger(this.destination, '.env.jsonc'), + // new ConfigMerger(this.destination, 'src/config.ts'), + // new ContainerMerger(this.destination, 'src/container.ts'), + ] + } + + protected async prepareFolder() { + if (await Files.existsAndIsDir(this.destination)) { + await fs.rm(this.destination, { recursive: true }) + } + await fs.mkdir(this.destination, { recursive: true }) + } + + public async build() { + try { + await this.logger.loader(`Preparing clean folder`, this.prepareFolder()) + + await this.logger.loader( + `Preparing folder structure`, + this.buildStarter(Builder.BASE_STARTER_DIR) + ) + + for (const starter of this.starters) { + await this.logger.loader( + `Adding ${starter.config.name} ${starter.config.module}`, + this.buildStarter(starter.path, starter.config) + ) + } + + await this.logger.loader(`npm install`, this.npm.run(['install'])) + + const prebuildScripts: Array = this.starters + .map(starter => starter.config.prebuild) + .filter(script => script !== undefined) + + for (const script of prebuildScripts) { + await this.logger.loader( + `npm run ${script.join(' ')}`, + this.npm.run(['run', ...script]) + ) + } + + await this.logger.loader(`npm run build`, this.npm.run(['run', 'build'])) + + this.logger.info( + `Your app is ready in ${path.relative( + process.cwd(), + this.destination + )}! 🚀` + ) + } catch (error) { + this.logger.error(error) + process.exit(1) + } + } + + protected async buildStarter(starterDir: string, config?: StarterConfig) { + const destDir = path.normalize(path.join(process.cwd(), this.destination)) + const files = await glob(`${starterDir}/*`, { + cwd: starterDir, + dot: true, + onlyFiles: false, + }) + + const ignoredFiles = Builder.INGORED_FILES.map(file => + path.join(starterDir, file) + ) + + const mergedFiles = await Promise.all( + this.fileMergers.map(async merger => { + return { + path: merger.getDestPath(), + content: await merger.merge(starterDir), + } + }) + ) + + await Promise.all( + files.map(async filePath => { + if (ignoredFiles.includes(filePath)) { + return + } + const destFilePath = path.join(destDir, path.basename(filePath)) + if (await Files.existsAndIsDir(filePath)) { + await fs.cp(filePath, destFilePath, { recursive: true }) + return + } + await fs.cp(filePath, destFilePath) + }) + ) + + await Promise.all( + mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content)) + ) + + if (config?.replace) { + await Promise.all( + config.replace.map(async filePath => this.replaceInFile(filePath)) + ) + } + } + + public async replaceInFile(filePath: string) { + filePath = path.normalize(path.join(this.destination, filePath)) + let content = await fs.readFile(filePath, 'utf8') + content = Object.keys(this.replacements).reduce((acc, key) => { + return acc.replaceAll(key, this.replacements[key]) + }, content) + return fs.writeFile(filePath, content) + } + + public async symlink(linkName: string, linkedFile: string) { + linkName = path.normalize(linkName) + linkedFile = path.normalize(linkedFile) + this.logger.info(`> ln -s ${linkName} ${linkedFile}`) + try { + await fs.symlink(linkedFile, linkName) + } catch (error) { + if ('code' in error && error.code === 'EEXIST') { + // OK + } else { + throw error + } + } + } +} diff --git a/src/Files.ts b/src/Files.ts new file mode 100644 index 0000000..cebe52a --- /dev/null +++ b/src/Files.ts @@ -0,0 +1,13 @@ +import * as fsp from 'fs/promises' + +export class Files { + public static async exists(path: string) { + const stat = await fsp.stat(path).catch(() => undefined) + return Boolean(stat) + } + + public static async existsAndIsDir(path: string) { + const stat = await fsp.stat(path).catch(() => undefined) + return Boolean(stat?.isDirectory()) + } +} diff --git a/src/Logger.ts b/src/Logger.ts index 0265f8b..991dda2 100644 --- a/src/Logger.ts +++ b/src/Logger.ts @@ -1,11 +1,30 @@ -export const logger = { - info: (message: string) => { +import { oraPromise } from 'ora' + +export class Logger { + constructor(public readonly enableDebug: boolean = false) {} + + info(message: string) { console.log(message) - }, - verbose: (message: string) => { + } + verbose(message: string) { console.log(message) - }, - error: (message: string) => { + } + error(message: string) { console.log(message) - }, + } + debug(message: string) { + if (this.enableDebug) { + console.log(message) + } + } + loader(message: string, promise: Promise) { + if (this.enableDebug) { + this.info(message) + return promise + } + + return oraPromise(promise, { + text: message, + }) + } } diff --git a/src/Mergers/Merger.ts b/src/Mergers/Merger.ts new file mode 100644 index 0000000..f8a3a3b --- /dev/null +++ b/src/Mergers/Merger.ts @@ -0,0 +1,52 @@ +import fsp from 'fs/promises' +import path from 'path' +import { Files } from '../Files.js' + +export abstract class Merger { + abstract merge(originDir: string): Promise + + protected destPath: string + + constructor( + protected readonly destDir: string, + protected readonly pathToFile: string + ) { + this.destPath = path.join(this.destDir, this.pathToFile) + } + + public getDestPath() { + return this.destPath + } + + protected getPaths(originDir: string) { + return { + originPath: path.join(originDir, this.pathToFile), + destPath: this.getDestPath(), + } + } + + protected async getWhichExistsOrNull( + originDir: string + ): Promise { + const { originPath, destPath } = this.getPaths(originDir) + + const [originExists, destExists] = await Promise.all([ + Files.exists(originPath), + Files.exists(destPath), + ]) + + if (!originExists && destExists) { + return fsp.readFile(destPath, 'utf8') + } + + if (!destExists && originExists) { + return fsp.readFile(originPath, 'utf8') + } + + if (!originExists && !destExists) { + throw new Error(`No file found to merge: ${this.pathToFile}`) + } + + return null + } +} diff --git a/src/Npm.ts b/src/Npm.ts index cde2762..7d093c0 100644 --- a/src/Npm.ts +++ b/src/Npm.ts @@ -1,27 +1,72 @@ import * as childProcess from 'child_process' -import { logger } from './Logger.js' +import { Logger } from './Logger.js' import { Path } from './types.js' +export class NpmError extends Error { + constructor( + message: string, + public readonly code: number | null + ) { + super(message) + this.name = 'NpmError' + } +} + export class Npm { + protected readonly logger: Logger public readonly dir: Path - constructor(settings?: { dir?: Path }) { + + constructor(settings?: { dir?: Path; logger?: Logger }) { + this.logger = settings?.logger ?? new Logger() this.dir = settings?.dir as Path } + + protected spawn( + cmd: string, + args: ReadonlyArray, + options: childProcess.SpawnOptions = {} + ) { + return new Promise((resolve, reject) => { + const cp = childProcess.spawn(cmd, args, options) + const error: string[] = [] + const stdout: string[] = [] + + cp.stdout?.on('data', data => { + stdout.push(data.toString()) + }) + + cp.on('error', e => { + error.push(e.toString()) + }) + + cp.on('close', code => { + if (error.length || (code !== null && code > 0)) { + reject( + new NpmError( + error.length ? error.join('') : stdout.join(''), + code ?? null + ) + ) + } else { + resolve(undefined) + } + }) + }) + } + public run(args: string[]) { - logger.info(`> npm ${args.join(' ')}`) - const result = this.dir - ? childProcess.spawnSync('npm', args, { + this.logger.debug(`> npm ${args.join(' ')}`) + const options: childProcess.SpawnOptions = this.dir + ? { cwd: this.dir, - }) - : childProcess.spawnSync('npm', args) - if ((result?.status ?? 0) > 0) { - logger.info( - `Failed npm command: npm ${args.join(' ')}. ${String(result.output)}` - ) - } + stdio: this.logger.enableDebug ? 'inherit' : 'pipe', + } + : { stdio: this.logger.enableDebug ? 'inherit' : 'pipe' } + + return this.spawn('npm', args, options) } public init() { - this.run(['init', '--yes']) + return this.run(['init', '--yes']) } public i(module?: string) { @@ -29,10 +74,10 @@ export class Npm { return this.run(['i']) } const args = ['i', module] - this.run(args) + return this.run(args) } public iDev(module: string) { const args = ['i', '-D', module] - this.run(args) + return this.run(args) } } diff --git a/src/PackageJson.ts b/src/PackageJson.ts index 0774b82..42fdebf 100644 --- a/src/PackageJson.ts +++ b/src/PackageJson.ts @@ -2,7 +2,6 @@ import * as path from 'path' import * as fs from 'fs' import * as lodash from 'lodash-es' import { Npm } from './Npm.js' -import { logger } from './Logger.js' import { Path } from './types.js' export class PackageJson { @@ -25,7 +24,7 @@ export class PackageJson { return JSON.parse(fs.readFileSync(this.path, 'utf-8')) } public runScript(name: string) { - this.npm.run(['run', name]) + return this.npm.run(['run', name]) } public addNpmScript(name: string, command: string) { this.mergeWith({ @@ -37,7 +36,7 @@ export class PackageJson { // Updated package json using merge with given object public mergeWith(partialWith: any) { const json = lodash.merge(this.toJSON(), partialWith) - logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) + // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) fs.writeFileSync( path.join(this.path), JSON.stringify(json, null, 2), diff --git a/src/Starter.ts b/src/Starter.ts index 7a06f8f..a3f72a6 100644 --- a/src/Starter.ts +++ b/src/Starter.ts @@ -1,7 +1,7 @@ -import { Toolbelt } from './Toolbelt.js' +import { Builder } from './Builder.js' export interface Starter { readonly name: string - setToolbelt(toolbelt: Toolbelt): Starter + setToolbelt(toolbelt: Builder): Starter install(): void } diff --git a/src/StarterLoader.ts b/src/StarterLoader.ts new file mode 100644 index 0000000..3ae00d5 --- /dev/null +++ b/src/StarterLoader.ts @@ -0,0 +1,123 @@ +import glob from 'fast-glob' +import fs from 'node:fs' +import path from 'node:path' + +export interface StarterConfig { + module: string + name: string + prebuild?: string[] + replace?: string[] + merge?: string[] +} + +export interface LoadedStarter { + name: string + config: StarterConfig + + path: string + configPath: string +} + +export interface StarterModule { + name: string + starters: string[] +} + +export class StarterLoader { + private static readonly starterPath: string = path.normalize( + path.join(import.meta.dirname, '..', 'starter') + ) + private readonly starters: Map = new Map() + private readonly modules: StarterModule[] = [] + + constructor() { + const configFiles = glob.sync( + `${StarterLoader.starterPath}/**/node-app.jsonc` + ) + + for (const configFile of configFiles) { + const config = StarterLoader.validateConfig( + configFile, + JSON.parse(fs.readFileSync(configFile, 'utf8')) + ) + + const original = this.starters.get(config.name) + if (original) { + throw new Error( + `Duplicate starter: ${config.name}\n` + + `> Starter 1: ${original.path}\n` + + `> Starter 2: ${path.dirname(configFile)}` + ) + } + + this.starters.set(config.name, { + name: config.name, + config, + path: path.dirname(configFile), + configPath: configFile, + }) + + const module = this.modules.find(module => module.name === config.module) + if (module) { + module.starters.push(config.name) + continue + } + this.modules.push({ + name: config.module, + starters: [config.name], + }) + } + + this.modules.sort((a, b) => a.name.localeCompare(b.name)) + } + + getOptions(): StarterModule[] { + return this.modules + } + + getStarter(name: string): LoadedStarter { + const starter = this.starters.get(name) + if (!starter) { + throw new Error(`Starter ${name} not found`) + } + return starter + } + + private static validateConfig(path: string, config: any): StarterConfig { + if (!config.module) { + throw new Error(`Invalid config at ${path}: module key is required`) + } + if (!config.name) { + throw new Error(`Invalid config at ${path}: name key is required`) + } + if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { + throw new Error( + `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` + ) + } + + if (!StarterLoader.isValidOptionalStringArray(config.replace)) { + throw new Error( + `Invalid config at ${path}: "replace" must be array of files where strings should be replaced` + ) + } + + const invalidKeys = Object.keys(config).filter( + key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key) + ) + if (invalidKeys.length > 0) { + throw new Error( + `Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}` + ) + } + return config + } + + protected static isValidOptionalStringArray(array: any): array is string[] { + return ( + !array || + (Array.isArray(array) && + array.every((item: any) => typeof item === 'string')) + ) + } +} diff --git a/src/Toolbelt.ts b/src/Toolbelt.ts deleted file mode 100644 index afba9ce..0000000 --- a/src/Toolbelt.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' -import { PackageJson } from './PackageJson.js' -import { Npm } from './Npm.js' -import { logger } from './Logger.js' -import { Path } from './types.js' - -export class Toolbelt { - public readonly npm: Npm - public readonly packageJson: PackageJson - readonly assetDirectory: string - readonly sharedDirectory: string - readonly destination: string - readonly projectName?: string - constructor(params: { - npm: Npm - packageJson: PackageJson - assetDirectory: string - sharedDirectory: string - destination: string - projectName?: string - }) { - this.npm = params.npm - this.packageJson = params.packageJson - this.assetDirectory = params.assetDirectory - this.sharedDirectory = params.sharedDirectory - this.destination = params.destination - this.projectName = params.projectName - } - public stringToPath(str: string) { - return path.normalize(str) as Path - } - - public isDirectoryEmpty(dirpath: string): boolean { - const path = this.stringToPath(dirpath) - - if (!fs.existsSync(path)) { - return true - } - - const stat = fs.statSync(path) - if (!stat.isDirectory()) { - return true - } - - const contents = fs.readdirSync(path) - return contents.length === 0 - } - - public mkdir( - dirpath: string, - option?: { - /** If exists, remove recursively first */ - overwrite?: boolean - } - ) { - dirpath = this.stringToPath(dirpath) - const rootPath = ['.', './'] - if (!rootPath.includes(dirpath)) { - if (fs.existsSync(dirpath) && option?.overwrite) { - fs.rmSync(dirpath, { recursive: true }) - } - fs.mkdirSync(dirpath, { recursive: true }) - } - } - /** - * Like cp, but second argument does not need to include file name - * the name is preserved. - */ - public cpFile(a: string, b: string, option?: { destFileName?: string }) { - a = this.stringToPath(a) - b = this.stringToPath(b) - const file = path.basename(a) - this.cp(a, this.stringToPath(`${b}/${option?.destFileName ?? file}`)) - } - - public cp(a: string, b: string) { - a = this.stringToPath(a) - b = this.stringToPath(b) - logger.info(`> cp ${a} ${b}`) - fs.copyFileSync(a, b) - } - public copyAsset(name: string, destination?: string) { - let destinationName = name - if (path.basename(name) === '.gitignore') { - name = '.gitignore_' - destinationName = '.gitignore' - } - name = this.stringToPath(name) - destination = this.stringToPath(this.destination) - this.cpFile(`${this.assetDirectory}/${name}`, destination, { - destFileName: destinationName, - }) - } - public copySharedAsset(name: string, destination?: string) { - let destinationName = name - if (path.basename(name) === '.gitignore') { - name = '.gitignore_' - destinationName = '.gitignore' - } - name = this.stringToPath(name) - destination = this.stringToPath(this.destination) - this.cpFile(`${this.sharedDirectory}/${name}`, destination, { - destFileName: destinationName, - }) - } - public replaceInFile( - filePath: string, - placeholder: string, - replacement: string = 'REPLACEME' - ) { - filePath = this.stringToPath(`${this.destination}/${filePath}`) - let content = fs.readFileSync(filePath, 'utf8') - /* eslint-disable-next-line security/detect-non-literal-regexp */ - content = content.replace(new RegExp(placeholder, 'g'), replacement) - fs.writeFileSync(filePath, content) - } - public symlink(linkName: string, linkedFile: string) { - linkName = this.stringToPath(linkName) - linkedFile = this.stringToPath(linkedFile) - logger.info(`> ln -s ${linkName} ${linkedFile}`) - try { - fs.symlinkSync(linkedFile, linkName) - } catch (error) { - if ('code' in error && error.code === 'EEXIST') { - // OK - } else { - throw error - } - } - } -} diff --git a/src/cloudrun-graphql/GraphQLStarter.ts b/src/cloudrun-graphql/GraphQLStarter.ts deleted file mode 100644 index 36ec592..0000000 --- a/src/cloudrun-graphql/GraphQLStarter.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Starter } from '../Starter.js' -import { Toolbelt } from '../Toolbelt.js' - -export class GraphQLStarter implements Starter { - public readonly name = 'cloudrun-graphql' - protected toolbelt?: Toolbelt - - public setToolbelt(toolbelt: Toolbelt): Starter { - this.toolbelt = toolbelt - return this - } - - public install(): void { - if (this.toolbelt == null) { - throw new Error('No toolbelt') - } - const tb = this.toolbelt - tb.copySharedAsset('.gitignore') - tb.copySharedAsset('.gitlab-ci.yml') - tb.copySharedAsset('.nvmrc') - tb.copySharedAsset('Dockerfile') - tb.copySharedAsset('.dockerignore') - - tb.mkdir(tb.stringToPath(`${tb.destination}/ci-branch-config`)) - tb.copySharedAsset('ci-branch-config/common.env') - tb.replaceInFile( - `ci-branch-config/common.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/development.env') - tb.replaceInFile( - `ci-branch-config/development.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/stage.env') - tb.replaceInFile( - `ci-branch-config/stage.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/master.env') - tb.replaceInFile( - `ci-branch-config/master.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - - tb.mkdir(tb.stringToPath(`${tb.destination}/docker-compose`)) - tb.copySharedAsset('docker-compose/docker-compose-entrypoint.sh') - tb.copySharedAsset('docker-compose/docker-compose.ci.yml') - tb.copySharedAsset('docker-compose/docker-compose.local.yml') - tb.copySharedAsset('docker-compose/docker-compose.override.yml') - tb.symlink( - `${tb.destination}/docker-compose/docker-compose.override.yml`, - `${tb.destination}/docker-compose/docker-compose.local.yml` - ) - tb.copySharedAsset('docker-compose/docker-compose.yml') - tb.replaceInFile( - `docker-compose/docker-compose.yml`, - '{{PROJECT_NAME}}', - tb.projectName - ) - - tb.npm.iDev('typescript') - tb.npm.iDev('@types/node') - tb.npm.iDev('ts-node') - tb.npm.i('source-map-support') - tb.copySharedAsset('tsconfig.json') - tb.packageJson.addNpmScript( - 'build:graphql-types', - 'graphql-codegen --config codegen.yml' - ) - tb.packageJson.addNpmScript( - 'build:copy-schema', - 'mkdir -p ./dist/view/graphql/schema && cp -r ./src/view/graphql/schema ./dist/view/graphql/schema' - ) - tb.packageJson.addNpmScript( - 'build', - 'npm run build:graphql-types && npm run build:copy-schema && tsc' - ) - tb.packageJson.addNpmScript( - 'start', - 'node -r source-map-support/register dist/index.js' - ) - - tb.npm.i('configuru') - tb.npm.i('pino') - tb.npm.iDev('pino-pretty') - tb.copyAsset('.env.jsonc') - tb.mkdir(tb.stringToPath(`${tb.destination}/src`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/errors`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/ports`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/utils`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/domain/errors/codes.ts') - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/ports/logger.d.ts') - tb.copyAsset('src/adapters/pino.logger.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql/resolvers`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/graphql/schema`)) - tb.copyAsset('src/config.ts') - tb.copyAsset('src/index.ts') - tb.copyAsset('src/container.ts') - tb.copyAsset('src/context.ts') - tb.copyAsset('codegen.yml') - - tb.copyAsset('src/view/controller.ts') - tb.copyAsset('src/view/server.ts') - tb.copyAsset('src/view/graphql/schema.ts') - tb.copyAsset('src/view/graphql/resolvers.ts') - tb.copyAsset('src/view/graphql/resolvers/greeting.resolver.ts') - tb.copyAsset('src/view/graphql/schema/schema.graphql') - - tb.npm.iDev('mocha') - tb.npm.iDev('mocha-junit-reporter') - tb.npm.iDev('mocha-multi-reporters') - tb.npm.iDev('nyc') - tb.npm.iDev('tsx') - tb.npm.iDev('@types/mocha') - tb.npm.iDev('@istanbuljs/nyc-config-typescript') - tb.copySharedAsset('.mocharc.json', tb.destination) - tb.copySharedAsset('.mocha-junit-config.json', tb.destination) - tb.packageJson.setType('module') - tb.packageJson.addNpmScript('test', 'mocha') - - tb.packageJson.addNpmScript( - 'ci-test:no-coverage', - 'npm run test -- --parallel=false -R mocha-multi-reporters --reporter-options configFile=.mocha-junit-config.json' - ) - tb.packageJson.addNpmScript( - 'ci-test', - 'nyc -a -r cobertura --report-dir output npm run ci-test:no-coverage' - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test`)) - tb.copySharedAsset('src/test/setup.ts') - tb.copyAsset('src/test/helloWorld.test.ts') - - tb.npm.i('@as-integrations/express5') - tb.npm.iDev('@ackee/styleguide-backend-config') - tb.npm.iDev('prettier') - tb.npm.iDev('eslint') - tb.npm.iDev('eslint-formatter-gitlab@^5.0.0') - tb.copyAsset('.eslint.tsconfig.json') - tb.copyAsset('.eslintrc.cjs') - tb.copySharedAsset('prettier.config.cjs') - - tb.packageJson.addNpmScript( - 'prettier', - "prettier --check --write '**/*.{ts,js,json,md}'" - ) - tb.packageJson.addNpmScript( - 'lint', - 'eslint --ext .ts --ext .graphql src -f codeframe --fix' - ) - tb.packageJson.addNpmScript('codestyle', 'npm run prettier && npm run lint') - tb.packageJson.addNpmScript( - 'ci-lint', - 'npm run lint -- -f checkstyle -o ./output/checkstyle-result.xml' - ) - - tb.npm.i('@apollo/server') - tb.npm.iDev('@graphql-codegen/cli') - tb.npm.iDev('lodash') - tb.npm.iDev('@graphql-eslint/eslint-plugin') - tb.npm.iDev('@graphql-codegen/typescript') - tb.npm.iDev('@graphql-codegen/typescript-resolvers') - tb.npm.i('@graphql-tools/load-files') - tb.npm.i('@graphql-tools/merge') - tb.npm.i('@graphql-tools/schema') - tb.npm.i('graphql') - tb.npm.i('express') - tb.npm.iDev('@types/express') - - tb.packageJson.runScript('build') - } -} diff --git a/src/cloudrun/CloudRunStarter.ts b/src/cloudrun/CloudRunStarter.ts deleted file mode 100644 index 7481dde..0000000 --- a/src/cloudrun/CloudRunStarter.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Starter } from '../Starter.js' -import { Toolbelt } from '../Toolbelt.js' - -export class CloudRunStarter implements Starter { - public readonly name = 'cloudrun' - protected toolbelt?: Toolbelt - - public setToolbelt(toolbelt: Toolbelt): Starter { - this.toolbelt = toolbelt - return this - } - - public install(): void { - if (this.toolbelt == null) { - throw new Error('No toolbelt') - } - const tb = this.toolbelt - tb.copySharedAsset('.gitignore') - tb.copySharedAsset('.gitlab-ci.yml') - tb.copySharedAsset('.nvmrc') - tb.copySharedAsset('Dockerfile') - tb.copySharedAsset('.dockerignore') - - tb.mkdir(tb.stringToPath(`${tb.destination}/ci-branch-config`)) - tb.copySharedAsset('ci-branch-config/common.env') - tb.replaceInFile( - `ci-branch-config/common.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/development.env') - tb.replaceInFile( - `ci-branch-config/development.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/stage.env') - tb.replaceInFile( - `ci-branch-config/stage.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.copySharedAsset('ci-branch-config/master.env') - tb.replaceInFile( - `ci-branch-config/master.env`, - '{{PROJECT_NAME}}', - tb.projectName - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/docker-compose`)) - tb.copySharedAsset('docker-compose/docker-compose-entrypoint.sh') - tb.copySharedAsset('docker-compose/docker-compose.ci.yml') - tb.copySharedAsset('docker-compose/docker-compose.local.yml') - tb.symlink( - `${tb.destination}/docker-compose/docker-compose.override.yml`, - `${tb.destination}/docker-compose/docker-compose.local.yml` - ) - tb.copySharedAsset('docker-compose/docker-compose.yml') - tb.replaceInFile( - `docker-compose/docker-compose.yml`, - '{{PROJECT_NAME}}', - tb.projectName - ) - - tb.npm.iDev('typescript') - tb.npm.iDev('@types/node') - tb.npm.iDev('ts-node') - tb.npm.i('source-map-support') - tb.copySharedAsset('tsconfig.json') - tb.packageJson.setType('module') - tb.packageJson.addNpmScript('build', 'tsc') - tb.packageJson.addNpmScript( - 'start', - 'node -r source-map-support/register dist/index.js' - ) - - tb.npm.i('configuru') - tb.npm.i('pino') - tb.npm.i('pino-http') - tb.npm.iDev('pino-pretty') - tb.copyAsset('.env.jsonc') - tb.mkdir(tb.stringToPath(`${tb.destination}/src`)) - tb.copyAsset('src/config.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/container.ts') - tb.copyAsset('src/context.ts') - tb.copyAsset('src/index.ts') - - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view`)) - tb.copyAsset(`/src/view/server.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest`)) - tb.copyAsset(`/src/view/rest/request.d.ts`) - tb.copyAsset(`/src/view/rest/routes.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/controller`)) - tb.copyAsset(`/src/view/rest/controller/health-check.controller.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/middleware`)) - tb.copyAsset(`/src/view/rest/middleware/context-middleware.ts`) - tb.copyAsset(`/src/view/rest/middleware/error-handler.ts`) - tb.copyAsset(`/src/view/rest/middleware/request-logger.ts`) - - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/util`)) - tb.copyAsset(`/src/view/rest/util/openapi.util.ts`) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/rest/spec`)) - tb.copyAsset(`/src/view/rest/spec/openapi.yml`) - tb.npm.i('node-healthz') - - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain`)) - tb.copyAsset('src/domain/health-check.service.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/errors`)) - tb.copyAsset('src/domain/errors/errors.ts') - tb.copyAsset('src/domain/errors/codes.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/util`)) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/domain/ports`)) - tb.copyAsset('src/domain/ports/logger.d.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/adapters`)) - tb.copyAsset('src/adapters/pino.logger.ts') - - tb.npm.i('express') - tb.npm.iDev('@types/express') - - tb.npm.iDev('mocha') - tb.npm.iDev('mocha-junit-reporter') - tb.npm.iDev('mocha-multi-reporters') - tb.npm.iDev('nyc') - tb.npm.iDev('@types/mocha') - tb.npm.iDev('@istanbuljs/nyc-config-typescript') - tb.npm.iDev('supertest') - tb.npm.iDev('@types/supertest') - tb.copySharedAsset('.mocharc.json', tb.destination) - tb.copySharedAsset('.mocha-junit-config.json', tb.destination) - tb.packageJson.addNpmScript('test', 'mocha') - tb.packageJson.addNpmScript( - 'ci-test:no-coverage', - 'npm run test -- --parallel=false -R mocha-multi-reporters --reporter-options configFile=.mocha-junit-config.json' - ) - tb.packageJson.addNpmScript( - 'ci-test', - 'nyc -a -r cobertura --report-dir output npm run ci-test:no-coverage' - ) - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test`)) - tb.copySharedAsset('src/test/setup.ts') - tb.copyAsset('src/test/health-check.test.ts') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/test/util`)) - tb.copyAsset('src/test/util/openapi-test.util.ts') - - tb.npm.iDev('@ackee/styleguide-backend-config') - tb.npm.iDev('prettier') - tb.npm.iDev('eslint') - tb.npm.iDev('eslint-formatter-gitlab@^5.0.0') - tb.copyAsset('.eslint.tsconfig.json') - tb.copyAsset('.eslintrc.cjs') - tb.copySharedAsset('prettier.config.cjs') - tb.packageJson.addNpmScript( - 'prettier', - "prettier --check --write '**/*.{ts,js,json,md}'" - ) - tb.packageJson.addNpmScript('lint', "eslint '**/*.ts' -f codeframe --fix") - tb.packageJson.addNpmScript('codestyle', 'npm run prettier && npm run lint') - tb.packageJson.addNpmScript( - 'ci-lint', - 'npm run lint -- -f checkstyle -o ./output/checkstyle-result.xml' - ) - - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/cli`)) - tb.copyAsset('src/view/cli/cli.ts') - tb.copyAsset('src/view/cli/README.md') - tb.mkdir(tb.stringToPath(`${tb.destination}/src/view/cli/openapi`)) - tb.copyAsset('src/view/cli/openapi/generate.ts') - tb.npm.iDev('yargs') - tb.npm.iDev('@types/yargs') - tb.npm.iDev('yaml') - tb.packageJson.addNpmScript('cli', 'tsx ./src/view/cli/cli.ts') - tb.npm.iDev('openapi-typescript') - tb.npm.iDev('tsx') - tb.packageJson.addNpmScript( - 'generate:api', - 'npm run cli openapi generate src/view/rest/spec/openapi.yml && npm run codestyle' - ) - - tb.packageJson.runScript('generate:api') - tb.packageJson.runScript('build') - } -} diff --git a/starter/cloudrun-graphql/.env.jsonc b/starter/cloudrun-graphql/.env.jsonc deleted file mode 100644 index 84b52f8..0000000 --- a/starter/cloudrun-graphql/.env.jsonc +++ /dev/null @@ -1,12 +0,0 @@ -{ - // Server will run on this port - "SERVER_PORT": 3000, - // Logging level, see https://github.com/pinojs/pino/blob/master/docs/api.md#logger-level - "LOGGER_DEFAULT_LEVEL": "debug", - // Enable/disable logging object multiline formatted logging https://github.com/pinojs/pino/blob/master/docs/api.md#prettyprint-boolean--object - "LOGGER_PRETTY": false, - // Response development errors for debugging - "SERVER_ALLOW_RESPONSE_ERRORS": false, - // Enable GraphQL introspection - "SERVER_ENABLE_INTROSPECTION": true -} diff --git a/starter/cloudrun-graphql/.eslint.tsconfig.json b/starter/cloudrun-graphql/.eslint.tsconfig.json deleted file mode 100644 index 4af9741..0000000 --- a/starter/cloudrun-graphql/.eslint.tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["src/generated/**/*"] -} diff --git a/starter/cloudrun-graphql/.eslintrc.cjs b/starter/cloudrun-graphql/.eslintrc.cjs deleted file mode 100644 index cb60eb7..0000000 --- a/starter/cloudrun-graphql/.eslintrc.cjs +++ /dev/null @@ -1,43 +0,0 @@ -const { omit } = require('lodash') -const defaultConfig = { - ...require('@ackee/styleguide-backend-config/eslint'), -} - -module.exports = { - root: true, - ignorePatterns: ['dist', 'docs', 'src/generated'], - overrides: [ - { - ...omit(defaultConfig, ['ignorePatterns']), - files: ['*.ts', '*.js'], - parserOptions: { - project: '.eslint.tsconfig.json', - }, - rules: { - ...defaultConfig['rules'], - '@typescript-eslint/strict-boolean-expressions': 0, - '@typescript-eslint/no-misused-promises': 'warn', - }, - }, - { - files: ['**/*.graphql'], - extends: 'plugin:@graphql-eslint/schema-recommended', - parserOptions: { - graphQLConfig: { - schema: './src/view/graphql/schema/*.graphql', - }, - }, - rules: { - '@graphql-eslint/strict-id-in-types': 0, - '@graphql-eslint/require-description': [ - 'warn', - { - types: true, - DirectiveDefinition: true, - }, - ], - '@graphql-eslint/no-unreachable-types': 'warn', - }, - }, - ], -} diff --git a/starter/cloudrun-graphql/README.md b/starter/cloudrun-graphql/README.md deleted file mode 100644 index 70852f4..0000000 --- a/starter/cloudrun-graphql/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# GraphQL Cloudrun Starter - -Node.js project scaffolded with Cloudrun and GraphQL - -## 🎉 Initialize project - -Run `create-node-app cloudrun-graphql` to init your project. By default project is created in `../node-app` folder. - -You can pass `destination` argument into the command as well. -Example: - -- `create-node-app cloudrun-graphql /Users/foo/Documents/bar ` - -## 👷 Continuous Integration - -### Environment variables - -Make sure you replace all the variable values containing `REPLACEME` in `ci-branch-config` files. - -The following variables must be set for each branch in `ci-branch-config` directory. - -- `GCP_PROJECT_ID` - GCP project identifier -- `ENVIRONMENT` - e.g. `development` - -Optional variables: - -- `ALLOCATED_MEMORY` - Memory allocated at Cloudrun, default is 384Mi -- `CLOUD_RUN_SERVICE_ACCOUNT` - CloudRun service account -- `ENV_SECRETS` - Secret variables of the deployment, format is: `KEY=[NAME OF SECRET IN SECRET MANAGER:VERSION],...` -- `ENV_VARS` - Environment variables of the deployment, format is: `KEY=VALUE,...` -- `GCP_SA_KEY` - We use different service accounts for different environments. So we have to overwrite `GCP_SA_KEY` variable e.g.`GCP_SA_KEY=$SECRET_GCP_SA_KEY_DEVELOPMENT` Variable `SECRET_GCP_SA_KEY_` should be set in `GitLab CI secret variables` -- `GCP_SECRET_NAME` - Name of secret in Google Secret Manager -- `MAX_INSTANCES` - Maximum instance count in Cloudrun, default is 8 -- `MIN_INSTANCES` - Minimum instance count in Cloudrun, default is 0 -- `SECRET_PATH` - CloudRun volume where secrets will be injected e.g. `/config/secrets.json` can't be the same path ass app work dir e.g. `/usr/src/app` deploy will fail cause secret protection. But have to be identical with ENV `CFG_JSON_PATH` in Dockerfile -- `SKIP_AUDIT` - Skip NPM Audit, defaults to `false` -- `SKIP_LINT` - Skip lint, defaults to `false` -- `SKIP_TESTS` - Skip tests, defaults to `false` -- `SQL_INSTANCE_NAME` - SQL instance name -- `VPC_CONNECTOR_NAME` - serverless connector name, has to be in the same region -- `DOCKER_REGISTRY_TYPE` - whenever to push Docker image into Container Registry or Artifacts Registry, default is - `container` -- `DOCKER_REGISTRY_URL` - hostname of Docker Registry, defaults to `eu.gcr.io` -- `ANY_ADDITIONAL_CLOUDRUN_ARGS` - any argument required by Cloud Run, eg `--concurrency=1000 --clear-labels ...` - -Common variables: - -- `GCP_TEST_SECRET_PROJECT_ID` - GCP project identifier for test secrets -- `GCP_SECRET_TEST_NAME` - Name of test secret in Google Secret Manager - -## 📄 Additional Resources - -- [Google Cloudrun docs](https://cloud.google.com/run/docs) diff --git a/starter/cloudrun-graphql/codegen.yml b/starter/cloudrun-graphql/codegen.yml deleted file mode 100644 index a9d7de6..0000000 --- a/starter/cloudrun-graphql/codegen.yml +++ /dev/null @@ -1,11 +0,0 @@ -schema: './src/view/graphql/schema/schema.graphql' -overwrite: true -generates: - src/generated/graphql.ts: - config: - useIndexSignature: true - scalars: - mappers: - plugins: - - 'typescript' - - 'typescript-resolvers' diff --git a/starter/cloudrun-graphql/src/adapters/pino.logger.ts b/starter/cloudrun-graphql/src/adapters/pino.logger.ts deleted file mode 100644 index a4ddbe1..0000000 --- a/starter/cloudrun-graphql/src/adapters/pino.logger.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { pino as pinoLogger } from 'pino' -import { LoggerFactoryPort } from '../domain/ports/logger.d.js' - -// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity -const PinoLevelToSeverityLookup: Record = { - trace: 'DEBUG', - debug: 'DEBUG', - info: 'INFO', - warn: 'WARNING', - error: 'ERROR', - fatal: 'CRITICAL', -} as const - -const defaultPinoConf = (defaultLevel: string) => ({ - messageKey: 'message', - formatters: { - messageKey: 'message', - level: (label: string, num: number) => { - return { - severity: - PinoLevelToSeverityLookup[label] ?? - PinoLevelToSeverityLookup[defaultLevel], - level: num, - } - }, - }, -}) - -export const pinoLoggerFactory: LoggerFactoryPort = { - create: config => { - return pinoLogger({ - ...defaultPinoConf(config.defaultLevel), - transport: config.enablePrettyPrint - ? { - target: 'pino-pretty', - options: { - colorize: true, - }, - } - : undefined, - level: config.defaultLevel, - }) - }, -} diff --git a/starter/cloudrun-graphql/src/config.ts b/starter/cloudrun-graphql/src/config.ts deleted file mode 100644 index 2892ea0..0000000 --- a/starter/cloudrun-graphql/src/config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { createLoader, maskedValues, values } from 'configuru' -import { Level } from 'pino' - -const loader = createLoader({ - defaultConfigPath: '.env.jsonc', -}) - -const configSchema = { - server: { - port: loader.number('SERVER_PORT'), - allowResponseErrors: loader.bool('SERVER_ALLOW_RESPONSE_ERRORS'), - enableIntrospection: loader.bool('SERVER_ENABLE_INTROSPECTION'), - }, - logger: { - defaultLevel: loader.custom(x => x as any as Level)('LOGGER_DEFAULT_LEVEL'), - pretty: loader.bool('LOGGER_PRETTY'), - }, -} - -export const config = values(configSchema) -export const safeConfig = maskedValues(configSchema) diff --git a/starter/cloudrun-graphql/src/container.ts b/starter/cloudrun-graphql/src/container.ts deleted file mode 100644 index 8a46ab7..0000000 --- a/starter/cloudrun-graphql/src/container.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { config } from './config.js' -import { pinoLoggerFactory } from './adapters/pino.logger.js' -import { LoggerPort } from './domain/ports/logger.d.js' - -export interface Container { - logger: LoggerPort -} - -export const createContainer = (): Container => { - const logger = pinoLoggerFactory.create(config.logger) - - return { - logger, - } -} diff --git a/starter/cloudrun-graphql/src/context.ts b/starter/cloudrun-graphql/src/context.ts deleted file mode 100644 index 1784f41..0000000 --- a/starter/cloudrun-graphql/src/context.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Container } from './container.js' - -interface BaseContext { - container: Container - note?: string -} - -export interface ApiUserRequestContext extends BaseContext { - type: 'api-user' - user: null // Add UserContext if Auth is implemented -} - -export interface ServerRequestContext extends BaseContext { - type: 'server' -} - -/** - * Defines context in which current application runs. - * Context should be created with every external call (user request, cli input, ...) - */ -export type RequestContext = ApiUserRequestContext | ServerRequestContext - -/** - * Creator of the context for the App. Every API layer is responsible for - * creating the context based on the Api parameters (http headers, protocol settings etc...) - */ -export type RequestContextFactory = ( - container: Readonly, - ...params: Params -) => Promise - -/** - * Creator of the server context that should be used only in executions that are invoked - * on server, f.e. CLI, server start up etc. - */ -export type ServerRequestContextFactory = ( - container: Readonly, - ...params: Params -) => Promise diff --git a/starter/cloudrun-graphql/src/domain/errors/codes.ts b/starter/cloudrun-graphql/src/domain/errors/codes.ts deleted file mode 100644 index c3eb641..0000000 --- a/starter/cloudrun-graphql/src/domain/errors/codes.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum ErrorCode { - UNKNOWN = 0, - VALIDATION = 1000, -} - -export const errorMessages = { - [ErrorCode.UNKNOWN]: 'Unknown error', - [ErrorCode.VALIDATION]: 'Validation error', -} diff --git a/starter/cloudrun-graphql/src/domain/errors/errors.ts b/starter/cloudrun-graphql/src/domain/errors/errors.ts deleted file mode 100644 index b722438..0000000 --- a/starter/cloudrun-graphql/src/domain/errors/errors.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ErrorCode, errorMessages } from './codes.js' - -export class DomainError extends Error { - constructor( - public readonly code: ErrorCode, - public readonly message: string = errorMessages[code], - public readonly data?: AdditionalData - ) { - super(message) - } -} - -export type ValidationErrorData = - | Array<{ field: string; message: string }> - | { field: string; message: string } - -export class ValidationError extends DomainError { - constructor(errors: ValidationErrorData) { - super( - ErrorCode.VALIDATION, - errorMessages[ErrorCode.VALIDATION], - Array.isArray(errors) ? errors : [errors] - ) - } -} diff --git a/starter/cloudrun-graphql/src/domain/ports/logger.d.ts b/starter/cloudrun-graphql/src/domain/ports/logger.d.ts deleted file mode 100644 index 5ec9a85..0000000 --- a/starter/cloudrun-graphql/src/domain/ports/logger.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface LoggerConfig { - defaultLevel: string - enablePrettyPrint?: boolean -} - -type BaseLoggerFn = (object: any, message?: string) => void - -export interface LoggerPort { - level: string - debug: BaseLoggerFn - info: BaseLoggerFn - warn: BaseLoggerFn - error: BaseLoggerFn - fatal: BaseLoggerFn - trace: BaseLoggerFn - silent: BaseLoggerFn -} - -export interface LoggerFactoryPort { - create: (config: LoggerConfig) => LoggerPort -} diff --git a/starter/cloudrun-graphql/src/index.ts b/starter/cloudrun-graphql/src/index.ts deleted file mode 100644 index 9534d17..0000000 --- a/starter/cloudrun-graphql/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { safeConfig } from './config.js' -import { createAppServer, startServer } from './view/server.js' -import { createContainer } from './container.js' - -const appContainer = createContainer() -const { logger } = appContainer - -logger.info({ config: safeConfig }, 'Loaded config') - -const appServer = createAppServer() -void startServer({ ...appServer, container: appContainer }) diff --git a/starter/cloudrun-graphql/src/test/helloWorld.test.ts b/starter/cloudrun-graphql/src/test/helloWorld.test.ts deleted file mode 100644 index 68beb79..0000000 --- a/starter/cloudrun-graphql/src/test/helloWorld.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'node:assert' -import { describe, it } from 'mocha' -import { createAppServer } from '../view/server.js' -import { gql } from 'graphql-tag' - -describe('Hello world', () => { - it('should return greeting', async () => { - const query = gql` - query Hello { - greeting - } - ` - const { server } = createAppServer() - const res = await server.executeOperation({ query }) - - assert(res.body.kind === 'single') - assert.deepStrictEqual(res.body.singleResult.errors, undefined) - assert.deepStrictEqual( - res.body.singleResult.data?.greeting, - 'Hello, world! 🎉' - ) - }) -}) diff --git a/starter/cloudrun-graphql/src/view/controller.ts b/starter/cloudrun-graphql/src/view/controller.ts deleted file mode 100644 index a7edd4e..0000000 --- a/starter/cloudrun-graphql/src/view/controller.ts +++ /dev/null @@ -1,42 +0,0 @@ -import express from 'express' -import { Handler, RequestHandler } from 'express' - -/** - * pipeMiddleware takes multiple middlewares and creates and merges them into - * one using express Router. - */ -export const pipeMiddleware = (...middlewares: RequestHandler[]) => { - const router = express.Router({ mergeParams: true }) - middlewares.forEach(m => router.use(m)) - return router -} - -const asyncHandler = - (controllerHandler: Handler): Handler => - async (req, res, next) => { - try { - await controllerHandler(req, res, next) - } catch (err) { - next(err) - } - } - -/** - * ctrl is a scoped object for controller functions - */ -export const ctrl = { - json: pipeMiddleware( - express.json(), - // Monkeypatch res.json to assign the body to res.out first in order - // to log it by pino - (_req, res, next) => { - const resJson = res.json.bind(res) - res.json = (body?: any) => { - ;(res as any).out = body - return resJson(body) - } - next() - } - ), - asyncHandler, -} diff --git a/starter/cloudrun-graphql/src/view/graphql/resolvers.ts b/starter/cloudrun-graphql/src/view/graphql/resolvers.ts deleted file mode 100644 index d84414c..0000000 --- a/starter/cloudrun-graphql/src/view/graphql/resolvers.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Resolvers } from '../../generated/graphql.js' -import { greetingQueryResolver } from './resolvers/greeting.resolver.js' - -export const resolvers: Resolvers = { - Query: greetingQueryResolver, -} diff --git a/starter/cloudrun-graphql/src/view/graphql/resolvers/greeting.resolver.ts b/starter/cloudrun-graphql/src/view/graphql/resolvers/greeting.resolver.ts deleted file mode 100644 index 9f2ed08..0000000 --- a/starter/cloudrun-graphql/src/view/graphql/resolvers/greeting.resolver.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { QueryResolvers } from '../../../generated/graphql.js' - -export const greetingQueryResolver: QueryResolvers = { - greeting: () => 'Hello, world! 🎉', -} diff --git a/starter/cloudrun-graphql/src/view/graphql/schema.ts b/starter/cloudrun-graphql/src/view/graphql/schema.ts deleted file mode 100644 index 509355a..0000000 --- a/starter/cloudrun-graphql/src/view/graphql/schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { loadFilesSync } from '@graphql-tools/load-files' -import { mergeTypeDefs } from '@graphql-tools/merge' -import path from 'path' - -export const schema = [ - mergeTypeDefs(loadFilesSync(path.join(import.meta.dirname, 'schema'))), -] diff --git a/starter/cloudrun-graphql/src/view/graphql/schema/schema.graphql b/starter/cloudrun-graphql/src/view/graphql/schema/schema.graphql deleted file mode 100644 index 146669f..0000000 --- a/starter/cloudrun-graphql/src/view/graphql/schema/schema.graphql +++ /dev/null @@ -1,6 +0,0 @@ -""" -All data queries -""" -type Query { - greeting: String! -} diff --git a/starter/cloudrun-graphql/src/view/server.ts b/starter/cloudrun-graphql/src/view/server.ts deleted file mode 100644 index 4b532b9..0000000 --- a/starter/cloudrun-graphql/src/view/server.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ApolloServer } from '@apollo/server' -import { makeExecutableSchema } from '@graphql-tools/schema' -import { resolvers } from './graphql/resolvers.js' -import { config } from '../config.js' -import { schema } from './graphql/schema.js' -import express from 'express' -import { ctrl } from './controller.js' -import { expressMiddleware } from '@as-integrations/express5' -import { Container } from '../container.js' - -export const clientSchema = makeExecutableSchema({ - typeDefs: schema, - resolvers, -}) - -export const createAppServer = () => { - const app = express() - app.disable('x-powered-by') - - const server = new ApolloServer({ - schema: clientSchema, - includeStacktraceInErrorResponses: config.server.allowResponseErrors, - introspection: config.server.enableIntrospection, - }) - - return { app, server } -} - -export async function startServer({ - app, - server, - container, -}: ReturnType & { container: Container }) { - const { logger } = container - - await server.start() - - app.use('/api/graphql', ctrl.json, expressMiddleware(server)) - - app.listen({ port: config.server.port }, () => - logger.info({}, `Server started, port=${config.server.port}`) - ) - - return { app, server } -} diff --git a/starter/cloudrun/.env.jsonc b/starter/cloudrun/.env.jsonc deleted file mode 100644 index 67f8be0..0000000 --- a/starter/cloudrun/.env.jsonc +++ /dev/null @@ -1,10 +0,0 @@ -{ - // Logging level, see https://github.com/pinojs/pino/blob/master/docs/api.md#logger-level - "LOGGER_DEFAULT_LEVEL": "debug", - // Enable/disable logging object multiline formatted logging https://github.com/pinojs/pino/blob/master/docs/api.md#prettyprint-boolean--object - "LOGGER_PRETTY": false, - // API server listening port. - "SERVER_PORT": 3000, - // Boolean to remove sensitive info from http error responses - "ENABLE_PRODUCTION_HTTP_ERROR_RESPONSES": false -} diff --git a/starter/cloudrun/.eslint.tsconfig.json b/starter/cloudrun/.eslint.tsconfig.json deleted file mode 100644 index a8d4317..0000000 --- a/starter/cloudrun/.eslint.tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": [] -} diff --git a/starter/cloudrun/.eslintrc.cjs b/starter/cloudrun/.eslintrc.cjs deleted file mode 100644 index 5ce82e8..0000000 --- a/starter/cloudrun/.eslintrc.cjs +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - ...require('@ackee/styleguide-backend-config/eslint'), - root: true, - ignorePatterns: ['dist', 'src/openapi', 'docs'], - parserOptions: { - project: '.eslint.tsconfig.json', - }, -} diff --git a/starter/cloudrun/README.md b/starter/cloudrun/README.md deleted file mode 100644 index 9c4a4d9..0000000 --- a/starter/cloudrun/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Cloudrun Starter - -Node.js project scaffolded with create-node-app Cloudrun - -## 🎉 Initialize new project - -Run `create-node-app cloudrun` to init your project. By default project is created in `../node-app` folder. - -You can pass `destination` argument into the command as well. -Example: - -- `create-node-app cloudrun /Users/foo/Documents/bar` - -## 🧑‍💻 Development - -Project follows port-adapters folder structure. Three main layers can be found in [src folder](src): - -- [domain](src/domain) containing all domain services and ports for external services, -- [adapters](src/adapters) containing implementations of ports, -- and [view](src/view) containing entrypoints to the application (rest, cli). - -The main entrypoint of the application is defined in the [src/index.ts](index.ts) file. The applicaiton dependencies are defined -and maintained in the [container.ts](src/container.ts) file which loads configuration from [config.ts](src/config.ts) using [configuru library](https://github.com/AckeeCZ/configuru). - -Tests are divided in the two parts: - -- integration tests should be maintained in the [test folder](src/test/) -- unit tests should be kept close to the targeted file and hold the same name as the tested file but with `test.ts` suffix - -## 👷 Continuous Integration - -### Environment variables - -If you didn't provided GCloud project parameter, make sure you replace all the variable values containing `node-app` in `ci-branch-config` files. - -The following variables must be set for each branch in `ci-branch-config` directory. - -- `GCP_PROJECT_ID` - GCP project identifier -- `ENVIRONMENT` - e.g. `development` - -Optional variables: - -- `ALLOCATED_MEMORY` - Memory allocated at Cloudrun, default is 384Mi -- `CLOUD_RUN_SERVICE_ACCOUNT` - CloudRun service account -- `ENV_SECRETS` - Secret variables of the deployment, format is: `KEY=[NAME OF SECRET IN SECRET MANAGER:VERSION],...` -- `ENV_VARS` - Environment variables of the deployment, format is: `KEY=VALUE,...` -- `GCP_SA_KEY` - We use different service accounts for different environments. So we have to overwrite `GCP_SA_KEY` variable e.g.`GCP_SA_KEY=$SECRET_GCP_SA_KEY_DEVELOPMENT` Variable `SECRET_GCP_SA_KEY_` should be set in `GitLab CI secret variables` -- `GCP_SECRET_NAME` - Name of secret in Google Secret Manager -- `MAX_INSTANCES` - Maximum instance count in Cloudrun, default is 8 -- `MIN_INSTANCES` - Minimum instance count in Cloudrun, default is 0 -- `SECRET_PATH` - CloudRun volume where secrets will be injected e.g. `/config/secrets.json` can't be the same path ass app work dir e.g. `/usr/src/app` deploy will fail cause secret protection. But have to be identical with ENV `CFG_JSON_PATH` in Dockerfile -- `SKIP_AUDIT` - Skip NPM Audit, defaults to `false` -- `SKIP_LINT` - Skip lint, defaults to `false` -- `SKIP_TESTS` - Skip tests, defaults to `false` -- `SQL_INSTANCE_NAME` - SQL instance name -- `VPC_CONNECTOR_NAME` - serverless connector name, has to be in the same region -- `DOCKER_REGISTRY_TYPE` - whenever to push Docker image into Container Registry or Artifacts Registry, default is - `container` -- `DOCKER_REGISTRY_URL` - hostname of Docker Registry, defaults to `eu.gcr.io` -- `ANY_ADDITIONAL_CLOUDRUN_ARGS` - any argument required by Cloud Run, eg `--concurrency=1000 --clear-labels ...` - -Common variables: - -- `GCP_TEST_SECRET_PROJECT_ID` - GCP project identifier for test secrets -- `GCP_SECRET_TEST_NAME` - Name of test secret in Google Secret Manager - -## 📄 Additional Resources - -- [Google Cloudrun docs](https://cloud.google.com/run/docs) diff --git a/starter/cloudrun/src/adapters/pino.logger.ts b/starter/cloudrun/src/adapters/pino.logger.ts deleted file mode 100644 index a4ddbe1..0000000 --- a/starter/cloudrun/src/adapters/pino.logger.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { pino as pinoLogger } from 'pino' -import { LoggerFactoryPort } from '../domain/ports/logger.d.js' - -// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity -const PinoLevelToSeverityLookup: Record = { - trace: 'DEBUG', - debug: 'DEBUG', - info: 'INFO', - warn: 'WARNING', - error: 'ERROR', - fatal: 'CRITICAL', -} as const - -const defaultPinoConf = (defaultLevel: string) => ({ - messageKey: 'message', - formatters: { - messageKey: 'message', - level: (label: string, num: number) => { - return { - severity: - PinoLevelToSeverityLookup[label] ?? - PinoLevelToSeverityLookup[defaultLevel], - level: num, - } - }, - }, -}) - -export const pinoLoggerFactory: LoggerFactoryPort = { - create: config => { - return pinoLogger({ - ...defaultPinoConf(config.defaultLevel), - transport: config.enablePrettyPrint - ? { - target: 'pino-pretty', - options: { - colorize: true, - }, - } - : undefined, - level: config.defaultLevel, - }) - }, -} diff --git a/starter/cloudrun/src/config.ts b/starter/cloudrun/src/config.ts deleted file mode 100644 index f650f88..0000000 --- a/starter/cloudrun/src/config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { createLoader, maskedValues, values } from 'configuru' -import { Level } from 'pino' - -const loader = createLoader({ - defaultConfigPath: '.env.jsonc', -}) - -const configSchema = { - logger: { - defaultLevel: loader.custom(x => x as Level)('LOGGER_DEFAULT_LEVEL'), - pretty: loader.bool('LOGGER_PRETTY'), - }, - server: { - port: loader.number('SERVER_PORT'), - enableProductionHttpErrorResponses: loader.bool( - 'ENABLE_PRODUCTION_HTTP_ERROR_RESPONSES' - ), - }, -} - -export const config = values(configSchema) -export const safeConfig = maskedValues(configSchema) diff --git a/starter/cloudrun/src/container.ts b/starter/cloudrun/src/container.ts deleted file mode 100644 index 966eac6..0000000 --- a/starter/cloudrun/src/container.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { config } from './config.js' -import { pinoLoggerFactory } from './adapters/pino.logger.js' -import { LoggerPort } from './domain/ports/logger.d.js' -import { healthCheckService } from './domain/health-check.service.js' - -export interface Container { - logger: LoggerPort - healthCheckService: typeof healthCheckService -} - -export const createContainer = (): Container => { - const logger = pinoLoggerFactory.create(config.logger) - - return { - logger, - healthCheckService, - } -} diff --git a/starter/cloudrun/src/context.ts b/starter/cloudrun/src/context.ts deleted file mode 100644 index 1784f41..0000000 --- a/starter/cloudrun/src/context.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Container } from './container.js' - -interface BaseContext { - container: Container - note?: string -} - -export interface ApiUserRequestContext extends BaseContext { - type: 'api-user' - user: null // Add UserContext if Auth is implemented -} - -export interface ServerRequestContext extends BaseContext { - type: 'server' -} - -/** - * Defines context in which current application runs. - * Context should be created with every external call (user request, cli input, ...) - */ -export type RequestContext = ApiUserRequestContext | ServerRequestContext - -/** - * Creator of the context for the App. Every API layer is responsible for - * creating the context based on the Api parameters (http headers, protocol settings etc...) - */ -export type RequestContextFactory = ( - container: Readonly, - ...params: Params -) => Promise - -/** - * Creator of the server context that should be used only in executions that are invoked - * on server, f.e. CLI, server start up etc. - */ -export type ServerRequestContextFactory = ( - container: Readonly, - ...params: Params -) => Promise diff --git a/starter/cloudrun/src/domain/errors/codes.ts b/starter/cloudrun/src/domain/errors/codes.ts deleted file mode 100644 index c3eb641..0000000 --- a/starter/cloudrun/src/domain/errors/codes.ts +++ /dev/null @@ -1,9 +0,0 @@ -export enum ErrorCode { - UNKNOWN = 0, - VALIDATION = 1000, -} - -export const errorMessages = { - [ErrorCode.UNKNOWN]: 'Unknown error', - [ErrorCode.VALIDATION]: 'Validation error', -} diff --git a/starter/cloudrun/src/domain/errors/errors.ts b/starter/cloudrun/src/domain/errors/errors.ts deleted file mode 100644 index b722438..0000000 --- a/starter/cloudrun/src/domain/errors/errors.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ErrorCode, errorMessages } from './codes.js' - -export class DomainError extends Error { - constructor( - public readonly code: ErrorCode, - public readonly message: string = errorMessages[code], - public readonly data?: AdditionalData - ) { - super(message) - } -} - -export type ValidationErrorData = - | Array<{ field: string; message: string }> - | { field: string; message: string } - -export class ValidationError extends DomainError { - constructor(errors: ValidationErrorData) { - super( - ErrorCode.VALIDATION, - errorMessages[ErrorCode.VALIDATION], - Array.isArray(errors) ? errors : [errors] - ) - } -} diff --git a/starter/cloudrun/src/domain/health-check.service.ts b/starter/cloudrun/src/domain/health-check.service.ts deleted file mode 100644 index ab06c7e..0000000 --- a/starter/cloudrun/src/domain/health-check.service.ts +++ /dev/null @@ -1,15 +0,0 @@ -import * as healthz from 'node-healthz' - -export const healthCheckService = { - check: async (): Promise => { - return await healthz.check({ - checks: [ - { - id: 'Server', - required: true, - fn: () => Promise.resolve('OK'), - }, - ], - }) - }, -} diff --git a/starter/cloudrun/src/domain/ports/logger.d.ts b/starter/cloudrun/src/domain/ports/logger.d.ts deleted file mode 100644 index 5ec9a85..0000000 --- a/starter/cloudrun/src/domain/ports/logger.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -export interface LoggerConfig { - defaultLevel: string - enablePrettyPrint?: boolean -} - -type BaseLoggerFn = (object: any, message?: string) => void - -export interface LoggerPort { - level: string - debug: BaseLoggerFn - info: BaseLoggerFn - warn: BaseLoggerFn - error: BaseLoggerFn - fatal: BaseLoggerFn - trace: BaseLoggerFn - silent: BaseLoggerFn -} - -export interface LoggerFactoryPort { - create: (config: LoggerConfig) => LoggerPort -} diff --git a/starter/cloudrun/src/index.ts b/starter/cloudrun/src/index.ts deleted file mode 100644 index a1d9515..0000000 --- a/starter/cloudrun/src/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { config } from './config.js' -import { createContainer } from './container.js' -import { createServer } from './view/server.js' - -const appContainer = createContainer() -const { logger } = appContainer - -const server = createServer(appContainer) - -server.listen(config.server.port, () => { - logger.info( - { port: config.server.port }, - `🚀 Server is running on port ${config.server.port}` - ) -}) - -export default server diff --git a/starter/cloudrun/src/test/health-check.test.ts b/starter/cloudrun/src/test/health-check.test.ts deleted file mode 100644 index c8d7288..0000000 --- a/starter/cloudrun/src/test/health-check.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import assert from 'node:assert' -import { describe, it, before } from 'mocha' -import { createServer } from '../view/server.js' -import { createContainer } from '../container.js' -import { requestTyped } from './util/openapi-test.util.js' - -describe('Health Check API', () => { - let server: ReturnType - let container: ReturnType - - before(() => { - container = createContainer() - server = createServer(container) - }) - - describe('GET /api/v1/healthz', () => { - it('should return successful health check response', async () => { - const response = await requestTyped(server, '/api/v1/healthz', 'get') - .sendTyped() - .expect(200) - .responseTyped() - assert.equal(response.body.status, 0) - }) - }) -}) diff --git a/starter/cloudrun/src/test/util/openapi-test.util.ts b/starter/cloudrun/src/test/util/openapi-test.util.ts deleted file mode 100644 index 9546da9..0000000 --- a/starter/cloudrun/src/test/util/openapi-test.util.ts +++ /dev/null @@ -1,71 +0,0 @@ -import supertest from 'supertest' -import { - OpenApiRoutePathParam, - OpenApiRouteQueryParam, - OpenApiRouteRequestBody, - OpenApiRouteResponseBody, -} from '../../view/rest/util/openapi.util.js' -import * as openapi from '../../view/rest/spec/openapi.js' -import type { Express } from 'express' - -export const request = (server: Express) => supertest(server) - -type KeysOfUnion = T extends T ? keyof T : never - -export type Response = Omit< - Awaited['get']>>, - 'body' -> & { body: TBody } - -export const requestTyped = < - Resource extends KeysOfUnion, - Method extends Exclude, 'parameters'>, - Resp extends OpenApiRouteResponseBody, - Openapi extends openapi.paths = openapi.paths ->( - server: Express, - resource: Resource, - method: Method, - routeParams?: OpenApiRoutePathParam, - queryParams?: OpenApiRouteQueryParam -) => { - const replacedResource = Object.keys( - (routeParams ?? {}) as Record - ).reduce((resource, param) => { - return resource.replace( - // eslint-disable-next-line security/detect-non-literal-regexp - new RegExp(`{${param}}`, 'g'), - (routeParams as Record)[param] - ) - }, resource) - - const url = new URL('https://ackee.cz') - Object.entries(queryParams ?? {}).forEach(([key, val]) => { - if (val !== undefined && val !== null) { - url.searchParams.set(key, val.toString()) - } - }) - - const req = request(server)[method]( - replacedResource + '?' + url.searchParams.toString() - ) - const originalSend = req.send.bind(req) - const enhanced = Object.assign(req, { - sendTyped( - body?: OpenApiRouteRequestBody, - headers?: Record - ) { - if (headers) { - Object.entries(headers).forEach(([key, value]) => { - void req.set(key, value) - }) - } - void originalSend(body as any) - return enhanced - }, - responseTyped: () => { - return req as Promise> - }, - }) - return enhanced -} diff --git a/starter/cloudrun/src/view/cli/README.md b/starter/cloudrun/src/view/cli/README.md deleted file mode 100644 index 16495c1..0000000 --- a/starter/cloudrun/src/view/cli/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# CLI - -Cli tool to operate scripts that can use internal services or to add development toolkit with the same interface and usage. Currently cli tool is operated using tsx tool. - -## 👷 Development - -The CLI tool is build around `yargs` library. Each command needs its own `ts` file in current folder or in subfolder. The entrypoint `Cli.ts` scans the sub folders for `ts` files and intrerprets them as a command under the module called same as the folder. - -For example, current [openapi folder](./openapi) creates a new module `openapi` and assigns a new command called `generate` based on contents in [generate.ts](./openapi/generate.ts) file. - -Each command should export only functions and variables according to `CommandDefinition` in [cli.ts file](./cli.ts). - -Take a look at the [generate.ts](./openapi/generate.ts) for example how to implement own command. - -## ⚠️ Usage in production scripts - -If the cli tool is used to run production scripts (CRON), make sure all dependencies are also listed in production list and build the code instead of using `tsx` (e.g. `yargs`). diff --git a/starter/cloudrun/src/view/cli/cli.ts b/starter/cloudrun/src/view/cli/cli.ts deleted file mode 100644 index 1b19e0d..0000000 --- a/starter/cloudrun/src/view/cli/cli.ts +++ /dev/null @@ -1,94 +0,0 @@ -import yargs, { Argv } from 'yargs' -import { hideBin } from 'yargs/helpers' -import { readdir, stat } from 'fs/promises' -import { join, extname } from 'path' - -interface CommandDefinition { - // Description of the command used for help - description: string - // Defines positional description of the command (e.g. '' will mean it takes one positional required argument) - positional?: string - // Function that will be called when the command is executed - run: (argv: any) => Promise | void - // Function that will be called to add options to the command - options?: (yargs: Argv) => Argv - // Any other properties that will be added to the command - [key: string]: any -} - -const loadCommand = async ( - commandName: string, - commandPath: string, - yargsInstance: Argv -) => { - try { - const modulePath = `file://${commandPath}` - const commandModule = (await import(modulePath)) as CommandDefinition - - if (typeof commandModule.run === 'function') { - yargsInstance.command( - commandModule.positional - ? `${commandName} ${commandModule.positional}` - : commandName, - commandModule.description ?? `${commandName} command`, - (yargs: Argv) => { - if (typeof commandModule.options === 'function') { - return commandModule.options(yargs) - } - }, - async (argv: any) => { - try { - await commandModule.run(argv) - } catch (error) { - console.error(`Error running ${commandName}:`, error) - process.exit(1) - } - } - ) - } else { - console.warn(`Warning: ${commandPath} does not export a 'run' function`) - } - } catch (error) { - console.error(`Error loading command from ${commandPath}:`, error) - } -} - -const loadCommandsFromDirectory = async ( - dirPath: string, - yargsInstance: Argv -): Promise => { - const items = await readdir(dirPath) - - for (const item of items) { - const fullPath = join(dirPath, item) - const stats = await stat(fullPath) - - if (stats.isDirectory()) { - yargsInstance.command(item, `${item} module`, (yargs: Argv) => { - yargs.demandCommand(1, 'You need to specify a command.') - yargs.help() - return loadCommandsFromDirectory(fullPath, yargs) - }) - } else if (stats.isFile() && extname(item) === '.ts' && item !== 'cli.ts') { - const commandName = item.replace('.ts', '') - await loadCommand(commandName, fullPath, yargsInstance) - } - } -} - -export const cli = async () => { - const yargsInstance = yargs(hideBin(process.argv)) - .scriptName('cli') - .usage('Usage: $0 [options]') - .demandCommand(1, 'You need to specify a command or module.') - .help() - .version() - - await loadCommandsFromDirectory(import.meta.dirname, yargsInstance) - - await yargsInstance.parse() - - process.exit(0) -} - -void cli() diff --git a/starter/cloudrun/src/view/cli/openapi/generate.ts b/starter/cloudrun/src/view/cli/openapi/generate.ts deleted file mode 100644 index d59bbaf..0000000 --- a/starter/cloudrun/src/view/cli/openapi/generate.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { spawn } from 'node:child_process' -import { readFileSync, writeFileSync } from 'node:fs' -import path from 'node:path' -import yaml from 'yaml' - -export const description = 'Generate OpenAPI types and validate the spec' -export const positional = '' - -export const run = async (argv: any): Promise => { - console.log('Generating OpenAPI types and validate the spec...') - const yamlFilePath = argv.filepath - const tsFilePath = path.join( - path.dirname(argv.filepath), - `${path.basename(argv.filepath, path.extname(argv.filepath))}.ts` - ) - - await new Promise((resolve, reject) => { - const process = spawn( - 'npx', - ['openapi-typescript', yamlFilePath, '--output', tsFilePath], - { stdio: 'inherit' } - ) - process.on('exit', resolve) - process.on('error', reject) - }) - - console.log('Generating path mappings...') - - const types = readFileSync(tsFilePath) - const spec = yaml.parse(readFileSync(yamlFilePath).toString()) - - const pathsWithOperationIds = Object.keys(spec.paths).reduce((acc, path) => { - const pathSpec = spec.paths[path] - Object.keys(pathSpec).forEach(method => { - if (pathSpec[method]?.operationId) { - acc[pathSpec[method].operationId] = { - method, - path, - } - } - }) - return acc - }, {} as any) - - const finalSpec = - `/* eslint-disable sonarjs/no-duplicate-string */\n` + - `/* eslint-disable sonarjs/use-type-alias */\n` + - types.toString('utf8').replaceAll('requestBody?:', 'requestBody:') + - `export const operationPaths = ${JSON.stringify( - pathsWithOperationIds - )} as const\n` - - writeFileSync(tsFilePath, finalSpec) - - console.log('Done 🎉') -} - -export const options = (yargs: any) => { - return yargs.positional('filepath', { - required: true, - type: 'string', - description: 'Input OpenAPI specification file', - }) -} diff --git a/starter/cloudrun/src/view/rest/controller/health-check.controller.ts b/starter/cloudrun/src/view/rest/controller/health-check.controller.ts deleted file mode 100644 index ca8553f..0000000 --- a/starter/cloudrun/src/view/rest/controller/health-check.controller.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Result } from 'node-healthz' -import { components } from '../spec/openapi.js' -import { ctrl } from '../util/openapi.util.js' - -export enum Status { - Healthy, - Unhealthy, -} - -const mapHealthCheckToApi = ( - healthCheck: Result -): components['schemas']['HealthCheckResponse'] => { - return { - status: healthCheck.status, - checks: healthCheck.checks.map(check => ({ - id: check.id, - status: check.status, - output: String(check.output), - required: check.required, - latency: check.latency, - latencyStatus: check.latencyStatus, - })), - } -} - -export const healthCheckController = ctrl.createRestController({ - healthz: async ctx => { - const { healthCheckService } = ctx.container - - const healthCheck = await healthCheckService.check() - return mapHealthCheckToApi(healthCheck) - }, -}) diff --git a/starter/cloudrun/src/view/rest/middleware/context-middleware.ts b/starter/cloudrun/src/view/rest/middleware/context-middleware.ts deleted file mode 100644 index 72f4282..0000000 --- a/starter/cloudrun/src/view/rest/middleware/context-middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NextFunction, Request, Response } from 'express' -import { RequestContextFactory } from '../../../context.js' -import { Container } from '../../../container.js' - -const createContextFromHttpRequestFactory = - (): RequestContextFactory<[Request, Response]> => - async (container, _req, _res) => { - return { - type: 'api-user', - container, - user: null, // Implement Authentication if needed (Auth header, cookie, ...) - } - } - -export const createContextMiddleware = - (container: Container) => - async (req: Request, res: Response, next: NextFunction): Promise => { - try { - req.context = await createContextFromHttpRequestFactory()( - container, - req, - res - ) - next() - } catch (err) { - next(err) - } - } diff --git a/starter/cloudrun/src/view/rest/middleware/error-handler.ts b/starter/cloudrun/src/view/rest/middleware/error-handler.ts deleted file mode 100644 index 44411e3..0000000 --- a/starter/cloudrun/src/view/rest/middleware/error-handler.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ErrorCode } from '../../../domain/errors/codes.js' -import { DomainError } from '../../../domain/errors/errors.js' -import express from 'express' - -export interface ErrorHandlerConfig { - enableProductionHttpErrorResponses: boolean -} - -const ERROR_DOMAIN_CODE_TO_HTTP_STATUS: Record = { - [ErrorCode.UNKNOWN]: 500, - [ErrorCode.VALIDATION]: 422, -} - -const errorToProductionObject = (error: T) => { - if (error instanceof DomainError) { - return { - ...error.data, - code: error.code, - } - } - return {} -} - -const errorToDevObject = (error: T) => { - if (error instanceof DomainError) { - return { - ...error.data, - code: error.code, - message: error.message, - stack: error.stack, - } - } - return { - message: error.message, - stack: error.stack, - } -} - -export function createErrorHandler( - config: ErrorHandlerConfig -): express.ErrorRequestHandler { - return (error, _req, res, _next) => { - const statusCode = - error instanceof DomainError - ? ERROR_DOMAIN_CODE_TO_HTTP_STATUS[error.code] ?? 500 - : 500 - - ;(res as any).error = errorToDevObject(error) - - const mappedError = config.enableProductionHttpErrorResponses - ? errorToProductionObject(error) - : errorToDevObject(error) - - res.status(statusCode) - if (mappedError) { - ;(res as any).out = mappedError - res.json(mappedError) - } - } -} diff --git a/starter/cloudrun/src/view/rest/middleware/request-logger.ts b/starter/cloudrun/src/view/rest/middleware/request-logger.ts deleted file mode 100644 index cf3531b..0000000 --- a/starter/cloudrun/src/view/rest/middleware/request-logger.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { pinoHttp, Options } from 'pino-http' -import { LoggerPort } from '../../../domain/ports/logger.d.js' - -export function createRequestLogger(innerLogger: LoggerPort) { - const options: Options = { - logger: innerLogger as any, - serializers: { - res: res => { - res.error = res.raw.error - res.out = res.raw.out - return res - }, - req: req => { - req.body = req.raw.body - return req - }, - }, - customReceivedMessage: (req, _res) => - `--- ${String(req.method)} ${String(req.url)} - Request accepted`, - customSuccessMessage: (req, res) => - `${res.statusCode} ${String(req.method)} ${String( - req.url - )} - Standard output`, - customErrorMessage: (req, res, _error) => - `${res.statusCode} ${String(req.method)} ${String(req.url)} - ${ - res.statusMessage - }`, - customErrorObject: (_req, res, _error, val) => ({ - ...val, - error: (res as any).error, - }), - customAttributeKeys: { - err: 'error', - }, - } - return pinoHttp(options) -} diff --git a/starter/cloudrun/src/view/rest/request.d.ts b/starter/cloudrun/src/view/rest/request.d.ts deleted file mode 100644 index fbeec0a..0000000 --- a/starter/cloudrun/src/view/rest/request.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { RequestContext } from '../../context.js' - -declare global { - namespace Express { - export interface Request { - context: RequestContext - } - } -} diff --git a/starter/cloudrun/src/view/rest/routes.ts b/starter/cloudrun/src/view/rest/routes.ts deleted file mode 100644 index d54c3f8..0000000 --- a/starter/cloudrun/src/view/rest/routes.ts +++ /dev/null @@ -1,15 +0,0 @@ -import express from 'express' -import { openApiRouter, registerOpenApiRoutes } from './util/openapi.util.js' -import { healthCheckController } from './controller/health-check.controller.js' - -const apiRouter = openApiRouter(express.Router(), { - removePrefix: '/api/v1', -}) - -registerOpenApiRoutes(apiRouter, { - ...healthCheckController, -}) - -export const routes = { - api: apiRouter.express, -} diff --git a/starter/cloudrun/src/view/rest/spec/openapi.yml b/starter/cloudrun/src/view/rest/spec/openapi.yml deleted file mode 100644 index 400e43a..0000000 --- a/starter/cloudrun/src/view/rest/spec/openapi.yml +++ /dev/null @@ -1,65 +0,0 @@ -openapi: '3.0.3' -info: - title: Node App API - version: '0.0.1' - -paths: - /api/v1/healthz: - get: - operationId: healthz - summary: Health check - tags: - - healthz - responses: - '200': - description: Health check response - content: - application/json: - schema: - $ref: '#/components/schemas/HealthCheckResponse' - -components: - schemas: - HealthCheckResponse: - type: object - properties: - checks: - type: array - description: List of component health checks - items: - $ref: '#/components/schemas/HealthCheck' - status: - type: integer - description: Overall status (0=healthy, 1=error) - enum: [0, 1] - required: - - checks - - status - HealthCheck: - type: object - properties: - id: - type: string - description: Unique identifier of the checked component - example: 'postgres' - latency: - type: integer - description: Response time in milliseconds - example: 1 - latencyStatus: - type: integer - description: Status of the latency check (0=good, 1=error, 2=timeout) - enum: [0, 1, 2] - example: 0 - output: - type: string - description: Additional output information - example: '' - required: - type: boolean - description: Whether this component is required for the system to function - example: true - status: - type: integer - description: Status of the component (0=Low, 1=Medium, 2=High) - enum: [0, 1, 2] \ No newline at end of file diff --git a/starter/cloudrun/src/view/rest/util/openapi.util.ts b/starter/cloudrun/src/view/rest/util/openapi.util.ts deleted file mode 100644 index 6be3436..0000000 --- a/starter/cloudrun/src/view/rest/util/openapi.util.ts +++ /dev/null @@ -1,310 +0,0 @@ -import express from 'express' -import bodyParser from 'body-parser' -import { operations, paths, operationPaths } from '../spec/openapi.js' -import { RequestContext } from '../../../context.js' - -export type OpenApiRouteResponseBodyMethod< - T, - TMethod extends string = 'get', - TDefault = unknown -> = T extends { - [key in TMethod]: { - responses: { 200: { content: { 'application/json': infer U } } } - } -} - ? U - : TDefault - -export type OpenApiRouteResponseBody< - T, - TMethod extends string = 'get' -> = OpenApiRouteResponseBodyMethod< - T, - TMethod, - OpenApiRouteResponseBodyMethod< - T, - 'post', - OpenApiRouteResponseBodyMethod< - T, - 'put', - OpenApiRouteResponseBodyMethod - > - > -> - -type LowercaseKeys> = { - [key in keyof T as key extends string ? Lowercase : key]: T[key] -} - -export type OpenApiRoutePathParam = T extends { - get: { parameters: { path: infer U } } -} - ? U - : T extends { post: { parameters: { path: infer U } } } - ? U - : T extends { put: { parameters: { path: infer U } } } - ? U - : unknown - -export type OpenApiRouteQueryParam = T extends { - get: { parameters: { query: infer U } } -} - ? U - : T extends { post: { parameters: { query: infer U } } } - ? U - : unknown - -export type OpenApiRouteHeaderParam = T extends { - get: { - parameters: { - header: infer U extends Record - } - } -} - ? LowercaseKeys - : T extends { - post: { - parameters: { - header: infer U extends Record - } - } - } - ? LowercaseKeys - : unknown - -export type OpenApiRouteParam = OpenApiRoutePathParam & - OpenApiRouteQueryParam & - OpenApiRouteHeaderParam - -export type OpenApiRouteRequestBodyMethod< - T, - TMethod extends string = 'post', - TDefault = unknown -> = T extends { - [key in TMethod]: { - requestBody: { - content: - | { 'application/json': infer U } - | { 'multipart/form-data': infer U } - } - } -} - ? U - : TDefault - -export type OpenApiRouteRequestBody< - T, - TMethod extends string = 'post' -> = OpenApiRouteRequestBodyMethod< - T, - TMethod, - OpenApiRouteRequestBodyMethod< - T, - 'put', - OpenApiRouteRequestBodyMethod< - T, - 'delete', - OpenApiRouteRequestBodyMethod - > - > -> - -/** - * pipeMiddleware takes multiple middlewares and creates and merges them into - * one using express Router. - */ -export const pipeMiddleware = (...middlewares: express.RequestHandler[]) => { - const router = express.Router({ mergeParams: true }) - middlewares.forEach(m => router.use(m)) - return router -} - -export type ApiHandler = ( - ctx: Readonly, - req: express.Request, - res: express.Response -) => TRes | Promise - -const asyncHandler = - (fn: ApiHandler): express.Handler => - async (req, res, next) => { - try { - const result = await fn(req.context, req, res) - res.json(result) - } catch (error: unknown) { - next(error) - } - } - -export type OperationIds = keyof operations - -type ApiMimeTypes = string - -type Content = { - content: any -} - -type MimeContent = { - content: { [key in MimeType]?: any } -} - -type MimeContentValue< - MimeType extends ApiMimeTypes, - T extends MimeContent -> = { - [K in keyof T['content']]: T['content'][K] -}[keyof T['content']] - -type OpenApiContentTypes> = { - [K in keyof OpenApiContent]: OpenApiContent[K] extends Content - ? MimeContentValue - : never -}[keyof OpenApiContent] - -export type OperationParams = - operations[OperationId]['parameters']['path'] - -export type OperationQuery = - operations[OperationId]['parameters']['query'] - -export type OperationResponse = - OpenApiContentTypes - -export type OperationBody = - operations[OperationId] extends never - ? never - : MimeContentValue - -export type OpenApiHandler = - express.RequestHandler< - OperationParams, - OperationResponse, - OperationBody, - OperationQuery - > - -export type OperationHandler = ( - ctx: RequestContext, - req: Parameters>[0], - res: Parameters>[1] -) => Promise> - -type RouteHandlers = { - [Key in SubsetOperationIds]: OperationHandler -} - -export type RestApiController< - SubsetOperationIds extends OperationIds = OperationIds -> = { - [Key in SubsetOperationIds]: OpenApiHandler -} - -const handleOperationAsync = ( - fn: OperationHandler -): OpenApiHandler => asyncHandler(fn as any) as any - -const createRestController = ( - def: RouteHandlers -): RestApiController => { - return Object.entries(def).reduce((ctrl, [operationId, handler]) => { - if (!handler) { - return ctrl - } - ctrl[operationId as SubsetOperationIds] = handleOperationAsync( - handler as any - ) as any - return ctrl - }, {} as RestApiController) -} - -export const openApiRouter = ( - router: express.Router, - { removePrefix }: { removePrefix: string } = { removePrefix: '' } -) => ({ - express: router, - route: < - Method extends keyof paths[Path], - Path extends keyof paths, - OperationId extends OperationIds - >( - method: Method, - path: Path, - handler: OpenApiHandler - ) => { - const route = path - .toString() - .replaceAll('}', '') - .replaceAll('{', ':') - // eslint-disable-next-line security/detect-non-literal-regexp - .replace(new RegExp(`^${removePrefix}`), '') - - switch (method) { - case 'get': - router.get(route, handler) - break - case 'post': - router.post(route, handler) - break - case 'patch': - router.patch(route, handler) - break - case 'delete': - router.delete(route, handler) - break - case 'head': - router.head(route, handler) - break - case 'trace': - router.trace(route, handler) - break - case 'options': - router.options(route, handler) - break - default: - throw new Error( - `The OpenApi router received invalid HTTP method to be registered: ${method.toString()}` - ) - } - }, -}) - -export const registerOpenApiRoutes = ( - router: ReturnType, - controller: Partial> -) => { - const operations = Object.keys(controller) as SomeOperationIds[] - operations.forEach(operation => { - if (!controller[operation]) { - return - } - - router.route( - operationPaths[operation].method, - operationPaths[operation].path, - controller[operation] - ) - }) -} - -/** - * ctrl is a scoped object for controller functions - */ -export const ctrl = { - json: pipeMiddleware( - bodyParser.json(), - // Monkeypatch res.json to assign the body to res.out first in order - // to log it by pino - (_req, res: any, next) => { - const resJson = res.json.bind(res) - res.json = (body?: any) => { - res.out = body - return resJson(body) - } - next() - } - ), - asyncHandler, - createRestController, - openApiRouter, - registerOpenApiRoutes, -} diff --git a/starter/cloudrun/src/view/server.ts b/starter/cloudrun/src/view/server.ts deleted file mode 100644 index 29bb2b6..0000000 --- a/starter/cloudrun/src/view/server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import express from 'express' -import { createRequestLogger } from './rest/middleware/request-logger.js' -import { Container } from '../container.js' -import { config } from '../config.js' -import { createErrorHandler } from './rest/middleware/error-handler.js' -import { routes } from './rest/routes.js' -import { createContextMiddleware } from './rest/middleware/context-middleware.js' - -export const createServer = (appContainer: Container) => { - const { logger } = appContainer - - const server = express() - const errorHandler = createErrorHandler(config.server) - const requestLogger = createRequestLogger(logger) - const contextMiddleware = createContextMiddleware(appContainer) - - server.disable('x-powered-by') - - server.use(requestLogger) - server.use(contextMiddleware) - server.use('/api/v1/', routes.api) - server.use(errorHandler) - - return server -} diff --git a/starter/shared/.dockerignore b/starter/shared/.dockerignore deleted file mode 100644 index 212cc2f..0000000 --- a/starter/shared/.dockerignore +++ /dev/null @@ -1,19 +0,0 @@ -.editorconfig -.git -.gitignore -.gitlab -.gitlab-ci.yml -.huskyrc.json -.npm -Dockerfile -LICENSE -README.md -ci-branch-config -commitlint.config.js -dist -docker-compose -docs -docs-output -helm -node_modules -prettier.config.js diff --git a/starter/shared/.gitignore_ b/starter/shared/.gitignore_ deleted file mode 100644 index 7fc4872..0000000 --- a/starter/shared/.gitignore_ +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -dist -tsconfig.tsbuildinfo -junit.xml -coverage \ No newline at end of file diff --git a/starter/shared/.gitlab-ci.yml b/starter/shared/.gitlab-ci.yml deleted file mode 100644 index 51c0716..0000000 --- a/starter/shared/.gitlab-ci.yml +++ /dev/null @@ -1,199 +0,0 @@ -variables: - # Default configuration, check .export_variables job for calculated env variables based on branch config - - # Node image for pipeline runner - NODE_BASE_IMAGE: node:24.5.0 - # Where to store json secrets from Cloud provider - SECRETS_PATH: '/config/secrets.json' - - ## GCP configuration ## - # Where to temporary store Google service account - GCP_SA_KEY_JSON_PATH: /tmp/key.json - - ## Docker artifact registry configuration ## - # Project name for docker compose CI job runs - DOCKER_COMPOSE_PROJECT_NAME: $CI_PROJECT_NAME-job-$CI_JOB_ID - # Build docker version tag - DOCKER_IMAGE_TAG: $CI_COMMIT_SHORT_SHA - -default: - image: ackee/gitlab-builder - before_script: - - echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc - - echo "//${CI_SERVER_HOST}/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc; - -cache: &docker_cache - key: "$CI_COMMIT_REF_NAME" - paths: - - app_image*.tar - policy: pull - -stages: - - build - - test - - deploy - -## -# HELPERS -## -# Before script extension that exports ci-branch-config environment variables and stores the GCP service -# account on the disk to be used by another jobs in the CI. This should be extended by every job that uses -# any of the branch configuration, docker image or CI SA -.export_variables: - before_script: - - source ci-branch-config/common.env - - | - if [ -f "ci-branch-config/${CI_COMMIT_REF_NAME}.env" ]; then - source ci-branch-config/${CI_COMMIT_REF_NAME}.env - fi - - export DOCKER_REGISTRY_URL="$GCP_REGION-docker.pkg.dev" - - export DOCKER_IMAGE_NAME="$DOCKER_REGISTRY_URL/$GCP_PROJECT_ID/${GCP_PROJECT_ID}-docker/$CI_PROJECT_NAME" - - export DOCKER_IMAGE_BUILDER_NAME="$DOCKER_IMAGE_NAME-builder" - - echo "$GCP_SA_KEY" | base64 -d > "$GCP_SA_KEY_JSON_PATH" - - -# Fetches built docker builder image from remote storage or cache -.fetch_build_image: &fetch_build_image - - | - if ! docker inspect --type=image "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" >/dev/null; then - docker load -i app_image_builder.tar - fi - -## -# BUILD STAGE -## -# Builds the builder docker image that can be used for running jobs using the CI docker dependencies and -# configuration (tests, lint, audit, ...) -build image: - stage: build - interruptible: true - extends: .export_variables - script: - - | - echo "app_image*" >> .dockerignore - docker build --target builder \ - -t "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" . - - docker save "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" > app_image_builder.tar - cache: - <<: *docker_cache - policy: push - when: on_success - -## -# TEST STAGE -## -# Performs npm ci-lint script in builder image. -# Make sure the ci-lint outputs json file "output/checkstyle-result.json" that reports the result of the linter -lint: - stage: test - interruptible: true - extends: .export_variables - script: - - if [ "$SKIP_LINT" == "true" ]; then warn "Lint skipped."; exit 0; fi - - *fetch_build_image - - cd "$CI_PROJECT_DIR/docker-compose" - - | - DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ - -f docker-compose.yml -f docker-compose.ci.yml \ - run --rm --no-deps app npm run ci-lint - needs: - - build image - artifacts: - reports: - codequality: output/checkstyle-result.json - -# Npm audit run inside of builder image -npm audit: - stage: test - interruptible: true - extends: .export_variables - script: - - if [ "$SKIP_AUDIT" == "true" ]; then warn "Audit skipped."; exit 0; fi - - *fetch_build_image - - cd "$CI_PROJECT_DIR/docker-compose" - - | - DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ - -f docker-compose.yml -f docker-compose.ci.yml \ - run --rm --no-deps app npm audit --production --audit-level=high - needs: - - build image - -# Test job running npm ci-test script. Make sure the ci-test script exports two files on disk: -# output/test.xml - jUnit reporter with test results -# output/cobertura-coverage.xml - Test coverage results -test: - stage: test - interruptible: true - extends: .export_variables - script: - - if [ "$SKIP_TESTS" == "true" ]; then warn "Tests skipped."; exit 0; fi - - *fetch_build_image - - cd "$CI_PROJECT_DIR/docker-compose" - - | - DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" \ - docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ - -f docker-compose.yml -f docker-compose.ci.yml \ - run --rm -e RUN_DOCKER_COMPOSE_DEPS=true -e DB_HOST=postgres app npm run ci-test - artifacts: - when: always - reports: - junit: output/test.xml - coverage_report: - coverage_format: cobertura - path: output/cobertura-coverage.xml - needs: - - build image - -## -# DEPLOY STAGE -## -# Build production image and push to registry -# Signs in with GCP SA key from pipeline, perform build of the image and pushes it to remote registry -# Make sure /ci-branch-config-name/{branch} exists when adding new branches to "only" field -build and push to registry: - stage: deploy - extends: .export_variables - script: - - docker login -u _json_key --password-stdin $DOCKER_REGISTRY_URL < "$GCP_SA_KEY_JSON_PATH" - - set -a && source ci-branch-config/${CI_COMMIT_REF_NAME}.env && set +a - - | - docker build \ - -t "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \ - $(for var in $(cat ci-branch-config/${CI_COMMIT_REF_NAME}.env | sed 's/=.*//'); do echo "--build-arg $var=${!var} "; done) \ - --build-arg "SECRETS_PATH=$SECRETS_PATH" \ - . - - docker push "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" - needs: - - build image - - npm audit - - test - only: - - stage - - master - -# Deploy to Google Cloud Run based on image stored on remote registry -deploy cloud run: - stage: deploy - image: google/cloud-sdk:slim - extends: .export_variables - script: - - gcloud auth activate-service-account --key-file "$GCP_SA_KEY_JSON_PATH" - - gcloud config set project $GCP_PROJECT_ID - - gcloud auth configure-docker - - | - gcloud run deploy wake-arena-project-api \ - --image "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \ - --project "$GCP_PROJECT_ID" \ - --platform managed \ - --port 3000 \ - --region "$GCP_REGION" \ - --allow-unauthenticated \ - --memory "$GCP_CLOUD_RUN_ALLOCATED_MEMORY" \ - --service-account="$GCP_CLOUD_RUN_SA_NAME" \ - --set-secrets=$SECRETS_PATH="$GCP_SECRETS_NAME:$GCP_SECRETS_VERSION" \ - --set-cloudsql-instances="$GCP_PROJECT_ID:$GCP_REGION:$GCP_SQL_INSTANCE_NAME" - only: - - stage - - master - needs: - - build and push to registry diff --git a/starter/shared/.mocha-junit-config.json b/starter/shared/.mocha-junit-config.json deleted file mode 100644 index 53db979..0000000 --- a/starter/shared/.mocha-junit-config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "reporterEnabled": "spec, mocha-junit-reporter", - "mochaJunitReporterReporterOptions": { - "mochaFile": "output/test.xml" - } -} diff --git a/starter/shared/.mocharc.json b/starter/shared/.mocharc.json deleted file mode 100644 index 5055306..0000000 --- a/starter/shared/.mocharc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extension": ["ts"], - "spec": "src/**/*.test.ts", - "parallel": false, - "enable-source-maps": true, - "node-option": ["import=tsx"], - "file": ["src/test/setup.ts"] -} diff --git a/starter/shared/.nvmrc b/starter/shared/.nvmrc deleted file mode 100644 index eb95d54..0000000 --- a/starter/shared/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -24.5.0 \ No newline at end of file diff --git a/starter/shared/Dockerfile b/starter/shared/Dockerfile deleted file mode 100644 index 26b79f1..0000000 --- a/starter/shared/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -# BUILDER IMAGE -FROM node:24.5.0 AS builder -ARG NPM_TOKEN -ENV NODE_PATH=. -ENV JOBS="max" -WORKDIR /usr/src/app - -ENV DOCKERIZE_VERSION v0.6.1 -RUN wget -q "https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" && \ - tar -C /usr/local/bin -xzvf "dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" && \ - rm "dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" - -ENV TINI_VERSION v0.19.0 -RUN wget -q https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini -O /tini && \ - chmod +x /tini - -# important, otherwise postinstall hook fails -RUN npm set progress=false -RUN npm set loglevel=error -# copy all relevant files -COPY . . -# RUN echo "//registry.npmjs.org//:_authToken=$NPM_TOKEN" >> .npmrc -# install dependencies -RUN npm ci -# compile typescript -RUN npm run build - -# MAIN IMAGE -FROM node:24.5.0-slim - -ENV NODE_PATH=dist -ENV CFG_JSON_PATH="/config/secrets.json" -WORKDIR /usr/src/app -COPY --from=builder /usr/src/app /usr/src/app -COPY --from=builder /tini /tini -COPY --from=builder /usr/local/bin/dockerize /usr/local/bin/dockerize -EXPOSE 3000 -USER node -ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] -CMD [ "node", "-r", "source-map-support/register", "dist/index.js"] diff --git a/starter/shared/ci-branch-config/common.env b/starter/shared/ci-branch-config/common.env deleted file mode 100644 index 1304062..0000000 --- a/starter/shared/ci-branch-config/common.env +++ /dev/null @@ -1,7 +0,0 @@ -ENVIRONMENT=development -GCP_PROJECT_ID={{PROJECT_NAME}}-development -GCP_REGION=europe-west3 -GCP_SECRETS_NAME={{PROJECT_NAME}}-development-config -GCP_SECRETS_VERSION=latest -GCP_SA_KEY=$SECRET_GCP_SA_KEY_DEVELOPMENT -GCP_CLOUD_RUN_SA_NAME=REPLACEME diff --git a/starter/shared/ci-branch-config/development.env b/starter/shared/ci-branch-config/development.env deleted file mode 100644 index fd449c2..0000000 --- a/starter/shared/ci-branch-config/development.env +++ /dev/null @@ -1,7 +0,0 @@ -ENVIRONMENT=development -GCP_PROJECT_ID={{PROJECT_NAME}}-development -GCP_REGION=europe-west3 -GCP_SECRETS_NAME={{PROJECT_NAME}}-development-config -GCP_SECRETS_VERSION=latest -GCP_SA_KEY=$SECRET_GCP_SA_KEY_DEVELOPMENT -GCP_CLOUD_RUN_SA_NAME=REPLACEME \ No newline at end of file diff --git a/starter/shared/ci-branch-config/master.env b/starter/shared/ci-branch-config/master.env deleted file mode 100644 index 317e809..0000000 --- a/starter/shared/ci-branch-config/master.env +++ /dev/null @@ -1,7 +0,0 @@ -ENVIRONMENT=production -GCP_PROJECT_ID={{PROJECT_NAME}}-production -GCP_REGION=europe-west3 -GCP_SECRETS_NAME={{PROJECT_NAME}}-production-config -GCP_SECRETS_VERSION=latest -GCP_SA_KEY=$SECRET_GCP_SA_KEY_PRODUCTION -GCP_CLOUD_RUN_SA_NAME=REPLACEME diff --git a/starter/shared/ci-branch-config/stage.env b/starter/shared/ci-branch-config/stage.env deleted file mode 100644 index 02f856d..0000000 --- a/starter/shared/ci-branch-config/stage.env +++ /dev/null @@ -1,7 +0,0 @@ -ENVIRONMENT=stage -GCP_PROJECT_ID={{PROJECT_NAME}}-stage -GCP_REGION=europe-west3 -GCP_SECRETS_NAME={{PROJECT_NAME}}-stage-config -GCP_SECRETS_VERSION=latest -GCP_SA_KEY=$SECRET_GCP_SA_KEY_PRODUCTION -GCP_CLOUD_RUN_SA_NAME=REPLACEME \ No newline at end of file diff --git a/starter/shared/docker-compose/docker-compose-entrypoint.sh b/starter/shared/docker-compose/docker-compose-entrypoint.sh deleted file mode 100755 index 1e906e8..0000000 --- a/starter/shared/docker-compose/docker-compose-entrypoint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -if [ "$RUN_DOCKER_COMPOSE_DEPS" == true ]; then - dockerize -wait tcp://postgres:5432 -timeout 5m "$@" -else - exec $@ -fi diff --git a/starter/shared/docker-compose/docker-compose.ci.yml b/starter/shared/docker-compose/docker-compose.ci.yml deleted file mode 100644 index 20b6991..0000000 --- a/starter/shared/docker-compose/docker-compose.ci.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: '3.8' -services: - app: - build: - context: .. - image: '$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG' - user: root - depends_on: - - postgres - volumes: - - '$CI_PROJECT_DIR/docs-output:/usr/src/app/docs-output' - - '$CI_PROJECT_DIR/output:/usr/src/app/output' - - '$CI_PROJECT_DIR/secrets-test.json:/usr/src/app/secrets-test.json:ro' - - '$PWD/docker-compose-entrypoint.sh:/docker-entrypoint.sh:ro' - entrypoint: /docker-entrypoint.sh - environment: - - NODE_PATH=. - - ENABLE_TESTS=true - - CFG_JSON_PATH=secrets-test.json diff --git a/starter/shared/docker-compose/docker-compose.local.yml b/starter/shared/docker-compose/docker-compose.local.yml deleted file mode 100644 index 197a108..0000000 --- a/starter/shared/docker-compose/docker-compose.local.yml +++ /dev/null @@ -1,5 +0,0 @@ -version: '3.8' -services: - postgres: - ports: - - 5432:5432 diff --git a/starter/shared/docker-compose/docker-compose.override.yml b/starter/shared/docker-compose/docker-compose.override.yml deleted file mode 100644 index 197a108..0000000 --- a/starter/shared/docker-compose/docker-compose.override.yml +++ /dev/null @@ -1,5 +0,0 @@ -version: '3.8' -services: - postgres: - ports: - - 5432:5432 diff --git a/starter/shared/docker-compose/docker-compose.yml b/starter/shared/docker-compose/docker-compose.yml deleted file mode 100644 index 2a8ca69..0000000 --- a/starter/shared/docker-compose/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3.8' -services: - postgres: - image: postgres:15 - environment: - - POSTGRES_DB={{PROJECT_NAME}}_docker - - POSTGRES_USER={{PROJECT_NAME}}_docker - - POSTGRES_PASSWORD={{PROJECT_NAME}}_docker diff --git a/starter/shared/jest.config.js b/starter/shared/jest.config.js deleted file mode 100644 index 93ab1ff..0000000 --- a/starter/shared/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - preset: 'ts-jest', - roots: ['/src'], - transform: { - '^.+\\.(t|j)sx?$': 'ts-jest', - }, - testEnvironment: 'node', - testMatch: ['/src/**/*.test.ts'], - collectCoverageFrom: ['/src/app/**/*.(t|j)s'], - moduleFileExtensions: ['ts', 'js', 'json', 'node'], - modulePaths: ['/src/'], -} diff --git a/starter/shared/prettier.config.cjs b/starter/shared/prettier.config.cjs deleted file mode 100644 index 2a4cd0b..0000000 --- a/starter/shared/prettier.config.cjs +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@ackee/styleguide-backend-config/prettier') diff --git a/starter/shared/src/test/setup.ts b/starter/shared/src/test/setup.ts deleted file mode 100644 index 472e99f..0000000 --- a/starter/shared/src/test/setup.ts +++ /dev/null @@ -1 +0,0 @@ -// Add setup for tests (db connection, ...) if needed diff --git a/starter/shared/tsconfig.json b/starter/shared/tsconfig.json deleted file mode 100644 index 025b4dc..0000000 --- a/starter/shared/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "include": ["src/**/*"], - "exclude": ["dangerfile.ts"], - "ts-node": { - "files": true - }, - "compilerOptions": { - "target": "ES2021", - "module": "node16", - "lib": ["ES2021", "DOM"], - "allowJs": true, - "incremental": true, - "checkJs": true, - "sourceMap": true, - "outDir": "dist", - "rootDir": "src", - "strict": true, - "moduleResolution": "node16", - "useUnknownInCatchVariables": false, - "esModuleInterop": true - } -} From 7847c04a599eaac2f0b5d77f6523baeb2fc96d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:54:25 +0200 Subject: [PATCH 06/28] =?UTF-8?q?=E2=9C=A8=20Add=20file=20mergers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Builder.js | 16 +- lib/Builder.js.map | 2 +- lib/Mergers/ConfigMerger.js | 22 +++ lib/Mergers/ConfigMerger.js.map | 1 + lib/Mergers/ContainerMerger.js | 172 +++++++++++++++++++ lib/Mergers/ContainerMerger.js.map | 1 + lib/Mergers/EnvJsonMerger.js | 20 +++ lib/Mergers/EnvJsonMerger.js.map | 1 + lib/Mergers/EnvJsoncMerger.js | 20 +++ lib/Mergers/EnvJsoncMerger.js.map | 1 + lib/Mergers/PackageJsonMerger.js | 36 ++++ lib/Mergers/PackageJsonMerger.js.map | 1 + src/Builder.ts | 16 +- src/Mergers/ConfigMerger.ts | 28 ++++ src/Mergers/ContainerMerger.ts | 241 +++++++++++++++++++++++++++ src/Mergers/EnvJsoncMerger.ts | 24 +++ src/Mergers/PackageJsonMerger.ts | 45 +++++ 17 files changed, 630 insertions(+), 17 deletions(-) create mode 100644 lib/Mergers/ConfigMerger.js create mode 100644 lib/Mergers/ConfigMerger.js.map create mode 100644 lib/Mergers/ContainerMerger.js create mode 100644 lib/Mergers/ContainerMerger.js.map create mode 100644 lib/Mergers/EnvJsonMerger.js create mode 100644 lib/Mergers/EnvJsonMerger.js.map create mode 100644 lib/Mergers/EnvJsoncMerger.js create mode 100644 lib/Mergers/EnvJsoncMerger.js.map create mode 100644 lib/Mergers/PackageJsonMerger.js create mode 100644 lib/Mergers/PackageJsonMerger.js.map create mode 100644 src/Mergers/ConfigMerger.ts create mode 100644 src/Mergers/ContainerMerger.ts create mode 100644 src/Mergers/EnvJsoncMerger.ts create mode 100644 src/Mergers/PackageJsonMerger.ts diff --git a/lib/Builder.js b/lib/Builder.js index 3b692fc..920a67b 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -1,10 +1,10 @@ import glob from 'fast-glob'; import * as fs from 'fs/promises'; import * as path from 'path'; -// import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' -// import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' -// import { ConfigMerger } from './Mergers/ConfigMerger.js' -// import { ContainerMerger } from './Mergers/ContainerMerger.js' +import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js'; +import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js'; +import { ConfigMerger } from './Mergers/ConfigMerger.js'; +import { ContainerMerger } from './Mergers/ContainerMerger.js'; import { Files } from './Files.js'; export class Builder { constructor(params) { @@ -17,10 +17,10 @@ export class Builder { '{{PROJECT_NAME}}': this.projectName, }; this.fileMergers = [ - // new PackageJsonMerger(this.projectName, this.destination, 'package.json'), - // new EnvJsoncMerger(this.destination, '.env.jsonc'), - // new ConfigMerger(this.destination, 'src/config.ts'), - // new ContainerMerger(this.destination, 'src/container.ts'), + new PackageJsonMerger(this.projectName, this.destination, 'package.json'), + new EnvJsoncMerger(this.destination, '.env.jsonc'), + new ConfigMerger(this.destination, 'src/config.ts'), + new ContainerMerger(this.destination, 'src/container.ts'), ]; } async prepareFolder() { diff --git a/lib/Builder.js.map b/lib/Builder.js.map index d966101..f4cb2eb 100644 --- a/lib/Builder.js.map +++ b/lib/Builder.js.map @@ -1 +1 @@ -{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Mergers/ConfigMerger.js b/lib/Mergers/ConfigMerger.js new file mode 100644 index 0000000..4f669fe --- /dev/null +++ b/lib/Mergers/ConfigMerger.js @@ -0,0 +1,22 @@ +import { Merger } from './Merger.js'; +import fsp from 'fs/promises'; +export class ConfigMerger extends Merger { + constructor() { + super(...arguments); + this.name = 'Config'; + } + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir); + if (content) { + return content; + } + const { originPath, destPath } = this.getPaths(originDir); + const [destTsConfig, originTsConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]); + const configContent = /configSchema = {\n(.*?)\n}\n/s.exec(originTsConfig)?.[1]; + return destTsConfig.replace('\n}\n', configContent ? `${configContent}\n}\n` : '}\n'); + } +} +//# sourceMappingURL=ConfigMerger.js.map \ No newline at end of file diff --git a/lib/Mergers/ConfigMerger.js.map b/lib/Mergers/ConfigMerger.js.map new file mode 100644 index 0000000..c26e47f --- /dev/null +++ b/lib/Mergers/ConfigMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ConfigMerger.js","sourceRoot":"","sources":["../../src/Mergers/ConfigMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,YAAa,SAAQ,MAAM;IAAxC;;QACE,SAAI,GAAG,QAAQ,CAAA;IAuBjB,CAAC;IArBC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QACF,MAAM,aAAa,GAAG,+BAA+B,CAAC,IAAI,CACxD,cAAc,CACf,EAAE,CAAC,CAAC,CAAC,CAAA;QAEN,OAAO,YAAY,CAAC,OAAO,CACzB,OAAO,EACP,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,OAAO,CAAC,CAAC,CAAC,KAAK,CAChD,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/ContainerMerger.js b/lib/Mergers/ContainerMerger.js new file mode 100644 index 0000000..daebea1 --- /dev/null +++ b/lib/Mergers/ContainerMerger.js @@ -0,0 +1,172 @@ +import { Merger } from './Merger.js'; +import fsp from 'fs/promises'; +import * as ts from 'typescript'; +export class ContainerMerger extends Merger { + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir); + if (content) { + return content; + } + const { originPath, destPath } = this.getPaths(originDir); + const [originContainer, destContainer] = await Promise.all([ + fsp.readFile(originPath, 'utf8'), + fsp.readFile(destPath, 'utf8'), + ]); + const originAst = this.parseFile(originContainer); + const destAst = this.parseFile(destContainer); + const mergedImports = this.mergeImports(originAst, destAst); + const mergedInterface = this.mergeInterface(originAst, destAst); + const mergedFunction = this.mergeFunction(originAst, destAst); + return `${mergedImports}\n\n${mergedInterface}\n\nexport type ContainerFactory = () => Promise\n\n${mergedFunction}\n`; + } + parseFile(content) { + return ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true); + } + mergeImports(originAst, destAst) { + const imports = []; + const addImports = (node) => { + if (!ts.isImportDeclaration(node)) { + node.forEachChild(addImports); + return; + } + const importText = node.getText(); + if (!imports.includes(importText)) { + imports.push(importText); + } + node.forEachChild(addImports); + }; + addImports(originAst); + addImports(destAst); + return imports.join('\n'); + } + mergeInterface(originAst, destAst) { + const properties = []; + const extractProperties = (node) => { + if (!ts.isInterfaceDeclaration(node) || node.name.text !== 'Container') { + node.forEachChild(extractProperties); + return; + } + node.members.forEach(member => { + if (!ts.isPropertySignature(member)) { + return; + } + const propText = member.getText().trim(); + if (!properties.includes(propText)) { + properties.push(propText); + } + }); + node.forEachChild(extractProperties); + }; + extractProperties(originAst); + extractProperties(destAst); + return `export interface Container {\n ${properties.join('\n ')}\n}`; + } + mergeFunction(originAst, destAst) { + const originFunctionBody = this.extractFunctionBody(originAst); + const destFunctionBody = this.extractFunctionBody(destAst); + const allFunctionContent = [ + ...originFunctionBody.content, + ...destFunctionBody.content, + ]; + const mergedReturnProps = this.mergeReturnProperties(originFunctionBody.returnProps, destFunctionBody.returnProps); + let body = ''; + if (allFunctionContent.length > 0) { + body += allFunctionContent.join('\n') + '\n\n'; + } + body += ` return {\n ${mergedReturnProps.join(',\n ')}\n }`; + return `export const createContainer = async (): Promise => {\n${body}\n}`; + } + mergeReturnProperties(originProps, destProps) { + const mergedProps = new Map(); + originProps.forEach(prop => { + const propName = this.getPropertyName(prop); + if (propName) { + mergedProps.set(propName, prop); + } + }); + destProps.forEach(prop => { + const propName = this.getPropertyName(prop); + if (propName) { + mergedProps.set(propName, prop); + } + }); + return Array.from(mergedProps.values()).map(prop => { + const propText = prop.getText(); + if (ts.isPropertyAssignment(prop) && + ts.isObjectLiteralExpression(prop.initializer)) { + return this.formatNestedObject(propText); + } + return propText; + }); + } + getPropertyName(prop) { + if (ts.isPropertyAssignment(prop)) { + if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { + return prop.name.text; + } + } + else if (ts.isShorthandPropertyAssignment(prop)) { + return prop.name.text; + } + return null; + } + formatNestedObject(propText) { + const lines = propText.split('\n'); + return lines + .map((line, index) => { + if (index === 0) { + return line; + } + return ' ' + line; + }) + .join('\n'); + } + extractFunctionBody(ast) { + const content = []; + const returnProps = []; + const visit = (node) => { + if (!this.isCreateContainer(node)) { + ts.forEachChild(node, visit); + return; + } + if (!node.initializer || !ts.isArrowFunction(node.initializer)) { + ts.forEachChild(node, visit); + return; + } + const body = node.initializer.body; + if (!ts.isBlock(body)) { + ts.forEachChild(node, visit); + return; + } + body.statements.forEach(statement => { + if (this.isContentStatement(statement)) { + content.push(statement.getText().trim()); + } + else if (this.isReturnWithObject(statement)) { + // Extract the actual property nodes, not just their names + statement.expression.properties.forEach(prop => { + returnProps.push(prop); + }); + } + }); + ts.forEachChild(node, visit); + }; + visit(ast); + return { content, returnProps }; + } + isCreateContainer(node) { + return (ts.isVariableDeclaration(node) && + node.name.getText() === 'createContainer'); + } + isContentStatement(statement) { + return (ts.isVariableStatement(statement) || + (ts.isExpressionStatement(statement) && + ts.isCallExpression(statement.expression))); + } + isReturnWithObject(statement) { + return (ts.isReturnStatement(statement) && + !!statement.expression && + ts.isObjectLiteralExpression(statement.expression)); + } +} +//# sourceMappingURL=ContainerMerger.js.map \ No newline at end of file diff --git a/lib/Mergers/ContainerMerger.js.map b/lib/Mergers/ContainerMerger.js.map new file mode 100644 index 0000000..5fa3dff --- /dev/null +++ b/lib/Mergers/ContainerMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ContainerMerger.js","sourceRoot":"","sources":["../../src/Mergers/ContainerMerger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Mergers/EnvJsonMerger.js b/lib/Mergers/EnvJsonMerger.js new file mode 100644 index 0000000..03c9b1f --- /dev/null +++ b/lib/Mergers/EnvJsonMerger.js @@ -0,0 +1,20 @@ +import { Merger } from './Merger.js' +import fsp from 'fs/promises' +export class EnvJsocMerger extends Merger { + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content + } + const { originPath, destPath } = this.getPaths(originDir) + const [destEnvConfig, originEnvConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') + return destEnvConfig + .replace(',\n}\n', '\n}\n') + .replace('\n}\n', `,\n${originWithoutOpenBracket}`) + } +} +//# sourceMappingURL=EnvJsonMerger.js.map diff --git a/lib/Mergers/EnvJsonMerger.js.map b/lib/Mergers/EnvJsonMerger.js.map new file mode 100644 index 0000000..1202724 --- /dev/null +++ b/lib/Mergers/EnvJsonMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EnvJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,aAAc,SAAQ,MAAM;IACvC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/EnvJsoncMerger.js b/lib/Mergers/EnvJsoncMerger.js new file mode 100644 index 0000000..550ebc6 --- /dev/null +++ b/lib/Mergers/EnvJsoncMerger.js @@ -0,0 +1,20 @@ +import { Merger } from './Merger.js'; +import fsp from 'fs/promises'; +export class EnvJsoncMerger extends Merger { + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir); + if (content) { + return content; + } + const { originPath, destPath } = this.getPaths(originDir); + const [destEnvConfig, originEnvConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]); + const originWithoutOpenBracket = originEnvConfig.replace('{\n', ''); + return destEnvConfig + .replace(',\n}\n', '\n}\n') + .replace('\n}\n', `,\n${originWithoutOpenBracket}`); + } +} +//# sourceMappingURL=EnvJsoncMerger.js.map \ No newline at end of file diff --git a/lib/Mergers/EnvJsoncMerger.js.map b/lib/Mergers/EnvJsoncMerger.js.map new file mode 100644 index 0000000..3948232 --- /dev/null +++ b/lib/Mergers/EnvJsoncMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"EnvJsoncMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsoncMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,cAAe,SAAQ,MAAM;IACxC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/PackageJsonMerger.js b/lib/Mergers/PackageJsonMerger.js new file mode 100644 index 0000000..ef21840 --- /dev/null +++ b/lib/Mergers/PackageJsonMerger.js @@ -0,0 +1,36 @@ +import { Merger } from './Merger.js'; +import fsp from 'fs/promises'; +export class PackageJsonMerger extends Merger { + constructor(projectName, destDir, pathToFile) { + super(destDir, pathToFile); + this.projectName = projectName; + } + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir); + if (content) { + return content; + } + const { originPath, destPath } = this.getPaths(originDir); + const [destPckgJson, starterPckgJson] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]); + const destPckgJsonObj = JSON.parse(destPckgJson); + const starterPckgJsonObj = JSON.parse(starterPckgJson); + destPckgJsonObj.name = this.projectName; + destPckgJsonObj.scripts = { + ...destPckgJsonObj.scripts, + ...starterPckgJsonObj.scripts, + }; + destPckgJsonObj.dependencies = { + ...destPckgJsonObj.dependencies, + ...starterPckgJsonObj.dependencies, + }; + destPckgJsonObj.devDependencies = { + ...destPckgJsonObj.devDependencies, + ...starterPckgJsonObj.devDependencies, + }; + return JSON.stringify(destPckgJsonObj, null, 2); + } +} +//# sourceMappingURL=PackageJsonMerger.js.map \ No newline at end of file diff --git a/lib/Mergers/PackageJsonMerger.js.map b/lib/Mergers/PackageJsonMerger.js.map new file mode 100644 index 0000000..9ee7303 --- /dev/null +++ b/lib/Mergers/PackageJsonMerger.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PackageJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/PackageJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,iBAAkB,SAAQ,MAAM;IAC3C,YACmB,WAAmB,EACpC,OAAe,EACf,UAAkB;QAElB,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAJT,gBAAW,GAAX,WAAW,CAAQ;IAKtC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QAEtD,eAAe,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAA;QACvC,eAAe,CAAC,OAAO,GAAG;YACxB,GAAG,eAAe,CAAC,OAAO;YAC1B,GAAG,kBAAkB,CAAC,OAAO;SAC9B,CAAA;QACD,eAAe,CAAC,YAAY,GAAG;YAC7B,GAAG,eAAe,CAAC,YAAY;YAC/B,GAAG,kBAAkB,CAAC,YAAY;SACnC,CAAA;QACD,eAAe,CAAC,eAAe,GAAG;YAChC,GAAG,eAAe,CAAC,eAAe;YAClC,GAAG,kBAAkB,CAAC,eAAe;SACtC,CAAA;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;CACF"} \ No newline at end of file diff --git a/src/Builder.ts b/src/Builder.ts index 5551a39..6953a5e 100644 --- a/src/Builder.ts +++ b/src/Builder.ts @@ -6,10 +6,10 @@ import { Npm } from './Npm.js' import { PackageJson } from './PackageJson.js' import { LoadedStarter, StarterConfig } from './StarterLoader.js' import { Merger } from './Mergers/Merger.js' -// import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' -// import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' -// import { ConfigMerger } from './Mergers/ConfigMerger.js' -// import { ContainerMerger } from './Mergers/ContainerMerger.js' +import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' +import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' +import { ConfigMerger } from './Mergers/ConfigMerger.js' +import { ContainerMerger } from './Mergers/ContainerMerger.js' import { Files } from './Files.js' export class Builder { @@ -43,10 +43,10 @@ export class Builder { '{{PROJECT_NAME}}': this.projectName, } this.fileMergers = [ - // new PackageJsonMerger(this.projectName, this.destination, 'package.json'), - // new EnvJsoncMerger(this.destination, '.env.jsonc'), - // new ConfigMerger(this.destination, 'src/config.ts'), - // new ContainerMerger(this.destination, 'src/container.ts'), + new PackageJsonMerger(this.projectName, this.destination, 'package.json'), + new EnvJsoncMerger(this.destination, '.env.jsonc'), + new ConfigMerger(this.destination, 'src/config.ts'), + new ContainerMerger(this.destination, 'src/container.ts'), ] } diff --git a/src/Mergers/ConfigMerger.ts b/src/Mergers/ConfigMerger.ts new file mode 100644 index 0000000..e9ca597 --- /dev/null +++ b/src/Mergers/ConfigMerger.ts @@ -0,0 +1,28 @@ +import { Merger } from './Merger.js' +import fsp from 'fs/promises' + +export class ConfigMerger extends Merger { + name = 'Config' + + async merge(originDir: string): Promise { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content + } + + const { originPath, destPath } = this.getPaths(originDir) + + const [destTsConfig, originTsConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + const configContent = /configSchema = {\n(.*?)\n}\n/s.exec( + originTsConfig + )?.[1] + + return destTsConfig.replace( + '\n}\n', + configContent ? `${configContent}\n}\n` : '}\n' + ) + } +} diff --git a/src/Mergers/ContainerMerger.ts b/src/Mergers/ContainerMerger.ts new file mode 100644 index 0000000..a17642b --- /dev/null +++ b/src/Mergers/ContainerMerger.ts @@ -0,0 +1,241 @@ +import { Merger } from './Merger.js' +import fsp from 'fs/promises' +import * as ts from 'typescript' + +export class ContainerMerger extends Merger { + async merge(originDir: string): Promise { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content + } + + const { originPath, destPath } = this.getPaths(originDir) + + const [originContainer, destContainer] = await Promise.all([ + fsp.readFile(originPath, 'utf8'), + fsp.readFile(destPath, 'utf8'), + ]) + + const originAst = this.parseFile(originContainer) + const destAst = this.parseFile(destContainer) + + const mergedImports = this.mergeImports(originAst, destAst) + const mergedInterface = this.mergeInterface(originAst, destAst) + const mergedFunction = this.mergeFunction(originAst, destAst) + + return `${mergedImports}\n\n${mergedInterface}\n\nexport type ContainerFactory = () => Promise\n\n${mergedFunction}\n` + } + + protected parseFile(content: string): ts.SourceFile { + return ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true) + } + + protected mergeImports( + originAst: ts.SourceFile, + destAst: ts.SourceFile + ): string { + const imports: string[] = [] + + const addImports = (node: ts.Node) => { + if (!ts.isImportDeclaration(node)) { + node.forEachChild(addImports) + return + } + + const importText = node.getText() + if (!imports.includes(importText)) { + imports.push(importText) + } + + node.forEachChild(addImports) + } + + addImports(originAst) + addImports(destAst) + + return imports.join('\n') + } + + protected mergeInterface( + originAst: ts.SourceFile, + destAst: ts.SourceFile + ): string { + const properties: string[] = [] + + const extractProperties = (node: ts.Node) => { + if (!ts.isInterfaceDeclaration(node) || node.name.text !== 'Container') { + node.forEachChild(extractProperties) + return + } + + node.members.forEach(member => { + if (!ts.isPropertySignature(member)) { + return + } + + const propText = member.getText().trim() + if (!properties.includes(propText)) { + properties.push(propText) + } + }) + + node.forEachChild(extractProperties) + } + + extractProperties(originAst) + extractProperties(destAst) + + return `export interface Container {\n ${properties.join('\n ')}\n}` + } + + protected mergeFunction( + originAst: ts.SourceFile, + destAst: ts.SourceFile + ): string { + const originFunctionBody = this.extractFunctionBody(originAst) + const destFunctionBody = this.extractFunctionBody(destAst) + + const allFunctionContent = [ + ...originFunctionBody.content, + ...destFunctionBody.content, + ] + + const mergedReturnProps = this.mergeReturnProperties( + originFunctionBody.returnProps, + destFunctionBody.returnProps + ) + + let body = '' + if (allFunctionContent.length > 0) { + body += allFunctionContent.join('\n') + '\n\n' + } + + body += ` return {\n ${mergedReturnProps.join(',\n ')}\n }` + + return `export const createContainer = async (): Promise => {\n${body}\n}` + } + + protected mergeReturnProperties( + originProps: ts.ObjectLiteralElementLike[], + destProps: ts.ObjectLiteralElementLike[] + ): string[] { + const mergedProps = new Map() + + originProps.forEach(prop => { + const propName = this.getPropertyName(prop) + if (propName) { + mergedProps.set(propName, prop) + } + }) + + destProps.forEach(prop => { + const propName = this.getPropertyName(prop) + if (propName) { + mergedProps.set(propName, prop) + } + }) + + return Array.from(mergedProps.values()).map(prop => { + const propText = prop.getText() + if ( + ts.isPropertyAssignment(prop) && + ts.isObjectLiteralExpression(prop.initializer) + ) { + return this.formatNestedObject(propText) + } + return propText + }) + } + + protected getPropertyName(prop: ts.ObjectLiteralElementLike): string | null { + if (ts.isPropertyAssignment(prop)) { + if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { + return prop.name.text + } + } else if (ts.isShorthandPropertyAssignment(prop)) { + return prop.name.text + } + return null + } + + protected formatNestedObject(propText: string): string { + const lines = propText.split('\n') + return lines + .map((line, index) => { + if (index === 0) { + return line + } + return ' ' + line + }) + .join('\n') + } + + protected extractFunctionBody(ast: ts.SourceFile): { + content: string[] + returnProps: ts.ObjectLiteralElementLike[] + } { + const content: string[] = [] + const returnProps: ts.ObjectLiteralElementLike[] = [] + + const visit = (node: ts.Node) => { + if (!this.isCreateContainer(node)) { + ts.forEachChild(node, visit) + return + } + + if (!node.initializer || !ts.isArrowFunction(node.initializer)) { + ts.forEachChild(node, visit) + return + } + + const body = node.initializer.body + if (!ts.isBlock(body)) { + ts.forEachChild(node, visit) + return + } + + body.statements.forEach(statement => { + if (this.isContentStatement(statement)) { + content.push(statement.getText().trim()) + } else if (this.isReturnWithObject(statement)) { + // Extract the actual property nodes, not just their names + statement.expression.properties.forEach(prop => { + returnProps.push(prop) + }) + } + }) + + ts.forEachChild(node, visit) + } + + visit(ast) + return { content, returnProps } + } + + protected isCreateContainer(node: ts.Node): node is ts.VariableDeclaration { + return ( + ts.isVariableDeclaration(node) && + node.name.getText() === 'createContainer' + ) + } + + protected isContentStatement(statement: ts.Statement): boolean { + return ( + ts.isVariableStatement(statement) || + (ts.isExpressionStatement(statement) && + ts.isCallExpression(statement.expression)) + ) + } + + protected isReturnWithObject( + statement: ts.Statement + ): statement is ts.ReturnStatement & { + expression: ts.ObjectLiteralExpression + } { + return ( + ts.isReturnStatement(statement) && + !!statement.expression && + ts.isObjectLiteralExpression(statement.expression) + ) + } +} diff --git a/src/Mergers/EnvJsoncMerger.ts b/src/Mergers/EnvJsoncMerger.ts new file mode 100644 index 0000000..5bed6e5 --- /dev/null +++ b/src/Mergers/EnvJsoncMerger.ts @@ -0,0 +1,24 @@ +import { Merger } from './Merger.js' +import fsp from 'fs/promises' + +export class EnvJsoncMerger extends Merger { + async merge(originDir: string): Promise { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content + } + + const { originPath, destPath } = this.getPaths(originDir) + + const [destEnvConfig, originEnvConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + + const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') + + return destEnvConfig + .replace(',\n}\n', '\n}\n') + .replace('\n}\n', `,\n${originWithoutOpenBracket}`) + } +} diff --git a/src/Mergers/PackageJsonMerger.ts b/src/Mergers/PackageJsonMerger.ts new file mode 100644 index 0000000..0f584a2 --- /dev/null +++ b/src/Mergers/PackageJsonMerger.ts @@ -0,0 +1,45 @@ +import { Merger } from './Merger.js' +import fsp from 'fs/promises' + +export class PackageJsonMerger extends Merger { + constructor( + private readonly projectName: string, + destDir: string, + pathToFile: string + ) { + super(destDir, pathToFile) + } + + async merge(originDir: string): Promise { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content + } + + const { originPath, destPath } = this.getPaths(originDir) + + const [destPckgJson, starterPckgJson] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + + const destPckgJsonObj = JSON.parse(destPckgJson) + const starterPckgJsonObj = JSON.parse(starterPckgJson) + + destPckgJsonObj.name = this.projectName + destPckgJsonObj.scripts = { + ...destPckgJsonObj.scripts, + ...starterPckgJsonObj.scripts, + } + destPckgJsonObj.dependencies = { + ...destPckgJsonObj.dependencies, + ...starterPckgJsonObj.dependencies, + } + destPckgJsonObj.devDependencies = { + ...destPckgJsonObj.devDependencies, + ...starterPckgJsonObj.devDependencies, + } + + return JSON.stringify(destPckgJsonObj, null, 2) + } +} From fe887e53ab02458c0ee37d198c2b05f62778a03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:55:42 +0200 Subject: [PATCH 07/28] =?UTF-8?q?=E2=9C=A8=20Add=20base=20project=20struct?= =?UTF-8?q?ure=20starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/_base/.dockerignore | 19 +++++ starter/_base/.env.jsonc | 6 ++ starter/_base/.eslint.tsconfig.json | 4 + starter/_base/.eslintrc.cjs | 9 +++ starter/_base/.gitignore | 5 ++ starter/_base/.mocha-junit-config.json | 6 ++ starter/_base/.mocharc.json | 8 ++ starter/_base/.nvmrc | 1 + starter/_base/Dockerfile | 40 +++++++++ starter/_base/package.json | 45 +++++++++++ starter/_base/prettier.config.cjs | 1 + starter/_base/src/adapters/pino.logger.ts | 44 ++++++++++ starter/_base/src/config.ts | 16 ++++ starter/_base/src/container.ts | 17 ++++ starter/_base/src/context.ts | 39 +++++++++ starter/_base/src/domain/errors/codes.ts | 9 +++ starter/_base/src/domain/errors/errors.ts | 25 ++++++ starter/_base/src/domain/ports/logger.d.ts | 21 +++++ starter/_base/src/index.ts | 15 ++++ starter/_base/src/test/setup.ts | 1 + starter/_base/src/view/cli/README.md | 13 +++ starter/_base/src/view/cli/cli.ts | 94 ++++++++++++++++++++++ starter/_base/tsconfig.json | 22 +++++ 23 files changed, 460 insertions(+) create mode 100644 starter/_base/.dockerignore create mode 100644 starter/_base/.env.jsonc create mode 100644 starter/_base/.eslint.tsconfig.json create mode 100644 starter/_base/.eslintrc.cjs create mode 100644 starter/_base/.gitignore create mode 100644 starter/_base/.mocha-junit-config.json create mode 100644 starter/_base/.mocharc.json create mode 100644 starter/_base/.nvmrc create mode 100644 starter/_base/Dockerfile create mode 100644 starter/_base/package.json create mode 100644 starter/_base/prettier.config.cjs create mode 100644 starter/_base/src/adapters/pino.logger.ts create mode 100644 starter/_base/src/config.ts create mode 100644 starter/_base/src/container.ts create mode 100644 starter/_base/src/context.ts create mode 100644 starter/_base/src/domain/errors/codes.ts create mode 100644 starter/_base/src/domain/errors/errors.ts create mode 100644 starter/_base/src/domain/ports/logger.d.ts create mode 100644 starter/_base/src/index.ts create mode 100644 starter/_base/src/test/setup.ts create mode 100644 starter/_base/src/view/cli/README.md create mode 100644 starter/_base/src/view/cli/cli.ts create mode 100644 starter/_base/tsconfig.json diff --git a/starter/_base/.dockerignore b/starter/_base/.dockerignore new file mode 100644 index 0000000..212cc2f --- /dev/null +++ b/starter/_base/.dockerignore @@ -0,0 +1,19 @@ +.editorconfig +.git +.gitignore +.gitlab +.gitlab-ci.yml +.huskyrc.json +.npm +Dockerfile +LICENSE +README.md +ci-branch-config +commitlint.config.js +dist +docker-compose +docs +docs-output +helm +node_modules +prettier.config.js diff --git a/starter/_base/.env.jsonc b/starter/_base/.env.jsonc new file mode 100644 index 0000000..fd6a1d1 --- /dev/null +++ b/starter/_base/.env.jsonc @@ -0,0 +1,6 @@ +{ + // Logging level, see https://github.com/pinojs/pino/blob/master/docs/api.md#logger-level + "LOGGER_DEFAULT_LEVEL": "debug", + // Enable/disable logging object multiline formatted logging https://github.com/pinojs/pino/blob/master/docs/api.md#prettyprint-boolean--object + "LOGGER_PRETTY": false +} diff --git a/starter/_base/.eslint.tsconfig.json b/starter/_base/.eslint.tsconfig.json new file mode 100644 index 0000000..a8d4317 --- /dev/null +++ b/starter/_base/.eslint.tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": [] +} diff --git a/starter/_base/.eslintrc.cjs b/starter/_base/.eslintrc.cjs new file mode 100644 index 0000000..6bd9011 --- /dev/null +++ b/starter/_base/.eslintrc.cjs @@ -0,0 +1,9 @@ + +module.exports = { + ...require('@ackee/styleguide-backend-config/eslint'), + root: true, + ignorePatterns: ['dist', 'docs', 'knexfile.ts'], + parserOptions: { + project: '.eslint.tsconfig.json', + }, +} \ No newline at end of file diff --git a/starter/_base/.gitignore b/starter/_base/.gitignore new file mode 100644 index 0000000..7fc4872 --- /dev/null +++ b/starter/_base/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +tsconfig.tsbuildinfo +junit.xml +coverage \ No newline at end of file diff --git a/starter/_base/.mocha-junit-config.json b/starter/_base/.mocha-junit-config.json new file mode 100644 index 0000000..53db979 --- /dev/null +++ b/starter/_base/.mocha-junit-config.json @@ -0,0 +1,6 @@ +{ + "reporterEnabled": "spec, mocha-junit-reporter", + "mochaJunitReporterReporterOptions": { + "mochaFile": "output/test.xml" + } +} diff --git a/starter/_base/.mocharc.json b/starter/_base/.mocharc.json new file mode 100644 index 0000000..5055306 --- /dev/null +++ b/starter/_base/.mocharc.json @@ -0,0 +1,8 @@ +{ + "extension": ["ts"], + "spec": "src/**/*.test.ts", + "parallel": false, + "enable-source-maps": true, + "node-option": ["import=tsx"], + "file": ["src/test/setup.ts"] +} diff --git a/starter/_base/.nvmrc b/starter/_base/.nvmrc new file mode 100644 index 0000000..eb95d54 --- /dev/null +++ b/starter/_base/.nvmrc @@ -0,0 +1 @@ +24.5.0 \ No newline at end of file diff --git a/starter/_base/Dockerfile b/starter/_base/Dockerfile new file mode 100644 index 0000000..26b79f1 --- /dev/null +++ b/starter/_base/Dockerfile @@ -0,0 +1,40 @@ +# BUILDER IMAGE +FROM node:24.5.0 AS builder +ARG NPM_TOKEN +ENV NODE_PATH=. +ENV JOBS="max" +WORKDIR /usr/src/app + +ENV DOCKERIZE_VERSION v0.6.1 +RUN wget -q "https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" && \ + tar -C /usr/local/bin -xzvf "dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" && \ + rm "dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz" + +ENV TINI_VERSION v0.19.0 +RUN wget -q https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini -O /tini && \ + chmod +x /tini + +# important, otherwise postinstall hook fails +RUN npm set progress=false +RUN npm set loglevel=error +# copy all relevant files +COPY . . +# RUN echo "//registry.npmjs.org//:_authToken=$NPM_TOKEN" >> .npmrc +# install dependencies +RUN npm ci +# compile typescript +RUN npm run build + +# MAIN IMAGE +FROM node:24.5.0-slim + +ENV NODE_PATH=dist +ENV CFG_JSON_PATH="/config/secrets.json" +WORKDIR /usr/src/app +COPY --from=builder /usr/src/app /usr/src/app +COPY --from=builder /tini /tini +COPY --from=builder /usr/local/bin/dockerize /usr/local/bin/dockerize +EXPOSE 3000 +USER node +ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] +CMD [ "node", "-r", "source-map-support/register", "dist/index.js"] diff --git a/starter/_base/package.json b/starter/_base/package.json new file mode 100644 index 0000000..93b98d3 --- /dev/null +++ b/starter/_base/package.json @@ -0,0 +1,45 @@ +{ + "name": "app", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "mocha", + "build": "tsc", + "start": "node -r source-map-support/register dist/index.js", + "ci-test:no-coverage": "npm run test -- --parallel=false -R mocha-multi-reporters --reporter-options configFile=.mocha-junit-config.json", + "ci-test": "nyc -a -r cobertura --report-dir output npm run ci-test:no-coverage", + "prettier": "prettier --check --write '**/*.{ts,js,json,jsonc,md}'", + "lint": "eslint '**/*.ts' -f codeframe --fix", + "codestyle": "npm run lint && npm run prettier", + "ci-lint": "npm run lint -- -f checkstyle -o ./output/checkstyle-result.xml", + "cli": "tsx ./src/view/cli/cli.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "configuru": "^1.0.0", + "pino": "^9.0.0", + "source-map-support": "^0.5.0" + }, + "devDependencies": { + "@ackee/styleguide-backend-config": "^1.0.1", + "@istanbuljs/nyc-config-typescript": "^1.0.0", + "@types/mocha": "^10.0.0", + "@types/node": "^24.0.0", + "eslint": "^8.0.0", + "eslint-formatter-gitlab": "^5.0.0", + "mocha": "^11.0.0", + "mocha-junit-reporter": "^2.0.0", + "mocha-multi-reporters": "^1.0.0", + "nyc": "^17.0.0", + "pino-pretty": "^13.0.0", + "prettier": "^3.0.0", + "ts-node": "^10.0.0", + "tsx": "^4.0.0", + "typescript": "^5.0.0", + "yargs": "^18.0.0" + }, + "type": "module" +} diff --git a/starter/_base/prettier.config.cjs b/starter/_base/prettier.config.cjs new file mode 100644 index 0000000..2a4cd0b --- /dev/null +++ b/starter/_base/prettier.config.cjs @@ -0,0 +1 @@ +module.exports = require('@ackee/styleguide-backend-config/prettier') diff --git a/starter/_base/src/adapters/pino.logger.ts b/starter/_base/src/adapters/pino.logger.ts new file mode 100644 index 0000000..55d28f8 --- /dev/null +++ b/starter/_base/src/adapters/pino.logger.ts @@ -0,0 +1,44 @@ +import pinoLogger from 'pino' +import { LoggerFactoryPort } from '../domain/ports/logger.d.js' + +// https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#logseverity +const PinoLevelToSeverityLookup: Record = { + trace: 'DEBUG', + debug: 'DEBUG', + info: 'INFO', + warn: 'WARNING', + error: 'ERROR', + fatal: 'CRITICAL', +} as const + +const defaultPinoConf = (defaultLevel: string) => ({ + messageKey: 'message', + formatters: { + messageKey: 'message', + level: (label: string, num: number) => { + return { + severity: + PinoLevelToSeverityLookup[label] ?? + PinoLevelToSeverityLookup[defaultLevel], + level: num, + } + }, + }, +}) + +export const pinoLoggerFactory: LoggerFactoryPort = { + create: config => { + return pinoLogger({ + ...defaultPinoConf(config.defaultLevel), + transport: config.enablePrettyPrint + ? { + target: 'pino-pretty', + options: { + colorize: true, + }, + } + : undefined, + level: config.defaultLevel, + }) + }, +} diff --git a/starter/_base/src/config.ts b/starter/_base/src/config.ts new file mode 100644 index 0000000..2b3df52 --- /dev/null +++ b/starter/_base/src/config.ts @@ -0,0 +1,16 @@ +import { createLoader, maskedValues, values } from 'configuru' +import { Level } from 'pino' + +const loader = createLoader({ + defaultConfigPath: '.env.jsonc', +}) + +const configSchema = { + logger: { + defaultLevel: loader.custom(x => x as Level)('LOGGER_DEFAULT_LEVEL'), + pretty: loader.bool('LOGGER_PRETTY'), + }, +} + +export const config = values(configSchema) +export const safeConfig = maskedValues(configSchema) diff --git a/starter/_base/src/container.ts b/starter/_base/src/container.ts new file mode 100644 index 0000000..f7868c3 --- /dev/null +++ b/starter/_base/src/container.ts @@ -0,0 +1,17 @@ +import { config } from './config.js' +import { pinoLoggerFactory } from './adapters/pino.logger.js' +import { LoggerPort } from './domain/ports/logger.d.js' + +export interface Container { + logger: LoggerPort +} + +export type ContainerFactory = () => Promise + +export const createContainer: ContainerFactory = async () => { + const logger = pinoLoggerFactory.create(config.logger) + + return { + logger, + } +} diff --git a/starter/_base/src/context.ts b/starter/_base/src/context.ts new file mode 100644 index 0000000..1784f41 --- /dev/null +++ b/starter/_base/src/context.ts @@ -0,0 +1,39 @@ +import { Container } from './container.js' + +interface BaseContext { + container: Container + note?: string +} + +export interface ApiUserRequestContext extends BaseContext { + type: 'api-user' + user: null // Add UserContext if Auth is implemented +} + +export interface ServerRequestContext extends BaseContext { + type: 'server' +} + +/** + * Defines context in which current application runs. + * Context should be created with every external call (user request, cli input, ...) + */ +export type RequestContext = ApiUserRequestContext | ServerRequestContext + +/** + * Creator of the context for the App. Every API layer is responsible for + * creating the context based on the Api parameters (http headers, protocol settings etc...) + */ +export type RequestContextFactory = ( + container: Readonly, + ...params: Params +) => Promise + +/** + * Creator of the server context that should be used only in executions that are invoked + * on server, f.e. CLI, server start up etc. + */ +export type ServerRequestContextFactory = ( + container: Readonly, + ...params: Params +) => Promise diff --git a/starter/_base/src/domain/errors/codes.ts b/starter/_base/src/domain/errors/codes.ts new file mode 100644 index 0000000..c3eb641 --- /dev/null +++ b/starter/_base/src/domain/errors/codes.ts @@ -0,0 +1,9 @@ +export enum ErrorCode { + UNKNOWN = 0, + VALIDATION = 1000, +} + +export const errorMessages = { + [ErrorCode.UNKNOWN]: 'Unknown error', + [ErrorCode.VALIDATION]: 'Validation error', +} diff --git a/starter/_base/src/domain/errors/errors.ts b/starter/_base/src/domain/errors/errors.ts new file mode 100644 index 0000000..b722438 --- /dev/null +++ b/starter/_base/src/domain/errors/errors.ts @@ -0,0 +1,25 @@ +import { ErrorCode, errorMessages } from './codes.js' + +export class DomainError extends Error { + constructor( + public readonly code: ErrorCode, + public readonly message: string = errorMessages[code], + public readonly data?: AdditionalData + ) { + super(message) + } +} + +export type ValidationErrorData = + | Array<{ field: string; message: string }> + | { field: string; message: string } + +export class ValidationError extends DomainError { + constructor(errors: ValidationErrorData) { + super( + ErrorCode.VALIDATION, + errorMessages[ErrorCode.VALIDATION], + Array.isArray(errors) ? errors : [errors] + ) + } +} diff --git a/starter/_base/src/domain/ports/logger.d.ts b/starter/_base/src/domain/ports/logger.d.ts new file mode 100644 index 0000000..5ec9a85 --- /dev/null +++ b/starter/_base/src/domain/ports/logger.d.ts @@ -0,0 +1,21 @@ +export interface LoggerConfig { + defaultLevel: string + enablePrettyPrint?: boolean +} + +type BaseLoggerFn = (object: any, message?: string) => void + +export interface LoggerPort { + level: string + debug: BaseLoggerFn + info: BaseLoggerFn + warn: BaseLoggerFn + error: BaseLoggerFn + fatal: BaseLoggerFn + trace: BaseLoggerFn + silent: BaseLoggerFn +} + +export interface LoggerFactoryPort { + create: (config: LoggerConfig) => LoggerPort +} diff --git a/starter/_base/src/index.ts b/starter/_base/src/index.ts new file mode 100644 index 0000000..178937d --- /dev/null +++ b/starter/_base/src/index.ts @@ -0,0 +1,15 @@ +import { config } from './config.js' +import { createContainer } from './container.js' +import { RequestContext } from './context.js' + +const entrypoint = async () => { + const ctx: RequestContext = { + container: await createContainer(), + type: 'api-user', + user: null, + } + + ctx.container.logger.info('Hello World! 🎉') +} + +void entrypoint() diff --git a/starter/_base/src/test/setup.ts b/starter/_base/src/test/setup.ts new file mode 100644 index 0000000..472e99f --- /dev/null +++ b/starter/_base/src/test/setup.ts @@ -0,0 +1 @@ +// Add setup for tests (db connection, ...) if needed diff --git a/starter/_base/src/view/cli/README.md b/starter/_base/src/view/cli/README.md new file mode 100644 index 0000000..dc5c363 --- /dev/null +++ b/starter/_base/src/view/cli/README.md @@ -0,0 +1,13 @@ +# CLI + +Cli tool to operate scripts that can use internal services or to add development toolkit with the same interface and usage. Currently cli tool is operated using tsx tool. + +## 👷 Development + +The CLI tool is build around `yargs` library. Each command needs its own `ts` file in current folder or in subfolder. The entrypoint `Cli.ts` scans the sub folders for `ts` files and intrerprets them as a command under the module called same as the folder. Each command should export only functions and variables according to `CommandDefinition` in [cli.ts file](./cli.ts). + +For example, if you add `openapi` folder, it will creates a new module `openapi`. If you add a new command called `generate.ts`, it will also assign a new generate command to this module. + +## ⚠️ Usage in production scripts + +If the cli tool is used to run production scripts (CRON), make sure all dependencies are also listed in production list and build the code instead of using `tsx` (e.g. `yargs`). diff --git a/starter/_base/src/view/cli/cli.ts b/starter/_base/src/view/cli/cli.ts new file mode 100644 index 0000000..1b19e0d --- /dev/null +++ b/starter/_base/src/view/cli/cli.ts @@ -0,0 +1,94 @@ +import yargs, { Argv } from 'yargs' +import { hideBin } from 'yargs/helpers' +import { readdir, stat } from 'fs/promises' +import { join, extname } from 'path' + +interface CommandDefinition { + // Description of the command used for help + description: string + // Defines positional description of the command (e.g. '' will mean it takes one positional required argument) + positional?: string + // Function that will be called when the command is executed + run: (argv: any) => Promise | void + // Function that will be called to add options to the command + options?: (yargs: Argv) => Argv + // Any other properties that will be added to the command + [key: string]: any +} + +const loadCommand = async ( + commandName: string, + commandPath: string, + yargsInstance: Argv +) => { + try { + const modulePath = `file://${commandPath}` + const commandModule = (await import(modulePath)) as CommandDefinition + + if (typeof commandModule.run === 'function') { + yargsInstance.command( + commandModule.positional + ? `${commandName} ${commandModule.positional}` + : commandName, + commandModule.description ?? `${commandName} command`, + (yargs: Argv) => { + if (typeof commandModule.options === 'function') { + return commandModule.options(yargs) + } + }, + async (argv: any) => { + try { + await commandModule.run(argv) + } catch (error) { + console.error(`Error running ${commandName}:`, error) + process.exit(1) + } + } + ) + } else { + console.warn(`Warning: ${commandPath} does not export a 'run' function`) + } + } catch (error) { + console.error(`Error loading command from ${commandPath}:`, error) + } +} + +const loadCommandsFromDirectory = async ( + dirPath: string, + yargsInstance: Argv +): Promise => { + const items = await readdir(dirPath) + + for (const item of items) { + const fullPath = join(dirPath, item) + const stats = await stat(fullPath) + + if (stats.isDirectory()) { + yargsInstance.command(item, `${item} module`, (yargs: Argv) => { + yargs.demandCommand(1, 'You need to specify a command.') + yargs.help() + return loadCommandsFromDirectory(fullPath, yargs) + }) + } else if (stats.isFile() && extname(item) === '.ts' && item !== 'cli.ts') { + const commandName = item.replace('.ts', '') + await loadCommand(commandName, fullPath, yargsInstance) + } + } +} + +export const cli = async () => { + const yargsInstance = yargs(hideBin(process.argv)) + .scriptName('cli') + .usage('Usage: $0 [options]') + .demandCommand(1, 'You need to specify a command or module.') + .help() + .version() + + await loadCommandsFromDirectory(import.meta.dirname, yargsInstance) + + await yargsInstance.parse() + + process.exit(0) +} + +void cli() diff --git a/starter/_base/tsconfig.json b/starter/_base/tsconfig.json new file mode 100644 index 0000000..025b4dc --- /dev/null +++ b/starter/_base/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["src/**/*"], + "exclude": ["dangerfile.ts"], + "ts-node": { + "files": true + }, + "compilerOptions": { + "target": "ES2021", + "module": "node16", + "lib": ["ES2021", "DOM"], + "allowJs": true, + "incremental": true, + "checkJs": true, + "sourceMap": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "moduleResolution": "node16", + "useUnknownInCatchVariables": false, + "esModuleInterop": true + } +} From f94a4921f64fe235fec257ec7a8bc57e82e160d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:55:56 +0200 Subject: [PATCH 08/28] =?UTF-8?q?=E2=9C=A8=20Add=20REST=20API=20starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/api/rest/.env.jsonc | 6 + starter/api/rest/.eslintrc.cjs | 8 + starter/api/rest/node-app.jsonc | 5 + starter/api/rest/package.json | 25 ++ starter/api/rest/src/config.ts | 17 + starter/api/rest/src/container.ts | 13 + .../rest/src/domain/health-check.service.ts | 15 + starter/api/rest/src/index.ts | 17 + .../api/rest/src/test/health-check.test.ts | 23 ++ .../rest/src/test/util/openapi-test.util.ts | 71 ++++ .../api/rest/src/view/cli/openapi/generate.ts | 64 ++++ .../controllers/health-check.controller.ts | 33 ++ .../rest/middleware/context-middleware.ts | 28 ++ .../src/view/rest/middleware/error-handler.ts | 60 ++++ .../view/rest/middleware/request-logger.ts | 37 +++ starter/api/rest/src/view/rest/request.d.ts | 9 + starter/api/rest/src/view/rest/routes.ts | 15 + .../api/rest/src/view/rest/spec/openapi.yml | 65 ++++ .../rest/src/view/rest/util/openapi.util.ts | 310 ++++++++++++++++++ starter/api/rest/src/view/server.ts | 27 ++ 20 files changed, 848 insertions(+) create mode 100644 starter/api/rest/.env.jsonc create mode 100644 starter/api/rest/.eslintrc.cjs create mode 100644 starter/api/rest/node-app.jsonc create mode 100644 starter/api/rest/package.json create mode 100644 starter/api/rest/src/config.ts create mode 100644 starter/api/rest/src/container.ts create mode 100644 starter/api/rest/src/domain/health-check.service.ts create mode 100644 starter/api/rest/src/index.ts create mode 100644 starter/api/rest/src/test/health-check.test.ts create mode 100644 starter/api/rest/src/test/util/openapi-test.util.ts create mode 100644 starter/api/rest/src/view/cli/openapi/generate.ts create mode 100644 starter/api/rest/src/view/rest/controllers/health-check.controller.ts create mode 100644 starter/api/rest/src/view/rest/middleware/context-middleware.ts create mode 100644 starter/api/rest/src/view/rest/middleware/error-handler.ts create mode 100644 starter/api/rest/src/view/rest/middleware/request-logger.ts create mode 100644 starter/api/rest/src/view/rest/request.d.ts create mode 100644 starter/api/rest/src/view/rest/routes.ts create mode 100644 starter/api/rest/src/view/rest/spec/openapi.yml create mode 100644 starter/api/rest/src/view/rest/util/openapi.util.ts create mode 100644 starter/api/rest/src/view/server.ts diff --git a/starter/api/rest/.env.jsonc b/starter/api/rest/.env.jsonc new file mode 100644 index 0000000..ec0b4a3 --- /dev/null +++ b/starter/api/rest/.env.jsonc @@ -0,0 +1,6 @@ +{ + // API server listening port. + "SERVER_PORT": 3000, + // Boolean to remove sensitive info from http error responses + "ENABLE_PRODUCTION_HTTP_ERROR_RESPONSES": false +} diff --git a/starter/api/rest/.eslintrc.cjs b/starter/api/rest/.eslintrc.cjs new file mode 100644 index 0000000..da2d49f --- /dev/null +++ b/starter/api/rest/.eslintrc.cjs @@ -0,0 +1,8 @@ +module.exports = { + ...require('@ackee/styleguide-backend-config/eslint'), + root: true, + ignorePatterns: ['dist', 'src/openapi', 'docs', 'knexfile.ts'], + parserOptions: { + project: '.eslint.tsconfig.json', + }, +} diff --git a/starter/api/rest/node-app.jsonc b/starter/api/rest/node-app.jsonc new file mode 100644 index 0000000..bb5c73c --- /dev/null +++ b/starter/api/rest/node-app.jsonc @@ -0,0 +1,5 @@ +{ + "module": "API", + "name": "RESTful", + "prebuild": ["generate:api"] +} diff --git a/starter/api/rest/package.json b/starter/api/rest/package.json new file mode 100644 index 0000000..7c45b07 --- /dev/null +++ b/starter/api/rest/package.json @@ -0,0 +1,25 @@ +{ + "name": "app-api-rest", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "generate:api": "npm run cli openapi generate src/view/rest/spec/openapi.yml && npm run codestyle" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "express": "^5.0.0", + "node-healthz": "^2.0.0", + "pino-http": "^10.0.0" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/supertest": "^6.0.0", + "openapi-typescript": "^7.0.0", + "supertest": "^7.0.0", + "yaml": "^2.0.0" + }, + "type": "module" +} diff --git a/starter/api/rest/src/config.ts b/starter/api/rest/src/config.ts new file mode 100644 index 0000000..cea6879 --- /dev/null +++ b/starter/api/rest/src/config.ts @@ -0,0 +1,17 @@ +import { createLoader, maskedValues, values } from 'configuru' + +const loader = createLoader({ + defaultConfigPath: '.env.jsonc', +}) + +const configSchema = { + server: { + port: loader.number('SERVER_PORT'), + enableProductionHttpErrorResponses: loader.bool( + 'ENABLE_PRODUCTION_HTTP_ERROR_RESPONSES' + ), + }, +} + +export const config = values(configSchema) +export const safeConfig = maskedValues(configSchema) diff --git a/starter/api/rest/src/container.ts b/starter/api/rest/src/container.ts new file mode 100644 index 0000000..ce48ceb --- /dev/null +++ b/starter/api/rest/src/container.ts @@ -0,0 +1,13 @@ +import { healthCheckService } from './domain/health-check.service.js' + +export interface Container { + healthCheckService: typeof healthCheckService +} + +export type ContainerFactory = () => Promise + +export const createContainer: ContainerFactory = async () => { + return { + healthCheckService, + } +} diff --git a/starter/api/rest/src/domain/health-check.service.ts b/starter/api/rest/src/domain/health-check.service.ts new file mode 100644 index 0000000..ab06c7e --- /dev/null +++ b/starter/api/rest/src/domain/health-check.service.ts @@ -0,0 +1,15 @@ +import * as healthz from 'node-healthz' + +export const healthCheckService = { + check: async (): Promise => { + return await healthz.check({ + checks: [ + { + id: 'Server', + required: true, + fn: () => Promise.resolve('OK'), + }, + ], + }) + }, +} diff --git a/starter/api/rest/src/index.ts b/starter/api/rest/src/index.ts new file mode 100644 index 0000000..44fdd5a --- /dev/null +++ b/starter/api/rest/src/index.ts @@ -0,0 +1,17 @@ +import { config } from './config.js' +import { createServer } from './view/server.js' +import { pinoLoggerFactory } from './adapters/pino.logger.js' +import { createContainer } from './container.js' + +const logger = pinoLoggerFactory.create(config.logger) + +const server = await createServer(createContainer) + +server.listen(config.server.port, () => { + logger.info( + { port: config.server.port }, + `🚀 Server is running on port ${config.server.port}` + ) +}) + +export default server diff --git a/starter/api/rest/src/test/health-check.test.ts b/starter/api/rest/src/test/health-check.test.ts new file mode 100644 index 0000000..3c5dd5f --- /dev/null +++ b/starter/api/rest/src/test/health-check.test.ts @@ -0,0 +1,23 @@ +import assert from 'node:assert' +import { describe, it, before } from 'mocha' +import { createServer } from '../view/server.js' +import { createContainer } from '../container.js' +import { requestTyped } from './util/openapi-test.util.js' + +describe('Health Check API', () => { + let server: Awaited> + + before(async () => { + server = await createServer(createContainer) + }) + + describe('GET /api/v1/healthz', () => { + it('should return successful health check response', async () => { + const response = await requestTyped(server, '/api/v1/healthz', 'get') + .sendTyped() + .expect(200) + .responseTyped() + assert.equal(response.body.status, 0) + }) + }) +}) diff --git a/starter/api/rest/src/test/util/openapi-test.util.ts b/starter/api/rest/src/test/util/openapi-test.util.ts new file mode 100644 index 0000000..f9d5f25 --- /dev/null +++ b/starter/api/rest/src/test/util/openapi-test.util.ts @@ -0,0 +1,71 @@ +import supertest from 'supertest' +import { + OpenApiRoutePathParam, + OpenApiRouteQueryParam, + OpenApiRouteRequestBody, + OpenApiRouteResponseBody, +} from '../../view/rest/util/openapi.util.js' +import * as openapi from '../../view/rest/spec/openapi.js' +import type { Express } from 'express' + +export const request = (server: Express) => supertest(server) + +type KeysOfUnion = T extends T ? keyof T : never + +export type Response = Omit< + Awaited['get']>>, + 'body' +> & { body: TBody } + +export const requestTyped = < + Resource extends KeysOfUnion, + Method extends Exclude, 'parameters'>, + Resp extends OpenApiRouteResponseBody, + Openapi extends openapi.paths = openapi.paths, +>( + server: Express, + resource: Resource, + method: Method, + routeParams?: OpenApiRoutePathParam, + queryParams?: OpenApiRouteQueryParam +) => { + const replacedResource = Object.keys( + (routeParams ?? {}) as Record + ).reduce((resource, param) => { + return resource.replace( + // eslint-disable-next-line security/detect-non-literal-regexp + new RegExp(`{${param}}`, 'g'), + (routeParams as Record)[param] + ) + }, resource) + + const url = new URL('https://ackee.cz') + Object.entries(queryParams ?? {}).forEach(([key, val]) => { + if (val !== undefined && val !== null) { + url.searchParams.set(key, val.toString()) + } + }) + + const req = request(server)[method]( + replacedResource + '?' + url.searchParams.toString() + ) + const originalSend = req.send.bind(req) + const enhanced = Object.assign(req, { + sendTyped( + body?: OpenApiRouteRequestBody, + headers?: Record + ) { + if (headers) { + Object.entries(headers).forEach(([key, value]) => { + req.set(key, value) + }) + } + originalSend(body as any) + return enhanced + }, + responseTyped: () => { + return req as Promise> + }, + }) + return enhanced +} diff --git a/starter/api/rest/src/view/cli/openapi/generate.ts b/starter/api/rest/src/view/cli/openapi/generate.ts new file mode 100644 index 0000000..d59bbaf --- /dev/null +++ b/starter/api/rest/src/view/cli/openapi/generate.ts @@ -0,0 +1,64 @@ +import { spawn } from 'node:child_process' +import { readFileSync, writeFileSync } from 'node:fs' +import path from 'node:path' +import yaml from 'yaml' + +export const description = 'Generate OpenAPI types and validate the spec' +export const positional = '' + +export const run = async (argv: any): Promise => { + console.log('Generating OpenAPI types and validate the spec...') + const yamlFilePath = argv.filepath + const tsFilePath = path.join( + path.dirname(argv.filepath), + `${path.basename(argv.filepath, path.extname(argv.filepath))}.ts` + ) + + await new Promise((resolve, reject) => { + const process = spawn( + 'npx', + ['openapi-typescript', yamlFilePath, '--output', tsFilePath], + { stdio: 'inherit' } + ) + process.on('exit', resolve) + process.on('error', reject) + }) + + console.log('Generating path mappings...') + + const types = readFileSync(tsFilePath) + const spec = yaml.parse(readFileSync(yamlFilePath).toString()) + + const pathsWithOperationIds = Object.keys(spec.paths).reduce((acc, path) => { + const pathSpec = spec.paths[path] + Object.keys(pathSpec).forEach(method => { + if (pathSpec[method]?.operationId) { + acc[pathSpec[method].operationId] = { + method, + path, + } + } + }) + return acc + }, {} as any) + + const finalSpec = + `/* eslint-disable sonarjs/no-duplicate-string */\n` + + `/* eslint-disable sonarjs/use-type-alias */\n` + + types.toString('utf8').replaceAll('requestBody?:', 'requestBody:') + + `export const operationPaths = ${JSON.stringify( + pathsWithOperationIds + )} as const\n` + + writeFileSync(tsFilePath, finalSpec) + + console.log('Done 🎉') +} + +export const options = (yargs: any) => { + return yargs.positional('filepath', { + required: true, + type: 'string', + description: 'Input OpenAPI specification file', + }) +} diff --git a/starter/api/rest/src/view/rest/controllers/health-check.controller.ts b/starter/api/rest/src/view/rest/controllers/health-check.controller.ts new file mode 100644 index 0000000..ca8553f --- /dev/null +++ b/starter/api/rest/src/view/rest/controllers/health-check.controller.ts @@ -0,0 +1,33 @@ +import { Result } from 'node-healthz' +import { components } from '../spec/openapi.js' +import { ctrl } from '../util/openapi.util.js' + +export enum Status { + Healthy, + Unhealthy, +} + +const mapHealthCheckToApi = ( + healthCheck: Result +): components['schemas']['HealthCheckResponse'] => { + return { + status: healthCheck.status, + checks: healthCheck.checks.map(check => ({ + id: check.id, + status: check.status, + output: String(check.output), + required: check.required, + latency: check.latency, + latencyStatus: check.latencyStatus, + })), + } +} + +export const healthCheckController = ctrl.createRestController({ + healthz: async ctx => { + const { healthCheckService } = ctx.container + + const healthCheck = await healthCheckService.check() + return mapHealthCheckToApi(healthCheck) + }, +}) diff --git a/starter/api/rest/src/view/rest/middleware/context-middleware.ts b/starter/api/rest/src/view/rest/middleware/context-middleware.ts new file mode 100644 index 0000000..72f4282 --- /dev/null +++ b/starter/api/rest/src/view/rest/middleware/context-middleware.ts @@ -0,0 +1,28 @@ +import { NextFunction, Request, Response } from 'express' +import { RequestContextFactory } from '../../../context.js' +import { Container } from '../../../container.js' + +const createContextFromHttpRequestFactory = + (): RequestContextFactory<[Request, Response]> => + async (container, _req, _res) => { + return { + type: 'api-user', + container, + user: null, // Implement Authentication if needed (Auth header, cookie, ...) + } + } + +export const createContextMiddleware = + (container: Container) => + async (req: Request, res: Response, next: NextFunction): Promise => { + try { + req.context = await createContextFromHttpRequestFactory()( + container, + req, + res + ) + next() + } catch (err) { + next(err) + } + } diff --git a/starter/api/rest/src/view/rest/middleware/error-handler.ts b/starter/api/rest/src/view/rest/middleware/error-handler.ts new file mode 100644 index 0000000..9a7c955 --- /dev/null +++ b/starter/api/rest/src/view/rest/middleware/error-handler.ts @@ -0,0 +1,60 @@ +import { ErrorCode } from '../../../domain/errors/codes.js' +import { DomainError } from '../../../domain/errors/errors.js' +import express from 'express' + +export interface ErrorHandlerConfig { + enableProductionHttpErrorResponses: boolean +} + +const ERROR_DOMAIN_CODE_TO_HTTP_STATUS: Record = { + [ErrorCode.UNKNOWN]: 500, + [ErrorCode.VALIDATION]: 422, +} + +const errorToProductionObject = (error: T) => { + if (error instanceof DomainError) { + return { + ...error.data, + code: error.code, + } + } + return {} +} + +const errorToDevObject = (error: T) => { + if (error instanceof DomainError) { + return { + ...error.data, + code: error.code, + message: error.message, + stack: error.stack, + } + } + return { + message: error.message, + stack: error.stack, + } +} + +export function createErrorHandler( + config: ErrorHandlerConfig +): express.ErrorRequestHandler { + return (error, _req, res, _next) => { + const statusCode = + error instanceof DomainError + ? (ERROR_DOMAIN_CODE_TO_HTTP_STATUS[error.code] ?? 500) + : 500 + + ;(res as any).error = errorToDevObject(error) + + const mappedError = config.enableProductionHttpErrorResponses + ? errorToProductionObject(error) + : errorToDevObject(error) + + res.status(statusCode) + if (mappedError) { + ;(res as any).out = mappedError + res.json(mappedError) + } + } +} diff --git a/starter/api/rest/src/view/rest/middleware/request-logger.ts b/starter/api/rest/src/view/rest/middleware/request-logger.ts new file mode 100644 index 0000000..cf3531b --- /dev/null +++ b/starter/api/rest/src/view/rest/middleware/request-logger.ts @@ -0,0 +1,37 @@ +import { pinoHttp, Options } from 'pino-http' +import { LoggerPort } from '../../../domain/ports/logger.d.js' + +export function createRequestLogger(innerLogger: LoggerPort) { + const options: Options = { + logger: innerLogger as any, + serializers: { + res: res => { + res.error = res.raw.error + res.out = res.raw.out + return res + }, + req: req => { + req.body = req.raw.body + return req + }, + }, + customReceivedMessage: (req, _res) => + `--- ${String(req.method)} ${String(req.url)} - Request accepted`, + customSuccessMessage: (req, res) => + `${res.statusCode} ${String(req.method)} ${String( + req.url + )} - Standard output`, + customErrorMessage: (req, res, _error) => + `${res.statusCode} ${String(req.method)} ${String(req.url)} - ${ + res.statusMessage + }`, + customErrorObject: (_req, res, _error, val) => ({ + ...val, + error: (res as any).error, + }), + customAttributeKeys: { + err: 'error', + }, + } + return pinoHttp(options) +} diff --git a/starter/api/rest/src/view/rest/request.d.ts b/starter/api/rest/src/view/rest/request.d.ts new file mode 100644 index 0000000..fbeec0a --- /dev/null +++ b/starter/api/rest/src/view/rest/request.d.ts @@ -0,0 +1,9 @@ +import { RequestContext } from '../../context.js' + +declare global { + namespace Express { + export interface Request { + context: RequestContext + } + } +} diff --git a/starter/api/rest/src/view/rest/routes.ts b/starter/api/rest/src/view/rest/routes.ts new file mode 100644 index 0000000..a1a88b4 --- /dev/null +++ b/starter/api/rest/src/view/rest/routes.ts @@ -0,0 +1,15 @@ +import express from 'express' +import { openApiRouter, registerOpenApiRoutes } from './util/openapi.util.js' +import { healthCheckController } from './controllers/health-check.controller.js' + +const apiRouter = openApiRouter(express.Router(), { + removePrefix: '/api/v1', +}) + +registerOpenApiRoutes(apiRouter, { + ...healthCheckController, +}) + +export const routes = { + api: apiRouter.express, +} diff --git a/starter/api/rest/src/view/rest/spec/openapi.yml b/starter/api/rest/src/view/rest/spec/openapi.yml new file mode 100644 index 0000000..400e43a --- /dev/null +++ b/starter/api/rest/src/view/rest/spec/openapi.yml @@ -0,0 +1,65 @@ +openapi: '3.0.3' +info: + title: Node App API + version: '0.0.1' + +paths: + /api/v1/healthz: + get: + operationId: healthz + summary: Health check + tags: + - healthz + responses: + '200': + description: Health check response + content: + application/json: + schema: + $ref: '#/components/schemas/HealthCheckResponse' + +components: + schemas: + HealthCheckResponse: + type: object + properties: + checks: + type: array + description: List of component health checks + items: + $ref: '#/components/schemas/HealthCheck' + status: + type: integer + description: Overall status (0=healthy, 1=error) + enum: [0, 1] + required: + - checks + - status + HealthCheck: + type: object + properties: + id: + type: string + description: Unique identifier of the checked component + example: 'postgres' + latency: + type: integer + description: Response time in milliseconds + example: 1 + latencyStatus: + type: integer + description: Status of the latency check (0=good, 1=error, 2=timeout) + enum: [0, 1, 2] + example: 0 + output: + type: string + description: Additional output information + example: '' + required: + type: boolean + description: Whether this component is required for the system to function + example: true + status: + type: integer + description: Status of the component (0=Low, 1=Medium, 2=High) + enum: [0, 1, 2] \ No newline at end of file diff --git a/starter/api/rest/src/view/rest/util/openapi.util.ts b/starter/api/rest/src/view/rest/util/openapi.util.ts new file mode 100644 index 0000000..f8efc7e --- /dev/null +++ b/starter/api/rest/src/view/rest/util/openapi.util.ts @@ -0,0 +1,310 @@ +import express from 'express' +import bodyParser from 'body-parser' +import { operations, paths, operationPaths } from '../spec/openapi.js' +import { RequestContext } from '../../../context.js' + +export type OpenApiRouteResponseBodyMethod< + T, + TMethod extends string = 'get', + TDefault = unknown, +> = T extends { + [key in TMethod]: { + responses: { 200: { content: { 'application/json': infer U } } } + } +} + ? U + : TDefault + +export type OpenApiRouteResponseBody< + T, + TMethod extends string = 'get', +> = OpenApiRouteResponseBodyMethod< + T, + TMethod, + OpenApiRouteResponseBodyMethod< + T, + 'post', + OpenApiRouteResponseBodyMethod< + T, + 'put', + OpenApiRouteResponseBodyMethod + > + > +> + +type LowercaseKeys> = { + [key in keyof T as key extends string ? Lowercase : key]: T[key] +} + +export type OpenApiRoutePathParam = T extends { + get: { parameters: { path: infer U } } +} + ? U + : T extends { post: { parameters: { path: infer U } } } + ? U + : T extends { put: { parameters: { path: infer U } } } + ? U + : unknown + +export type OpenApiRouteQueryParam = T extends { + get: { parameters: { query: infer U } } +} + ? U + : T extends { post: { parameters: { query: infer U } } } + ? U + : unknown + +export type OpenApiRouteHeaderParam = T extends { + get: { + parameters: { + header: infer U extends Record + } + } +} + ? LowercaseKeys + : T extends { + post: { + parameters: { + header: infer U extends Record + } + } + } + ? LowercaseKeys + : unknown + +export type OpenApiRouteParam = OpenApiRoutePathParam & + OpenApiRouteQueryParam & + OpenApiRouteHeaderParam + +export type OpenApiRouteRequestBodyMethod< + T, + TMethod extends string = 'post', + TDefault = unknown, +> = T extends { + [key in TMethod]: { + requestBody: { + content: + | { 'application/json': infer U } + | { 'multipart/form-data': infer U } + } + } +} + ? U + : TDefault + +export type OpenApiRouteRequestBody< + T, + TMethod extends string = 'post', +> = OpenApiRouteRequestBodyMethod< + T, + TMethod, + OpenApiRouteRequestBodyMethod< + T, + 'put', + OpenApiRouteRequestBodyMethod< + T, + 'delete', + OpenApiRouteRequestBodyMethod + > + > +> + +/** + * pipeMiddleware takes multiple middlewares and creates and merges them into + * one using express Router. + */ +export const pipeMiddleware = (...middlewares: express.RequestHandler[]) => { + const router = express.Router({ mergeParams: true }) + middlewares.forEach(m => router.use(m)) + return router +} + +export type ApiHandler = ( + ctx: Readonly, + req: express.Request, + res: express.Response +) => TRes | Promise + +const asyncHandler = + (fn: ApiHandler): express.Handler => + async (req, res, next) => { + try { + const result = await fn(req.context, req, res) + res.json(result) + } catch (error: unknown) { + next(error) + } + } + +export type OperationIds = keyof operations + +type ApiMimeTypes = string + +type Content = { + content: any +} + +type MimeContent = { + content: { [key in MimeType]?: any } +} + +type MimeContentValue< + MimeType extends ApiMimeTypes, + T extends MimeContent, +> = { + [K in keyof T['content']]: T['content'][K] +}[keyof T['content']] + +type OpenApiContentTypes> = { + [K in keyof OpenApiContent]: OpenApiContent[K] extends Content + ? MimeContentValue + : never +}[keyof OpenApiContent] + +export type OperationParams = + operations[OperationId]['parameters']['path'] + +export type OperationQuery = + operations[OperationId]['parameters']['query'] + +export type OperationResponse = + OpenApiContentTypes + +export type OperationBody = + operations[OperationId] extends never + ? never + : MimeContentValue + +export type OpenApiHandler = + express.RequestHandler< + OperationParams, + OperationResponse, + OperationBody, + OperationQuery + > + +export type OperationHandler = ( + ctx: RequestContext, + req: Parameters>[0], + res: Parameters>[1] +) => Promise> + +type RouteHandlers = { + [Key in SubsetOperationIds]: OperationHandler +} + +export type RestApiController< + SubsetOperationIds extends OperationIds = OperationIds, +> = { + [Key in SubsetOperationIds]: OpenApiHandler +} + +const handleOperationAsync = ( + fn: OperationHandler +): OpenApiHandler => asyncHandler(fn as any) as any + +const createRestController = ( + def: RouteHandlers +): RestApiController => { + return Object.entries(def).reduce((ctrl, [operationId, handler]) => { + if (!handler) { + return ctrl + } + ctrl[operationId as SubsetOperationIds] = handleOperationAsync( + handler as any + ) as any + return ctrl + }, {} as RestApiController) +} + +export const openApiRouter = ( + router: express.Router, + { removePrefix }: { removePrefix: string } = { removePrefix: '' } +) => ({ + express: router, + route: < + Method extends keyof paths[Path], + Path extends keyof paths, + OperationId extends OperationIds, + >( + method: Method, + path: Path, + handler: OpenApiHandler + ) => { + const route = path + .toString() + .replaceAll('}', '') + .replaceAll('{', ':') + // eslint-disable-next-line security/detect-non-literal-regexp + .replace(new RegExp(`^${removePrefix}`), '') + + switch (method) { + case 'get': + router.get(route, handler) + break + case 'post': + router.post(route, handler) + break + case 'patch': + router.patch(route, handler) + break + case 'delete': + router.delete(route, handler) + break + case 'head': + router.head(route, handler) + break + case 'trace': + router.trace(route, handler) + break + case 'options': + router.options(route, handler) + break + default: + throw new Error( + `The OpenApi router received invalid HTTP method to be registered: ${method.toString()}` + ) + } + }, +}) + +export const registerOpenApiRoutes = ( + router: ReturnType, + controller: Partial> +) => { + const operations = Object.keys(controller) as SomeOperationIds[] + operations.forEach(operation => { + if (!controller[operation]) { + return + } + + router.route( + operationPaths[operation].method, + operationPaths[operation].path, + controller[operation] + ) + }) +} + +/** + * ctrl is a scoped object for controller functions + */ +export const ctrl = { + json: pipeMiddleware( + bodyParser.json(), + // Monkeypatch res.json to assign the body to res.out first in order + // to log it by pino + (_req, res: any, next) => { + const resJson = res.json.bind(res) + res.json = (body?: any) => { + res.out = body + return resJson(body) + } + next() + } + ), + asyncHandler, + createRestController, + openApiRouter, + registerOpenApiRoutes, +} diff --git a/starter/api/rest/src/view/server.ts b/starter/api/rest/src/view/server.ts new file mode 100644 index 0000000..7ab9540 --- /dev/null +++ b/starter/api/rest/src/view/server.ts @@ -0,0 +1,27 @@ +import express from 'express' +import { createRequestLogger } from './rest/middleware/request-logger.js' +import { config } from '../config.js' +import { createErrorHandler } from './rest/middleware/error-handler.js' +import { routes } from './rest/routes.js' +import { createContextMiddleware } from './rest/middleware/context-middleware.js' +import { ContainerFactory } from '../container.js' + +export const createServer = async (containerFactory: ContainerFactory) => { + const container = await containerFactory() + const { logger } = container + + const server = express() + + const errorHandler = createErrorHandler(config.server) + const requestLogger = createRequestLogger(logger) + const contextMiddleware = createContextMiddleware(container) + + server.disable('x-powered-by') + + server.use(requestLogger) + server.use(contextMiddleware) + server.use('/api/v1/', routes.api) + server.use(errorHandler) + + return server +} From e4b88b4258730102f5ff2be41a59516c70e5a338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:56:12 +0200 Subject: [PATCH 09/28] =?UTF-8?q?=E2=9C=A8=20Add=20GraphQL=20starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/api/graphql/.env.jsonc | 8 +++ starter/api/graphql/.eslint.tsconfig.json | 4 ++ starter/api/graphql/.eslintrc.cjs | 42 ++++++++++++++ starter/api/graphql/codegen.yml | 11 ++++ starter/api/graphql/node-app.jsonc | 5 ++ starter/api/graphql/package.json | 36 ++++++++++++ starter/api/graphql/src/config.ts | 17 ++++++ starter/api/graphql/src/index.ts | 11 ++++ .../api/graphql/src/test/helloWorld.test.ts | 34 ++++++++++++ starter/api/graphql/src/view/controller.ts | 42 ++++++++++++++ .../src/view/graphql/context-factory.ts | 13 +++++ .../api/graphql/src/view/graphql/resolvers.ts | 6 ++ .../graphql/resolvers/greeting.resolver.ts | 5 ++ .../api/graphql/src/view/graphql/schema.ts | 7 +++ .../src/view/graphql/schema/schema.graphql | 6 ++ starter/api/graphql/src/view/server.ts | 55 +++++++++++++++++++ 16 files changed, 302 insertions(+) create mode 100644 starter/api/graphql/.env.jsonc create mode 100644 starter/api/graphql/.eslint.tsconfig.json create mode 100644 starter/api/graphql/.eslintrc.cjs create mode 100644 starter/api/graphql/codegen.yml create mode 100644 starter/api/graphql/node-app.jsonc create mode 100644 starter/api/graphql/package.json create mode 100644 starter/api/graphql/src/config.ts create mode 100644 starter/api/graphql/src/index.ts create mode 100644 starter/api/graphql/src/test/helloWorld.test.ts create mode 100644 starter/api/graphql/src/view/controller.ts create mode 100644 starter/api/graphql/src/view/graphql/context-factory.ts create mode 100644 starter/api/graphql/src/view/graphql/resolvers.ts create mode 100644 starter/api/graphql/src/view/graphql/resolvers/greeting.resolver.ts create mode 100644 starter/api/graphql/src/view/graphql/schema.ts create mode 100644 starter/api/graphql/src/view/graphql/schema/schema.graphql create mode 100644 starter/api/graphql/src/view/server.ts diff --git a/starter/api/graphql/.env.jsonc b/starter/api/graphql/.env.jsonc new file mode 100644 index 0000000..f4d4de8 --- /dev/null +++ b/starter/api/graphql/.env.jsonc @@ -0,0 +1,8 @@ +{ + // Server will run on this port + "SERVER_PORT": 3000, + // Response development errors for debugging + "SERVER_ALLOW_RESPONSE_ERRORS": false, + // Enable GraphQL introspection + "SERVER_ENABLE_INTROSPECTION": true +} diff --git a/starter/api/graphql/.eslint.tsconfig.json b/starter/api/graphql/.eslint.tsconfig.json new file mode 100644 index 0000000..4af9741 --- /dev/null +++ b/starter/api/graphql/.eslint.tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["src/generated/**/*"] +} diff --git a/starter/api/graphql/.eslintrc.cjs b/starter/api/graphql/.eslintrc.cjs new file mode 100644 index 0000000..e2e6f9b --- /dev/null +++ b/starter/api/graphql/.eslintrc.cjs @@ -0,0 +1,42 @@ +const { omit } = require('lodash') +const defaultConfig = { + ...require('@ackee/styleguide-backend-config/eslint'), +} + +module.exports = { + root: true, + ignorePatterns: ['dist', 'src/openapi', 'docs', 'knexfile.ts'], + overrides: [ + { + ...omit(defaultConfig, ['ignorePatterns']), + files: ['*.ts', '*.js'], + parserOptions: { + project: '.eslint.tsconfig.json', + }, + rules: { + ...defaultConfig.rules, + '@typescript-eslint/no-empty-object-type': 0, + } + }, + { + files: ['**/*.graphql'], + extends: 'plugin:@graphql-eslint/schema-recommended', + parserOptions: { + graphQLConfig: { + schema: './src/view/graphql/schema/*.graphql', + }, + }, + rules: { + '@graphql-eslint/strict-id-in-types': 0, + '@graphql-eslint/require-description': [ + 'warn', + { + types: true, + DirectiveDefinition: true, + }, + ], + '@graphql-eslint/no-unreachable-types': 'warn', + }, + }, + ], +} diff --git a/starter/api/graphql/codegen.yml b/starter/api/graphql/codegen.yml new file mode 100644 index 0000000..a9d7de6 --- /dev/null +++ b/starter/api/graphql/codegen.yml @@ -0,0 +1,11 @@ +schema: './src/view/graphql/schema/schema.graphql' +overwrite: true +generates: + src/generated/graphql.ts: + config: + useIndexSignature: true + scalars: + mappers: + plugins: + - 'typescript' + - 'typescript-resolvers' diff --git a/starter/api/graphql/node-app.jsonc b/starter/api/graphql/node-app.jsonc new file mode 100644 index 0000000..212590f --- /dev/null +++ b/starter/api/graphql/node-app.jsonc @@ -0,0 +1,5 @@ +{ + "module": "API", + "name": "GraphQL", + "prebuild": ["generate:api"] +} diff --git a/starter/api/graphql/package.json b/starter/api/graphql/package.json new file mode 100644 index 0000000..fa92484 --- /dev/null +++ b/starter/api/graphql/package.json @@ -0,0 +1,36 @@ +{ + "name": "app-api-graphql", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "generate:api": "graphql-codegen --config codegen.yml && npm run codestyle", + "build:copy-schema": "mkdir -p ./dist/view/graphql/schema && cp -r ./src/view/graphql/schema ./dist/view/graphql/schema", + "build": "npm run build:copy-schema && tsc" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@as-integrations/express5": "^1.1.2", + "@apollo/server": "^4.1.0", + "@graphql-tools/load-files": "^7.0.0", + "@graphql-tools/merge": "^9.0.0", + "@graphql-tools/schema": "^10.0.0", + "lodash-es": "^4.17.21", + "express": "^5.0.0", + "graphql": "^16.0.0", + "node-healthz": "^2.0.0", + "pino-http": "^10.0.0" + }, + "devDependencies": { + "@graphql-codegen/cli": "^5.0.0", + "@graphql-codegen/typescript": "^4.0.0", + "@graphql-codegen/typescript-resolvers": "^4.0.0", + "@graphql-eslint/eslint-plugin": "^4.4.0", + "@types/express": "^5.0.0", + "@types/supertest": "^6.0.0", + "supertest": "^7.0.0" + }, + "type": "module" +} diff --git a/starter/api/graphql/src/config.ts b/starter/api/graphql/src/config.ts new file mode 100644 index 0000000..25abbea --- /dev/null +++ b/starter/api/graphql/src/config.ts @@ -0,0 +1,17 @@ +import { createLoader, maskedValues, values } from 'configuru' +import { Level } from 'pino' + +const loader = createLoader({ + defaultConfigPath: '.env.jsonc', +}) + +const configSchema = { + server: { + port: loader.number('SERVER_PORT'), + allowResponseErrors: loader.bool('SERVER_ALLOW_RESPONSE_ERRORS'), + enableIntrospection: loader.bool('SERVER_ENABLE_INTROSPECTION'), + }, +} + +export const config = values(configSchema) +export const safeConfig = maskedValues(configSchema) diff --git a/starter/api/graphql/src/index.ts b/starter/api/graphql/src/index.ts new file mode 100644 index 0000000..e6f8416 --- /dev/null +++ b/starter/api/graphql/src/index.ts @@ -0,0 +1,11 @@ +import { config, safeConfig } from './config.js' +import { createAppServer, startServer } from './view/server.js' +import { pinoLoggerFactory } from './adapters/pino.logger.js' +import { createContainer } from './container.js' + +const logger = pinoLoggerFactory.create(config.logger) + +logger.info({ config: safeConfig }, 'Loaded config') + +const appServer = createAppServer() +void startServer({ ...appServer, logger, containerFactory: createContainer }) diff --git a/starter/api/graphql/src/test/helloWorld.test.ts b/starter/api/graphql/src/test/helloWorld.test.ts new file mode 100644 index 0000000..04ca464 --- /dev/null +++ b/starter/api/graphql/src/test/helloWorld.test.ts @@ -0,0 +1,34 @@ +import assert from 'node:assert' +import { describe, before, test } from 'mocha' +import { createAppServer } from '../view/server.js' +import { gql } from 'graphql-tag' +import { ApolloServer } from '@apollo/server' + +describe('Hello world', () => { + let server: ApolloServer + + before(async () => { + const { server: appServer } = createAppServer() + server = appServer + }) + + after(async () => { + await server.stop() + }) + + test('should return greeting', async () => { + const query = gql` + query Hello { + greeting + } + ` + const res = await server.executeOperation({ query }) + + assert(res.body.kind === 'single') + assert.deepStrictEqual(res.body.singleResult.errors, undefined) + assert.deepStrictEqual( + res.body.singleResult.data?.greeting, + 'Hello, world! 🎉' + ) + }) +}) diff --git a/starter/api/graphql/src/view/controller.ts b/starter/api/graphql/src/view/controller.ts new file mode 100644 index 0000000..a7edd4e --- /dev/null +++ b/starter/api/graphql/src/view/controller.ts @@ -0,0 +1,42 @@ +import express from 'express' +import { Handler, RequestHandler } from 'express' + +/** + * pipeMiddleware takes multiple middlewares and creates and merges them into + * one using express Router. + */ +export const pipeMiddleware = (...middlewares: RequestHandler[]) => { + const router = express.Router({ mergeParams: true }) + middlewares.forEach(m => router.use(m)) + return router +} + +const asyncHandler = + (controllerHandler: Handler): Handler => + async (req, res, next) => { + try { + await controllerHandler(req, res, next) + } catch (err) { + next(err) + } + } + +/** + * ctrl is a scoped object for controller functions + */ +export const ctrl = { + json: pipeMiddleware( + express.json(), + // Monkeypatch res.json to assign the body to res.out first in order + // to log it by pino + (_req, res, next) => { + const resJson = res.json.bind(res) + res.json = (body?: any) => { + ;(res as any).out = body + return resJson(body) + } + next() + } + ), + asyncHandler, +} diff --git a/starter/api/graphql/src/view/graphql/context-factory.ts b/starter/api/graphql/src/view/graphql/context-factory.ts new file mode 100644 index 0000000..b0fe7e4 --- /dev/null +++ b/starter/api/graphql/src/view/graphql/context-factory.ts @@ -0,0 +1,13 @@ +import { RequestContext } from '../../context.js' +import { ContainerFactory } from '../../container.js' + +export const contextFactory = async ( + containerFactory: ContainerFactory +): Promise => { + const container = await containerFactory() + return { + type: 'api-user', + container, + user: null, // Add UserContext if Auth is implemented + } +} diff --git a/starter/api/graphql/src/view/graphql/resolvers.ts b/starter/api/graphql/src/view/graphql/resolvers.ts new file mode 100644 index 0000000..d84414c --- /dev/null +++ b/starter/api/graphql/src/view/graphql/resolvers.ts @@ -0,0 +1,6 @@ +import { Resolvers } from '../../generated/graphql.js' +import { greetingQueryResolver } from './resolvers/greeting.resolver.js' + +export const resolvers: Resolvers = { + Query: greetingQueryResolver, +} diff --git a/starter/api/graphql/src/view/graphql/resolvers/greeting.resolver.ts b/starter/api/graphql/src/view/graphql/resolvers/greeting.resolver.ts new file mode 100644 index 0000000..9f2ed08 --- /dev/null +++ b/starter/api/graphql/src/view/graphql/resolvers/greeting.resolver.ts @@ -0,0 +1,5 @@ +import { QueryResolvers } from '../../../generated/graphql.js' + +export const greetingQueryResolver: QueryResolvers = { + greeting: () => 'Hello, world! 🎉', +} diff --git a/starter/api/graphql/src/view/graphql/schema.ts b/starter/api/graphql/src/view/graphql/schema.ts new file mode 100644 index 0000000..509355a --- /dev/null +++ b/starter/api/graphql/src/view/graphql/schema.ts @@ -0,0 +1,7 @@ +import { loadFilesSync } from '@graphql-tools/load-files' +import { mergeTypeDefs } from '@graphql-tools/merge' +import path from 'path' + +export const schema = [ + mergeTypeDefs(loadFilesSync(path.join(import.meta.dirname, 'schema'))), +] diff --git a/starter/api/graphql/src/view/graphql/schema/schema.graphql b/starter/api/graphql/src/view/graphql/schema/schema.graphql new file mode 100644 index 0000000..146669f --- /dev/null +++ b/starter/api/graphql/src/view/graphql/schema/schema.graphql @@ -0,0 +1,6 @@ +""" +All data queries +""" +type Query { + greeting: String! +} diff --git a/starter/api/graphql/src/view/server.ts b/starter/api/graphql/src/view/server.ts new file mode 100644 index 0000000..7433723 --- /dev/null +++ b/starter/api/graphql/src/view/server.ts @@ -0,0 +1,55 @@ +import { ApolloServer } from '@apollo/server' +import { makeExecutableSchema } from '@graphql-tools/schema' +import { resolvers } from './graphql/resolvers.js' +import { config } from '../config.js' +import { schema } from './graphql/schema.js' +import express from 'express' +import { ctrl } from './controller.js' +import { expressMiddleware } from '@as-integrations/express5' +import { LoggerPort } from '../domain/ports/logger.d.js' +import { ContainerFactory } from '../container.js' +import { contextFactory } from './graphql/context-factory.js' + +export const clientSchema = makeExecutableSchema({ + typeDefs: schema, + resolvers, +}) + +export const createAppServer = () => { + const app = express() + app.disable('x-powered-by') + + const server = new ApolloServer({ + schema: clientSchema, + includeStacktraceInErrorResponses: config.server.allowResponseErrors, + introspection: config.server.enableIntrospection, + }) + + return { app, server } +} + +export async function startServer({ + app, + server, + logger, + containerFactory, +}: ReturnType & { + logger: LoggerPort + containerFactory: ContainerFactory +}) { + await server.start() + + app.use( + '/api/graphql', + ctrl.json, + expressMiddleware(server, { + context: () => contextFactory(containerFactory), + }) + ) + + app.listen({ port: config.server.port }, () => + logger.info({}, `Server started, port=${config.server.port}`) + ) + + return { app, server } +} From 28b79195130d36a80ee0cbc1ac6298bac4e4dd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:56:27 +0200 Subject: [PATCH 10/28] =?UTF-8?q?=E2=9C=A8=20Add=20PostgreSQL=20starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/infra/postgresql-knex/.env.jsonc | 5 ++++ .../docker-compose-entrypoint.sh | 7 ++++++ .../docker-compose/docker-compose.ci.yml | 19 +++++++++++++++ .../docker-compose/docker-compose.local.yml | 5 ++++ .../docker-compose.override.yml | 1 + .../docker-compose/docker-compose.yml | 8 +++++++ starter/infra/postgresql-knex/knexfile.ts | 15 ++++++++++++ starter/infra/postgresql-knex/node-app.jsonc | 5 ++++ starter/infra/postgresql-knex/package.json | 13 ++++++++++ .../src/adapters/knex.database.test.ts | 20 ++++++++++++++++ .../src/adapters/knex.database.ts | 14 +++++++++++ .../repositories/migration.repository.ts | 24 +++++++++++++++++++ starter/infra/postgresql-knex/src/config.ts | 14 +++++++++++ .../infra/postgresql-knex/src/container.ts | 24 +++++++++++++++++++ .../src/domain/ports/database.d.ts | 4 ++++ .../repositories/migration.repository.d.ts | 9 +++++++ .../infra/postgresql-knex/src/test/setup.ts | 16 +++++++++++++ 17 files changed, 203 insertions(+) create mode 100644 starter/infra/postgresql-knex/.env.jsonc create mode 100755 starter/infra/postgresql-knex/docker-compose/docker-compose-entrypoint.sh create mode 100644 starter/infra/postgresql-knex/docker-compose/docker-compose.ci.yml create mode 100644 starter/infra/postgresql-knex/docker-compose/docker-compose.local.yml create mode 120000 starter/infra/postgresql-knex/docker-compose/docker-compose.override.yml create mode 100644 starter/infra/postgresql-knex/docker-compose/docker-compose.yml create mode 100644 starter/infra/postgresql-knex/knexfile.ts create mode 100644 starter/infra/postgresql-knex/node-app.jsonc create mode 100644 starter/infra/postgresql-knex/package.json create mode 100644 starter/infra/postgresql-knex/src/adapters/knex.database.test.ts create mode 100644 starter/infra/postgresql-knex/src/adapters/knex.database.ts create mode 100644 starter/infra/postgresql-knex/src/adapters/repositories/migration.repository.ts create mode 100644 starter/infra/postgresql-knex/src/config.ts create mode 100644 starter/infra/postgresql-knex/src/container.ts create mode 100644 starter/infra/postgresql-knex/src/domain/ports/database.d.ts create mode 100644 starter/infra/postgresql-knex/src/domain/ports/repositories/migration.repository.d.ts create mode 100644 starter/infra/postgresql-knex/src/test/setup.ts diff --git a/starter/infra/postgresql-knex/.env.jsonc b/starter/infra/postgresql-knex/.env.jsonc new file mode 100644 index 0000000..99319ec --- /dev/null +++ b/starter/infra/postgresql-knex/.env.jsonc @@ -0,0 +1,5 @@ +{ + // DATABASE + /// Application PostgreSQL database connection props + "DB_CONNECTION_STRING": "postgres://{{PROJECT_NAME}}_docker:{{PROJECT_NAME}}_docker@localhost:5432/postgres" +} diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose-entrypoint.sh b/starter/infra/postgresql-knex/docker-compose/docker-compose-entrypoint.sh new file mode 100755 index 0000000..1e906e8 --- /dev/null +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ "$RUN_DOCKER_COMPOSE_DEPS" == true ]; then + dockerize -wait tcp://postgres:5432 -timeout 5m "$@" +else + exec $@ +fi diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose.ci.yml b/starter/infra/postgresql-knex/docker-compose/docker-compose.ci.yml new file mode 100644 index 0000000..20b6991 --- /dev/null +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose.ci.yml @@ -0,0 +1,19 @@ +version: '3.8' +services: + app: + build: + context: .. + image: '$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG' + user: root + depends_on: + - postgres + volumes: + - '$CI_PROJECT_DIR/docs-output:/usr/src/app/docs-output' + - '$CI_PROJECT_DIR/output:/usr/src/app/output' + - '$CI_PROJECT_DIR/secrets-test.json:/usr/src/app/secrets-test.json:ro' + - '$PWD/docker-compose-entrypoint.sh:/docker-entrypoint.sh:ro' + entrypoint: /docker-entrypoint.sh + environment: + - NODE_PATH=. + - ENABLE_TESTS=true + - CFG_JSON_PATH=secrets-test.json diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose.local.yml b/starter/infra/postgresql-knex/docker-compose/docker-compose.local.yml new file mode 100644 index 0000000..197a108 --- /dev/null +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose.local.yml @@ -0,0 +1,5 @@ +version: '3.8' +services: + postgres: + ports: + - 5432:5432 diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose.override.yml b/starter/infra/postgresql-knex/docker-compose/docker-compose.override.yml new file mode 120000 index 0000000..9912883 --- /dev/null +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose.override.yml @@ -0,0 +1 @@ +docker-compose.local.yml \ No newline at end of file diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose.yml b/starter/infra/postgresql-knex/docker-compose/docker-compose.yml new file mode 100644 index 0000000..2a8ca69 --- /dev/null +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3.8' +services: + postgres: + image: postgres:15 + environment: + - POSTGRES_DB={{PROJECT_NAME}}_docker + - POSTGRES_USER={{PROJECT_NAME}}_docker + - POSTGRES_PASSWORD={{PROJECT_NAME}}_docker diff --git a/starter/infra/postgresql-knex/knexfile.ts b/starter/infra/postgresql-knex/knexfile.ts new file mode 100644 index 0000000..fb582b9 --- /dev/null +++ b/starter/infra/postgresql-knex/knexfile.ts @@ -0,0 +1,15 @@ +import * as db from './src/app/database' + +module.exports = { + ...db.knexConfig, + migrations: { + directory: './src/db-migrations', + stub: './src/config/db-migration.template.ts', + extension: 'ts', + }, + seeds: { + directory: './src/db-seeds', + stub: './src/config/db-seed.template.ts', + extension: 'ts', + }, +} diff --git a/starter/infra/postgresql-knex/node-app.jsonc b/starter/infra/postgresql-knex/node-app.jsonc new file mode 100644 index 0000000..42d7eb3 --- /dev/null +++ b/starter/infra/postgresql-knex/node-app.jsonc @@ -0,0 +1,5 @@ +{ + "module": "database", + "name": "PostgreSQL with Knex", + "replace": ["docker-compose/docker-compose.yml", ".env.jsonc"] +} diff --git a/starter/infra/postgresql-knex/package.json b/starter/infra/postgresql-knex/package.json new file mode 100644 index 0000000..5ef6ac9 --- /dev/null +++ b/starter/infra/postgresql-knex/package.json @@ -0,0 +1,13 @@ +{ + "name": "infra-knex-postgresql", + "version": "1.0.0", + "description": "PostgreSQL infrastructure with Knex", + "main": "index.js", + "scripts": {}, + "author": "", + "license": "ISC", + "dependencies": { + "knex": "^3.1.0", + "pg": "^8.13.1" + } +} diff --git a/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts b/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts new file mode 100644 index 0000000..2be4f34 --- /dev/null +++ b/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts @@ -0,0 +1,20 @@ +import { knexConnection } from './knex.database.js' +import { config } from '../config.js' +import { describe, test } from 'mocha' +import assert from 'node:assert' +import { Knex } from 'knex' + +describe('knexConnection', () => { + test('connects to PostgreSQL and prints version', async () => { + let db: Knex | undefined + + await assert.doesNotReject( + knexConnection.connect(config.db.connectionString).then(connection => { + db = connection + }) + ) + assert(db) + await assert.doesNotReject(db.raw('SELECT version()')) + await assert.doesNotReject(knexConnection.disconnect(db)) + }) +}) diff --git a/starter/infra/postgresql-knex/src/adapters/knex.database.ts b/starter/infra/postgresql-knex/src/adapters/knex.database.ts new file mode 100644 index 0000000..42004ae --- /dev/null +++ b/starter/infra/postgresql-knex/src/adapters/knex.database.ts @@ -0,0 +1,14 @@ +import { DbConnection } from '../domain/ports/database.js' +import knex, { Knex } from 'knex' + +export const knexConnection: DbConnection = { + connect: async (connectionString: string) => { + return knex({ + client: 'pg', + connection: connectionString, + }) + }, + disconnect: async (db: Knex) => { + await db.destroy() + }, +} diff --git a/starter/infra/postgresql-knex/src/adapters/repositories/migration.repository.ts b/starter/infra/postgresql-knex/src/adapters/repositories/migration.repository.ts new file mode 100644 index 0000000..33c1086 --- /dev/null +++ b/starter/infra/postgresql-knex/src/adapters/repositories/migration.repository.ts @@ -0,0 +1,24 @@ +import { + Migration, + MigrationRepository, +} from '../../domain/ports/repositories/migration.repository.js' +import { Knex } from 'knex' + +const TABLE = 'knex_migrations' + +const mapMigrationToDomain = (migration: any): Migration => { + return { + id: migration.id, + name: migration.name, + executedAt: migration.migration_time, + } +} + +export const createMigrationsRepository: (knex: Knex) => MigrationRepository = ( + knex: Knex +) => ({ + list: async () => { + const migrations = await knex.select('*').from(TABLE) + return migrations.map(mapMigrationToDomain) + }, +}) diff --git a/starter/infra/postgresql-knex/src/config.ts b/starter/infra/postgresql-knex/src/config.ts new file mode 100644 index 0000000..79848bb --- /dev/null +++ b/starter/infra/postgresql-knex/src/config.ts @@ -0,0 +1,14 @@ +import { createLoader, maskedValues, values } from 'configuru' + +const loader = createLoader({ + defaultConfigPath: '.env.jsonc', +}) + +const configSchema = { + db: { + connectionString: loader.string('DB_CONNECTION_STRING'), + }, +} + +export const config = values(configSchema) +export const safeConfig = maskedValues(configSchema) diff --git a/starter/infra/postgresql-knex/src/container.ts b/starter/infra/postgresql-knex/src/container.ts new file mode 100644 index 0000000..d0ed259 --- /dev/null +++ b/starter/infra/postgresql-knex/src/container.ts @@ -0,0 +1,24 @@ +import { config } from './config.js' +import { DbConnection } from './domain/ports/database.js' +import { Knex } from 'knex' +import { MigrationRepository } from './domain/ports/repositories/migration.repository.js' +import { knexConnection } from './adapters/knex.database.js' +import { createMigrationsRepository } from './adapters/repositories/migration.repository.js' + +export interface Container { + database: DbConnection + repositories: { + migrations: MigrationRepository + } +} + +export const createContainer = async (): Promise => { + const db = await knexConnection.connect(config.db.connectionString) + + return { + database: knexConnection, + repositories: { + migrations: createMigrationsRepository(db), + }, + } +} diff --git a/starter/infra/postgresql-knex/src/domain/ports/database.d.ts b/starter/infra/postgresql-knex/src/domain/ports/database.d.ts new file mode 100644 index 0000000..8dee73a --- /dev/null +++ b/starter/infra/postgresql-knex/src/domain/ports/database.d.ts @@ -0,0 +1,4 @@ +export interface DbConnection { + connect: (connectionString: string) => Promise + disconnect: (db: Db) => Promise +} diff --git a/starter/infra/postgresql-knex/src/domain/ports/repositories/migration.repository.d.ts b/starter/infra/postgresql-knex/src/domain/ports/repositories/migration.repository.d.ts new file mode 100644 index 0000000..e43825a --- /dev/null +++ b/starter/infra/postgresql-knex/src/domain/ports/repositories/migration.repository.d.ts @@ -0,0 +1,9 @@ +export interface Migration { + id: number + name: string + executedAt: Date +} + +export interface MigrationRepository { + list: () => Promise +} diff --git a/starter/infra/postgresql-knex/src/test/setup.ts b/starter/infra/postgresql-knex/src/test/setup.ts new file mode 100644 index 0000000..d79c1b0 --- /dev/null +++ b/starter/infra/postgresql-knex/src/test/setup.ts @@ -0,0 +1,16 @@ +import { knexConnection } from '../adapters/knex.database.js' +import { config } from '../config.js' +import { Knex } from 'knex' +import { before, after } from 'mocha' + +let db: Knex + +before(async () => { + db = await knexConnection.connect(config.db.connectionString) +}) + +after(async () => { + if (db) { + await knexConnection.disconnect(db) + } +}) From 3b6fc211406405771556b44908c1d503f0acf9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 21:56:42 +0200 Subject: [PATCH 11/28] =?UTF-8?q?=E2=9C=A8=20Add=20GitLab=20CloudRun=20pip?= =?UTF-8?q?eline=20starter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pipeline/cloudrun-gitlab/.gitlab-ci.yml | 208 ++++++++++++++++++ .../pipeline/cloudrun-gitlab/node-app.jsonc | 5 + 2 files changed, 213 insertions(+) create mode 100644 starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml create mode 100644 starter/pipeline/cloudrun-gitlab/node-app.jsonc diff --git a/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml b/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml new file mode 100644 index 0000000..b36af21 --- /dev/null +++ b/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml @@ -0,0 +1,208 @@ +# Define environments in GitLab UI with vars according to the braches for the pipeline +# ENVIRONMENT={{develpoment/stage/production}} +# GCP_PROJECT_ID={{PROJECT_NAME}} +# GCP_REGION={{europe-west3}} +# GCP_SECRETS_NAME={{PROJECT_NAME}} +# GCP_SECRETS_VERSION=latest +# GCP_SA_KEY={{base64 encoded string with SA key to deploy Cloud Run}} +# GCP_CLOUD_RUN_SA_NAME={{name of the service account for Cloud Run}} + +variables: + # Default configuration, check .export_variables job for calculated env variables based on branch config + # Node image for pipeline runner + NODE_BASE_IMAGE: node:24.5.0 + # Where to store json secrets from Cloud provider + SECRETS_PATH: '/config/secrets.json' + + ## GCP configuration ## + # Where to temporary store Google service account + GCP_SA_KEY_JSON_PATH: /tmp/key.json + + ## Docker artifact registry configuration ## + # Project name for docker compose CI job runs + DOCKER_COMPOSE_PROJECT_NAME: $CI_PROJECT_NAME-job-$CI_JOB_ID + # Build docker version tag + DOCKER_IMAGE_TAG: $CI_COMMIT_SHORT_SHA + +default: + image: ackee/gitlab-builder + before_script: + - echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc + - echo "//${CI_SERVER_HOST}/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc; + +cache: &docker_cache + key: "$CI_COMMIT_REF_NAME" + paths: + - app_image*.tar + policy: pull + +stages: + - build + - test + - deploy + +## +# HELPERS +## +# Before script extension that exports ci-branch-config environment variables and stores the GCP service +# account on the disk to be used by another jobs in the CI. This should be extended by every job that uses +# any of the branch configuration, docker image or CI SA +.export_variables: + before_script: + - export DOCKER_REGISTRY_URL="$GCP_REGION-docker.pkg.dev" + - export DOCKER_IMAGE_NAME="$DOCKER_REGISTRY_URL/$GCP_PROJECT_ID/${GCP_PROJECT_ID}-docker/$CI_PROJECT_NAME" + - export DOCKER_IMAGE_BUILDER_NAME="$DOCKER_IMAGE_NAME-builder" + - echo "$GCP_SA_KEY" | base64 -d > "$GCP_SA_KEY_JSON_PATH" + + +# Fetches built docker builder image from remote storage or cache +.fetch_build_image: &fetch_build_image + - | + if ! docker inspect --type=image "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" >/dev/null; then + docker load -i app_image_builder.tar + fi + +## +# BUILD STAGE +## +# Builds the builder docker image that can be used for running jobs using the CI docker dependencies and +# configuration (tests, lint, audit, ...) +build image: + stage: build + interruptible: true + extends: .export_variables + script: + - | + echo "app_image*" >> .dockerignore + docker build --target builder \ + -t "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" . + - docker save "$DOCKER_IMAGE_BUILDER_NAME:$DOCKER_IMAGE_TAG" > app_image_builder.tar + cache: + <<: *docker_cache + policy: push + when: on_success + +## +# TEST STAGE +## +# Performs npm ci-lint script in builder image. +# Make sure the ci-lint outputs json file "output/checkstyle-result.json" that reports the result of the linter +lint: + stage: test + interruptible: true + extends: .export_variables + script: + - if [ "$SKIP_LINT" == "true" ]; then warn "Lint skipped."; exit 0; fi + - *fetch_build_image + - cd "$CI_PROJECT_DIR/docker-compose" + - | + DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ + -f docker-compose.yml -f docker-compose.ci.yml \ + run --rm --no-deps app npm run ci-lint + needs: + - build image + artifacts: + reports: + codequality: output/checkstyle-result.json + +# Npm audit run inside of builder image +npm audit: + stage: test + interruptible: true + extends: .export_variables + script: + - if [ "$SKIP_AUDIT" == "true" ]; then warn "Audit skipped."; exit 0; fi + - *fetch_build_image + - cd "$CI_PROJECT_DIR/docker-compose" + - | + DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ + -f docker-compose.yml -f docker-compose.ci.yml \ + run --rm --no-deps app npm audit --production --audit-level=high + needs: + - build image + +# Test job running npm ci-test script. Make sure the ci-test script exports two files on disk: +# output/test.xml - jUnit reporter with test results +# output/cobertura-coverage.xml - Test coverage results +test: + stage: test + interruptible: true + extends: .export_variables + script: + - if [ "$SKIP_TESTS" == "true" ]; then warn "Tests skipped."; exit 0; fi + - *fetch_build_image + - cd "$CI_PROJECT_DIR/docker-compose" + - | + DOCKER_IMAGE_NAME="$DOCKER_IMAGE_BUILDER_NAME" \ + docker-compose -p "$DOCKER_COMPOSE_PROJECT_NAME" \ + -f docker-compose.yml -f docker-compose.ci.yml \ + run --rm -e RUN_DOCKER_COMPOSE_DEPS=true -e DB_HOST=postgres app npm run ci-test + artifacts: + when: always + reports: + junit: output/test.xml + coverage_report: + coverage_format: cobertura + path: output/cobertura-coverage.xml + needs: + - build image + +## +# DEPLOY STAGE +## +# Build production image and push to registry +# Signs in with GCP SA key from pipeline, perform build of the image and pushes it to remote registry +# Make sure /ci-branch-config-name/{branch} exists when adding new branches to "only" field +build and push to registry: + stage: deploy + extends: .export_variables + script: + - docker login -u _json_key --password-stdin $DOCKER_REGISTRY_URL < "$GCP_SA_KEY_JSON_PATH" + - set -a && source ci-branch-config/${CI_COMMIT_REF_NAME}.env && set +a + - | + docker build \ + -t "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \ + $(for var in $(cat ci-branch-config/${CI_COMMIT_REF_NAME}.env | sed 's/=.*//'); do echo "--build-arg $var=${!var} "; done) \ + --build-arg "SECRETS_PATH=$SECRETS_PATH" \ + . + - docker push "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" + needs: + - build image + - npm audit + - test + environment: + name: $CI_COMMIT_REF_NAME + only: + - development + - stage + - master + +# Deploy to Google Cloud Run based on image stored on remote registry +deploy cloud run: + stage: deploy + image: google/cloud-sdk:slim + extends: .export_variables + script: + - gcloud auth activate-service-account --key-file "$GCP_SA_KEY_JSON_PATH" + - gcloud config set project $GCP_PROJECT_ID + - gcloud auth configure-docker + - | + gcloud run deploy wake-arena-project-api \ + --image "$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" \ + --project "$GCP_PROJECT_ID" \ + --platform managed \ + --port 3000 \ + --region "$GCP_REGION" \ + --allow-unauthenticated \ + --memory "$GCP_CLOUD_RUN_ALLOCATED_MEMORY" \ + --service-account="$GCP_CLOUD_RUN_SA_NAME" \ + --set-secrets=$SECRETS_PATH="$GCP_SECRETS_NAME:$GCP_SECRETS_VERSION" \ + --set-cloudsql-instances="$GCP_PROJECT_ID:$GCP_REGION:$GCP_SQL_INSTANCE_NAME" + only: + - development + - stage + - master + environment: + name: $CI_COMMIT_REF_NAME + needs: + - build and push to registry diff --git a/starter/pipeline/cloudrun-gitlab/node-app.jsonc b/starter/pipeline/cloudrun-gitlab/node-app.jsonc new file mode 100644 index 0000000..bbf6912 --- /dev/null +++ b/starter/pipeline/cloudrun-gitlab/node-app.jsonc @@ -0,0 +1,5 @@ +{ + "module": "pipeline", + "name": "CloudRun for GitLab", + "replace": [".gitlab-ci.yml"] +} From d7b8d7fec3a3a77b4320343ef2ad3b6a6e2e8e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 22:17:27 +0200 Subject: [PATCH 12/28] =?UTF-8?q?=F0=9F=93=9D=20Add=20=5Fbase=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/_base/README.md | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 starter/_base/README.md diff --git a/starter/_base/README.md b/starter/_base/README.md new file mode 100644 index 0000000..d0e7e21 --- /dev/null +++ b/starter/_base/README.md @@ -0,0 +1,43 @@ +# Create-Node-App Project +Node.js application bootstrapped with [create-node-app](https://github.com/AckeeCZ/create-node-app) + +## 🏗️ Architecture +All domain logic should live inside the [domain folder](src/domain) and should be accessed only via [container](src/container.ts). Each external dependency should be referenced by its own port inside the [domain/port](src/domain/ports) folder. These ports are also part of the container and can be implemented by any external dependency inside the [adapters](src/adapters/) folder. + +The application should be accessed through the [view](src/view/) layer of the application. All UI and interfaces should be defined there. The main entrypoint is defined in the [index.ts](src/index.ts) file. + +Each domain service should always accept [RequestContext](src/context.ts) as the first parameter. Part of the context is also the container with all of the application dependencies. + +## 👷 Development +Application configuration is handled by [Configuru](https://github.com/AckeeCZ/configuru). All env options that can be changed or need to be set up are described in [.env.jsonc](.env.jsonc). + +The whole codebase is checked and improved using lint and prettier, run `lint:fix` and `prettier:fix` before committing changes to git. + +```bash +npm run lint:fix +npm run prettier:fix +``` + +## ✅ Tests +Test are written using [mocha](https://github.com/mochajs/mocha). They are maintained in two ways: + - **unit tests** should be next to the tested file with `test.ts` suffix, + - **integration tests** should be located inside the [test](src/test/) folder. + +Use mocked container during integration tests. + +If you need any prerequisites during tests, use the [setup.ts](src/test/setup.ts) file. + +To run tests use the test command: +```bash +npm run test +``` + +## 🚀 Quick start +1. Build the code +```bash +npm run build +``` +2. Start the entrypoint / server +```bash +npm run start +``` From 5c9564209c8df5c43e7bbd079a8f2c67d3ca2384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 22:27:49 +0200 Subject: [PATCH 13/28] =?UTF-8?q?=F0=9F=9A=A8=20Run=20prettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 179 +++++++++-------- lib/Builder.js | 224 +++++++++++---------- lib/Files.js | 20 +- lib/Logger.js | 52 ++--- lib/Mergers/ConfigMerger.js | 43 ++-- lib/Mergers/ContainerMerger.js | 333 ++++++++++++++++--------------- lib/Mergers/EnvJsoncMerger.js | 34 ++-- lib/Mergers/Merger.js | 62 +++--- lib/Mergers/PackageJsonMerger.js | 64 +++--- lib/Npm.js | 114 ++++++----- lib/PackageJson.js | 74 +++---- lib/Starter.js | 4 +- lib/StarterLoader.js | 146 ++++++++------ lib/types.js | 4 +- starter/_base/README.md | 22 +- 15 files changed, 722 insertions(+), 653 deletions(-) diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index 228f4f5..113e0fd 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -1,95 +1,94 @@ -import inquirer from 'inquirer'; -import * as path from 'path'; -import * as fs from 'fs'; -import yargs from 'yargs'; -import { hideBin } from 'yargs/helpers'; -import { Builder } from './Builder.js'; -import { Logger } from './Logger.js'; -import { Npm } from './Npm.js'; -import { PackageJson } from './PackageJson.js'; -import { StarterLoader } from './StarterLoader.js'; +import inquirer from 'inquirer' +import * as path from 'path' +import * as fs from 'fs' +import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' +import { Builder } from './Builder.js' +import { Logger } from './Logger.js' +import { Npm } from './Npm.js' +import { PackageJson } from './PackageJson.js' +import { StarterLoader } from './StarterLoader.js' export class Bootstrap { - constructor() { - this.starterLoader = new StarterLoader(); - } - async runCLI(args) { - try { - const cli = yargs(hideBin(args)) - .usage('create-node-app [options]') - .option('dir', { - type: 'string', - alias: 'd', - default: './node-app', - description: 'Destination directory', - }) - .option('debug', { - type: 'boolean', - alias: 'D', - default: false, - description: 'Enables debug logs', - }) - .option('project-name', { - type: 'string', - alias: 'n', - default: 'node-app', - description: 'Google Cloud project name', - }) - .option('force', { - type: 'boolean', - alias: 'f', - default: false, - description: "Overwrite existing destination directory if it's not empty", - }) - .version('1.0.0') - .help(); - const parsedArgs = cli.parseSync(); - const destination = path.normalize(parsedArgs.dir); - const logger = new Logger(parsedArgs.debug); - const npm = new Npm({ dir: destination, logger }); - const packageJson = new PackageJson(npm); - const starters = []; - if (fs.existsSync(destination) && !parsedArgs.force) { - if (!parsedArgs.force) { - const answer = await inquirer.prompt({ - type: 'confirm', - name: 'force', - message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, - }); - if (!answer.force) { - process.exit(0); - } - } - } - for (const module of this.starterLoader.getOptions()) { - const answer = await inquirer.prompt({ - type: 'list', - name: 'starter', - message: `Which ${module.name} would you like to use?`, - choices: [...module.starters, 'none'], - }); - if (answer.starter === 'none') { - continue; - } - starters.push(this.starterLoader.getStarter(answer.starter)); - } - const builder = new Builder({ - npm, - logger, - packageJson, - starters, - destination: destination, - projectName: parsedArgs.projectName, - }); - await builder.build(); + constructor() { + this.starterLoader = new StarterLoader() + } + async runCLI(args) { + try { + const cli = yargs(hideBin(args)) + .usage('create-node-app [options]') + .option('dir', { + type: 'string', + alias: 'd', + default: './node-app', + description: 'Destination directory', + }) + .option('debug', { + type: 'boolean', + alias: 'D', + default: false, + description: 'Enables debug logs', + }) + .option('project-name', { + type: 'string', + alias: 'n', + default: 'node-app', + description: 'Google Cloud project name', + }) + .option('force', { + type: 'boolean', + alias: 'f', + default: false, + description: + "Overwrite existing destination directory if it's not empty", + }) + .version('1.0.0') + .help() + const parsedArgs = cli.parseSync() + const destination = path.normalize(parsedArgs.dir) + const logger = new Logger(parsedArgs.debug) + const npm = new Npm({ dir: destination, logger }) + const packageJson = new PackageJson(npm) + const starters = [] + if (fs.existsSync(destination) && !parsedArgs.force) { + if (!parsedArgs.force) { + const answer = await inquirer.prompt({ + type: 'confirm', + name: 'force', + message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, + }) + if (!answer.force) { + process.exit(0) + } } - catch (error) { - if (error instanceof Error && error.name === 'ExitPromptError') { - process.exit(0); - } - else { - throw error; - } + } + for (const module of this.starterLoader.getOptions()) { + const answer = await inquirer.prompt({ + type: 'list', + name: 'starter', + message: `Which ${module.name} would you like to use?`, + choices: [...module.starters, 'none'], + }) + if (answer.starter === 'none') { + continue } + starters.push(this.starterLoader.getStarter(answer.starter)) + } + const builder = new Builder({ + npm, + logger, + packageJson, + starters, + destination: destination, + projectName: parsedArgs.projectName, + }) + await builder.build() + } catch (error) { + if (error instanceof Error && error.name === 'ExitPromptError') { + process.exit(0) + } else { + throw error + } } + } } -//# sourceMappingURL=Bootstrap.js.map \ No newline at end of file +//# sourceMappingURL=Bootstrap.js.map diff --git a/lib/Builder.js b/lib/Builder.js index 920a67b..37c2425 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -1,111 +1,131 @@ -import glob from 'fast-glob'; -import * as fs from 'fs/promises'; -import * as path from 'path'; -import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js'; -import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js'; -import { ConfigMerger } from './Mergers/ConfigMerger.js'; -import { ContainerMerger } from './Mergers/ContainerMerger.js'; -import { Files } from './Files.js'; +import glob from 'fast-glob' +import * as fs from 'fs/promises' +import * as path from 'path' +import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' +import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' +import { ConfigMerger } from './Mergers/ConfigMerger.js' +import { ContainerMerger } from './Mergers/ContainerMerger.js' +import { Files } from './Files.js' export class Builder { - constructor(params) { - this.npm = params.npm; - this.logger = params.logger; - this.starters = params.starters; - this.destination = params.destination; - this.projectName = params.projectName; - this.replacements = { - '{{PROJECT_NAME}}': this.projectName, - }; - this.fileMergers = [ - new PackageJsonMerger(this.projectName, this.destination, 'package.json'), - new EnvJsoncMerger(this.destination, '.env.jsonc'), - new ConfigMerger(this.destination, 'src/config.ts'), - new ContainerMerger(this.destination, 'src/container.ts'), - ]; + constructor(params) { + this.npm = params.npm + this.logger = params.logger + this.starters = params.starters + this.destination = params.destination + this.projectName = params.projectName + this.replacements = { + '{{PROJECT_NAME}}': this.projectName, } - async prepareFolder() { - if (await Files.existsAndIsDir(this.destination)) { - await fs.rm(this.destination, { recursive: true }); - } - await fs.mkdir(this.destination, { recursive: true }); + this.fileMergers = [ + new PackageJsonMerger(this.projectName, this.destination, 'package.json'), + new EnvJsoncMerger(this.destination, '.env.jsonc'), + new ConfigMerger(this.destination, 'src/config.ts'), + new ContainerMerger(this.destination, 'src/container.ts'), + ] + } + async prepareFolder() { + if (await Files.existsAndIsDir(this.destination)) { + await fs.rm(this.destination, { recursive: true }) } - async build() { - try { - await this.logger.loader(`Preparing clean folder`, this.prepareFolder()); - await this.logger.loader(`Preparing folder structure`, this.buildStarter(Builder.BASE_STARTER_DIR)); - for (const starter of this.starters) { - await this.logger.loader(`Adding ${starter.config.name} ${starter.config.module}`, this.buildStarter(starter.path, starter.config)); - } - await this.logger.loader(`npm install`, this.npm.run(['install'])); - const prebuildScripts = this.starters - .map(starter => starter.config.prebuild) - .filter(script => script !== undefined); - for (const script of prebuildScripts) { - await this.logger.loader(`npm run ${script.join(' ')}`, this.npm.run(['run', ...script])); - } - await this.logger.loader(`npm run build`, this.npm.run(['run', 'build'])); - this.logger.info(`Your app is ready in ${path.relative(process.cwd(), this.destination)}! 🚀`); - } - catch (error) { - this.logger.error(error); - process.exit(1); - } + await fs.mkdir(this.destination, { recursive: true }) + } + async build() { + try { + await this.logger.loader(`Preparing clean folder`, this.prepareFolder()) + await this.logger.loader( + `Preparing folder structure`, + this.buildStarter(Builder.BASE_STARTER_DIR) + ) + for (const starter of this.starters) { + await this.logger.loader( + `Adding ${starter.config.name} ${starter.config.module}`, + this.buildStarter(starter.path, starter.config) + ) + } + await this.logger.loader(`npm install`, this.npm.run(['install'])) + const prebuildScripts = this.starters + .map(starter => starter.config.prebuild) + .filter(script => script !== undefined) + for (const script of prebuildScripts) { + await this.logger.loader( + `npm run ${script.join(' ')}`, + this.npm.run(['run', ...script]) + ) + } + await this.logger.loader(`npm run build`, this.npm.run(['run', 'build'])) + this.logger.info( + `Your app is ready in ${path.relative(process.cwd(), this.destination)}! 🚀` + ) + } catch (error) { + this.logger.error(error) + process.exit(1) } - async buildStarter(starterDir, config) { - const destDir = path.normalize(path.join(process.cwd(), this.destination)); - const files = await glob(`${starterDir}/*`, { - cwd: starterDir, - dot: true, - onlyFiles: false, - }); - const ignoredFiles = Builder.INGORED_FILES.map(file => path.join(starterDir, file)); - const mergedFiles = await Promise.all(this.fileMergers.map(async (merger) => { - return { - path: merger.getDestPath(), - content: await merger.merge(starterDir), - }; - })); - await Promise.all(files.map(async (filePath) => { - if (ignoredFiles.includes(filePath)) { - return; - } - const destFilePath = path.join(destDir, path.basename(filePath)); - if (await Files.existsAndIsDir(filePath)) { - await fs.cp(filePath, destFilePath, { recursive: true }); - return; - } - await fs.cp(filePath, destFilePath); - })); - await Promise.all(mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content))); - if (config?.replace) { - await Promise.all(config.replace.map(async (filePath) => this.replaceInFile(filePath))); + } + async buildStarter(starterDir, config) { + const destDir = path.normalize(path.join(process.cwd(), this.destination)) + const files = await glob(`${starterDir}/*`, { + cwd: starterDir, + dot: true, + onlyFiles: false, + }) + const ignoredFiles = Builder.INGORED_FILES.map(file => + path.join(starterDir, file) + ) + const mergedFiles = await Promise.all( + this.fileMergers.map(async merger => { + return { + path: merger.getDestPath(), + content: await merger.merge(starterDir), } - } - async replaceInFile(filePath) { - filePath = path.normalize(path.join(this.destination, filePath)); - let content = await fs.readFile(filePath, 'utf8'); - content = Object.keys(this.replacements).reduce((acc, key) => { - return acc.replaceAll(key, this.replacements[key]); - }, content); - return fs.writeFile(filePath, content); - } - async symlink(linkName, linkedFile) { - linkName = path.normalize(linkName); - linkedFile = path.normalize(linkedFile); - this.logger.info(`> ln -s ${linkName} ${linkedFile}`); - try { - await fs.symlink(linkedFile, linkName); + }) + ) + await Promise.all( + files.map(async filePath => { + if (ignoredFiles.includes(filePath)) { + return } - catch (error) { - if ('code' in error && error.code === 'EEXIST') { - // OK - } - else { - throw error; - } + const destFilePath = path.join(destDir, path.basename(filePath)) + if (await Files.existsAndIsDir(filePath)) { + await fs.cp(filePath, destFilePath, { recursive: true }) + return } + await fs.cp(filePath, destFilePath) + }) + ) + await Promise.all( + mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content)) + ) + if (config?.replace) { + await Promise.all( + config.replace.map(async filePath => this.replaceInFile(filePath)) + ) + } + } + async replaceInFile(filePath) { + filePath = path.normalize(path.join(this.destination, filePath)) + let content = await fs.readFile(filePath, 'utf8') + content = Object.keys(this.replacements).reduce((acc, key) => { + return acc.replaceAll(key, this.replacements[key]) + }, content) + return fs.writeFile(filePath, content) + } + async symlink(linkName, linkedFile) { + linkName = path.normalize(linkName) + linkedFile = path.normalize(linkedFile) + this.logger.info(`> ln -s ${linkName} ${linkedFile}`) + try { + await fs.symlink(linkedFile, linkName) + } catch (error) { + if ('code' in error && error.code === 'EEXIST') { + // OK + } else { + throw error + } } + } } -Builder.BASE_STARTER_DIR = path.normalize(path.join(import.meta.dirname, '..', 'starter', '_base')); -Builder.INGORED_FILES = ['node-app.jsonc']; -//# sourceMappingURL=Builder.js.map \ No newline at end of file +Builder.BASE_STARTER_DIR = path.normalize( + path.join(import.meta.dirname, '..', 'starter', '_base') +) +Builder.INGORED_FILES = ['node-app.jsonc'] +//# sourceMappingURL=Builder.js.map diff --git a/lib/Files.js b/lib/Files.js index 6690b8a..010e8cd 100644 --- a/lib/Files.js +++ b/lib/Files.js @@ -1,12 +1,12 @@ -import * as fsp from 'fs/promises'; +import * as fsp from 'fs/promises' export class Files { - static async exists(path) { - const stat = await fsp.stat(path).catch(() => undefined); - return Boolean(stat); - } - static async existsAndIsDir(path) { - const stat = await fsp.stat(path).catch(() => undefined); - return Boolean(stat?.isDirectory()); - } + static async exists(path) { + const stat = await fsp.stat(path).catch(() => undefined) + return Boolean(stat) + } + static async existsAndIsDir(path) { + const stat = await fsp.stat(path).catch(() => undefined) + return Boolean(stat?.isDirectory()) + } } -//# sourceMappingURL=Files.js.map \ No newline at end of file +//# sourceMappingURL=Files.js.map diff --git a/lib/Logger.js b/lib/Logger.js index df8b3e8..de80a97 100644 --- a/lib/Logger.js +++ b/lib/Logger.js @@ -1,30 +1,30 @@ -import { oraPromise } from 'ora'; +import { oraPromise } from 'ora' export class Logger { - constructor(enableDebug = false) { - this.enableDebug = enableDebug; + constructor(enableDebug = false) { + this.enableDebug = enableDebug + } + info(message) { + console.log(message) + } + verbose(message) { + console.log(message) + } + error(message) { + console.log(message) + } + debug(message) { + if (this.enableDebug) { + console.log(message) } - info(message) { - console.log(message); - } - verbose(message) { - console.log(message); - } - error(message) { - console.log(message); - } - debug(message) { - if (this.enableDebug) { - console.log(message); - } - } - loader(message, promise) { - if (this.enableDebug) { - this.info(message); - return promise; - } - return oraPromise(promise, { - text: message, - }); + } + loader(message, promise) { + if (this.enableDebug) { + this.info(message) + return promise } + return oraPromise(promise, { + text: message, + }) + } } -//# sourceMappingURL=Logger.js.map \ No newline at end of file +//# sourceMappingURL=Logger.js.map diff --git a/lib/Mergers/ConfigMerger.js b/lib/Mergers/ConfigMerger.js index 4f669fe..65384d1 100644 --- a/lib/Mergers/ConfigMerger.js +++ b/lib/Mergers/ConfigMerger.js @@ -1,22 +1,27 @@ -import { Merger } from './Merger.js'; -import fsp from 'fs/promises'; +import { Merger } from './Merger.js' +import fsp from 'fs/promises' export class ConfigMerger extends Merger { - constructor() { - super(...arguments); - this.name = 'Config'; - } - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir); - if (content) { - return content; - } - const { originPath, destPath } = this.getPaths(originDir); - const [destTsConfig, originTsConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), - ]); - const configContent = /configSchema = {\n(.*?)\n}\n/s.exec(originTsConfig)?.[1]; - return destTsConfig.replace('\n}\n', configContent ? `${configContent}\n}\n` : '}\n'); + constructor() { + super(...arguments) + this.name = 'Config' + } + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content } + const { originPath, destPath } = this.getPaths(originDir) + const [destTsConfig, originTsConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + const configContent = /configSchema = {\n(.*?)\n}\n/s.exec( + originTsConfig + )?.[1] + return destTsConfig.replace( + '\n}\n', + configContent ? `${configContent}\n}\n` : '}\n' + ) + } } -//# sourceMappingURL=ConfigMerger.js.map \ No newline at end of file +//# sourceMappingURL=ConfigMerger.js.map diff --git a/lib/Mergers/ContainerMerger.js b/lib/Mergers/ContainerMerger.js index daebea1..8ddcac9 100644 --- a/lib/Mergers/ContainerMerger.js +++ b/lib/Mergers/ContainerMerger.js @@ -1,172 +1,181 @@ -import { Merger } from './Merger.js'; -import fsp from 'fs/promises'; -import * as ts from 'typescript'; +import { Merger } from './Merger.js' +import fsp from 'fs/promises' +import * as ts from 'typescript' export class ContainerMerger extends Merger { - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir); - if (content) { - return content; - } - const { originPath, destPath } = this.getPaths(originDir); - const [originContainer, destContainer] = await Promise.all([ - fsp.readFile(originPath, 'utf8'), - fsp.readFile(destPath, 'utf8'), - ]); - const originAst = this.parseFile(originContainer); - const destAst = this.parseFile(destContainer); - const mergedImports = this.mergeImports(originAst, destAst); - const mergedInterface = this.mergeInterface(originAst, destAst); - const mergedFunction = this.mergeFunction(originAst, destAst); - return `${mergedImports}\n\n${mergedInterface}\n\nexport type ContainerFactory = () => Promise\n\n${mergedFunction}\n`; - } - parseFile(content) { - return ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true); - } - mergeImports(originAst, destAst) { - const imports = []; - const addImports = (node) => { - if (!ts.isImportDeclaration(node)) { - node.forEachChild(addImports); - return; - } - const importText = node.getText(); - if (!imports.includes(importText)) { - imports.push(importText); - } - node.forEachChild(addImports); - }; - addImports(originAst); - addImports(destAst); - return imports.join('\n'); - } - mergeInterface(originAst, destAst) { - const properties = []; - const extractProperties = (node) => { - if (!ts.isInterfaceDeclaration(node) || node.name.text !== 'Container') { - node.forEachChild(extractProperties); - return; - } - node.members.forEach(member => { - if (!ts.isPropertySignature(member)) { - return; - } - const propText = member.getText().trim(); - if (!properties.includes(propText)) { - properties.push(propText); - } - }); - node.forEachChild(extractProperties); - }; - extractProperties(originAst); - extractProperties(destAst); - return `export interface Container {\n ${properties.join('\n ')}\n}`; - } - mergeFunction(originAst, destAst) { - const originFunctionBody = this.extractFunctionBody(originAst); - const destFunctionBody = this.extractFunctionBody(destAst); - const allFunctionContent = [ - ...originFunctionBody.content, - ...destFunctionBody.content, - ]; - const mergedReturnProps = this.mergeReturnProperties(originFunctionBody.returnProps, destFunctionBody.returnProps); - let body = ''; - if (allFunctionContent.length > 0) { - body += allFunctionContent.join('\n') + '\n\n'; - } - body += ` return {\n ${mergedReturnProps.join(',\n ')}\n }`; - return `export const createContainer = async (): Promise => {\n${body}\n}`; + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content } - mergeReturnProperties(originProps, destProps) { - const mergedProps = new Map(); - originProps.forEach(prop => { - const propName = this.getPropertyName(prop); - if (propName) { - mergedProps.set(propName, prop); - } - }); - destProps.forEach(prop => { - const propName = this.getPropertyName(prop); - if (propName) { - mergedProps.set(propName, prop); - } - }); - return Array.from(mergedProps.values()).map(prop => { - const propText = prop.getText(); - if (ts.isPropertyAssignment(prop) && - ts.isObjectLiteralExpression(prop.initializer)) { - return this.formatNestedObject(propText); - } - return propText; - }); + const { originPath, destPath } = this.getPaths(originDir) + const [originContainer, destContainer] = await Promise.all([ + fsp.readFile(originPath, 'utf8'), + fsp.readFile(destPath, 'utf8'), + ]) + const originAst = this.parseFile(originContainer) + const destAst = this.parseFile(destContainer) + const mergedImports = this.mergeImports(originAst, destAst) + const mergedInterface = this.mergeInterface(originAst, destAst) + const mergedFunction = this.mergeFunction(originAst, destAst) + return `${mergedImports}\n\n${mergedInterface}\n\nexport type ContainerFactory = () => Promise\n\n${mergedFunction}\n` + } + parseFile(content) { + return ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true) + } + mergeImports(originAst, destAst) { + const imports = [] + const addImports = node => { + if (!ts.isImportDeclaration(node)) { + node.forEachChild(addImports) + return + } + const importText = node.getText() + if (!imports.includes(importText)) { + imports.push(importText) + } + node.forEachChild(addImports) } - getPropertyName(prop) { - if (ts.isPropertyAssignment(prop)) { - if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { - return prop.name.text; - } + addImports(originAst) + addImports(destAst) + return imports.join('\n') + } + mergeInterface(originAst, destAst) { + const properties = [] + const extractProperties = node => { + if (!ts.isInterfaceDeclaration(node) || node.name.text !== 'Container') { + node.forEachChild(extractProperties) + return + } + node.members.forEach(member => { + if (!ts.isPropertySignature(member)) { + return } - else if (ts.isShorthandPropertyAssignment(prop)) { - return prop.name.text; + const propText = member.getText().trim() + if (!properties.includes(propText)) { + properties.push(propText) } - return null; - } - formatNestedObject(propText) { - const lines = propText.split('\n'); - return lines - .map((line, index) => { - if (index === 0) { - return line; - } - return ' ' + line; - }) - .join('\n'); - } - extractFunctionBody(ast) { - const content = []; - const returnProps = []; - const visit = (node) => { - if (!this.isCreateContainer(node)) { - ts.forEachChild(node, visit); - return; - } - if (!node.initializer || !ts.isArrowFunction(node.initializer)) { - ts.forEachChild(node, visit); - return; - } - const body = node.initializer.body; - if (!ts.isBlock(body)) { - ts.forEachChild(node, visit); - return; - } - body.statements.forEach(statement => { - if (this.isContentStatement(statement)) { - content.push(statement.getText().trim()); - } - else if (this.isReturnWithObject(statement)) { - // Extract the actual property nodes, not just their names - statement.expression.properties.forEach(prop => { - returnProps.push(prop); - }); - } - }); - ts.forEachChild(node, visit); - }; - visit(ast); - return { content, returnProps }; + }) + node.forEachChild(extractProperties) } - isCreateContainer(node) { - return (ts.isVariableDeclaration(node) && - node.name.getText() === 'createContainer'); + extractProperties(originAst) + extractProperties(destAst) + return `export interface Container {\n ${properties.join('\n ')}\n}` + } + mergeFunction(originAst, destAst) { + const originFunctionBody = this.extractFunctionBody(originAst) + const destFunctionBody = this.extractFunctionBody(destAst) + const allFunctionContent = [ + ...originFunctionBody.content, + ...destFunctionBody.content, + ] + const mergedReturnProps = this.mergeReturnProperties( + originFunctionBody.returnProps, + destFunctionBody.returnProps + ) + let body = '' + if (allFunctionContent.length > 0) { + body += allFunctionContent.join('\n') + '\n\n' } - isContentStatement(statement) { - return (ts.isVariableStatement(statement) || - (ts.isExpressionStatement(statement) && - ts.isCallExpression(statement.expression))); + body += ` return {\n ${mergedReturnProps.join(',\n ')}\n }` + return `export const createContainer = async (): Promise => {\n${body}\n}` + } + mergeReturnProperties(originProps, destProps) { + const mergedProps = new Map() + originProps.forEach(prop => { + const propName = this.getPropertyName(prop) + if (propName) { + mergedProps.set(propName, prop) + } + }) + destProps.forEach(prop => { + const propName = this.getPropertyName(prop) + if (propName) { + mergedProps.set(propName, prop) + } + }) + return Array.from(mergedProps.values()).map(prop => { + const propText = prop.getText() + if ( + ts.isPropertyAssignment(prop) && + ts.isObjectLiteralExpression(prop.initializer) + ) { + return this.formatNestedObject(propText) + } + return propText + }) + } + getPropertyName(prop) { + if (ts.isPropertyAssignment(prop)) { + if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { + return prop.name.text + } + } else if (ts.isShorthandPropertyAssignment(prop)) { + return prop.name.text } - isReturnWithObject(statement) { - return (ts.isReturnStatement(statement) && - !!statement.expression && - ts.isObjectLiteralExpression(statement.expression)); + return null + } + formatNestedObject(propText) { + const lines = propText.split('\n') + return lines + .map((line, index) => { + if (index === 0) { + return line + } + return ' ' + line + }) + .join('\n') + } + extractFunctionBody(ast) { + const content = [] + const returnProps = [] + const visit = node => { + if (!this.isCreateContainer(node)) { + ts.forEachChild(node, visit) + return + } + if (!node.initializer || !ts.isArrowFunction(node.initializer)) { + ts.forEachChild(node, visit) + return + } + const body = node.initializer.body + if (!ts.isBlock(body)) { + ts.forEachChild(node, visit) + return + } + body.statements.forEach(statement => { + if (this.isContentStatement(statement)) { + content.push(statement.getText().trim()) + } else if (this.isReturnWithObject(statement)) { + // Extract the actual property nodes, not just their names + statement.expression.properties.forEach(prop => { + returnProps.push(prop) + }) + } + }) + ts.forEachChild(node, visit) } + visit(ast) + return { content, returnProps } + } + isCreateContainer(node) { + return ( + ts.isVariableDeclaration(node) && + node.name.getText() === 'createContainer' + ) + } + isContentStatement(statement) { + return ( + ts.isVariableStatement(statement) || + (ts.isExpressionStatement(statement) && + ts.isCallExpression(statement.expression)) + ) + } + isReturnWithObject(statement) { + return ( + ts.isReturnStatement(statement) && + !!statement.expression && + ts.isObjectLiteralExpression(statement.expression) + ) + } } -//# sourceMappingURL=ContainerMerger.js.map \ No newline at end of file +//# sourceMappingURL=ContainerMerger.js.map diff --git a/lib/Mergers/EnvJsoncMerger.js b/lib/Mergers/EnvJsoncMerger.js index 550ebc6..9d50cba 100644 --- a/lib/Mergers/EnvJsoncMerger.js +++ b/lib/Mergers/EnvJsoncMerger.js @@ -1,20 +1,20 @@ -import { Merger } from './Merger.js'; -import fsp from 'fs/promises'; +import { Merger } from './Merger.js' +import fsp from 'fs/promises' export class EnvJsoncMerger extends Merger { - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir); - if (content) { - return content; - } - const { originPath, destPath } = this.getPaths(originDir); - const [destEnvConfig, originEnvConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), - ]); - const originWithoutOpenBracket = originEnvConfig.replace('{\n', ''); - return destEnvConfig - .replace(',\n}\n', '\n}\n') - .replace('\n}\n', `,\n${originWithoutOpenBracket}`); + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content } + const { originPath, destPath } = this.getPaths(originDir) + const [destEnvConfig, originEnvConfig] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') + return destEnvConfig + .replace(',\n}\n', '\n}\n') + .replace('\n}\n', `,\n${originWithoutOpenBracket}`) + } } -//# sourceMappingURL=EnvJsoncMerger.js.map \ No newline at end of file +//# sourceMappingURL=EnvJsoncMerger.js.map diff --git a/lib/Mergers/Merger.js b/lib/Mergers/Merger.js index 2a26ff0..7b45cce 100644 --- a/lib/Mergers/Merger.js +++ b/lib/Mergers/Merger.js @@ -1,37 +1,37 @@ -import fsp from 'fs/promises'; -import path from 'path'; -import { Files } from '../Files.js'; +import fsp from 'fs/promises' +import path from 'path' +import { Files } from '../Files.js' export class Merger { - constructor(destDir, pathToFile) { - this.destDir = destDir; - this.pathToFile = pathToFile; - this.destPath = path.join(this.destDir, this.pathToFile); + constructor(destDir, pathToFile) { + this.destDir = destDir + this.pathToFile = pathToFile + this.destPath = path.join(this.destDir, this.pathToFile) + } + getDestPath() { + return this.destPath + } + getPaths(originDir) { + return { + originPath: path.join(originDir, this.pathToFile), + destPath: this.getDestPath(), } - getDestPath() { - return this.destPath; + } + async getWhichExistsOrNull(originDir) { + const { originPath, destPath } = this.getPaths(originDir) + const [originExists, destExists] = await Promise.all([ + Files.exists(originPath), + Files.exists(destPath), + ]) + if (!originExists && destExists) { + return fsp.readFile(destPath, 'utf8') } - getPaths(originDir) { - return { - originPath: path.join(originDir, this.pathToFile), - destPath: this.getDestPath(), - }; + if (!destExists && originExists) { + return fsp.readFile(originPath, 'utf8') } - async getWhichExistsOrNull(originDir) { - const { originPath, destPath } = this.getPaths(originDir); - const [originExists, destExists] = await Promise.all([ - Files.exists(originPath), - Files.exists(destPath), - ]); - if (!originExists && destExists) { - return fsp.readFile(destPath, 'utf8'); - } - if (!destExists && originExists) { - return fsp.readFile(originPath, 'utf8'); - } - if (!originExists && !destExists) { - throw new Error(`No file found to merge: ${this.pathToFile}`); - } - return null; + if (!originExists && !destExists) { + throw new Error(`No file found to merge: ${this.pathToFile}`) } + return null + } } -//# sourceMappingURL=Merger.js.map \ No newline at end of file +//# sourceMappingURL=Merger.js.map diff --git a/lib/Mergers/PackageJsonMerger.js b/lib/Mergers/PackageJsonMerger.js index ef21840..c05c107 100644 --- a/lib/Mergers/PackageJsonMerger.js +++ b/lib/Mergers/PackageJsonMerger.js @@ -1,36 +1,36 @@ -import { Merger } from './Merger.js'; -import fsp from 'fs/promises'; +import { Merger } from './Merger.js' +import fsp from 'fs/promises' export class PackageJsonMerger extends Merger { - constructor(projectName, destDir, pathToFile) { - super(destDir, pathToFile); - this.projectName = projectName; + constructor(projectName, destDir, pathToFile) { + super(destDir, pathToFile) + this.projectName = projectName + } + async merge(originDir) { + const content = await this.getWhichExistsOrNull(originDir) + if (content) { + return content } - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir); - if (content) { - return content; - } - const { originPath, destPath } = this.getPaths(originDir); - const [destPckgJson, starterPckgJson] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), - ]); - const destPckgJsonObj = JSON.parse(destPckgJson); - const starterPckgJsonObj = JSON.parse(starterPckgJson); - destPckgJsonObj.name = this.projectName; - destPckgJsonObj.scripts = { - ...destPckgJsonObj.scripts, - ...starterPckgJsonObj.scripts, - }; - destPckgJsonObj.dependencies = { - ...destPckgJsonObj.dependencies, - ...starterPckgJsonObj.dependencies, - }; - destPckgJsonObj.devDependencies = { - ...destPckgJsonObj.devDependencies, - ...starterPckgJsonObj.devDependencies, - }; - return JSON.stringify(destPckgJsonObj, null, 2); + const { originPath, destPath } = this.getPaths(originDir) + const [destPckgJson, starterPckgJson] = await Promise.all([ + fsp.readFile(destPath, 'utf8'), + fsp.readFile(originPath, 'utf8'), + ]) + const destPckgJsonObj = JSON.parse(destPckgJson) + const starterPckgJsonObj = JSON.parse(starterPckgJson) + destPckgJsonObj.name = this.projectName + destPckgJsonObj.scripts = { + ...destPckgJsonObj.scripts, + ...starterPckgJsonObj.scripts, } + destPckgJsonObj.dependencies = { + ...destPckgJsonObj.dependencies, + ...starterPckgJsonObj.dependencies, + } + destPckgJsonObj.devDependencies = { + ...destPckgJsonObj.devDependencies, + ...starterPckgJsonObj.devDependencies, + } + return JSON.stringify(destPckgJsonObj, null, 2) + } } -//# sourceMappingURL=PackageJsonMerger.js.map \ No newline at end of file +//# sourceMappingURL=PackageJsonMerger.js.map diff --git a/lib/Npm.js b/lib/Npm.js index 6ed1975..42e7b0c 100644 --- a/lib/Npm.js +++ b/lib/Npm.js @@ -1,61 +1,65 @@ -import * as childProcess from 'child_process'; -import { Logger } from './Logger.js'; +import * as childProcess from 'child_process' +import { Logger } from './Logger.js' export class NpmError extends Error { - constructor(message, code) { - super(message); - this.code = code; - this.name = 'NpmError'; - } + constructor(message, code) { + super(message) + this.code = code + this.name = 'NpmError' + } } export class Npm { - constructor(settings) { - this.logger = settings?.logger ?? new Logger(); - this.dir = settings?.dir; - } - spawn(cmd, args, options = {}) { - return new Promise((resolve, reject) => { - const cp = childProcess.spawn(cmd, args, options); - const error = []; - const stdout = []; - cp.stdout?.on('data', data => { - stdout.push(data.toString()); - }); - cp.on('error', e => { - error.push(e.toString()); - }); - cp.on('close', code => { - if (error.length || (code !== null && code > 0)) { - reject(new NpmError(error.length ? error.join('') : stdout.join(''), code ?? null)); - } - else { - resolve(undefined); - } - }); - }); - } - run(args) { - this.logger.debug(`> npm ${args.join(' ')}`); - const options = this.dir - ? { - cwd: this.dir, - stdio: this.logger.enableDebug ? 'inherit' : 'pipe', - } - : { stdio: this.logger.enableDebug ? 'inherit' : 'pipe' }; - return this.spawn('npm', args, options); - } - init() { - return this.run(['init', '--yes']); - } - i(module) { - if (!module) { - return this.run(['i']); + constructor(settings) { + this.logger = settings?.logger ?? new Logger() + this.dir = settings?.dir + } + spawn(cmd, args, options = {}) { + return new Promise((resolve, reject) => { + const cp = childProcess.spawn(cmd, args, options) + const error = [] + const stdout = [] + cp.stdout?.on('data', data => { + stdout.push(data.toString()) + }) + cp.on('error', e => { + error.push(e.toString()) + }) + cp.on('close', code => { + if (error.length || (code !== null && code > 0)) { + reject( + new NpmError( + error.length ? error.join('') : stdout.join(''), + code ?? null + ) + ) + } else { + resolve(undefined) } - const args = ['i', module]; - return this.run(args); - } - iDev(module) { - const args = ['i', '-D', module]; - return this.run(args); + }) + }) + } + run(args) { + this.logger.debug(`> npm ${args.join(' ')}`) + const options = this.dir + ? { + cwd: this.dir, + stdio: this.logger.enableDebug ? 'inherit' : 'pipe', + } + : { stdio: this.logger.enableDebug ? 'inherit' : 'pipe' } + return this.spawn('npm', args, options) + } + init() { + return this.run(['init', '--yes']) + } + i(module) { + if (!module) { + return this.run(['i']) } + const args = ['i', module] + return this.run(args) + } + iDev(module) { + const args = ['i', '-D', module] + return this.run(args) + } } -//# sourceMappingURL=Npm.js.map \ No newline at end of file +//# sourceMappingURL=Npm.js.map diff --git a/lib/PackageJson.js b/lib/PackageJson.js index 807e077..db779de 100644 --- a/lib/PackageJson.js +++ b/lib/PackageJson.js @@ -1,38 +1,42 @@ -import * as path from 'path'; -import * as fs from 'fs'; -import * as lodash from 'lodash-es'; +import * as path from 'path' +import * as fs from 'fs' +import * as lodash from 'lodash-es' export class PackageJson { - constructor(npm) { - let packagejsonPath = './package.json'; - if (npm.dir) { - packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`); - } - this.path = packagejsonPath; - this.npm = npm; - } - setType(type) { - this.mergeWith({ - type, - }); - } - toJSON() { - return JSON.parse(fs.readFileSync(this.path, 'utf-8')); - } - runScript(name) { - return this.npm.run(['run', name]); - } - addNpmScript(name, command) { - this.mergeWith({ - scripts: { - [name]: command, - }, - }); - } - // Updated package json using merge with given object - mergeWith(partialWith) { - const json = lodash.merge(this.toJSON(), partialWith); - // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) - fs.writeFileSync(path.join(this.path), JSON.stringify(json, null, 2), 'utf-8'); + constructor(npm) { + let packagejsonPath = './package.json' + if (npm.dir) { + packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`) } + this.path = packagejsonPath + this.npm = npm + } + setType(type) { + this.mergeWith({ + type, + }) + } + toJSON() { + return JSON.parse(fs.readFileSync(this.path, 'utf-8')) + } + runScript(name) { + return this.npm.run(['run', name]) + } + addNpmScript(name, command) { + this.mergeWith({ + scripts: { + [name]: command, + }, + }) + } + // Updated package json using merge with given object + mergeWith(partialWith) { + const json = lodash.merge(this.toJSON(), partialWith) + // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) + fs.writeFileSync( + path.join(this.path), + JSON.stringify(json, null, 2), + 'utf-8' + ) + } } -//# sourceMappingURL=PackageJson.js.map \ No newline at end of file +//# sourceMappingURL=PackageJson.js.map diff --git a/lib/Starter.js b/lib/Starter.js index 9ace139..26460e8 100644 --- a/lib/Starter.js +++ b/lib/Starter.js @@ -1,2 +1,2 @@ -export {}; -//# sourceMappingURL=Starter.js.map \ No newline at end of file +export {} +//# sourceMappingURL=Starter.js.map diff --git a/lib/StarterLoader.js b/lib/StarterLoader.js index 2d15e8c..ea1ee71 100644 --- a/lib/StarterLoader.js +++ b/lib/StarterLoader.js @@ -1,71 +1,89 @@ -import glob from 'fast-glob'; -import fs from 'node:fs'; -import path from 'node:path'; +import glob from 'fast-glob' +import fs from 'node:fs' +import path from 'node:path' export class StarterLoader { - constructor() { - this.starters = new Map(); - this.modules = []; - const configFiles = glob.sync(`${StarterLoader.starterPath}/**/node-app.jsonc`); - for (const configFile of configFiles) { - const config = StarterLoader.validateConfig(configFile, JSON.parse(fs.readFileSync(configFile, 'utf8'))); - const original = this.starters.get(config.name); - if (original) { - throw new Error(`Duplicate starter: ${config.name}\n` + - `> Starter 1: ${original.path}\n` + - `> Starter 2: ${path.dirname(configFile)}`); - } - this.starters.set(config.name, { - name: config.name, - config, - path: path.dirname(configFile), - configPath: configFile, - }); - const module = this.modules.find(module => module.name === config.module); - if (module) { - module.starters.push(config.name); - continue; - } - this.modules.push({ - name: config.module, - starters: [config.name], - }); - } - this.modules.sort((a, b) => a.name.localeCompare(b.name)); + constructor() { + this.starters = new Map() + this.modules = [] + const configFiles = glob.sync( + `${StarterLoader.starterPath}/**/node-app.jsonc` + ) + for (const configFile of configFiles) { + const config = StarterLoader.validateConfig( + configFile, + JSON.parse(fs.readFileSync(configFile, 'utf8')) + ) + const original = this.starters.get(config.name) + if (original) { + throw new Error( + `Duplicate starter: ${config.name}\n` + + `> Starter 1: ${original.path}\n` + + `> Starter 2: ${path.dirname(configFile)}` + ) + } + this.starters.set(config.name, { + name: config.name, + config, + path: path.dirname(configFile), + configPath: configFile, + }) + const module = this.modules.find(module => module.name === config.module) + if (module) { + module.starters.push(config.name) + continue + } + this.modules.push({ + name: config.module, + starters: [config.name], + }) } - getOptions() { - return this.modules; + this.modules.sort((a, b) => a.name.localeCompare(b.name)) + } + getOptions() { + return this.modules + } + getStarter(name) { + const starter = this.starters.get(name) + if (!starter) { + throw new Error(`Starter ${name} not found`) } - getStarter(name) { - const starter = this.starters.get(name); - if (!starter) { - throw new Error(`Starter ${name} not found`); - } - return starter; + return starter + } + static validateConfig(path, config) { + if (!config.module) { + throw new Error(`Invalid config at ${path}: module key is required`) } - static validateConfig(path, config) { - if (!config.module) { - throw new Error(`Invalid config at ${path}: module key is required`); - } - if (!config.name) { - throw new Error(`Invalid config at ${path}: name key is required`); - } - if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { - throw new Error(`Invalid config at ${path}: "prebuild" must be array of npm script names or empty`); - } - if (!StarterLoader.isValidOptionalStringArray(config.replace)) { - throw new Error(`Invalid config at ${path}: "replace" must be array of files where strings should be replaced`); - } - const invalidKeys = Object.keys(config).filter(key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key)); - if (invalidKeys.length > 0) { - throw new Error(`Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}`); - } - return config; + if (!config.name) { + throw new Error(`Invalid config at ${path}: name key is required`) } - static isValidOptionalStringArray(array) { - return (!array || - (Array.isArray(array) && - array.every((item) => typeof item === 'string'))); + if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { + throw new Error( + `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` + ) } + if (!StarterLoader.isValidOptionalStringArray(config.replace)) { + throw new Error( + `Invalid config at ${path}: "replace" must be array of files where strings should be replaced` + ) + } + const invalidKeys = Object.keys(config).filter( + key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key) + ) + if (invalidKeys.length > 0) { + throw new Error( + `Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}` + ) + } + return config + } + static isValidOptionalStringArray(array) { + return ( + !array || + (Array.isArray(array) && array.every(item => typeof item === 'string')) + ) + } } -StarterLoader.starterPath = path.normalize(path.join(import.meta.dirname, '..', 'starter')); -//# sourceMappingURL=StarterLoader.js.map \ No newline at end of file +StarterLoader.starterPath = path.normalize( + path.join(import.meta.dirname, '..', 'starter') +) +//# sourceMappingURL=StarterLoader.js.map diff --git a/lib/types.js b/lib/types.js index 718fd38..4ef431e 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1,2 +1,2 @@ -export {}; -//# sourceMappingURL=types.js.map \ No newline at end of file +export {} +//# sourceMappingURL=types.js.map diff --git a/starter/_base/README.md b/starter/_base/README.md index d0e7e21..00fe1d4 100644 --- a/starter/_base/README.md +++ b/starter/_base/README.md @@ -1,15 +1,18 @@ # Create-Node-App Project -Node.js application bootstrapped with [create-node-app](https://github.com/AckeeCZ/create-node-app) + +Node.js application bootstrapped with [create-node-app](https://github.com/AckeeCZ/create-node-app) ## 🏗️ Architecture -All domain logic should live inside the [domain folder](src/domain) and should be accessed only via [container](src/container.ts). Each external dependency should be referenced by its own port inside the [domain/port](src/domain/ports) folder. These ports are also part of the container and can be implemented by any external dependency inside the [adapters](src/adapters/) folder. + +All domain logic should live inside the [domain folder](src/domain) and should be accessed only via [container](src/container.ts). Each external dependency should be referenced by its own port inside the [domain/port](src/domain/ports) folder. These ports are also part of the container and can be implemented by any external dependency inside the [adapters](src/adapters/) folder. The application should be accessed through the [view](src/view/) layer of the application. All UI and interfaces should be defined there. The main entrypoint is defined in the [index.ts](src/index.ts) file. Each domain service should always accept [RequestContext](src/context.ts) as the first parameter. Part of the context is also the container with all of the application dependencies. ## 👷 Development -Application configuration is handled by [Configuru](https://github.com/AckeeCZ/configuru). All env options that can be changed or need to be set up are described in [.env.jsonc](.env.jsonc). + +Application configuration is handled by [Configuru](https://github.com/AckeeCZ/configuru). All env options that can be changed or need to be set up are described in [.env.jsonc](.env.jsonc). The whole codebase is checked and improved using lint and prettier, run `lint:fix` and `prettier:fix` before committing changes to git. @@ -19,25 +22,32 @@ npm run prettier:fix ``` ## ✅ Tests + Test are written using [mocha](https://github.com/mochajs/mocha). They are maintained in two ways: - - **unit tests** should be next to the tested file with `test.ts` suffix, - - **integration tests** should be located inside the [test](src/test/) folder. + +- **unit tests** should be next to the tested file with `test.ts` suffix, +- **integration tests** should be located inside the [test](src/test/) folder. Use mocked container during integration tests. If you need any prerequisites during tests, use the [setup.ts](src/test/setup.ts) file. To run tests use the test command: + ```bash npm run test ``` ## 🚀 Quick start + 1. Build the code + ```bash npm run build ``` + 2. Start the entrypoint / server + ```bash npm run start -``` +``` From 7aef116bed3bd126092fd19767b8efc96ec03378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 22:29:07 +0200 Subject: [PATCH 14/28] =?UTF-8?q?=F0=9F=93=9D=20Update=20spec=20to=20new?= =?UTF-8?q?=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 71471f6..3918ba0 100644 --- a/README.md +++ b/README.md @@ -6,34 +6,34 @@ # Create Node App -CLI to help you set up Node.js TypeScript project. Set up project includes +CLI to help you set up a Node.js TypeScript project. The setup includes: - code style tools (prettier, lint) -- testing (using jest) -- infrastructure files of your choice (Docker, etc.) -- GitLab CI and npm ci-\* scripts (for Ackee CI/CD pipelines) +- testing (using mocha) +- infrastructure files of your choice (PostgreSQL etc.) +- CI pipeline templates (based on Ackee GitLab CI/CD pipelines) ## Usage Run directly from GitHub repo via npx: ``` -Usage: npm exec --ignore-scripts -- github:AckeeCZ/create-node-app STARTER [OPTIONS] [DIRECTORY] - -STARTER Which template to setup (required) +Usage: npm exec --ignore-scripts -- github:AckeeCZ/create-node-app [OPTIONS] Options: - --dir, -d DIR Destination directory (default: ./node-app) - --project-name, -n NAME Google Cloud project name (default: directory basename) - --force, -f Overwrite existing destination directory if it's not empty - --help, -h Show this help message - -Starters available: - cloudrun Cloud Run + express - cloudrun-graphql Cloud Run + graphql + --dir, -d PATH Specifies directory path for app (default: node-app) + --project-name, -n NAME Google Cloud project name (default: same as directory) + --force, -f Overwrite existing destination directory if it's not empty + --debug -D Enables debug logging + --help, -h Show this help message ``` -Supported starter templates: +## Setup options -- [Cloud Run](./starter/cloudrun/README.md) -- [GraphQL Cloud Run](./starter/cloudrun-graphql/README.md) +- API layer + - [RESTful](starter/api/rest/) + - [GraphQL](starter/api/graphql/) +- Infrastructure + - [PostgreSQL](starter/infra/postgresql-knex/) using [Knex](https://github.com/knex/knex) +- Pipelines + - [GitLab CloudRun](starter/pipeline/cloudrun-gitlab/) From 9d0caeb4787101f6ade68a97ce8f70457f60359f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 23:01:29 +0200 Subject: [PATCH 15/28] =?UTF-8?q?=E2=9C=A8=20Add=20all=20starters=20to=20C?= =?UTF-8?q?LI=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/create-projects.yml | 8 ++- lib/Bootstrap.js | 33 ++++++++-- lib/Bootstrap.js.map | 2 +- lib/StarterLoader.js | 19 +++--- lib/StarterLoader.js.map | 2 +- src/Bootstrap.ts | 63 ++++++++++++++----- src/StarterLoader.ts | 20 +++--- starter/api/graphql/node-app.jsonc | 1 + starter/api/rest/node-app.jsonc | 1 + starter/infra/postgresql-knex/node-app.jsonc | 1 + .../pipeline/cloudrun-gitlab/node-app.jsonc | 1 + 11 files changed, 111 insertions(+), 40 deletions(-) diff --git a/.github/workflows/create-projects.yml b/.github/workflows/create-projects.yml index a089957..a7fb593 100644 --- a/.github/workflows/create-projects.yml +++ b/.github/workflows/create-projects.yml @@ -17,7 +17,13 @@ jobs: strategy: fail-fast: false matrix: - create-app-command: ["create-node-app cloudrun", "create-node-app cloudrun-graphql"] + create-app-command: + - "create-node-app -f --api rest --database postgres-knex --pipeline cloudrun-gitlab" + - "create-node-app -f --api graphql --database postgres-knex --pipeline cloudrun-gitlab" + - "create-node-app -f --api rest --database none --pipeline cloudrun-gitlab" + - "create-node-app -f --api graphql --database none --pipeline cloudrun-gitlab" + - "create-node-app -f --api rest --database none --pipeline none" + - "create-node-app -f --api none --database none --pipeline cloudrun-gitlab" runs-on: ubuntu-latest steps: diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index 113e0fd..45ee72a 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -14,7 +14,7 @@ export class Bootstrap { } async runCLI(args) { try { - const cli = yargs(hideBin(args)) + let cli = yargs(hideBin(args)) .usage('create-node-app [options]') .option('dir', { type: 'string', @@ -41,8 +41,25 @@ export class Bootstrap { description: "Overwrite existing destination directory if it's not empty", }) + const starterOptions = this.starterLoader.getOptions() + for (const module of starterOptions) { + cli = cli.option(module.name.toLowerCase(), { + type: 'string', + choices: ['none', ...module.starters], + description: `Selects ${module.name}`, + }) + } + cli = cli .version('1.0.0') .help() + .check(argv => { + for (const [key, val] of Object.entries(argv)) { + if (Array.isArray(val) && key !== '_') { + throw new Error(`Option --${key} specified multiple times`) + } + } + return true + }) const parsedArgs = cli.parseSync() const destination = path.normalize(parsedArgs.dir) const logger = new Logger(parsedArgs.debug) @@ -61,17 +78,23 @@ export class Bootstrap { } } } - for (const module of this.starterLoader.getOptions()) { + for (const module of starterOptions) { + const moduleOption = parsedArgs[module.name.toLowerCase()] + if (moduleOption) { + if (moduleOption !== 'none') { + starters.push(this.starterLoader.getStarter(moduleOption)) + } + continue + } const answer = await inquirer.prompt({ type: 'list', name: 'starter', message: `Which ${module.name} would you like to use?`, choices: [...module.starters, 'none'], }) - if (answer.starter === 'none') { - continue + if (answer.starter !== 'none') { + starters.push(this.starterLoader.getStarter(answer.starter)) } - starters.push(this.starterLoader.getStarter(answer.starter)) } const builder = new Builder({ npm, diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 2776275..57f8c3b 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/StarterLoader.js b/lib/StarterLoader.js index ea1ee71..15810c8 100644 --- a/lib/StarterLoader.js +++ b/lib/StarterLoader.js @@ -13,7 +13,7 @@ export class StarterLoader { configFile, JSON.parse(fs.readFileSync(configFile, 'utf8')) ) - const original = this.starters.get(config.name) + const original = this.starters.get(config.id) if (original) { throw new Error( `Duplicate starter: ${config.name}\n` + @@ -21,7 +21,7 @@ export class StarterLoader { `> Starter 2: ${path.dirname(configFile)}` ) } - this.starters.set(config.name, { + this.starters.set(config.id, { name: config.name, config, path: path.dirname(configFile), @@ -29,12 +29,12 @@ export class StarterLoader { }) const module = this.modules.find(module => module.name === config.module) if (module) { - module.starters.push(config.name) + module.starters.push(config.id) continue } this.modules.push({ name: config.module, - starters: [config.name], + starters: [config.id], }) } this.modules.sort((a, b) => a.name.localeCompare(b.name)) @@ -42,10 +42,10 @@ export class StarterLoader { getOptions() { return this.modules } - getStarter(name) { - const starter = this.starters.get(name) + getStarter(id) { + const starter = this.starters.get(id) if (!starter) { - throw new Error(`Starter ${name} not found`) + throw new Error(`Starter ${id} not found`) } return starter } @@ -56,6 +56,9 @@ export class StarterLoader { if (!config.name) { throw new Error(`Invalid config at ${path}: name key is required`) } + if (!config.id) { + throw new Error(`Invalid config at ${path}: id key is required`) + } if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { throw new Error( `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` @@ -67,7 +70,7 @@ export class StarterLoader { ) } const invalidKeys = Object.keys(config).filter( - key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key) + key => !['module', 'name', 'prebuild', 'replace', 'id'].includes(key) ) if (invalidKeys.length > 0) { throw new Error( diff --git a/lib/StarterLoader.js.map b/lib/StarterLoader.js.map index 994eb88..85d6ea7 100644 --- a/lib/StarterLoader.js.map +++ b/lib/StarterLoader.js.map @@ -1 +1 @@ -{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/Bootstrap.ts b/src/Bootstrap.ts index 0b0ecf9..0e0da07 100644 --- a/src/Bootstrap.ts +++ b/src/Bootstrap.ts @@ -13,9 +13,35 @@ import { Path } from './types.js' export class Bootstrap { protected starterLoader = new StarterLoader() + private async askMissingOptions(parsedArgs: any) { + const starters: LoadedStarter[] = [] + + for (const module of this.starterLoader.getOptions()) { + const moduleOption = parsedArgs[module.name.toLowerCase()] as string + if (moduleOption) { + if (moduleOption !== 'none') { + starters.push(this.starterLoader.getStarter(moduleOption)) + } + continue + } + + const answer = await inquirer.prompt<{ starter: string }>({ + type: 'list', + name: 'starter', + message: `Which ${module.name} would you like to use?`, + choices: [...module.starters, 'none'], + }) + + if (answer.starter !== 'none') { + starters.push(this.starterLoader.getStarter(answer.starter)) + } + } + return starters + } + public async runCLI(args: string[]) { try { - const cli = yargs(hideBin(args)) + let cli = yargs(hideBin(args)) .usage('create-node-app [options]') .option('dir', { type: 'string', @@ -42,8 +68,27 @@ export class Bootstrap { description: "Overwrite existing destination directory if it's not empty", }) + + const starterOptions = this.starterLoader.getOptions() + for (const module of starterOptions) { + cli = cli.option(module.name.toLowerCase(), { + type: 'string', + choices: ['none', ...module.starters], + description: `Selects ${module.name}`, + }) + } + + cli = cli .version('1.0.0') .help() + .check(argv => { + for (const [key, val] of Object.entries(argv)) { + if (Array.isArray(val) && key !== '_') { + throw new Error(`Option --${key} specified multiple times`) + } + } + return true + }) const parsedArgs = cli.parseSync() @@ -53,8 +98,6 @@ export class Bootstrap { const npm = new Npm({ dir: destination, logger }) const packageJson = new PackageJson(npm) - const starters: LoadedStarter[] = [] - if (fs.existsSync(destination) && !parsedArgs.force) { const answer = await inquirer.prompt<{ force: boolean }>({ type: 'confirm', @@ -66,19 +109,7 @@ export class Bootstrap { } } - for (const module of this.starterLoader.getOptions()) { - const answer = await inquirer.prompt<{ starter: string }>({ - type: 'list', - name: 'starter', - message: `Which ${module.name} would you like to use?`, - choices: [...module.starters, 'none'], - }) - - if (answer.starter === 'none') { - continue - } - starters.push(this.starterLoader.getStarter(answer.starter)) - } + const starters = await this.askMissingOptions(parsedArgs) const builder = new Builder({ npm, diff --git a/src/StarterLoader.ts b/src/StarterLoader.ts index 3ae00d5..4aa8997 100644 --- a/src/StarterLoader.ts +++ b/src/StarterLoader.ts @@ -5,6 +5,7 @@ import path from 'node:path' export interface StarterConfig { module: string name: string + id: string prebuild?: string[] replace?: string[] merge?: string[] @@ -41,7 +42,7 @@ export class StarterLoader { JSON.parse(fs.readFileSync(configFile, 'utf8')) ) - const original = this.starters.get(config.name) + const original = this.starters.get(config.id) if (original) { throw new Error( `Duplicate starter: ${config.name}\n` + @@ -50,7 +51,7 @@ export class StarterLoader { ) } - this.starters.set(config.name, { + this.starters.set(config.id, { name: config.name, config, path: path.dirname(configFile), @@ -59,12 +60,12 @@ export class StarterLoader { const module = this.modules.find(module => module.name === config.module) if (module) { - module.starters.push(config.name) + module.starters.push(config.id) continue } this.modules.push({ name: config.module, - starters: [config.name], + starters: [config.id], }) } @@ -75,10 +76,10 @@ export class StarterLoader { return this.modules } - getStarter(name: string): LoadedStarter { - const starter = this.starters.get(name) + getStarter(id: string): LoadedStarter { + const starter = this.starters.get(id) if (!starter) { - throw new Error(`Starter ${name} not found`) + throw new Error(`Starter ${id} not found`) } return starter } @@ -90,6 +91,9 @@ export class StarterLoader { if (!config.name) { throw new Error(`Invalid config at ${path}: name key is required`) } + if (!config.id) { + throw new Error(`Invalid config at ${path}: id key is required`) + } if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { throw new Error( `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` @@ -103,7 +107,7 @@ export class StarterLoader { } const invalidKeys = Object.keys(config).filter( - key => !['module', 'name', 'prebuild', 'replace', 'merge'].includes(key) + key => !['module', 'name', 'prebuild', 'replace', 'id'].includes(key) ) if (invalidKeys.length > 0) { throw new Error( diff --git a/starter/api/graphql/node-app.jsonc b/starter/api/graphql/node-app.jsonc index 212590f..52a7d12 100644 --- a/starter/api/graphql/node-app.jsonc +++ b/starter/api/graphql/node-app.jsonc @@ -1,5 +1,6 @@ { "module": "API", + "id": "graphql", "name": "GraphQL", "prebuild": ["generate:api"] } diff --git a/starter/api/rest/node-app.jsonc b/starter/api/rest/node-app.jsonc index bb5c73c..64cacc8 100644 --- a/starter/api/rest/node-app.jsonc +++ b/starter/api/rest/node-app.jsonc @@ -1,5 +1,6 @@ { "module": "API", + "id": "rest", "name": "RESTful", "prebuild": ["generate:api"] } diff --git a/starter/infra/postgresql-knex/node-app.jsonc b/starter/infra/postgresql-knex/node-app.jsonc index 42d7eb3..a51823b 100644 --- a/starter/infra/postgresql-knex/node-app.jsonc +++ b/starter/infra/postgresql-knex/node-app.jsonc @@ -1,5 +1,6 @@ { "module": "database", + "id": "postgres-knex", "name": "PostgreSQL with Knex", "replace": ["docker-compose/docker-compose.yml", ".env.jsonc"] } diff --git a/starter/pipeline/cloudrun-gitlab/node-app.jsonc b/starter/pipeline/cloudrun-gitlab/node-app.jsonc index bbf6912..6e78f19 100644 --- a/starter/pipeline/cloudrun-gitlab/node-app.jsonc +++ b/starter/pipeline/cloudrun-gitlab/node-app.jsonc @@ -1,5 +1,6 @@ { "module": "pipeline", + "id": "cloudrun-gitlab", "name": "CloudRun for GitLab", "replace": [".gitlab-ci.yml"] } From 5fe4ce0ea5a31fc702472079a6bc83d58991b895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 23:01:52 +0200 Subject: [PATCH 16/28] =?UTF-8?q?=F0=9F=93=9D=20Add=20CLI=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3918ba0..03adf36 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,19 @@ Run directly from GitHub repo via npx: Usage: npm exec --ignore-scripts -- github:AckeeCZ/create-node-app [OPTIONS] Options: - --dir, -d PATH Specifies directory path for app (default: node-app) - --project-name, -n NAME Google Cloud project name (default: same as directory) - --force, -f Overwrite existing destination directory if it's not empty - --debug -D Enables debug logging - --help, -h Show this help message + -d, --dir Destination directory [string] [default: "./node-app"] + -D, --debug Enables debug logs [boolean] [default: false] + -n, --project-name Google Cloud project name [string] [default: "node-app"] + -f, --force Overwrite existing destination if it's not empty + [boolean] [default: false] + --api Selects API + [string] [choices: "graphql", "rest"] + --database Selects database as database + [string] [choices: "postgres-knex"] + --pipeline Selects pipeline + [string] [choices: "cloudrun-gitlab"] + --version Show version number [boolean] + --help Show help [boolean] ``` ## Setup options @@ -33,7 +41,7 @@ Options: - API layer - [RESTful](starter/api/rest/) - [GraphQL](starter/api/graphql/) -- Infrastructure +- Database - [PostgreSQL](starter/infra/postgresql-knex/) using [Knex](https://github.com/knex/knex) - Pipelines - [GitLab CloudRun](starter/pipeline/cloudrun-gitlab/) From 4c62bc2f78ffc04f9545345bddf2a2397e878a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Wed, 27 Aug 2025 23:15:08 +0200 Subject: [PATCH 17/28] =?UTF-8?q?=F0=9F=93=9D=20Add=20pavel.svagr=20as=20a?= =?UTF-8?q?n=20author?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AUTHORS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 30f14d2..c2ebf77 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ Jiri Smolik Patrik Hoffmann -Roman Filatov \ No newline at end of file +Roman Filatov +Pavel Švagr \ No newline at end of file From e0402ebd517c900f3674801b7cc4844986957c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Thu, 28 Aug 2025 07:21:48 +0200 Subject: [PATCH 18/28] =?UTF-8?q?=F0=9F=91=B7=20Separate=20PostgreSQL=20pi?= =?UTF-8?q?peline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/test-create-project/action.yml | 74 ++++++++++++++++ .github/workflows/create-projects.yml | 84 +++++++++---------- starter/_base/src/index.ts | 1 - .../docker-compose/docker-compose.yml | 2 +- .../src/adapters/knex.database.test.ts | 3 +- 5 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 .github/actions/test-create-project/action.yml diff --git a/.github/actions/test-create-project/action.yml b/.github/actions/test-create-project/action.yml new file mode 100644 index 0000000..778d475 --- /dev/null +++ b/.github/actions/test-create-project/action.yml @@ -0,0 +1,74 @@ +name: 'Create project' +description: 'Runs create-node-project and performs tests' +inputs: + options: + description: 'create-node-app options' + required: true + dbConnection: + description: 'DB_CONNECTION_STRING env var' + default: 'postgresql://postgres:postgres@localhost:5432/postgres' +runs: + using: 'composite' + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: npm- + + - name: Install dependencies + run: npm ci --ignore-scripts + shell: bash + + - name: Build the project + run: npm run build + shell: bash + + - name: Run npm link + run: npm link + shell: bash + + - name: Create project + run: create-node-app ${{ inputs.options }} + shell: bash + + - name: Build created project + working-directory: ./node-app + run: npm run build + shell: bash + + - name: Run lint + working-directory: ./node-app + run: npm run ci-lint + shell: bash + + - name: Run tests + working-directory: ./node-app + run: npm run ci-test + shell: bash + env: + DB_CONNECTION_STRING: ${{inputs.dbConnection}} + + - name: Run start + if: ${{ !contains(inputs.options, '--api none') }} + working-directory: ./node-app + run: | + npm run start & + start_pid=$! + sleep 10 + kill $start_pid + shell: bash + env: + DB_CONNECTION_STRING: ${{inputs.dbConnection}} + + - name: Run start + if: ${{ contains(inputs.options, '--api none') }} + working-directory: ./node-app + shell: bash + run: npm run start + env: + DB_CONNECTION_STRING: ${{inputs.dbConnection}} diff --git a/.github/workflows/create-projects.yml b/.github/workflows/create-projects.yml index a7fb593..5f80fdc 100644 --- a/.github/workflows/create-projects.yml +++ b/.github/workflows/create-projects.yml @@ -17,54 +17,48 @@ jobs: strategy: fail-fast: false matrix: - create-app-command: - - "create-node-app -f --api rest --database postgres-knex --pipeline cloudrun-gitlab" - - "create-node-app -f --api graphql --database postgres-knex --pipeline cloudrun-gitlab" - - "create-node-app -f --api rest --database none --pipeline cloudrun-gitlab" - - "create-node-app -f --api graphql --database none --pipeline cloudrun-gitlab" - - "create-node-app -f --api rest --database none --pipeline none" - - "create-node-app -f --api none --database none --pipeline cloudrun-gitlab" + create-app-options: + - "--api rest --database none --pipeline cloudrun-gitlab" + - "--api graphql --database none --pipeline cloudrun-gitlab" + - "--api rest --database none --pipeline none" + - "--api none --database none --pipeline cloudrun-gitlab" runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Cache dependencies - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - id: create-node-app + uses: ./.github/actions/test-create-project with: - path: ~/.npm - key: npm-${{ hashFiles('package-lock.json') }} - restore-keys: npm- - - - name: Install dependencies - run: npm ci --ignore-scripts - - - name: Build the project - run: npm run build - - - name: Run npm link - run: npm link - - - name: Create project - run: ${{ matrix.create-app-command }} - - - name: Build created project - working-directory: ./node-app - run: npm run build + options: ${{ matrix.create-app-options }} - - name: Run lint - working-directory: ./node-app - run: npm run ci-lint + create-projects-with-postgres: + needs: "build" + services: + postgres: + image: postgres:17 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: postgres + POSTGRES_USER: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + strategy: + fail-fast: false + matrix: + create-app-options: + - "--api none --database postgres-knex --pipeline none" + - "--api rest --database postgres-knex --pipeline cloudrun-gitlab" + - "--api graphql --database postgres-knex --pipeline cloudrun-gitlab" + runs-on: ubuntu-latest - - name: Run tests - working-directory: ./node-app - run: npm run ci-test - - - name: Run start - working-directory: ./node-app - run: | - npm run start & - start_pid=$! - sleep 10 - kill $start_pid \ No newline at end of file + steps: + - uses: actions/checkout@v4 + - id: create-node-app + uses: ./.github/actions/test-create-project + with: + options: ${{ matrix.create-app-options }} \ No newline at end of file diff --git a/starter/_base/src/index.ts b/starter/_base/src/index.ts index 178937d..1480d81 100644 --- a/starter/_base/src/index.ts +++ b/starter/_base/src/index.ts @@ -1,4 +1,3 @@ -import { config } from './config.js' import { createContainer } from './container.js' import { RequestContext } from './context.js' diff --git a/starter/infra/postgresql-knex/docker-compose/docker-compose.yml b/starter/infra/postgresql-knex/docker-compose/docker-compose.yml index 2a8ca69..6a44bc0 100644 --- a/starter/infra/postgresql-knex/docker-compose/docker-compose.yml +++ b/starter/infra/postgresql-knex/docker-compose/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: postgres: - image: postgres:15 + image: postgres:17 environment: - POSTGRES_DB={{PROJECT_NAME}}_docker - POSTGRES_USER={{PROJECT_NAME}}_docker diff --git a/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts b/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts index 2be4f34..d7f120c 100644 --- a/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts +++ b/starter/infra/postgresql-knex/src/adapters/knex.database.test.ts @@ -14,7 +14,8 @@ describe('knexConnection', () => { }) ) assert(db) - await assert.doesNotReject(db.raw('SELECT version()')) + const version = await db.raw('SELECT version()') + assert(version.rows.length === 1) await assert.doesNotReject(knexConnection.disconnect(db)) }) }) From c9af7d52650a2fb8b2d55fa0f6de957ddf463441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Thu, 28 Aug 2025 11:06:52 +0200 Subject: [PATCH 19/28] =?UTF-8?q?=E2=9C=A8=20Allow=20absolute=20paths=20in?= =?UTF-8?q?=20dir=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 42 +++++++++++++++++++++++------------------- lib/Bootstrap.js.map | 2 +- lib/Builder.js | 2 +- lib/Builder.js.map | 2 +- src/Builder.ts | 2 +- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index 45ee72a..b9f0c23 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -12,6 +12,28 @@ export class Bootstrap { constructor() { this.starterLoader = new StarterLoader() } + async askMissingOptions(parsedArgs) { + const starters = [] + for (const module of this.starterLoader.getOptions()) { + const moduleOption = parsedArgs[module.name.toLowerCase()] + if (moduleOption) { + if (moduleOption !== 'none') { + starters.push(this.starterLoader.getStarter(moduleOption)) + } + continue + } + const answer = await inquirer.prompt({ + type: 'list', + name: 'starter', + message: `Which ${module.name} would you like to use?`, + choices: [...module.starters, 'none'], + }) + if (answer.starter !== 'none') { + starters.push(this.starterLoader.getStarter(answer.starter)) + } + } + return starters + } async runCLI(args) { try { let cli = yargs(hideBin(args)) @@ -65,7 +87,6 @@ export class Bootstrap { const logger = new Logger(parsedArgs.debug) const npm = new Npm({ dir: destination, logger }) const packageJson = new PackageJson(npm) - const starters = [] if (fs.existsSync(destination) && !parsedArgs.force) { if (!parsedArgs.force) { const answer = await inquirer.prompt({ @@ -78,24 +99,7 @@ export class Bootstrap { } } } - for (const module of starterOptions) { - const moduleOption = parsedArgs[module.name.toLowerCase()] - if (moduleOption) { - if (moduleOption !== 'none') { - starters.push(this.starterLoader.getStarter(moduleOption)) - } - continue - } - const answer = await inquirer.prompt({ - type: 'list', - name: 'starter', - message: `Which ${module.name} would you like to use?`, - choices: [...module.starters, 'none'], - }) - if (answer.starter !== 'none') { - starters.push(this.starterLoader.getStarter(answer.starter)) - } - } + const starters = await this.askMissingOptions(parsedArgs) const builder = new Builder({ npm, logger, diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 57f8c3b..9eff7b6 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Builder.js b/lib/Builder.js index 37c2425..af48880 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -62,7 +62,7 @@ export class Builder { } } async buildStarter(starterDir, config) { - const destDir = path.normalize(path.join(process.cwd(), this.destination)) + const destDir = path.normalize(path.resolve(this.destination)) const files = await glob(`${starterDir}/*`, { cwd: starterDir, dot: true, diff --git a/lib/Builder.js.map b/lib/Builder.js.map index f4cb2eb..971c285 100644 --- a/lib/Builder.js.map +++ b/lib/Builder.js.map @@ -1 +1 @@ -{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,aAAa,CAAA;AACjC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAM5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,OAAO,OAAO;IAclB,YAAY,MAOX;QACC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAA;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAA;QAC/B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAA;QACrC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAA;QACrC,IAAI,CAAC,YAAY,GAAG;YAClB,kBAAkB,EAAE,IAAI,CAAC,WAAW;SACrC,CAAA;QACD,IAAI,CAAC,WAAW,GAAG;YACjB,IAAI,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC;YACzE,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;YAClD,IAAI,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC;YACnD,IAAI,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC;SAC1D,CAAA;IACH,CAAC;IAES,KAAK,CAAC,aAAa;QAC3B,IAAI,MAAM,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACjD,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACpD,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IACvD,CAAC;IAEM,KAAK,CAAC,KAAK;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,wBAAwB,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAA;YAExE,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CACtB,4BAA4B,EAC5B,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAC5C,CAAA;YAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACpC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CACtB,UAAU,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,EACxD,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAChD,CAAA;YACH,CAAC;YAED,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAElE,MAAM,eAAe,GAAoB,IAAI,CAAC,QAAQ;iBACnD,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;iBACvC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAA;YAEzC,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;gBACrC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CACtB,WAAW,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,CACjC,CAAA;YACH,CAAC;YAED,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;YAEzE,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,wBAAwB,IAAI,CAAC,QAAQ,CACnC,OAAO,CAAC,GAAG,EAAE,EACb,IAAI,CAAC,WAAW,CACjB,MAAM,CACR,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IAES,KAAK,CAAC,YAAY,CAAC,UAAkB,EAAE,MAAsB;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAA;QAC9D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,UAAU,IAAI,EAAE;YAC1C,GAAG,EAAE,UAAU;YACf,GAAG,EAAE,IAAI;YACT,SAAS,EAAE,KAAK;SACjB,CAAC,CAAA;QAEF,MAAM,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CACpD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAC5B,CAAA;QAED,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAC,MAAM,EAAC,EAAE;YAClC,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE;gBAC1B,OAAO,EAAE,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;aACxC,CAAA;QACH,CAAC,CAAC,CACH,CAAA;QAED,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,GAAG,CAAC,KAAK,EAAC,QAAQ,EAAC,EAAE;YACzB,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpC,OAAM;YACR,CAAC;YACD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;YAChE,IAAI,MAAM,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzC,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;gBACxD,OAAM;YACR,CAAC;YACD,MAAM,EAAE,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;QACrC,CAAC,CAAC,CACH,CAAA;QAED,MAAM,OAAO,CAAC,GAAG,CACf,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAC1E,CAAA;QAED,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAC,QAAQ,EAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CACnE,CAAA;QACH,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,aAAa,CAAC,QAAgB;QACzC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAA;QAChE,IAAI,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACjD,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3D,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAA;QACpD,CAAC,EAAE,OAAO,CAAC,CAAA;QACX,OAAO,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxC,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,UAAkB;QACvD,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACnC,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;QACvC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,QAAQ,IAAI,UAAU,EAAE,CAAC,CAAA;QACrD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC/C,KAAK;YACP,CAAC;iBAAM,CAAC;gBACN,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;;AA3JsB,wBAAgB,GAAG,IAAI,CAAC,SAAS,CACtD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CACzD,CAAA;AACsB,qBAAa,GAAG,CAAC,gBAAgB,CAAC,CAAA"} \ No newline at end of file diff --git a/src/Builder.ts b/src/Builder.ts index 6953a5e..2bc9517 100644 --- a/src/Builder.ts +++ b/src/Builder.ts @@ -101,7 +101,7 @@ export class Builder { } protected async buildStarter(starterDir: string, config?: StarterConfig) { - const destDir = path.normalize(path.join(process.cwd(), this.destination)) + const destDir = path.normalize(path.resolve(this.destination)) const files = await glob(`${starterDir}/*`, { cwd: starterDir, dot: true, From 94956d5aa34ca3f08decbd57a778af1e4f586d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Thu, 28 Aug 2025 14:25:27 +0200 Subject: [PATCH 20/28] =?UTF-8?q?=F0=9F=97=83=EF=B8=8F=20Add=20knex=20migr?= =?UTF-8?q?ation=20and=20seed=20templates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/infra/postgresql-knex/knexfile.ts | 13 +++++++------ starter/infra/postgresql-knex/src/container.ts | 13 ++++++------- .../postgresql-knex/src/db/migration.template.ts | 4 ++++ .../postgresql-knex/src/db/migrations/.gitkeep | 0 .../infra/postgresql-knex/src/db/seed.template.ts | 3 +++ starter/infra/postgresql-knex/src/db/seeds/.gitkeep | 0 6 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 starter/infra/postgresql-knex/src/db/migration.template.ts create mode 100644 starter/infra/postgresql-knex/src/db/migrations/.gitkeep create mode 100644 starter/infra/postgresql-knex/src/db/seed.template.ts create mode 100644 starter/infra/postgresql-knex/src/db/seeds/.gitkeep diff --git a/starter/infra/postgresql-knex/knexfile.ts b/starter/infra/postgresql-knex/knexfile.ts index fb582b9..51d1b85 100644 --- a/starter/infra/postgresql-knex/knexfile.ts +++ b/starter/infra/postgresql-knex/knexfile.ts @@ -1,15 +1,16 @@ -import * as db from './src/app/database' +import { config } from './src/config.js' module.exports = { - ...db.knexConfig, + client: 'pg', + connection: config.db.connectionString, migrations: { - directory: './src/db-migrations', - stub: './src/config/db-migration.template.ts', + directory: './src/db/migrations', + stub: './src/db/migration.template.ts', extension: 'ts', }, seeds: { - directory: './src/db-seeds', - stub: './src/config/db-seed.template.ts', + directory: './src/db/seeds', + stub: './src/db/seed.template.ts', extension: 'ts', }, } diff --git a/starter/infra/postgresql-knex/src/container.ts b/starter/infra/postgresql-knex/src/container.ts index d0ed259..a6324ac 100644 --- a/starter/infra/postgresql-knex/src/container.ts +++ b/starter/infra/postgresql-knex/src/container.ts @@ -1,24 +1,23 @@ -import { config } from './config.js' -import { DbConnection } from './domain/ports/database.js' import { Knex } from 'knex' -import { MigrationRepository } from './domain/ports/repositories/migration.repository.js' import { knexConnection } from './adapters/knex.database.js' import { createMigrationsRepository } from './adapters/repositories/migration.repository.js' +import { config } from './config.js' +import { MigrationRepository } from './domain/ports/repositories/migration.repository.js' export interface Container { - database: DbConnection + database: Knex repositories: { migrations: MigrationRepository } } export const createContainer = async (): Promise => { - const db = await knexConnection.connect(config.db.connectionString) + const database = await knexConnection.connect(config.db.connectionString) return { - database: knexConnection, + database, repositories: { - migrations: createMigrationsRepository(db), + migrations: createMigrationsRepository(database), }, } } diff --git a/starter/infra/postgresql-knex/src/db/migration.template.ts b/starter/infra/postgresql-knex/src/db/migration.template.ts new file mode 100644 index 0000000..da29e13 --- /dev/null +++ b/starter/infra/postgresql-knex/src/db/migration.template.ts @@ -0,0 +1,4 @@ +import { Knex } from 'knex' + +export const up = (knex: Knex) => knex +export const down = () => Promise.resolve(/* no-op */) diff --git a/starter/infra/postgresql-knex/src/db/migrations/.gitkeep b/starter/infra/postgresql-knex/src/db/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/starter/infra/postgresql-knex/src/db/seed.template.ts b/starter/infra/postgresql-knex/src/db/seed.template.ts new file mode 100644 index 0000000..1711ebb --- /dev/null +++ b/starter/infra/postgresql-knex/src/db/seed.template.ts @@ -0,0 +1,3 @@ +import { Knex } from 'knex' + +export const seed = (knex: Knex) => knex diff --git a/starter/infra/postgresql-knex/src/db/seeds/.gitkeep b/starter/infra/postgresql-knex/src/db/seeds/.gitkeep new file mode 100644 index 0000000..e69de29 From 2e04c090a5f6da7dc696e793120d1044d2ba72d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 14:36:42 +0200 Subject: [PATCH 21/28] =?UTF-8?q?=F0=9F=94=88=20Add=20debug=20logging=20in?= =?UTF-8?q?=20PackageJson?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Bootstrap.ts | 2 +- src/PackageJson.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Bootstrap.ts b/src/Bootstrap.ts index 0e0da07..e882108 100644 --- a/src/Bootstrap.ts +++ b/src/Bootstrap.ts @@ -96,7 +96,7 @@ export class Bootstrap { const logger = new Logger(parsedArgs.debug) const npm = new Npm({ dir: destination, logger }) - const packageJson = new PackageJson(npm) + const packageJson = new PackageJson(npm, logger) if (fs.existsSync(destination) && !parsedArgs.force) { const answer = await inquirer.prompt<{ force: boolean }>({ diff --git a/src/PackageJson.ts b/src/PackageJson.ts index 42fdebf..9b92d49 100644 --- a/src/PackageJson.ts +++ b/src/PackageJson.ts @@ -3,17 +3,20 @@ import * as fs from 'fs' import * as lodash from 'lodash-es' import { Npm } from './Npm.js' import { Path } from './types.js' +import { Logger } from './Logger.js' export class PackageJson { public readonly path: Path protected npm: Npm - constructor(npm: Npm) { + protected logger: Logger + constructor(npm: Npm, logger: Logger) { let packagejsonPath = './package.json' as Path if (npm.dir) { packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`) as Path } this.path = packagejsonPath this.npm = npm + this.logger = logger } public setType(type: 'module' | 'commonjs') { this.mergeWith({ @@ -36,7 +39,7 @@ export class PackageJson { // Updated package json using merge with given object public mergeWith(partialWith: any) { const json = lodash.merge(this.toJSON(), partialWith) - // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) + this.logger.debug(`> package.json updated ${JSON.stringify(partialWith)}`) fs.writeFileSync( path.join(this.path), JSON.stringify(json, null, 2), From 0ca8dc65d97472b2dd8a528a6f22af2b9af6912c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 14:41:47 +0200 Subject: [PATCH 22/28] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20readUtf8File?= =?UTF-8?q?=20util?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 2 +- lib/Bootstrap.js.map | 2 +- lib/Builder.js | 2 +- lib/Builder.js.map | 2 +- lib/Files.js | 3 +++ lib/Files.js.map | 2 +- lib/Mergers/ConfigMerger.js | 6 +++--- lib/Mergers/ConfigMerger.js.map | 2 +- lib/Mergers/ContainerMerger.js | 6 +++--- lib/Mergers/ContainerMerger.js.map | 2 +- lib/Mergers/EnvJsoncMerger.js | 6 +++--- lib/Mergers/EnvJsoncMerger.js.map | 2 +- lib/Mergers/Merger.js | 5 ++--- lib/Mergers/Merger.js.map | 2 +- lib/Mergers/PackageJsonMerger.js | 6 +++--- lib/Mergers/PackageJsonMerger.js.map | 2 +- lib/PackageJson.js | 5 +++-- lib/PackageJson.js.map | 2 +- src/Builder.ts | 2 +- src/Files.ts | 8 ++++++++ src/Mergers/ConfigMerger.ts | 6 +++--- src/Mergers/ContainerMerger.ts | 6 +++--- src/Mergers/EnvJsoncMerger.ts | 6 +++--- src/Mergers/Merger.ts | 5 ++--- src/Mergers/PackageJsonMerger.ts | 6 +++--- 25 files changed, 54 insertions(+), 44 deletions(-) diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index b9f0c23..2f5fb01 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -86,7 +86,7 @@ export class Bootstrap { const destination = path.normalize(parsedArgs.dir) const logger = new Logger(parsedArgs.debug) const npm = new Npm({ dir: destination, logger }) - const packageJson = new PackageJson(npm) + const packageJson = new PackageJson(npm, logger) if (fs.existsSync(destination) && !parsedArgs.force) { if (!parsedArgs.force) { const answer = await inquirer.prompt({ diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 9eff7b6..7d048e2 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Builder.js b/lib/Builder.js index af48880..dc20e7e 100644 --- a/lib/Builder.js +++ b/lib/Builder.js @@ -103,7 +103,7 @@ export class Builder { } async replaceInFile(filePath) { filePath = path.normalize(path.join(this.destination, filePath)) - let content = await fs.readFile(filePath, 'utf8') + let content = await Files.readUtf8File(filePath) content = Object.keys(this.replacements).reduce((acc, key) => { return acc.replaceAll(key, this.replacements[key]) }, content) diff --git a/lib/Builder.js.map b/lib/Builder.js.map index 971c285..94736f9 100644 --- a/lib/Builder.js.map +++ b/lib/Builder.js.map @@ -1 +1 @@ -{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Files.js b/lib/Files.js index 010e8cd..9b887b3 100644 --- a/lib/Files.js +++ b/lib/Files.js @@ -8,5 +8,8 @@ export class Files { const stat = await fsp.stat(path).catch(() => undefined) return Boolean(stat?.isDirectory()) } + static async readUtf8File(path) { + return fsp.readFile(path, 'utf8') + } } //# sourceMappingURL=Files.js.map diff --git a/lib/Files.js.map b/lib/Files.js.map index a94edd1..84880b4 100644 --- a/lib/Files.js.map +++ b/lib/Files.js.map @@ -1 +1 @@ -{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAElC,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAY;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAY;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAElC,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAY;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAY;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;IACM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAY;QAC3C,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/ConfigMerger.js b/lib/Mergers/ConfigMerger.js index 65384d1..f46439a 100644 --- a/lib/Mergers/ConfigMerger.js +++ b/lib/Mergers/ConfigMerger.js @@ -1,5 +1,5 @@ +import { Files } from '../Files.js' import { Merger } from './Merger.js' -import fsp from 'fs/promises' export class ConfigMerger extends Merger { constructor() { super(...arguments) @@ -12,8 +12,8 @@ export class ConfigMerger extends Merger { } const { originPath, destPath } = this.getPaths(originDir) const [destTsConfig, originTsConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const configContent = /configSchema = {\n(.*?)\n}\n/s.exec( originTsConfig diff --git a/lib/Mergers/ConfigMerger.js.map b/lib/Mergers/ConfigMerger.js.map index c26e47f..8925085 100644 --- a/lib/Mergers/ConfigMerger.js.map +++ b/lib/Mergers/ConfigMerger.js.map @@ -1 +1 @@ -{"version":3,"file":"ConfigMerger.js","sourceRoot":"","sources":["../../src/Mergers/ConfigMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,YAAa,SAAQ,MAAM;IAAxC;;QACE,SAAI,GAAG,QAAQ,CAAA;IAuBjB,CAAC;IArBC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QACF,MAAM,aAAa,GAAG,+BAA+B,CAAC,IAAI,CACxD,cAAc,CACf,EAAE,CAAC,CAAC,CAAC,CAAA;QAEN,OAAO,YAAY,CAAC,OAAO,CACzB,OAAO,EACP,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,OAAO,CAAC,CAAC,CAAC,KAAK,CAChD,CAAA;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"ConfigMerger.js","sourceRoot":"","sources":["../../src/Mergers/ConfigMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,OAAO,YAAa,SAAQ,MAAM;IAAxC;;QACE,SAAI,GAAG,QAAQ,CAAA;IAuBjB,CAAC;IArBC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QACF,MAAM,aAAa,GAAG,+BAA+B,CAAC,IAAI,CACxD,cAAc,CACf,EAAE,CAAC,CAAC,CAAC,CAAA;QAEN,OAAO,YAAY,CAAC,OAAO,CACzB,OAAO,EACP,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,OAAO,CAAC,CAAC,CAAC,KAAK,CAChD,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/ContainerMerger.js b/lib/Mergers/ContainerMerger.js index 8ddcac9..b861a54 100644 --- a/lib/Mergers/ContainerMerger.js +++ b/lib/Mergers/ContainerMerger.js @@ -1,6 +1,6 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' import * as ts from 'typescript' +import { Files } from '../Files.js' export class ContainerMerger extends Merger { async merge(originDir) { const content = await this.getWhichExistsOrNull(originDir) @@ -9,8 +9,8 @@ export class ContainerMerger extends Merger { } const { originPath, destPath } = this.getPaths(originDir) const [originContainer, destContainer] = await Promise.all([ - fsp.readFile(originPath, 'utf8'), - fsp.readFile(destPath, 'utf8'), + Files.readUtf8File(originPath), + Files.readUtf8File(destPath), ]) const originAst = this.parseFile(originContainer) const destAst = this.parseFile(destContainer) diff --git a/lib/Mergers/ContainerMerger.js.map b/lib/Mergers/ContainerMerger.js.map index 5fa3dff..64849b6 100644 --- a/lib/Mergers/ContainerMerger.js.map +++ b/lib/Mergers/ContainerMerger.js.map @@ -1 +1 @@ -{"version":3,"file":"ContainerMerger.js","sourceRoot":"","sources":["../../src/Mergers/ContainerMerger.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"ContainerMerger.js","sourceRoot":"","sources":["../../src/Mergers/ContainerMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,YAAY,CAAA;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAO,eAAgB,SAAQ,MAAM;IACzC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;YAC9B,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;SAC7B,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAA;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAA;QAE7C,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC3D,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC/D,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAE7D,OAAO,GAAG,aAAa,OAAO,eAAe,kEAAkE,cAAc,IAAI,CAAA;IACnI,CAAC;IAES,SAAS,CAAC,OAAe;QACjC,OAAO,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAC9E,CAAC;IAES,YAAY,CACpB,SAAwB,EACxB,OAAsB;QAEtB,MAAM,OAAO,GAAa,EAAE,CAAA;QAE5B,MAAM,UAAU,GAAG,CAAC,IAAa,EAAE,EAAE;YACnC,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;gBAC7B,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;YACjC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YAC1B,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;QAC/B,CAAC,CAAA;QAED,UAAU,CAAC,SAAS,CAAC,CAAA;QACrB,UAAU,CAAC,OAAO,CAAC,CAAA;QAEnB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3B,CAAC;IAES,cAAc,CACtB,SAAwB,EACxB,OAAsB;QAEtB,MAAM,UAAU,GAAa,EAAE,CAAA;QAE/B,MAAM,iBAAiB,GAAG,CAAC,IAAa,EAAE,EAAE;YAC1C,IAAI,CAAC,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBACvE,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;gBACpC,OAAM;YACR,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;gBAC5B,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC;oBACpC,OAAM;gBACR,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAA;gBACxC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACnC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBAC3B,CAAC;YACH,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAA;QACtC,CAAC,CAAA;QAED,iBAAiB,CAAC,SAAS,CAAC,CAAA;QAC5B,iBAAiB,CAAC,OAAO,CAAC,CAAA;QAE1B,OAAO,mCAAmC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAA;IACxE,CAAC;IAES,aAAa,CACrB,SAAwB,EACxB,OAAsB;QAEtB,MAAM,kBAAkB,GAAG,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAA;QAC9D,MAAM,gBAAgB,GAAG,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAA;QAE1D,MAAM,kBAAkB,GAAG;YACzB,GAAG,kBAAkB,CAAC,OAAO;YAC7B,GAAG,gBAAgB,CAAC,OAAO;SAC5B,CAAA;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAClD,kBAAkB,CAAC,WAAW,EAC9B,gBAAgB,CAAC,WAAW,CAC7B,CAAA;QAED,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,IAAI,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,IAAI,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAA;QAChD,CAAC;QAED,IAAI,IAAI,mBAAmB,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAA;QAEnE,OAAO,qEAAqE,IAAI,KAAK,CAAA;IACvF,CAAC;IAES,qBAAqB,CAC7B,WAA0C,EAC1C,SAAwC;QAExC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAuC,CAAA;QAElE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YACjC,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;YAC3C,IAAI,QAAQ,EAAE,CAAC;gBACb,WAAW,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YACjC,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;YAC/B,IACE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC;gBAC7B,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,EAC9C,CAAC;gBACD,OAAO,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAA;YAC1C,CAAC;YACD,OAAO,QAAQ,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC;IAES,eAAe,CAAC,IAAiC;QACzD,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;YACvB,CAAC;QACH,CAAC;aAAM,IAAI,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,EAAE,CAAC;YAClD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAA;QACvB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAES,kBAAkB,CAAC,QAAgB;QAC3C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAClC,OAAO,KAAK;aACT,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACnB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;gBAChB,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,MAAM,GAAG,IAAI,CAAA;QACtB,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC,CAAA;IACf,CAAC;IAES,mBAAmB,CAAC,GAAkB;QAI9C,MAAM,OAAO,GAAa,EAAE,CAAA;QAC5B,MAAM,WAAW,GAAkC,EAAE,CAAA;QAErD,MAAM,KAAK,GAAG,CAAC,IAAa,EAAE,EAAE;YAC9B,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC5B,OAAM;YACR,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC/D,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC5B,OAAM;YACR,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAA;YAClC,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtB,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;gBAC5B,OAAM;YACR,CAAC;YAED,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;gBAClC,IAAI,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;oBACvC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;gBAC1C,CAAC;qBAAM,IAAI,IAAI,CAAC,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC9C,0DAA0D;oBAC1D,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;wBAC7C,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACxB,CAAC,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC,CAAC,CAAA;YAEF,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;QAC9B,CAAC,CAAA;QAED,KAAK,CAAC,GAAG,CAAC,CAAA;QACV,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;IACjC,CAAC;IAES,iBAAiB,CAAC,IAAa;QACvC,OAAO,CACL,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,iBAAiB,CAC1C,CAAA;IACH,CAAC;IAES,kBAAkB,CAAC,SAAuB;QAClD,OAAO,CACL,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC;YACjC,CAAC,EAAE,CAAC,qBAAqB,CAAC,SAAS,CAAC;gBAClC,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAC7C,CAAA;IACH,CAAC;IAES,kBAAkB,CAC1B,SAAuB;QAIvB,OAAO,CACL,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;YAC/B,CAAC,CAAC,SAAS,CAAC,UAAU;YACtB,EAAE,CAAC,yBAAyB,CAAC,SAAS,CAAC,UAAU,CAAC,CACnD,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/EnvJsoncMerger.js b/lib/Mergers/EnvJsoncMerger.js index 9d50cba..f1d3fb7 100644 --- a/lib/Mergers/EnvJsoncMerger.js +++ b/lib/Mergers/EnvJsoncMerger.js @@ -1,5 +1,5 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' +import { Files } from '../Files.js' export class EnvJsoncMerger extends Merger { async merge(originDir) { const content = await this.getWhichExistsOrNull(originDir) @@ -8,8 +8,8 @@ export class EnvJsoncMerger extends Merger { } const { originPath, destPath } = this.getPaths(originDir) const [destEnvConfig, originEnvConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') return destEnvConfig diff --git a/lib/Mergers/EnvJsoncMerger.js.map b/lib/Mergers/EnvJsoncMerger.js.map index 3948232..622649c 100644 --- a/lib/Mergers/EnvJsoncMerger.js.map +++ b/lib/Mergers/EnvJsoncMerger.js.map @@ -1 +1 @@ -{"version":3,"file":"EnvJsoncMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsoncMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,cAAe,SAAQ,MAAM;IACxC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"EnvJsoncMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsoncMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAO,cAAe,SAAQ,MAAM;IACxC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/Merger.js b/lib/Mergers/Merger.js index 7b45cce..15c0e37 100644 --- a/lib/Mergers/Merger.js +++ b/lib/Mergers/Merger.js @@ -1,4 +1,3 @@ -import fsp from 'fs/promises' import path from 'path' import { Files } from '../Files.js' export class Merger { @@ -23,10 +22,10 @@ export class Merger { Files.exists(destPath), ]) if (!originExists && destExists) { - return fsp.readFile(destPath, 'utf8') + return Files.readUtf8File(destPath) } if (!destExists && originExists) { - return fsp.readFile(originPath, 'utf8') + return Files.readUtf8File(originPath) } if (!originExists && !destExists) { throw new Error(`No file found to merge: ${this.pathToFile}`) diff --git a/lib/Mergers/Merger.js.map b/lib/Mergers/Merger.js.map index 49d0a38..7c7cced 100644 --- a/lib/Mergers/Merger.js.map +++ b/lib/Mergers/Merger.js.map @@ -1 +1 @@ -{"version":3,"file":"Merger.js","sourceRoot":"","sources":["../../src/Mergers/Merger.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAgB,MAAM;IAK1B,YACqB,OAAe,EACf,UAAkB;QADlB,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAQ;QAErC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1D,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAES,QAAQ,CAAC,SAAiB;QAClC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;YACjD,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE;SAC7B,CAAA;IACH,CAAC;IAES,KAAK,CAAC,oBAAoB,CAClC,SAAiB;QAEjB,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACvC,CAAC;QAED,IAAI,CAAC,UAAU,IAAI,YAAY,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;QACzC,CAAC;QAED,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"Merger.js","sourceRoot":"","sources":["../../src/Mergers/Merger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAgB,MAAM;IAK1B,YACqB,OAAe,EACf,UAAkB;QADlB,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAQ;QAErC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1D,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAES,QAAQ,CAAC,SAAiB;QAClC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;YACjD,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE;SAC7B,CAAA;IACH,CAAC;IAES,KAAK,CAAC,oBAAoB,CAClC,SAAiB;QAEjB,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;QACrC,CAAC;QAED,IAAI,CAAC,UAAU,IAAI,YAAY,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;QACvC,CAAC;QAED,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/PackageJsonMerger.js b/lib/Mergers/PackageJsonMerger.js index c05c107..de395d1 100644 --- a/lib/Mergers/PackageJsonMerger.js +++ b/lib/Mergers/PackageJsonMerger.js @@ -1,5 +1,5 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' +import { Files } from '../Files.js' export class PackageJsonMerger extends Merger { constructor(projectName, destDir, pathToFile) { super(destDir, pathToFile) @@ -12,8 +12,8 @@ export class PackageJsonMerger extends Merger { } const { originPath, destPath } = this.getPaths(originDir) const [destPckgJson, starterPckgJson] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const destPckgJsonObj = JSON.parse(destPckgJson) const starterPckgJsonObj = JSON.parse(starterPckgJson) diff --git a/lib/Mergers/PackageJsonMerger.js.map b/lib/Mergers/PackageJsonMerger.js.map index 9ee7303..6247886 100644 --- a/lib/Mergers/PackageJsonMerger.js.map +++ b/lib/Mergers/PackageJsonMerger.js.map @@ -1 +1 @@ -{"version":3,"file":"PackageJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/PackageJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,iBAAkB,SAAQ,MAAM;IAC3C,YACmB,WAAmB,EACpC,OAAe,EACf,UAAkB;QAElB,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAJT,gBAAW,GAAX,WAAW,CAAQ;IAKtC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QAEtD,eAAe,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAA;QACvC,eAAe,CAAC,OAAO,GAAG;YACxB,GAAG,eAAe,CAAC,OAAO;YAC1B,GAAG,kBAAkB,CAAC,OAAO;SAC9B,CAAA;QACD,eAAe,CAAC,YAAY,GAAG;YAC7B,GAAG,eAAe,CAAC,YAAY;YAC/B,GAAG,kBAAkB,CAAC,YAAY;SACnC,CAAA;QACD,eAAe,CAAC,eAAe,GAAG;YAChC,GAAG,eAAe,CAAC,eAAe;YAClC,GAAG,kBAAkB,CAAC,eAAe;SACtC,CAAA;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"PackageJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/PackageJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAO,iBAAkB,SAAQ,MAAM;IAC3C,YACmB,WAAmB,EACpC,OAAe,EACf,UAAkB;QAElB,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAJT,gBAAW,GAAX,WAAW,CAAQ;IAKtC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QAEtD,eAAe,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAA;QACvC,eAAe,CAAC,OAAO,GAAG;YACxB,GAAG,eAAe,CAAC,OAAO;YAC1B,GAAG,kBAAkB,CAAC,OAAO;SAC9B,CAAA;QACD,eAAe,CAAC,YAAY,GAAG;YAC7B,GAAG,eAAe,CAAC,YAAY;YAC/B,GAAG,kBAAkB,CAAC,YAAY;SACnC,CAAA;QACD,eAAe,CAAC,eAAe,GAAG;YAChC,GAAG,eAAe,CAAC,eAAe;YAClC,GAAG,kBAAkB,CAAC,eAAe;SACtC,CAAA;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/PackageJson.js b/lib/PackageJson.js index db779de..e44b644 100644 --- a/lib/PackageJson.js +++ b/lib/PackageJson.js @@ -2,13 +2,14 @@ import * as path from 'path' import * as fs from 'fs' import * as lodash from 'lodash-es' export class PackageJson { - constructor(npm) { + constructor(npm, logger) { let packagejsonPath = './package.json' if (npm.dir) { packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`) } this.path = packagejsonPath this.npm = npm + this.logger = logger } setType(type) { this.mergeWith({ @@ -31,7 +32,7 @@ export class PackageJson { // Updated package json using merge with given object mergeWith(partialWith) { const json = lodash.merge(this.toJSON(), partialWith) - // logger.info(`> package.json updated ${JSON.stringify(partialWith)}`) + this.logger.debug(`> package.json updated ${JSON.stringify(partialWith)}`) fs.writeFileSync( path.join(this.path), JSON.stringify(json, null, 2), diff --git a/lib/PackageJson.js.map b/lib/PackageJson.js.map index c9cbfff..87c2bb2 100644 --- a/lib/PackageJson.js.map +++ b/lib/PackageJson.js.map @@ -1 +1 @@ -{"version":3,"file":"PackageJson.js","sourceRoot":"","sources":["../src/PackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AAInC,MAAM,OAAO,WAAW;IAGtB,YAAY,GAAQ;QAClB,IAAI,eAAe,GAAG,gBAAwB,CAAA;QAC9C,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,eAAe,EAAE,CAAS,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;IAChB,CAAC;IACM,OAAO,CAAC,IAA2B;QACxC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IACM,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IACM,SAAS,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IACpC,CAAC;IACM,YAAY,CAAC,IAAY,EAAE,OAAe;QAC/C,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE;gBACP,CAAC,IAAI,CAAC,EAAE,OAAO;aAChB;SACF,CAAC,CAAA;IACJ,CAAC;IACD,qDAAqD;IAC9C,SAAS,CAAC,WAAgB;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;QACrD,uEAAuE;QACvE,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACpB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAA;IACH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"PackageJson.js","sourceRoot":"","sources":["../src/PackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AAKnC,MAAM,OAAO,WAAW;IAItB,YAAY,GAAQ,EAAE,MAAc;QAClC,IAAI,eAAe,GAAG,gBAAwB,CAAA;QAC9C,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,eAAe,EAAE,CAAS,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IACM,OAAO,CAAC,IAA2B;QACxC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IACM,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IACM,SAAS,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IACpC,CAAC;IACM,YAAY,CAAC,IAAY,EAAE,OAAe;QAC/C,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE;gBACP,CAAC,IAAI,CAAC,EAAE,OAAO;aAChB;SACF,CAAC,CAAA;IACJ,CAAC;IACD,qDAAqD;IAC9C,SAAS,CAAC,WAAgB;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;QACrD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAC1E,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACpB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/src/Builder.ts b/src/Builder.ts index 2bc9517..7ab4ae7 100644 --- a/src/Builder.ts +++ b/src/Builder.ts @@ -148,7 +148,7 @@ export class Builder { public async replaceInFile(filePath: string) { filePath = path.normalize(path.join(this.destination, filePath)) - let content = await fs.readFile(filePath, 'utf8') + let content = await Files.readUtf8File(filePath) content = Object.keys(this.replacements).reduce((acc, key) => { return acc.replaceAll(key, this.replacements[key]) }, content) diff --git a/src/Files.ts b/src/Files.ts index cebe52a..e03c317 100644 --- a/src/Files.ts +++ b/src/Files.ts @@ -1,6 +1,11 @@ import * as fsp from 'fs/promises' +import path from 'path' export class Files { + public static readonly PROJECT_DIR = path.normalize( + path.join(import.meta.dirname, '..') + ) + public static async exists(path: string) { const stat = await fsp.stat(path).catch(() => undefined) return Boolean(stat) @@ -10,4 +15,7 @@ export class Files { const stat = await fsp.stat(path).catch(() => undefined) return Boolean(stat?.isDirectory()) } + public static async readUtf8File(path: string) { + return fsp.readFile(path, 'utf8') + } } diff --git a/src/Mergers/ConfigMerger.ts b/src/Mergers/ConfigMerger.ts index e9ca597..81a9270 100644 --- a/src/Mergers/ConfigMerger.ts +++ b/src/Mergers/ConfigMerger.ts @@ -1,5 +1,5 @@ +import { Files } from '../Files.js' import { Merger } from './Merger.js' -import fsp from 'fs/promises' export class ConfigMerger extends Merger { name = 'Config' @@ -13,8 +13,8 @@ export class ConfigMerger extends Merger { const { originPath, destPath } = this.getPaths(originDir) const [destTsConfig, originTsConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const configContent = /configSchema = {\n(.*?)\n}\n/s.exec( originTsConfig diff --git a/src/Mergers/ContainerMerger.ts b/src/Mergers/ContainerMerger.ts index a17642b..2d5b990 100644 --- a/src/Mergers/ContainerMerger.ts +++ b/src/Mergers/ContainerMerger.ts @@ -1,6 +1,6 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' import * as ts from 'typescript' +import { Files } from '../Files.js' export class ContainerMerger extends Merger { async merge(originDir: string): Promise { @@ -12,8 +12,8 @@ export class ContainerMerger extends Merger { const { originPath, destPath } = this.getPaths(originDir) const [originContainer, destContainer] = await Promise.all([ - fsp.readFile(originPath, 'utf8'), - fsp.readFile(destPath, 'utf8'), + Files.readUtf8File(originPath), + Files.readUtf8File(destPath), ]) const originAst = this.parseFile(originContainer) diff --git a/src/Mergers/EnvJsoncMerger.ts b/src/Mergers/EnvJsoncMerger.ts index 5bed6e5..5f3405d 100644 --- a/src/Mergers/EnvJsoncMerger.ts +++ b/src/Mergers/EnvJsoncMerger.ts @@ -1,5 +1,5 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' +import { Files } from '../Files.js' export class EnvJsoncMerger extends Merger { async merge(originDir: string): Promise { @@ -11,8 +11,8 @@ export class EnvJsoncMerger extends Merger { const { originPath, destPath } = this.getPaths(originDir) const [destEnvConfig, originEnvConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') diff --git a/src/Mergers/Merger.ts b/src/Mergers/Merger.ts index f8a3a3b..02d6f05 100644 --- a/src/Mergers/Merger.ts +++ b/src/Mergers/Merger.ts @@ -1,4 +1,3 @@ -import fsp from 'fs/promises' import path from 'path' import { Files } from '../Files.js' @@ -36,11 +35,11 @@ export abstract class Merger { ]) if (!originExists && destExists) { - return fsp.readFile(destPath, 'utf8') + return Files.readUtf8File(destPath) } if (!destExists && originExists) { - return fsp.readFile(originPath, 'utf8') + return Files.readUtf8File(originPath) } if (!originExists && !destExists) { diff --git a/src/Mergers/PackageJsonMerger.ts b/src/Mergers/PackageJsonMerger.ts index 0f584a2..c06131e 100644 --- a/src/Mergers/PackageJsonMerger.ts +++ b/src/Mergers/PackageJsonMerger.ts @@ -1,5 +1,5 @@ import { Merger } from './Merger.js' -import fsp from 'fs/promises' +import { Files } from '../Files.js' export class PackageJsonMerger extends Merger { constructor( @@ -19,8 +19,8 @@ export class PackageJsonMerger extends Merger { const { originPath, destPath } = this.getPaths(originDir) const [destPckgJson, starterPckgJson] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), + Files.readUtf8File(destPath), + Files.readUtf8File(originPath), ]) const destPckgJsonObj = JSON.parse(destPckgJson) From c9b8fd3a66200a9a14e7b414ad64ee3817d5f982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 14:55:59 +0200 Subject: [PATCH 23/28] =?UTF-8?q?=E2=9C=A8=20=20Replace=20validation=20-?= =?UTF-8?q?=20only=20files=20in=20project=20dir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 16 +++++++--------- lib/Bootstrap.js.map | 2 +- lib/Files.js | 18 ++++++++++++------ lib/Files.js.map | 2 +- lib/StarterLoader.js | 30 +++++++++++++++++++++++------- lib/StarterLoader.js.map | 2 +- src/Files.ts | 21 +++++++++++---------- src/StarterLoader.ts | 35 ++++++++++++++++++++++++++++------- 8 files changed, 84 insertions(+), 42 deletions(-) diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js index 2f5fb01..0680bb2 100644 --- a/lib/Bootstrap.js +++ b/lib/Bootstrap.js @@ -88,15 +88,13 @@ export class Bootstrap { const npm = new Npm({ dir: destination, logger }) const packageJson = new PackageJson(npm, logger) if (fs.existsSync(destination) && !parsedArgs.force) { - if (!parsedArgs.force) { - const answer = await inquirer.prompt({ - type: 'confirm', - name: 'force', - message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, - }) - if (!answer.force) { - process.exit(0) - } + const answer = await inquirer.prompt({ + type: 'confirm', + name: 'force', + message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, + }) + if (!answer.force) { + process.exit(0) } } const starters = await this.askMissingOptions(parsedArgs) diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 7d048e2..91852d5 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Files.js b/lib/Files.js index 9b887b3..ac868e4 100644 --- a/lib/Files.js +++ b/lib/Files.js @@ -1,15 +1,21 @@ import * as fsp from 'fs/promises' +import path from 'path' export class Files { - static async exists(path) { - const stat = await fsp.stat(path).catch(() => undefined) + static async exists(filepath) { + const stat = await fsp.stat(filepath).catch(() => undefined) return Boolean(stat) } - static async existsAndIsDir(path) { - const stat = await fsp.stat(path).catch(() => undefined) + static async existsAndIsDir(filepath) { + const stat = await fsp.stat(filepath).catch(() => undefined) return Boolean(stat?.isDirectory()) } - static async readUtf8File(path) { - return fsp.readFile(path, 'utf8') + static async readUtf8File(filepath) { + return fsp.readFile(filepath, 'utf8') + } + static isInSameTree(tree, filepath) { + const file = path.normalize(filepath) + const fileTree = path.normalize(tree) + return file.startsWith(fileTree) } } //# sourceMappingURL=Files.js.map diff --git a/lib/Files.js.map b/lib/Files.js.map index 84880b4..3567e24 100644 --- a/lib/Files.js.map +++ b/lib/Files.js.map @@ -1 +1 @@ -{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAElC,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAY;QACrC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAY;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;IACM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,IAAY;QAC3C,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;IACnC,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAClC,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAgB;QACzC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,QAAgB;QACjD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;IACM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,QAAgB;QAC/C,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACvC,CAAC;IACM,MAAM,CAAC,YAAY,CAAC,IAAY,EAAE,QAAgB;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrC,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IAClC,CAAC;CACF"} \ No newline at end of file diff --git a/lib/StarterLoader.js b/lib/StarterLoader.js index 15810c8..7c4876c 100644 --- a/lib/StarterLoader.js +++ b/lib/StarterLoader.js @@ -1,6 +1,7 @@ import glob from 'fast-glob' import fs from 'node:fs' import path from 'node:path' +import { Files } from './Files.js' export class StarterLoader { constructor() { this.starters = new Map() @@ -49,32 +50,47 @@ export class StarterLoader { } return starter } - static validateConfig(path, config) { + static validateConfig(configPath, config) { if (!config.module) { - throw new Error(`Invalid config at ${path}: module key is required`) + throw new Error(`Invalid config at ${configPath}: module key is required`) } if (!config.name) { - throw new Error(`Invalid config at ${path}: name key is required`) + throw new Error(`Invalid config at ${configPath}: name key is required`) } if (!config.id) { - throw new Error(`Invalid config at ${path}: id key is required`) + throw new Error(`Invalid config at ${configPath}: id key is required`) } if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { throw new Error( - `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` + `Invalid config at ${configPath}: "prebuild" must be array of npm script names or empty` ) } if (!StarterLoader.isValidOptionalStringArray(config.replace)) { throw new Error( - `Invalid config at ${path}: "replace" must be array of files where strings should be replaced` + `Invalid config at ${configPath}: "replace" must be array of files where strings should be replaced` ) } + if (config.replace) { + const configDir = path.dirname(configPath) + const invalidReplace = config.replace.find(replace => { + const replacePath = path.join(configDir, replace) + return ( + !fs.existsSync(replacePath) || + !Files.isInSameTree(configDir, path.join(configDir, replacePath)) + ) + }) + if (invalidReplace) { + throw new Error( + `Invalid config at ${configPath}: "replace" must be array of files in the project directory, got: ${invalidReplace}` + ) + } + } const invalidKeys = Object.keys(config).filter( key => !['module', 'name', 'prebuild', 'replace', 'id'].includes(key) ) if (invalidKeys.length > 0) { throw new Error( - `Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}` + `Invalid config at ${configPath}: Unknown key(s): ${invalidKeys.join(', ')}` ) } return config diff --git a/lib/StarterLoader.js.map b/lib/StarterLoader.js.map index 85d6ea7..54ddfd4 100644 --- a/lib/StarterLoader.js.map +++ b/lib/StarterLoader.js.map @@ -1 +1 @@ -{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/Files.ts b/src/Files.ts index e03c317..56e6bf6 100644 --- a/src/Files.ts +++ b/src/Files.ts @@ -2,20 +2,21 @@ import * as fsp from 'fs/promises' import path from 'path' export class Files { - public static readonly PROJECT_DIR = path.normalize( - path.join(import.meta.dirname, '..') - ) - - public static async exists(path: string) { - const stat = await fsp.stat(path).catch(() => undefined) + public static async exists(filepath: string) { + const stat = await fsp.stat(filepath).catch(() => undefined) return Boolean(stat) } - public static async existsAndIsDir(path: string) { - const stat = await fsp.stat(path).catch(() => undefined) + public static async existsAndIsDir(filepath: string) { + const stat = await fsp.stat(filepath).catch(() => undefined) return Boolean(stat?.isDirectory()) } - public static async readUtf8File(path: string) { - return fsp.readFile(path, 'utf8') + public static async readUtf8File(filepath: string) { + return fsp.readFile(filepath, 'utf8') + } + public static isInSameTree(tree: string, filepath: string) { + const file = path.normalize(filepath) + const fileTree = path.normalize(tree) + return file.startsWith(fileTree) } } diff --git a/src/StarterLoader.ts b/src/StarterLoader.ts index 4aa8997..8c7aba0 100644 --- a/src/StarterLoader.ts +++ b/src/StarterLoader.ts @@ -1,6 +1,7 @@ import glob from 'fast-glob' import fs from 'node:fs' import path from 'node:path' +import { Files } from './Files.js' export interface StarterConfig { module: string @@ -84,34 +85,54 @@ export class StarterLoader { return starter } - private static validateConfig(path: string, config: any): StarterConfig { + private static validateConfig( + configPath: string, + config: any + ): StarterConfig { if (!config.module) { - throw new Error(`Invalid config at ${path}: module key is required`) + throw new Error(`Invalid config at ${configPath}: module key is required`) } if (!config.name) { - throw new Error(`Invalid config at ${path}: name key is required`) + throw new Error(`Invalid config at ${configPath}: name key is required`) } if (!config.id) { - throw new Error(`Invalid config at ${path}: id key is required`) + throw new Error(`Invalid config at ${configPath}: id key is required`) } if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { throw new Error( - `Invalid config at ${path}: "prebuild" must be array of npm script names or empty` + `Invalid config at ${configPath}: "prebuild" must be array of npm script names or empty` ) } if (!StarterLoader.isValidOptionalStringArray(config.replace)) { throw new Error( - `Invalid config at ${path}: "replace" must be array of files where strings should be replaced` + `Invalid config at ${configPath}: "replace" must be array of files where strings should be replaced` ) } + if (config.replace) { + const configDir = path.dirname(configPath) + + const invalidReplace = config.replace.find((replace: string) => { + const replacePath = path.join(configDir, replace) + return ( + !fs.existsSync(replacePath) || + !Files.isInSameTree(configDir, path.join(configDir, replacePath)) + ) + }) + + if (invalidReplace) { + throw new Error( + `Invalid config at ${configPath}: "replace" must be array of files in the project directory, got: ${invalidReplace}` + ) + } + } const invalidKeys = Object.keys(config).filter( key => !['module', 'name', 'prebuild', 'replace', 'id'].includes(key) ) if (invalidKeys.length > 0) { throw new Error( - `Invalid config at ${path}: Unknown key(s): ${invalidKeys.join(', ')}` + `Invalid config at ${configPath}: Unknown key(s): ${invalidKeys.join(', ')}` ) } return config From 774ca8554a3960a1b983b2d9566e8f9452caecc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 15:04:25 +0200 Subject: [PATCH 24/28] =?UTF-8?q?=E2=9C=A8=20Add=20parametr=20types=20in?= =?UTF-8?q?=20askMissingOptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js.map | 2 +- src/Bootstrap.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map index 91852d5..b49132c 100644 --- a/lib/Bootstrap.js.map +++ b/lib/Bootstrap.js.map @@ -1 +1 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/src/Bootstrap.ts b/src/Bootstrap.ts index e882108..d4ea29c 100644 --- a/src/Bootstrap.ts +++ b/src/Bootstrap.ts @@ -10,10 +10,18 @@ import { PackageJson } from './PackageJson.js' import { LoadedStarter, StarterLoader } from './StarterLoader.js' import { Path } from './types.js' +interface ParsedArgs { + dir: string + debug: boolean + force: boolean + projectName: string + [key: string]: unknown +} + export class Bootstrap { protected starterLoader = new StarterLoader() - private async askMissingOptions(parsedArgs: any) { + private async askMissingOptions(parsedArgs: ParsedArgs) { const starters: LoadedStarter[] = [] for (const module of this.starterLoader.getOptions()) { From 7dd99e31849b48047d230aa4853000658b0a1db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 15:05:39 +0200 Subject: [PATCH 25/28] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20pi?= =?UTF-8?q?peine=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml b/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml index b36af21..96d8ddc 100644 --- a/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml +++ b/starter/pipeline/cloudrun-gitlab/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# Define environments in GitLab UI with vars according to the braches for the pipeline +# Define environments in GitLab UI with vars according to the branches for the pipeline # ENVIRONMENT={{develpoment/stage/production}} # GCP_PROJECT_ID={{PROJECT_NAME}} # GCP_REGION={{europe-west3}} From ff0eb9ac95c5b5d4e7f24767e8ae28ae41e6227b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 15:31:36 +0200 Subject: [PATCH 26/28] =?UTF-8?q?=F0=9F=93=9D=20Add=20docs=20for=20develop?= =?UTF-8?q?ment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/development.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/development.md diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..e576c56 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,42 @@ +# Development 👷 + +## Starters + +Starters are folders containing files and structure that can be optionally added to the final created repository by `create-node-app` (cna). The base folder structure and tooling that should be available in every application created by cna is located in the [\_base](../starter/_base) folder. + +Each additional starter is an addition to this base. When a user selects a specific starter, all contents are copied into the final directory **except for specific files that require content merging**. Every file that is merged uses its own merger defined in the [Mergers](../src/Mergers/) directory. + +Each starter directory must contain a `node-app.jsonc` configuration file that defines the starter's metadata for the cna. + +### Grouping and sorting + +Starters are grouped into categories called "modules". These indicate that only one starter should be selected from each group. For example, under the API module there can be GraphQL and RESTful API starters, but the created application can have only one of those. + +The order for injection into the final repository is alphabetical based on the module fields. The logic for grouping and sorting is as follows: + +1. **Base Starter**: The `_base` starter is always built first +2. **Selected Starters**: Each selected starter is built in the order of user selection +3. **npm install**: Dependencies are installed +4. **Prebuild Scripts**: All `prebuild` scripts from selected starters are executed +5. **Final Build**: `npm run build` is executed + +### Configuration + +The `node-app.jsonc` file is a JSON with Comments file that configures how a starter behaves during the build process. The structure is as follows (fields with \* are required): + +- `id`\* (`string`): Unique identifier +- `name`\* (`string`): Displayed in the CLI in the form of ` ` e.g. `RESTful API` +- `module`\* (`string`): The category this starter belongs to (e.g., "API", "database") +- `prebuild` (`string[]`): Array of npm script names to run before the main build +- `replace` (`string[]`): Array of file paths (relative to project root) where string replacements should be applied + +#### Example RESTful API `node-app.jsonc` + +```jsonc +{ + "module": "API", + "id": "rest", + "name": "RESTful", + "prebuild": ["generate:api"], +} +``` From be2daaeeb64fdc4430728808ec6ef0e719cbcb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 19 Sep 2025 16:03:13 +0200 Subject: [PATCH 27/28] =?UTF-8?q?=F0=9F=94=A5=20Remove=20lib=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/Bootstrap.js | 119 ------------------ lib/Bootstrap.js.map | 1 - lib/Builder.js | 131 ------------------- lib/Builder.js.map | 1 - lib/Files.js | 21 ---- lib/Files.js.map | 1 - lib/Logger.js | 30 ----- lib/Logger.js.map | 1 - lib/Mergers/ConfigMerger.js | 27 ---- lib/Mergers/ConfigMerger.js.map | 1 - lib/Mergers/ContainerMerger.js | 181 --------------------------- lib/Mergers/ContainerMerger.js.map | 1 - lib/Mergers/EnvJsonMerger.js | 20 --- lib/Mergers/EnvJsonMerger.js.map | 1 - lib/Mergers/EnvJsoncMerger.js | 20 --- lib/Mergers/EnvJsoncMerger.js.map | 1 - lib/Mergers/Merger.js | 36 ------ lib/Mergers/Merger.js.map | 1 - lib/Mergers/PackageJsonMerger.js | 36 ------ lib/Mergers/PackageJsonMerger.js.map | 1 - lib/Npm.js | 65 ---------- lib/Npm.js.map | 1 - lib/PackageJson.js | 43 ------- lib/PackageJson.js.map | 1 - lib/Starter.js | 2 - lib/Starter.js.map | 1 - lib/StarterLoader.js | 108 ---------------- lib/StarterLoader.js.map | 1 - lib/Toolbelt.js | 100 --------------- lib/Toolbelt.js.map | 1 - lib/types.js | 2 - lib/types.js.map | 1 - 32 files changed, 957 deletions(-) delete mode 100644 lib/Bootstrap.js delete mode 100644 lib/Bootstrap.js.map delete mode 100644 lib/Builder.js delete mode 100644 lib/Builder.js.map delete mode 100644 lib/Files.js delete mode 100644 lib/Files.js.map delete mode 100644 lib/Logger.js delete mode 100644 lib/Logger.js.map delete mode 100644 lib/Mergers/ConfigMerger.js delete mode 100644 lib/Mergers/ConfigMerger.js.map delete mode 100644 lib/Mergers/ContainerMerger.js delete mode 100644 lib/Mergers/ContainerMerger.js.map delete mode 100644 lib/Mergers/EnvJsonMerger.js delete mode 100644 lib/Mergers/EnvJsonMerger.js.map delete mode 100644 lib/Mergers/EnvJsoncMerger.js delete mode 100644 lib/Mergers/EnvJsoncMerger.js.map delete mode 100644 lib/Mergers/Merger.js delete mode 100644 lib/Mergers/Merger.js.map delete mode 100644 lib/Mergers/PackageJsonMerger.js delete mode 100644 lib/Mergers/PackageJsonMerger.js.map delete mode 100644 lib/Npm.js delete mode 100644 lib/Npm.js.map delete mode 100644 lib/PackageJson.js delete mode 100644 lib/PackageJson.js.map delete mode 100644 lib/Starter.js delete mode 100644 lib/Starter.js.map delete mode 100644 lib/StarterLoader.js delete mode 100644 lib/StarterLoader.js.map delete mode 100644 lib/Toolbelt.js delete mode 100644 lib/Toolbelt.js.map delete mode 100644 lib/types.js delete mode 100644 lib/types.js.map diff --git a/lib/Bootstrap.js b/lib/Bootstrap.js deleted file mode 100644 index 0680bb2..0000000 --- a/lib/Bootstrap.js +++ /dev/null @@ -1,119 +0,0 @@ -import inquirer from 'inquirer' -import * as path from 'path' -import * as fs from 'fs' -import yargs from 'yargs' -import { hideBin } from 'yargs/helpers' -import { Builder } from './Builder.js' -import { Logger } from './Logger.js' -import { Npm } from './Npm.js' -import { PackageJson } from './PackageJson.js' -import { StarterLoader } from './StarterLoader.js' -export class Bootstrap { - constructor() { - this.starterLoader = new StarterLoader() - } - async askMissingOptions(parsedArgs) { - const starters = [] - for (const module of this.starterLoader.getOptions()) { - const moduleOption = parsedArgs[module.name.toLowerCase()] - if (moduleOption) { - if (moduleOption !== 'none') { - starters.push(this.starterLoader.getStarter(moduleOption)) - } - continue - } - const answer = await inquirer.prompt({ - type: 'list', - name: 'starter', - message: `Which ${module.name} would you like to use?`, - choices: [...module.starters, 'none'], - }) - if (answer.starter !== 'none') { - starters.push(this.starterLoader.getStarter(answer.starter)) - } - } - return starters - } - async runCLI(args) { - try { - let cli = yargs(hideBin(args)) - .usage('create-node-app [options]') - .option('dir', { - type: 'string', - alias: 'd', - default: './node-app', - description: 'Destination directory', - }) - .option('debug', { - type: 'boolean', - alias: 'D', - default: false, - description: 'Enables debug logs', - }) - .option('project-name', { - type: 'string', - alias: 'n', - default: 'node-app', - description: 'Google Cloud project name', - }) - .option('force', { - type: 'boolean', - alias: 'f', - default: false, - description: - "Overwrite existing destination directory if it's not empty", - }) - const starterOptions = this.starterLoader.getOptions() - for (const module of starterOptions) { - cli = cli.option(module.name.toLowerCase(), { - type: 'string', - choices: ['none', ...module.starters], - description: `Selects ${module.name}`, - }) - } - cli = cli - .version('1.0.0') - .help() - .check(argv => { - for (const [key, val] of Object.entries(argv)) { - if (Array.isArray(val) && key !== '_') { - throw new Error(`Option --${key} specified multiple times`) - } - } - return true - }) - const parsedArgs = cli.parseSync() - const destination = path.normalize(parsedArgs.dir) - const logger = new Logger(parsedArgs.debug) - const npm = new Npm({ dir: destination, logger }) - const packageJson = new PackageJson(npm, logger) - if (fs.existsSync(destination) && !parsedArgs.force) { - const answer = await inquirer.prompt({ - type: 'confirm', - name: 'force', - message: `Destination directory "${destination}" already exists. Do you want to overwrite everything in it?`, - }) - if (!answer.force) { - process.exit(0) - } - } - const starters = await this.askMissingOptions(parsedArgs) - const builder = new Builder({ - npm, - logger, - packageJson, - starters, - destination: destination, - projectName: parsedArgs.projectName, - }) - await builder.build() - } catch (error) { - if (error instanceof Error && error.name === 'ExitPromptError') { - process.exit(0) - } else { - throw error - } - } - } -} -//# sourceMappingURL=Bootstrap.js.map diff --git a/lib/Bootstrap.js.map b/lib/Bootstrap.js.map deleted file mode 100644 index b49132c..0000000 --- a/lib/Bootstrap.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Bootstrap.js","sourceRoot":"","sources":["../src/Bootstrap.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Builder.js b/lib/Builder.js deleted file mode 100644 index dc20e7e..0000000 --- a/lib/Builder.js +++ /dev/null @@ -1,131 +0,0 @@ -import glob from 'fast-glob' -import * as fs from 'fs/promises' -import * as path from 'path' -import { PackageJsonMerger } from './Mergers/PackageJsonMerger.js' -import { EnvJsoncMerger } from './Mergers/EnvJsoncMerger.js' -import { ConfigMerger } from './Mergers/ConfigMerger.js' -import { ContainerMerger } from './Mergers/ContainerMerger.js' -import { Files } from './Files.js' -export class Builder { - constructor(params) { - this.npm = params.npm - this.logger = params.logger - this.starters = params.starters - this.destination = params.destination - this.projectName = params.projectName - this.replacements = { - '{{PROJECT_NAME}}': this.projectName, - } - this.fileMergers = [ - new PackageJsonMerger(this.projectName, this.destination, 'package.json'), - new EnvJsoncMerger(this.destination, '.env.jsonc'), - new ConfigMerger(this.destination, 'src/config.ts'), - new ContainerMerger(this.destination, 'src/container.ts'), - ] - } - async prepareFolder() { - if (await Files.existsAndIsDir(this.destination)) { - await fs.rm(this.destination, { recursive: true }) - } - await fs.mkdir(this.destination, { recursive: true }) - } - async build() { - try { - await this.logger.loader(`Preparing clean folder`, this.prepareFolder()) - await this.logger.loader( - `Preparing folder structure`, - this.buildStarter(Builder.BASE_STARTER_DIR) - ) - for (const starter of this.starters) { - await this.logger.loader( - `Adding ${starter.config.name} ${starter.config.module}`, - this.buildStarter(starter.path, starter.config) - ) - } - await this.logger.loader(`npm install`, this.npm.run(['install'])) - const prebuildScripts = this.starters - .map(starter => starter.config.prebuild) - .filter(script => script !== undefined) - for (const script of prebuildScripts) { - await this.logger.loader( - `npm run ${script.join(' ')}`, - this.npm.run(['run', ...script]) - ) - } - await this.logger.loader(`npm run build`, this.npm.run(['run', 'build'])) - this.logger.info( - `Your app is ready in ${path.relative(process.cwd(), this.destination)}! 🚀` - ) - } catch (error) { - this.logger.error(error) - process.exit(1) - } - } - async buildStarter(starterDir, config) { - const destDir = path.normalize(path.resolve(this.destination)) - const files = await glob(`${starterDir}/*`, { - cwd: starterDir, - dot: true, - onlyFiles: false, - }) - const ignoredFiles = Builder.INGORED_FILES.map(file => - path.join(starterDir, file) - ) - const mergedFiles = await Promise.all( - this.fileMergers.map(async merger => { - return { - path: merger.getDestPath(), - content: await merger.merge(starterDir), - } - }) - ) - await Promise.all( - files.map(async filePath => { - if (ignoredFiles.includes(filePath)) { - return - } - const destFilePath = path.join(destDir, path.basename(filePath)) - if (await Files.existsAndIsDir(filePath)) { - await fs.cp(filePath, destFilePath, { recursive: true }) - return - } - await fs.cp(filePath, destFilePath) - }) - ) - await Promise.all( - mergedFiles.map(async ({ path, content }) => fs.writeFile(path, content)) - ) - if (config?.replace) { - await Promise.all( - config.replace.map(async filePath => this.replaceInFile(filePath)) - ) - } - } - async replaceInFile(filePath) { - filePath = path.normalize(path.join(this.destination, filePath)) - let content = await Files.readUtf8File(filePath) - content = Object.keys(this.replacements).reduce((acc, key) => { - return acc.replaceAll(key, this.replacements[key]) - }, content) - return fs.writeFile(filePath, content) - } - async symlink(linkName, linkedFile) { - linkName = path.normalize(linkName) - linkedFile = path.normalize(linkedFile) - this.logger.info(`> ln -s ${linkName} ${linkedFile}`) - try { - await fs.symlink(linkedFile, linkName) - } catch (error) { - if ('code' in error && error.code === 'EEXIST') { - // OK - } else { - throw error - } - } - } -} -Builder.BASE_STARTER_DIR = path.normalize( - path.join(import.meta.dirname, '..', 'starter', '_base') -) -Builder.INGORED_FILES = ['node-app.jsonc'] -//# sourceMappingURL=Builder.js.map diff --git a/lib/Builder.js.map b/lib/Builder.js.map deleted file mode 100644 index 94736f9..0000000 --- a/lib/Builder.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Builder.js","sourceRoot":"","sources":["../src/Builder.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Files.js b/lib/Files.js deleted file mode 100644 index ac868e4..0000000 --- a/lib/Files.js +++ /dev/null @@ -1,21 +0,0 @@ -import * as fsp from 'fs/promises' -import path from 'path' -export class Files { - static async exists(filepath) { - const stat = await fsp.stat(filepath).catch(() => undefined) - return Boolean(stat) - } - static async existsAndIsDir(filepath) { - const stat = await fsp.stat(filepath).catch(() => undefined) - return Boolean(stat?.isDirectory()) - } - static async readUtf8File(filepath) { - return fsp.readFile(filepath, 'utf8') - } - static isInSameTree(tree, filepath) { - const file = path.normalize(filepath) - const fileTree = path.normalize(tree) - return file.startsWith(fileTree) - } -} -//# sourceMappingURL=Files.js.map diff --git a/lib/Files.js.map b/lib/Files.js.map deleted file mode 100644 index 3567e24..0000000 --- a/lib/Files.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Files.js","sourceRoot":"","sources":["../src/Files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,aAAa,CAAA;AAClC,OAAO,IAAI,MAAM,MAAM,CAAA;AAEvB,MAAM,OAAO,KAAK;IACT,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAgB;QACzC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,QAAgB;QACjD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC5D,OAAO,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,CAAC,CAAA;IACrC,CAAC;IACM,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,QAAgB;QAC/C,OAAO,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IACvC,CAAC;IACM,MAAM,CAAC,YAAY,CAAC,IAAY,EAAE,QAAgB;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QACrC,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAA;IAClC,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Logger.js b/lib/Logger.js deleted file mode 100644 index de80a97..0000000 --- a/lib/Logger.js +++ /dev/null @@ -1,30 +0,0 @@ -import { oraPromise } from 'ora' -export class Logger { - constructor(enableDebug = false) { - this.enableDebug = enableDebug - } - info(message) { - console.log(message) - } - verbose(message) { - console.log(message) - } - error(message) { - console.log(message) - } - debug(message) { - if (this.enableDebug) { - console.log(message) - } - } - loader(message, promise) { - if (this.enableDebug) { - this.info(message) - return promise - } - return oraPromise(promise, { - text: message, - }) - } -} -//# sourceMappingURL=Logger.js.map diff --git a/lib/Logger.js.map b/lib/Logger.js.map deleted file mode 100644 index 997c60d..0000000 --- a/lib/Logger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAEhC,MAAM,OAAO,MAAM;IACjB,YAA4B,cAAuB,KAAK;QAA5B,gBAAW,GAAX,WAAW,CAAiB;IAAG,CAAC;IAE5D,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,OAAO,CAAC,OAAe;QACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;IACtB,CAAC;IACD,KAAK,CAAC,OAAe;QACnB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IACD,MAAM,CAAC,OAAe,EAAE,OAAqB;QAC3C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;YAClB,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,OAAO,UAAU,CAAC,OAAO,EAAE;YACzB,IAAI,EAAE,OAAO;SACd,CAAC,CAAA;IACJ,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/ConfigMerger.js b/lib/Mergers/ConfigMerger.js deleted file mode 100644 index f46439a..0000000 --- a/lib/Mergers/ConfigMerger.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Files } from '../Files.js' -import { Merger } from './Merger.js' -export class ConfigMerger extends Merger { - constructor() { - super(...arguments) - this.name = 'Config' - } - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir) - if (content) { - return content - } - const { originPath, destPath } = this.getPaths(originDir) - const [destTsConfig, originTsConfig] = await Promise.all([ - Files.readUtf8File(destPath), - Files.readUtf8File(originPath), - ]) - const configContent = /configSchema = {\n(.*?)\n}\n/s.exec( - originTsConfig - )?.[1] - return destTsConfig.replace( - '\n}\n', - configContent ? `${configContent}\n}\n` : '}\n' - ) - } -} -//# sourceMappingURL=ConfigMerger.js.map diff --git a/lib/Mergers/ConfigMerger.js.map b/lib/Mergers/ConfigMerger.js.map deleted file mode 100644 index 8925085..0000000 --- a/lib/Mergers/ConfigMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ConfigMerger.js","sourceRoot":"","sources":["../../src/Mergers/ConfigMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,MAAM,OAAO,YAAa,SAAQ,MAAM;IAAxC;;QACE,SAAI,GAAG,QAAQ,CAAA;IAuBjB,CAAC;IArBC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QACF,MAAM,aAAa,GAAG,+BAA+B,CAAC,IAAI,CACxD,cAAc,CACf,EAAE,CAAC,CAAC,CAAC,CAAA;QAEN,OAAO,YAAY,CAAC,OAAO,CACzB,OAAO,EACP,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,OAAO,CAAC,CAAC,CAAC,KAAK,CAChD,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/ContainerMerger.js b/lib/Mergers/ContainerMerger.js deleted file mode 100644 index b861a54..0000000 --- a/lib/Mergers/ContainerMerger.js +++ /dev/null @@ -1,181 +0,0 @@ -import { Merger } from './Merger.js' -import * as ts from 'typescript' -import { Files } from '../Files.js' -export class ContainerMerger extends Merger { - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir) - if (content) { - return content - } - const { originPath, destPath } = this.getPaths(originDir) - const [originContainer, destContainer] = await Promise.all([ - Files.readUtf8File(originPath), - Files.readUtf8File(destPath), - ]) - const originAst = this.parseFile(originContainer) - const destAst = this.parseFile(destContainer) - const mergedImports = this.mergeImports(originAst, destAst) - const mergedInterface = this.mergeInterface(originAst, destAst) - const mergedFunction = this.mergeFunction(originAst, destAst) - return `${mergedImports}\n\n${mergedInterface}\n\nexport type ContainerFactory = () => Promise\n\n${mergedFunction}\n` - } - parseFile(content) { - return ts.createSourceFile('temp.ts', content, ts.ScriptTarget.Latest, true) - } - mergeImports(originAst, destAst) { - const imports = [] - const addImports = node => { - if (!ts.isImportDeclaration(node)) { - node.forEachChild(addImports) - return - } - const importText = node.getText() - if (!imports.includes(importText)) { - imports.push(importText) - } - node.forEachChild(addImports) - } - addImports(originAst) - addImports(destAst) - return imports.join('\n') - } - mergeInterface(originAst, destAst) { - const properties = [] - const extractProperties = node => { - if (!ts.isInterfaceDeclaration(node) || node.name.text !== 'Container') { - node.forEachChild(extractProperties) - return - } - node.members.forEach(member => { - if (!ts.isPropertySignature(member)) { - return - } - const propText = member.getText().trim() - if (!properties.includes(propText)) { - properties.push(propText) - } - }) - node.forEachChild(extractProperties) - } - extractProperties(originAst) - extractProperties(destAst) - return `export interface Container {\n ${properties.join('\n ')}\n}` - } - mergeFunction(originAst, destAst) { - const originFunctionBody = this.extractFunctionBody(originAst) - const destFunctionBody = this.extractFunctionBody(destAst) - const allFunctionContent = [ - ...originFunctionBody.content, - ...destFunctionBody.content, - ] - const mergedReturnProps = this.mergeReturnProperties( - originFunctionBody.returnProps, - destFunctionBody.returnProps - ) - let body = '' - if (allFunctionContent.length > 0) { - body += allFunctionContent.join('\n') + '\n\n' - } - body += ` return {\n ${mergedReturnProps.join(',\n ')}\n }` - return `export const createContainer = async (): Promise => {\n${body}\n}` - } - mergeReturnProperties(originProps, destProps) { - const mergedProps = new Map() - originProps.forEach(prop => { - const propName = this.getPropertyName(prop) - if (propName) { - mergedProps.set(propName, prop) - } - }) - destProps.forEach(prop => { - const propName = this.getPropertyName(prop) - if (propName) { - mergedProps.set(propName, prop) - } - }) - return Array.from(mergedProps.values()).map(prop => { - const propText = prop.getText() - if ( - ts.isPropertyAssignment(prop) && - ts.isObjectLiteralExpression(prop.initializer) - ) { - return this.formatNestedObject(propText) - } - return propText - }) - } - getPropertyName(prop) { - if (ts.isPropertyAssignment(prop)) { - if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) { - return prop.name.text - } - } else if (ts.isShorthandPropertyAssignment(prop)) { - return prop.name.text - } - return null - } - formatNestedObject(propText) { - const lines = propText.split('\n') - return lines - .map((line, index) => { - if (index === 0) { - return line - } - return ' ' + line - }) - .join('\n') - } - extractFunctionBody(ast) { - const content = [] - const returnProps = [] - const visit = node => { - if (!this.isCreateContainer(node)) { - ts.forEachChild(node, visit) - return - } - if (!node.initializer || !ts.isArrowFunction(node.initializer)) { - ts.forEachChild(node, visit) - return - } - const body = node.initializer.body - if (!ts.isBlock(body)) { - ts.forEachChild(node, visit) - return - } - body.statements.forEach(statement => { - if (this.isContentStatement(statement)) { - content.push(statement.getText().trim()) - } else if (this.isReturnWithObject(statement)) { - // Extract the actual property nodes, not just their names - statement.expression.properties.forEach(prop => { - returnProps.push(prop) - }) - } - }) - ts.forEachChild(node, visit) - } - visit(ast) - return { content, returnProps } - } - isCreateContainer(node) { - return ( - ts.isVariableDeclaration(node) && - node.name.getText() === 'createContainer' - ) - } - isContentStatement(statement) { - return ( - ts.isVariableStatement(statement) || - (ts.isExpressionStatement(statement) && - ts.isCallExpression(statement.expression)) - ) - } - isReturnWithObject(statement) { - return ( - ts.isReturnStatement(statement) && - !!statement.expression && - ts.isObjectLiteralExpression(statement.expression) - ) - } -} -//# sourceMappingURL=ContainerMerger.js.map diff --git a/lib/Mergers/ContainerMerger.js.map b/lib/Mergers/ContainerMerger.js.map deleted file mode 100644 index 64849b6..0000000 --- a/lib/Mergers/ContainerMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ContainerMerger.js","sourceRoot":"","sources":["../../src/Mergers/ContainerMerger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Mergers/EnvJsonMerger.js b/lib/Mergers/EnvJsonMerger.js deleted file mode 100644 index 03c9b1f..0000000 --- a/lib/Mergers/EnvJsonMerger.js +++ /dev/null @@ -1,20 +0,0 @@ -import { Merger } from './Merger.js' -import fsp from 'fs/promises' -export class EnvJsocMerger extends Merger { - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir) - if (content) { - return content - } - const { originPath, destPath } = this.getPaths(originDir) - const [destEnvConfig, originEnvConfig] = await Promise.all([ - fsp.readFile(destPath, 'utf8'), - fsp.readFile(originPath, 'utf8'), - ]) - const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') - return destEnvConfig - .replace(',\n}\n', '\n}\n') - .replace('\n}\n', `,\n${originWithoutOpenBracket}`) - } -} -//# sourceMappingURL=EnvJsonMerger.js.map diff --git a/lib/Mergers/EnvJsonMerger.js.map b/lib/Mergers/EnvJsonMerger.js.map deleted file mode 100644 index 1202724..0000000 --- a/lib/Mergers/EnvJsonMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EnvJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,GAAG,MAAM,aAAa,CAAA;AAE7B,MAAM,OAAO,aAAc,SAAQ,MAAM;IACvC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;YAC9B,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;SACjC,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/EnvJsoncMerger.js b/lib/Mergers/EnvJsoncMerger.js deleted file mode 100644 index f1d3fb7..0000000 --- a/lib/Mergers/EnvJsoncMerger.js +++ /dev/null @@ -1,20 +0,0 @@ -import { Merger } from './Merger.js' -import { Files } from '../Files.js' -export class EnvJsoncMerger extends Merger { - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir) - if (content) { - return content - } - const { originPath, destPath } = this.getPaths(originDir) - const [destEnvConfig, originEnvConfig] = await Promise.all([ - Files.readUtf8File(destPath), - Files.readUtf8File(originPath), - ]) - const originWithoutOpenBracket = originEnvConfig.replace('{\n', '') - return destEnvConfig - .replace(',\n}\n', '\n}\n') - .replace('\n}\n', `,\n${originWithoutOpenBracket}`) - } -} -//# sourceMappingURL=EnvJsoncMerger.js.map diff --git a/lib/Mergers/EnvJsoncMerger.js.map b/lib/Mergers/EnvJsoncMerger.js.map deleted file mode 100644 index 622649c..0000000 --- a/lib/Mergers/EnvJsoncMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EnvJsoncMerger.js","sourceRoot":"","sources":["../../src/Mergers/EnvJsoncMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAO,cAAe,SAAQ,MAAM;IACxC,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACzD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QAEF,MAAM,wBAAwB,GAAG,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEnE,OAAO,aAAa;aACjB,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC;aAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,wBAAwB,EAAE,CAAC,CAAA;IACvD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/Merger.js b/lib/Mergers/Merger.js deleted file mode 100644 index 15c0e37..0000000 --- a/lib/Mergers/Merger.js +++ /dev/null @@ -1,36 +0,0 @@ -import path from 'path' -import { Files } from '../Files.js' -export class Merger { - constructor(destDir, pathToFile) { - this.destDir = destDir - this.pathToFile = pathToFile - this.destPath = path.join(this.destDir, this.pathToFile) - } - getDestPath() { - return this.destPath - } - getPaths(originDir) { - return { - originPath: path.join(originDir, this.pathToFile), - destPath: this.getDestPath(), - } - } - async getWhichExistsOrNull(originDir) { - const { originPath, destPath } = this.getPaths(originDir) - const [originExists, destExists] = await Promise.all([ - Files.exists(originPath), - Files.exists(destPath), - ]) - if (!originExists && destExists) { - return Files.readUtf8File(destPath) - } - if (!destExists && originExists) { - return Files.readUtf8File(originPath) - } - if (!originExists && !destExists) { - throw new Error(`No file found to merge: ${this.pathToFile}`) - } - return null - } -} -//# sourceMappingURL=Merger.js.map diff --git a/lib/Mergers/Merger.js.map b/lib/Mergers/Merger.js.map deleted file mode 100644 index 7c7cced..0000000 --- a/lib/Mergers/Merger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Merger.js","sourceRoot":"","sources":["../../src/Mergers/Merger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAgB,MAAM;IAK1B,YACqB,OAAe,EACf,UAAkB;QADlB,YAAO,GAAP,OAAO,CAAQ;QACf,eAAU,GAAV,UAAU,CAAQ;QAErC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;IAC1D,CAAC;IAEM,WAAW;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAES,QAAQ,CAAC,SAAiB;QAClC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC;YACjD,QAAQ,EAAE,IAAI,CAAC,WAAW,EAAE;SAC7B,CAAA;IACH,CAAC;IAES,KAAK,CAAC,oBAAoB,CAClC,SAAiB;QAEjB,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;SACvB,CAAC,CAAA;QAEF,IAAI,CAAC,YAAY,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;QACrC,CAAC;QAED,IAAI,CAAC,UAAU,IAAI,YAAY,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,CAAA;QACvC,CAAC;QAED,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;QAC/D,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Mergers/PackageJsonMerger.js b/lib/Mergers/PackageJsonMerger.js deleted file mode 100644 index de395d1..0000000 --- a/lib/Mergers/PackageJsonMerger.js +++ /dev/null @@ -1,36 +0,0 @@ -import { Merger } from './Merger.js' -import { Files } from '../Files.js' -export class PackageJsonMerger extends Merger { - constructor(projectName, destDir, pathToFile) { - super(destDir, pathToFile) - this.projectName = projectName - } - async merge(originDir) { - const content = await this.getWhichExistsOrNull(originDir) - if (content) { - return content - } - const { originPath, destPath } = this.getPaths(originDir) - const [destPckgJson, starterPckgJson] = await Promise.all([ - Files.readUtf8File(destPath), - Files.readUtf8File(originPath), - ]) - const destPckgJsonObj = JSON.parse(destPckgJson) - const starterPckgJsonObj = JSON.parse(starterPckgJson) - destPckgJsonObj.name = this.projectName - destPckgJsonObj.scripts = { - ...destPckgJsonObj.scripts, - ...starterPckgJsonObj.scripts, - } - destPckgJsonObj.dependencies = { - ...destPckgJsonObj.dependencies, - ...starterPckgJsonObj.dependencies, - } - destPckgJsonObj.devDependencies = { - ...destPckgJsonObj.devDependencies, - ...starterPckgJsonObj.devDependencies, - } - return JSON.stringify(destPckgJsonObj, null, 2) - } -} -//# sourceMappingURL=PackageJsonMerger.js.map diff --git a/lib/Mergers/PackageJsonMerger.js.map b/lib/Mergers/PackageJsonMerger.js.map deleted file mode 100644 index 6247886..0000000 --- a/lib/Mergers/PackageJsonMerger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"PackageJsonMerger.js","sourceRoot":"","sources":["../../src/Mergers/PackageJsonMerger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAEnC,MAAM,OAAO,iBAAkB,SAAQ,MAAM;IAC3C,YACmB,WAAmB,EACpC,OAAe,EACf,UAAkB;QAElB,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAJT,gBAAW,GAAX,WAAW,CAAQ;IAKtC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAA;QAC1D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QAEzD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACxD,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC;YAC5B,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC;SAC/B,CAAC,CAAA;QAEF,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAChD,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QAEtD,eAAe,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAA;QACvC,eAAe,CAAC,OAAO,GAAG;YACxB,GAAG,eAAe,CAAC,OAAO;YAC1B,GAAG,kBAAkB,CAAC,OAAO;SAC9B,CAAA;QACD,eAAe,CAAC,YAAY,GAAG;YAC7B,GAAG,eAAe,CAAC,YAAY;YAC/B,GAAG,kBAAkB,CAAC,YAAY;SACnC,CAAA;QACD,eAAe,CAAC,eAAe,GAAG;YAChC,GAAG,eAAe,CAAC,eAAe;YAClC,GAAG,kBAAkB,CAAC,eAAe;SACtC,CAAA;QAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Npm.js b/lib/Npm.js deleted file mode 100644 index 42e7b0c..0000000 --- a/lib/Npm.js +++ /dev/null @@ -1,65 +0,0 @@ -import * as childProcess from 'child_process' -import { Logger } from './Logger.js' -export class NpmError extends Error { - constructor(message, code) { - super(message) - this.code = code - this.name = 'NpmError' - } -} -export class Npm { - constructor(settings) { - this.logger = settings?.logger ?? new Logger() - this.dir = settings?.dir - } - spawn(cmd, args, options = {}) { - return new Promise((resolve, reject) => { - const cp = childProcess.spawn(cmd, args, options) - const error = [] - const stdout = [] - cp.stdout?.on('data', data => { - stdout.push(data.toString()) - }) - cp.on('error', e => { - error.push(e.toString()) - }) - cp.on('close', code => { - if (error.length || (code !== null && code > 0)) { - reject( - new NpmError( - error.length ? error.join('') : stdout.join(''), - code ?? null - ) - ) - } else { - resolve(undefined) - } - }) - }) - } - run(args) { - this.logger.debug(`> npm ${args.join(' ')}`) - const options = this.dir - ? { - cwd: this.dir, - stdio: this.logger.enableDebug ? 'inherit' : 'pipe', - } - : { stdio: this.logger.enableDebug ? 'inherit' : 'pipe' } - return this.spawn('npm', args, options) - } - init() { - return this.run(['init', '--yes']) - } - i(module) { - if (!module) { - return this.run(['i']) - } - const args = ['i', module] - return this.run(args) - } - iDev(module) { - const args = ['i', '-D', module] - return this.run(args) - } -} -//# sourceMappingURL=Npm.js.map diff --git a/lib/Npm.js.map b/lib/Npm.js.map deleted file mode 100644 index b32f1dd..0000000 --- a/lib/Npm.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Npm.js","sourceRoot":"","sources":["../src/Npm.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/PackageJson.js b/lib/PackageJson.js deleted file mode 100644 index e44b644..0000000 --- a/lib/PackageJson.js +++ /dev/null @@ -1,43 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' -import * as lodash from 'lodash-es' -export class PackageJson { - constructor(npm, logger) { - let packagejsonPath = './package.json' - if (npm.dir) { - packagejsonPath = path.normalize(`${npm.dir}/${packagejsonPath}`) - } - this.path = packagejsonPath - this.npm = npm - this.logger = logger - } - setType(type) { - this.mergeWith({ - type, - }) - } - toJSON() { - return JSON.parse(fs.readFileSync(this.path, 'utf-8')) - } - runScript(name) { - return this.npm.run(['run', name]) - } - addNpmScript(name, command) { - this.mergeWith({ - scripts: { - [name]: command, - }, - }) - } - // Updated package json using merge with given object - mergeWith(partialWith) { - const json = lodash.merge(this.toJSON(), partialWith) - this.logger.debug(`> package.json updated ${JSON.stringify(partialWith)}`) - fs.writeFileSync( - path.join(this.path), - JSON.stringify(json, null, 2), - 'utf-8' - ) - } -} -//# sourceMappingURL=PackageJson.js.map diff --git a/lib/PackageJson.js.map b/lib/PackageJson.js.map deleted file mode 100644 index 87c2bb2..0000000 --- a/lib/PackageJson.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"PackageJson.js","sourceRoot":"","sources":["../src/PackageJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,MAAM,MAAM,WAAW,CAAA;AAKnC,MAAM,OAAO,WAAW;IAItB,YAAY,GAAQ,EAAE,MAAc;QAClC,IAAI,eAAe,GAAG,gBAAwB,CAAA;QAC9C,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,eAAe,EAAE,CAAS,CAAA;QAC3E,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;QACd,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IACM,OAAO,CAAC,IAA2B;QACxC,IAAI,CAAC,SAAS,CAAC;YACb,IAAI;SACL,CAAC,CAAA;IACJ,CAAC;IACM,MAAM;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,CAAC;IACM,SAAS,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IACpC,CAAC;IACM,YAAY,CAAC,IAAY,EAAE,OAAe;QAC/C,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE;gBACP,CAAC,IAAI,CAAC,EAAE,OAAO;aAChB;SACF,CAAC,CAAA;IACJ,CAAC;IACD,qDAAqD;IAC9C,SAAS,CAAC,WAAgB;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,CAAC,CAAA;QACrD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAC1E,EAAE,CAAC,aAAa,CACd,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACpB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAC7B,OAAO,CACR,CAAA;IACH,CAAC;CACF"} \ No newline at end of file diff --git a/lib/Starter.js b/lib/Starter.js deleted file mode 100644 index 26460e8..0000000 --- a/lib/Starter.js +++ /dev/null @@ -1,2 +0,0 @@ -export {} -//# sourceMappingURL=Starter.js.map diff --git a/lib/Starter.js.map b/lib/Starter.js.map deleted file mode 100644 index 7a9fc2e..0000000 --- a/lib/Starter.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Starter.js","sourceRoot":"","sources":["../src/Starter.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/lib/StarterLoader.js b/lib/StarterLoader.js deleted file mode 100644 index 7c4876c..0000000 --- a/lib/StarterLoader.js +++ /dev/null @@ -1,108 +0,0 @@ -import glob from 'fast-glob' -import fs from 'node:fs' -import path from 'node:path' -import { Files } from './Files.js' -export class StarterLoader { - constructor() { - this.starters = new Map() - this.modules = [] - const configFiles = glob.sync( - `${StarterLoader.starterPath}/**/node-app.jsonc` - ) - for (const configFile of configFiles) { - const config = StarterLoader.validateConfig( - configFile, - JSON.parse(fs.readFileSync(configFile, 'utf8')) - ) - const original = this.starters.get(config.id) - if (original) { - throw new Error( - `Duplicate starter: ${config.name}\n` + - `> Starter 1: ${original.path}\n` + - `> Starter 2: ${path.dirname(configFile)}` - ) - } - this.starters.set(config.id, { - name: config.name, - config, - path: path.dirname(configFile), - configPath: configFile, - }) - const module = this.modules.find(module => module.name === config.module) - if (module) { - module.starters.push(config.id) - continue - } - this.modules.push({ - name: config.module, - starters: [config.id], - }) - } - this.modules.sort((a, b) => a.name.localeCompare(b.name)) - } - getOptions() { - return this.modules - } - getStarter(id) { - const starter = this.starters.get(id) - if (!starter) { - throw new Error(`Starter ${id} not found`) - } - return starter - } - static validateConfig(configPath, config) { - if (!config.module) { - throw new Error(`Invalid config at ${configPath}: module key is required`) - } - if (!config.name) { - throw new Error(`Invalid config at ${configPath}: name key is required`) - } - if (!config.id) { - throw new Error(`Invalid config at ${configPath}: id key is required`) - } - if (!StarterLoader.isValidOptionalStringArray(config.prebuild)) { - throw new Error( - `Invalid config at ${configPath}: "prebuild" must be array of npm script names or empty` - ) - } - if (!StarterLoader.isValidOptionalStringArray(config.replace)) { - throw new Error( - `Invalid config at ${configPath}: "replace" must be array of files where strings should be replaced` - ) - } - if (config.replace) { - const configDir = path.dirname(configPath) - const invalidReplace = config.replace.find(replace => { - const replacePath = path.join(configDir, replace) - return ( - !fs.existsSync(replacePath) || - !Files.isInSameTree(configDir, path.join(configDir, replacePath)) - ) - }) - if (invalidReplace) { - throw new Error( - `Invalid config at ${configPath}: "replace" must be array of files in the project directory, got: ${invalidReplace}` - ) - } - } - const invalidKeys = Object.keys(config).filter( - key => !['module', 'name', 'prebuild', 'replace', 'id'].includes(key) - ) - if (invalidKeys.length > 0) { - throw new Error( - `Invalid config at ${configPath}: Unknown key(s): ${invalidKeys.join(', ')}` - ) - } - return config - } - static isValidOptionalStringArray(array) { - return ( - !array || - (Array.isArray(array) && array.every(item => typeof item === 'string')) - ) - } -} -StarterLoader.starterPath = path.normalize( - path.join(import.meta.dirname, '..', 'starter') -) -//# sourceMappingURL=StarterLoader.js.map diff --git a/lib/StarterLoader.js.map b/lib/StarterLoader.js.map deleted file mode 100644 index 54ddfd4..0000000 --- a/lib/StarterLoader.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"StarterLoader.js","sourceRoot":"","sources":["../src/StarterLoader.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/Toolbelt.js b/lib/Toolbelt.js deleted file mode 100644 index 9a1c313..0000000 --- a/lib/Toolbelt.js +++ /dev/null @@ -1,100 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' -import { logger } from './Logger.js' -export class Toolbelt { - constructor(params) { - this.npm = params.npm - this.packageJson = params.packageJson - this.assetDirectory = params.assetDirectory - this.sharedDirectory = params.sharedDirectory - this.destination = params.destination - this.projectName = params.projectName - } - stringToPath(str) { - return path.normalize(str) - } - isDirectoryEmpty(dirpath) { - const path = this.stringToPath(dirpath) - if (!fs.existsSync(path)) { - return true - } - const stat = fs.statSync(path) - if (!stat.isDirectory()) { - return true - } - const contents = fs.readdirSync(path) - return contents.length === 0 - } - mkdir(dirpath, option) { - dirpath = this.stringToPath(dirpath) - const rootPath = ['.', './'] - if (!rootPath.includes(dirpath)) { - if (fs.existsSync(dirpath) && option?.overwrite) { - fs.rmSync(dirpath, { recursive: true }) - } - fs.mkdirSync(dirpath, { recursive: true }) - } - } - /** - * Like cp, but second argument does not need to include file name - * the name is preserved. - */ - cpFile(a, b, option) { - a = this.stringToPath(a) - b = this.stringToPath(b) - const file = path.basename(a) - this.cp(a, this.stringToPath(`${b}/${option?.destFileName ?? file}`)) - } - cp(a, b) { - a = this.stringToPath(a) - b = this.stringToPath(b) - logger.info(`> cp ${a} ${b}`) - fs.copyFileSync(a, b) - } - copyAsset(name, destination) { - let destinationName = name - if (path.basename(name) === '.gitignore') { - name = '.gitignore_' - destinationName = '.gitignore' - } - name = this.stringToPath(name) - destination = this.stringToPath(this.destination) - this.cpFile(`${this.assetDirectory}/${name}`, destination, { - destFileName: destinationName, - }) - } - copySharedAsset(name, destination) { - let destinationName = name - if (path.basename(name) === '.gitignore') { - name = '.gitignore_' - destinationName = '.gitignore' - } - name = this.stringToPath(name) - destination = this.stringToPath(this.destination) - this.cpFile(`${this.sharedDirectory}/${name}`, destination, { - destFileName: destinationName, - }) - } - replaceInFile(filePath, placeholder, replacement = 'REPLACEME') { - filePath = this.stringToPath(`${this.destination}/${filePath}`) - let content = fs.readFileSync(filePath, 'utf8') - /* eslint-disable-next-line security/detect-non-literal-regexp */ - content = content.replace(new RegExp(placeholder, 'g'), replacement) - fs.writeFileSync(filePath, content) - } - symlink(linkName, linkedFile) { - linkName = this.stringToPath(linkName) - linkedFile = this.stringToPath(linkedFile) - logger.info(`> ln -s ${linkName} ${linkedFile}`) - try { - fs.symlinkSync(linkedFile, linkName) - } catch (error) { - if ('code' in error && error.code === 'EEXIST') { - // OK - } else { - throw error - } - } - } -} -//# sourceMappingURL=Toolbelt.js.map diff --git a/lib/Toolbelt.js.map b/lib/Toolbelt.js.map deleted file mode 100644 index bd30212..0000000 --- a/lib/Toolbelt.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"Toolbelt.js","sourceRoot":"","sources":["../src/Toolbelt.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/types.js b/lib/types.js deleted file mode 100644 index 4ef431e..0000000 --- a/lib/types.js +++ /dev/null @@ -1,2 +0,0 @@ -export {} -//# sourceMappingURL=types.js.map diff --git a/lib/types.js.map b/lib/types.js.map deleted file mode 100644 index c768b79..0000000 --- a/lib/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""} \ No newline at end of file From c5e7fcf98bde4c6ec1bf8f7522c3c6a1b4eae10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0vagr?= Date: Fri, 3 Oct 2025 09:34:17 +0200 Subject: [PATCH 28/28] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20INGORED=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Builder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Builder.ts b/src/Builder.ts index 7ab4ae7..d377516 100644 --- a/src/Builder.ts +++ b/src/Builder.ts @@ -16,7 +16,7 @@ export class Builder { public static readonly BASE_STARTER_DIR = path.normalize( path.join(import.meta.dirname, '..', 'starter', '_base') ) - public static readonly INGORED_FILES = ['node-app.jsonc'] + public static readonly IGNORED_FILES = ['node-app.jsonc'] public readonly npm: Npm protected readonly logger: Logger @@ -108,7 +108,7 @@ export class Builder { onlyFiles: false, }) - const ignoredFiles = Builder.INGORED_FILES.map(file => + const ignoredFiles = Builder.IGNORED_FILES.map(file => path.join(starterDir, file) )