From fabc29fa102a0b351ab7c7bb3cf4208ab9422e23 Mon Sep 17 00:00:00 2001 From: Richard Herman <1429781+GeekyEggo@users.noreply.github.com> Date: Fri, 3 May 2024 14:13:31 +0200 Subject: [PATCH] feat: pad version (#36) * feat: automatically pad version of manifest when packing * build: update publish tag to be automatic * docs: changelogs * docs: version format * build: remove subjective IDE settings * style: update to centralized prettier config * lint: fix prettier config --------- Co-authored-by: Richard Herman --- .editorconfig | 7 +- .github/workflows/release.yml | 6 +- .npmrc | 1 - .prettierrc | 17 -- .vscode/settings.json | 14 -- .vscode/tasks.json | 13 -- CHANGELOG.md | 5 + package-lock.json | 161 ++++++++++++++++- package.json | 168 +++++++++--------- src/commands/config.ts | 7 +- src/commands/create.ts | 34 ++-- src/commands/dev.ts | 12 +- src/commands/link.ts | 12 +- src/commands/pack.ts | 43 +++-- src/commands/validate.ts | 3 +- src/common/command.ts | 9 +- src/common/file-copier.ts | 6 +- src/common/runner.ts | 18 +- src/common/stdout.ts | 6 +- src/common/storage.ts | 3 +- src/config.ts | 30 ++-- src/index.ts | 1 + src/json/file-context.ts | 5 +- src/json/map.ts | 37 ++-- src/json/schema.ts | 34 ++-- src/package-manager.ts | 9 +- src/stream-deck.ts | 4 +- src/system/fs.ts | 13 +- src/system/path.ts | 7 +- src/validation/entry-collection.ts | 3 +- src/validation/entry.ts | 5 +- src/validation/plugin/index.ts | 2 +- src/validation/plugin/plugin.ts | 5 +- .../plugin/rules/layout-item-bounds.ts | 9 +- src/validation/plugin/rules/layout-schema.ts | 4 +- .../plugin/rules/manifest-category.ts | 8 +- .../plugin/rules/manifest-files-exist.ts | 27 ++- .../plugin/rules/manifest-schema.ts | 6 +- .../plugin/rules/manifest-urls-exist.ts | 17 +- src/validation/plugin/rules/manifest-uuids.ts | 2 +- src/validation/plugin/rules/path-input.ts | 11 +- src/validation/result.ts | 11 +- src/validation/validator.ts | 4 +- 43 files changed, 514 insertions(+), 285 deletions(-) delete mode 100644 .prettierrc delete mode 100644 .vscode/settings.json delete mode 100644 .vscode/tasks.json diff --git a/.editorconfig b/.editorconfig index 20725c5..dc65248 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,12 @@ charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = tab +max_line_length = 120 + +[*.{json,md}] +indent_size = 4 +indent_style = space [*.yml] indent_size = 2 -indent_style = space \ No newline at end of file +indent_style = space diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5deaa41..78418d7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,7 +37,11 @@ jobs: - name: "🖌️ Lint" run: npm run lint + - name: "📖 Retrieve package information" + uses: geekyeggo/npm-pkg@v1 + id: pkg + - name: "📤 Publish" - run: npm publish --provenance --access public + run: npm publish --provenance --access public --tag ${{ steps.pkg.outputs.version-tag }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.npmrc b/.npmrc index abf574f..af941b4 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1 @@ sign-git-tag=true -tag=beta \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index e39d893..0000000 --- a/.prettierrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "printWidth": 200, - "semi": true, - "trailingComma": "none", - "useTabs": true, - "importOrder": [ - "", - "^[./]" - ], - "importOrderParserPlugins": [ - "typescript", - "[\"decorators\", { \"decoratorsBeforeExport\": true }]" - ], - "importOrderSeparation": true, - "importOrderSortSpecifiers": true, - "importOrderCaseInsensitive": true -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index ec1b8ad..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - /** Prefer tabs over spaces for accessibility */ - "editor.insertSpaces": false, - "editor.detectIndentation": false, - /* Explorer */ - "explorer.fileNesting.enabled": true, - "explorer.fileNesting.patterns": { - "*.js": "${capture}.js.map, ${capture}.min.js, ${capture}.d.ts", - "*.ts": "${capture}.test.ts", - ".editorconfig": ".eslintrc, .prettierrc", - "package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, .npmrc", - "README.md": "LICENSE" - } -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 3a294f1..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "npm", - "script": "build", - "group": "build", - "problemMatcher": [], - "label": "npm: build", - "detail": "tsc" - } - ] -} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b2a6d8..f045625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ - Add `-v` option to display current version of CLI. - Add "Open in VSCode" prompt, as part of creation wizard, for macOS. +### ♻️ Update + +- Update template to `{major}.{minor}.{patch}.{build}` version format. +- Update `streamdeck pack` to automatically adjust version format. + ### 🐞 Bug Fixes - Fix support for Visual Studio integrated terminal. diff --git a/package-lock.json b/package-lock.json index 6f1514d..83f947f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,13 +16,13 @@ "streamdeck": "bin/streamdeck.mjs" }, "devDependencies": { + "@elgato/prettier-config": "^0.2.1", "@humanwhocodes/momoa": "^3.0.0", "@rollup/plugin-commonjs": "^25.0.5", "@rollup/plugin-json": "^6.0.1", "@rollup/plugin-node-resolve": "^15.2.2", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.5", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@tsconfig/node20": "^20.1.2", "@types/ejs": "^3.1.3", "@types/inquirer": "^9.0.3", @@ -45,7 +45,6 @@ "is-interactive": "^2.0.0", "lodash": "^4.17.21", "log-symbols": "^5.1.0", - "prettier": "^3.0.3", "rage-edit": "^1.2.0", "rollup": "^4.0.2", "semver": "^7.6.0", @@ -66,11 +65,37 @@ "node": ">=0.10.0" } }, + "node_modules/@augment-vir/common": { + "version": "23.4.0", + "resolved": "https://registry.npmjs.org/@augment-vir/common/-/common-23.4.0.tgz", + "integrity": "sha512-QIrJ1doD00TNbOzeVrk9KgPTzRlIjayxERnhtbQjK/AFPj5yElcB03GbnGdQZPzws/R+5gfMM5cZiH7QyBP+Kg==", + "dev": true, + "peer": true, + "dependencies": { + "browser-or-node": "^2.1.1", + "run-time-assertions": "^1.0.0", + "type-fest": "^4.10.2" + } + }, + "node_modules/@augment-vir/common/node_modules/type-fest": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.0.tgz", + "integrity": "sha512-+dbmiyliDY/2TTcjCS7NpI9yV2iEFlUDk5TKnsbkN7ZoRu5s7bT+zvYtNFhFXC2oLwURGT2frACAZvbbyNBI+w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, + "peer": true, "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -84,6 +109,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "peer": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -96,6 +122,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -110,6 +137,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "peer": true, "dependencies": { "color-name": "1.1.3" } @@ -118,13 +146,15 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -134,6 +164,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", "dev": true, + "peer": true, "dependencies": { "@babel/types": "^7.17.0", "jsesc": "^2.5.1", @@ -148,6 +179,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -157,6 +189,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, + "peer": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" @@ -170,6 +203,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -184,6 +218,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -196,6 +231,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -210,6 +246,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, + "peer": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -222,6 +259,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -236,6 +274,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -245,6 +284,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, + "peer": true, "engines": { "node": ">=6.9.0" } @@ -254,6 +294,7 @@ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -268,6 +309,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "peer": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -280,6 +322,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -294,6 +337,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "peer": true, "dependencies": { "color-name": "1.1.3" } @@ -302,13 +346,15 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -318,6 +364,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, + "peer": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -330,6 +377,7 @@ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/parser": "^7.23.9", @@ -344,6 +392,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -358,6 +407,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/generator": "^7.23.0", @@ -379,6 +429,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, + "peer": true, "dependencies": { "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", @@ -394,6 +445,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -408,6 +460,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", "dev": true, + "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "to-fast-properties": "^2.0.0" @@ -416,6 +469,17 @@ "node": ">=6.9.0" } }, + "node_modules/@elgato/prettier-config": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@elgato/prettier-config/-/prettier-config-0.2.1.tgz", + "integrity": "sha512-9+FUbenP5oXr4NCcoN8LhTTyGZdL0HmoGmhSoXSPchjPJ0ymK7H2KYio1S0pADRyRf9DJ5FuWVzZ9fmyGj5x3Q==", + "dev": true, + "peerDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", + "prettier": "^3.2.5", + "prettier-plugin-multiline-arrays": "^3.0.4" + } + }, "node_modules/@elgato/schemas": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@elgato/schemas/-/schemas-0.3.0.tgz", @@ -1184,6 +1248,7 @@ "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", "dev": true, + "peer": true, "dependencies": { "@babel/generator": "7.17.7", "@babel/parser": "^7.20.5", @@ -1672,6 +1737,13 @@ "node": ">=8" } }, + "node_modules/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "dev": true, + "peer": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2348,6 +2420,13 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-0.15.0.tgz", + "integrity": "sha512-yWnriYB4e8G54M5/fAFj7rCIBiKs1HAACaY13kCz6Ku0dezjS9aMcfcdVK2X8Tv2tEV1BPz/wKfQ7WA4S/d8aA==", + "dev": true, + "peer": true + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -2712,6 +2791,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -2739,6 +2819,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -3118,13 +3199,15 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -3152,6 +3235,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -3692,6 +3776,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -3714,6 +3799,30 @@ "node": ">=6.0.0" } }, + "node_modules/prettier-plugin-multiline-arrays": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-multiline-arrays/-/prettier-plugin-multiline-arrays-3.0.4.tgz", + "integrity": "sha512-Oj9ZfEisLOF0did1CGXXQLTA0WEChT2pfI06pNA1PbvWqbys9a5xoys6DoZlDL+Uio0uW1XwKZkeKMAkG1EEfQ==", + "dev": true, + "peer": true, + "dependencies": { + "@augment-vir/common": "^23.3.4", + "proxy-vir": "^1.0.0" + }, + "peerDependencies": { + "prettier": ">=3.0.0" + } + }, + "node_modules/proxy-vir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-vir/-/proxy-vir-1.0.0.tgz", + "integrity": "sha512-WV1gkBxUOwLSz0Bn09tisIqLK7leAqtFm/474t3L0hQKJw7/gdrkGcWw0/OT1PhSy+TDS6swfq7Niuoq3XJhkQ==", + "dev": true, + "peer": true, + "dependencies": { + "@augment-vir/common": "^23.3.4" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3951,6 +4060,43 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-time-assertions": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-time-assertions/-/run-time-assertions-1.2.0.tgz", + "integrity": "sha512-cQwqdopGyGpJytXHPYJoCHzt2rr+VzZyW3/bXD8kuKujqDuTtOc0Y6DrlLU6a//wnQogznA7n2LFEIar2Pa2cA==", + "dev": true, + "peer": true, + "dependencies": { + "@augment-vir/common": "^26.0.0", + "expect-type": "~0.15.0", + "type-fest": "^4.13.0" + } + }, + "node_modules/run-time-assertions/node_modules/@augment-vir/common": { + "version": "26.4.0", + "resolved": "https://registry.npmjs.org/@augment-vir/common/-/common-26.4.0.tgz", + "integrity": "sha512-rQoLA+t3bbvs269KvZYKZ76WO7Ofp468mqahsI4RAvPjJaOJ/6lCo49HgvUjnFlv7DoEVbTMfHT3G3+VztNnsA==", + "dev": true, + "peer": true, + "dependencies": { + "browser-or-node": "^2.1.1", + "run-time-assertions": "^1.0.0", + "type-fest": "^4.12.0" + } + }, + "node_modules/run-time-assertions/node_modules/type-fest": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.0.tgz", + "integrity": "sha512-+dbmiyliDY/2TTcjCS7NpI9yV2iEFlUDk5TKnsbkN7ZoRu5s7bT+zvYtNFhFXC2oLwURGT2frACAZvbbyNBI+w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -4065,6 +4211,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4190,6 +4337,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "peer": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -4307,6 +4455,7 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, + "peer": true, "engines": { "node": ">=4" } diff --git a/package.json b/package.json index 0fa7e61..6e554c5 100644 --- a/package.json +++ b/package.json @@ -1,86 +1,86 @@ { - "name": "@elgato/cli", - "version": "0.3.0-beta.6", - "description": "CLI tool for building with Stream Deck.", - "bin": { - "streamdeck": "bin/streamdeck.mjs", - "sd": "bin/streamdeck.mjs" - }, - "files": [ - "bin/streamdeck.mjs", - "template" - ], - "type": "module", - "engines": { - "node": "^20.1.0" - }, - "scripts": { - "build": "rm -rf ./dist && rollup --config rollup.config.ts --configPlugin typescript", - "watch": "rollup --config rollup.config.ts --configPlugin typescript --watch", - "lint": "eslint . --ext .ts --max-warnings 0", - "lint:fix": "prettier --config .prettierrc \"./src/**/*.ts\" --write", - "preversion": "npm run build && npm run lint" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/elgatosf/cli.git" - }, - "keywords": [ - "elgato", - "stream deck", - "plugin", - "cli", - "marketplace", - "maker" - ], - "author": { - "name": "Elgato", - "url": "https://www.elgato.com" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/elgatosf/cli/issues" - }, - "homepage": "https://github.com/elgatosf/cli#readme", - "devDependencies": { - "@humanwhocodes/momoa": "^3.0.0", - "@rollup/plugin-commonjs": "^25.0.5", - "@rollup/plugin-json": "^6.0.1", - "@rollup/plugin-node-resolve": "^15.2.2", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.5", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", - "@tsconfig/node20": "^20.1.2", - "@types/ejs": "^3.1.3", - "@types/inquirer": "^9.0.3", - "@types/lodash": "^4.14.199", - "@types/node": "^20.8.10", - "@typescript-eslint/eslint-plugin": "^6.7.4", - "@typescript-eslint/parser": "^6.7.4", - "@zip.js/zip.js": "^2.7.34", - "ajv": "^8.12.0", - "chalk": "^5.3.0", - "commander": "^11.0.0", - "ejs": "^3.1.9", - "eslint": "^8.51.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-jsdoc": "^46.8.2", - "eslint-plugin-prettier": "^5.0.0", - "find-process": "^1.4.7", - "ignore": "^5.3.1", - "inquirer": "^9.2.11", - "is-interactive": "^2.0.0", - "lodash": "^4.17.21", - "log-symbols": "^5.1.0", - "prettier": "^3.0.3", - "rage-edit": "^1.2.0", - "rollup": "^4.0.2", - "semver": "^7.6.0", - "tar": "^7.0.1", - "tslib": "^2.6.2", - "typescript": "^5.2.2" - }, - "dependencies": { - "@elgato/schemas": "^0.3.0" - } + "name": "@elgato/cli", + "version": "0.3.0-beta.6", + "description": "CLI tool for building with Stream Deck.", + "bin": { + "streamdeck": "bin/streamdeck.mjs", + "sd": "bin/streamdeck.mjs" + }, + "files": [ + "bin/streamdeck.mjs", + "template" + ], + "type": "module", + "engines": { + "node": "^20.1.0" + }, + "scripts": { + "build": "rm -rf ./dist && rollup --config rollup.config.ts --configPlugin typescript", + "watch": "rollup --config rollup.config.ts --configPlugin typescript --watch", + "lint": "eslint . --ext .ts --max-warnings 0", + "lint:fix": "prettier \"./src/**/*.ts\" --write", + "preversion": "npm run build && npm run lint" + }, + "prettier": "@elgato/prettier-config", + "repository": { + "type": "git", + "url": "git+https://github.com/elgatosf/cli.git" + }, + "keywords": [ + "elgato", + "stream deck", + "plugin", + "cli", + "marketplace", + "maker" + ], + "author": { + "name": "Elgato", + "url": "https://www.elgato.com" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/elgatosf/cli/issues" + }, + "homepage": "https://github.com/elgatosf/cli#readme", + "devDependencies": { + "@elgato/prettier-config": "^0.2.1", + "@humanwhocodes/momoa": "^3.0.0", + "@rollup/plugin-commonjs": "^25.0.5", + "@rollup/plugin-json": "^6.0.1", + "@rollup/plugin-node-resolve": "^15.2.2", + "@rollup/plugin-terser": "^0.4.4", + "@rollup/plugin-typescript": "^11.1.5", + "@tsconfig/node20": "^20.1.2", + "@types/ejs": "^3.1.3", + "@types/inquirer": "^9.0.3", + "@types/lodash": "^4.14.199", + "@types/node": "^20.8.10", + "@typescript-eslint/eslint-plugin": "^6.7.4", + "@typescript-eslint/parser": "^6.7.4", + "@zip.js/zip.js": "^2.7.34", + "ajv": "^8.12.0", + "chalk": "^5.3.0", + "commander": "^11.0.0", + "ejs": "^3.1.9", + "eslint": "^8.51.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-jsdoc": "^46.8.2", + "eslint-plugin-prettier": "^5.0.0", + "find-process": "^1.4.7", + "ignore": "^5.3.1", + "inquirer": "^9.2.11", + "is-interactive": "^2.0.0", + "lodash": "^4.17.21", + "log-symbols": "^5.1.0", + "rage-edit": "^1.2.0", + "rollup": "^4.0.2", + "semver": "^7.6.0", + "tar": "^7.0.1", + "tslib": "^2.6.2", + "typescript": "^5.2.2" + }, + "dependencies": { + "@elgato/schemas": "^0.3.0" + } } diff --git a/src/commands/config.ts b/src/commands/config.ts index d918512..e667ca0 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -2,7 +2,7 @@ import chalk from "chalk"; import { existsSync, rmSync } from "fs"; import _ from "lodash"; -import { GlobalOptions, command } from "../common/command"; +import { command, GlobalOptions } from "../common/command"; import { createConsole, createQuietConsole } from "../common/stdout"; import { defaultConfig, getFilePath, getLocalConfig, updateConfig } from "../config"; @@ -48,7 +48,10 @@ export const list = command((options, output) => { * @param options Options associated with the * @param output Output whereby the result will be sent. */ -export function reset(options: GlobalOptions = { quiet: false }, output = options.quiet ? createQuietConsole() : createConsole(false)): void { +export function reset( + options: GlobalOptions = { quiet: false }, + output = options.quiet ? createQuietConsole() : createConsole(false), +): void { const filePath = getFilePath(); if (existsSync(filePath)) { rmSync(filePath); diff --git a/src/commands/create.ts b/src/commands/create.ts index 51dfe3d..a9f6d5d 100644 --- a/src/commands/create.ts +++ b/src/commands/create.ts @@ -5,11 +5,11 @@ import path from "node:path"; import { command } from "../common/command"; import { createCopier } from "../common/file-copier"; -import { invalidCharacters, isExecutable, isSafeBaseName, relative } from "../system/path"; import { run } from "../common/runner"; import { StdOut } from "../common/stdout"; import { getConfig } from "../config"; import { generatePluginId, getPlugins, isValidPluginId } from "../stream-deck"; +import { invalidCharacters, isExecutable, isSafeBaseName, relative } from "../system/path"; import { setDeveloperMode } from "./dev"; import { link } from "./link"; @@ -80,13 +80,13 @@ async function promptForPluginInfo(): Promise { name: "author", message: "Author:", validate: required("Please enter the author."), - type: "input" + type: "input", }, { name: "name", message: "Plugin Name:", validate: required("Please enter the name of the plugin."), - type: "input" + type: "input", }, { name: "uuid", @@ -103,14 +103,14 @@ async function promptForPluginInfo(): Promise { return true; }, - type: "input" + type: "input", }, { name: "description", message: "Description:", validate: required("Please enter a brief description of what the plugin will do."), - type: "input" - } + type: "input", + }, ]); } @@ -130,7 +130,7 @@ async function validateDestination(dirName: string, stdout: StdOut): Promise { name: "confirm", message: "Create Stream Deck plugin from information above?", default: true, - type: "confirm" + type: "confirm", }) ).confirm; } @@ -198,7 +198,11 @@ async function isPluginInfoCorrect(stdout: StdOut): Promise { * @param options Supporting data supplied to the renderer. * @returns The file copier capable of rendering the template. */ -function createTemplateRenderer(destination: string, pluginInfo: PluginInfo, options = { isPreBuild: true }): ReturnType { +function createTemplateRenderer( + destination: string, + pluginInfo: PluginInfo, + options = { isPreBuild: true }, +): ReturnType { const config = getConfig(); return createCopier({ dest: destination, @@ -208,9 +212,9 @@ function createTemplateRenderer(destination: string, pluginInfo: PluginInfo, opt ...options, npm: { cli: config.npm.cli.version, - streamDeck: config.npm.streamDeck.version - } - } + streamDeck: config.npm.streamDeck.version, + }, + }, }); } @@ -229,7 +233,7 @@ async function renderTemplate(destination: string, pluginInfo: PluginInfo): Prom template.copy("_.gitignore", ".gitignore"), template.copy("package.json.ejs"), template.copy("rollup.config.mjs.ejs"), - template.copy("tsconfig.json.ejs") + template.copy("tsconfig.json.ejs"), ]); } @@ -243,7 +247,7 @@ async function finalize(destination: string, pluginInfo: PluginInfo): Promise name: "confirm", message: "Would you like to open the plugin in VS Code?", default: true, - type: "confirm" + type: "confirm", }); if (vsCode.confirm) { diff --git a/src/commands/dev.ts b/src/commands/dev.ts index e36cb8e..89e08aa 100644 --- a/src/commands/dev.ts +++ b/src/commands/dev.ts @@ -13,7 +13,13 @@ export const setDeveloperMode = command( const flagName = "developer_mode"; if (os.platform() === "darwin") { - childProcess.spawnSync("defaults", ["write", "com.elgato.StreamDeck", flagName, "-bool", options.disable ? "NO" : "YES"]); + childProcess.spawnSync("defaults", [ + "write", + "com.elgato.StreamDeck", + flagName, + "-bool", + options.disable ? "NO" : "YES", + ]); } else { Registry.set("HKEY_CURRENT_USER\\Software\\Elgato Systems GmbH\\StreamDeck", flagName, options.disable ? 0 : 1); } @@ -21,8 +27,8 @@ export const setDeveloperMode = command( output.success(`Developer mode ${options.disable ? "disabled" : "enabled"}`); }, { - disable: false - } + disable: false, + }, ); /** diff --git a/src/commands/link.ts b/src/commands/link.ts index 3a7481a..f467bf3 100644 --- a/src/commands/link.ts +++ b/src/commands/link.ts @@ -27,7 +27,9 @@ export const link = command( return feedback .error("Linking failed") .log(`Invalid directory name: ${basename(options.path)}`) - .log('Name must be in reverse DNS format, be suffixed with ".sdPlugin", and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), and periods (.).') + .log( + 'Name must be in reverse DNS format, be suffixed with ".sdPlugin", and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), and periods (.).', + ) .log(`Examples: ${chalk.green("com.elgato.wave-link.sdPlugin")}, ${chalk.green("tv.twitch.studio.sdPlugin")}`) .exit(1); } @@ -42,7 +44,9 @@ export const link = command( return feedback .error("Linking failed") .log(`Plugin already installed: ${uuid}`) - .log(`Another plugin with this UUID is already installed. Please uninstall the plugin, or rename the directory being linked, and try again.`) + .log( + `Another plugin with this UUID is already installed. Please uninstall the plugin, or rename the directory being linked, and try again.`, + ) .exit(1); } } @@ -51,8 +55,8 @@ export const link = command( feedback.success("Linked successfully"); }, { - path: process.cwd() - } + path: process.cwd(), + }, ); /** diff --git a/src/commands/pack.ts b/src/commands/pack.ts index 02f0f28..b6ec9a2 100644 --- a/src/commands/pack.ts +++ b/src/commands/pack.ts @@ -6,10 +6,11 @@ import { readFile, rm } from "node:fs/promises"; import { basename, dirname, join, resolve } from "node:path"; import { Readable, Writable } from "node:stream"; import type { ReadableStream } from "node:stream/web"; + import { command } from "../common/command"; import { StdoutError } from "../common/stdout"; import { getPluginId } from "../stream-deck"; -import { getFiles, mkdirIfNotExists, readJsonFile, sizeAsString, type FileInfo } from "../system/fs"; +import { type FileInfo, getFiles, mkdirIfNotExists, readJsonFile, sizeAsString } from "../system/fs"; import { defaultOptions, validate, type ValidateOptions } from "./validate"; /** @@ -22,15 +23,15 @@ export const pack = command( const outputPath = resolve(options.output, `${getPluginId(sourcePath)}.streamDeckPlugin`); // Version (optionally) and validate. - const versioner = options.version !== null ? await version(sourcePath, options.version) : undefined; + const versioner = await version(sourcePath, options.version); try { await validate({ ...options, - quietSuccess: true + quietSuccess: true, }); } catch (err) { if (err instanceof StdoutError) { - versioner?.undo(); + versioner.undo(); stdout.exit(1); } @@ -42,7 +43,10 @@ export const pack = command( if (options.force) { await rm(outputPath); } else { - stdout.error("File already exists").log("Specify a different -o|-output location, or -f|--force saving to overwrite the existing file").exit(1); + stdout + .error("File already exists") + .log("Specify a different -o|-output location, or -f|--force saving to overwrite the existing file") + .exit(1); } } @@ -60,7 +64,9 @@ export const pack = command( stdout.log(chalk.cyan("Plugin Contents")); contents.files.forEach((file, i) => { - stdout.log(`${chalk.dim(i === contents.files.length - 1 ? "└─" : "├─")} ${file.size.text.padEnd(contents.sizePad)} ${file.path.relative}`); + stdout.log( + `${chalk.dim(i === contents.files.length - 1 ? "└─" : "├─")} ${file.size.text.padEnd(contents.sizePad)} ${file.path.relative}`, + ); }); // Print the package details. @@ -87,8 +93,8 @@ export const pack = command( dryRun: false, force: false, output: process.cwd(), - version: null - } + version: null, + }, ); /** @@ -103,7 +109,7 @@ function getPackageBuilder(sourcePath: string, outputPath: string, dryRun = fals if (dryRun) { return { add: () => Promise.resolve(), - close: (): void => {} + close: (): void => {}, }; } @@ -116,7 +122,7 @@ function getPackageBuilder(sourcePath: string, outputPath: string, dryRun = fals const name = join(entryPrefix, file.path.relative).replaceAll("\\", "/"); await zipStream.add(name, stream ?? Readable.toWeb(createReadStream(file.path.absolute))); }, - close: () => zipStream.close() + close: () => zipStream.close(), }; } @@ -126,14 +132,17 @@ function getPackageBuilder(sourcePath: string, outputPath: string, dryRun = fals * @param fileFn Optional function called for each file that is considered part of the package. * @returns Information about the package contents. */ -async function getPackageContents(path: string, fileFn?: (file: FileInfo, stream?: ReadableStream) => Promise | void): Promise { +async function getPackageContents( + path: string, + fileFn?: (file: FileInfo, stream?: ReadableStream) => Promise | void, +): Promise { // Get the manifest, and generate the base contents. const manifest = await readJsonFile(join(path, "manifest.json")); const contents: PackageInfo = { files: [], manifest, size: 0, - sizePad: 0 + sizePad: 0, }; // Add each file to the contents. @@ -166,18 +175,20 @@ async function getPackageContents(path: string, fileFn?: (file: FileInfo, stream /** * Versions the manifest at the specified {@link path}. * @param path Path to the directory where the manifest is contained. - * @param version Desired version. + * @param version Optional preferred version. * @returns Object that allows for the versioning to be undone. */ -async function version(path: string, version: string): Promise { +async function version(path: string, version: string | null): Promise { const manifestPath = resolve(path, "manifest.json"); const write = (contents: string): void => writeFileSync(manifestPath, contents, { encoding: "utf-8" }); let original: string | undefined; if (existsSync(manifestPath)) { original = await readFile(manifestPath, { encoding: "utf-8" }); + const manifest = JSON.parse(original) as Partial; - const manifest = JSON.parse(original); + // Ensure the version in the manifest has the correct number of segments, [{major}.{minor}.{patch}.{build}] + version ??= manifest.Version?.toString() || ""; manifest.Version = `${version}${".0".repeat(Math.max(0, 4 - version.split(".").length))}`; write(JSON.stringify(manifest, undefined, "\t")); } @@ -187,7 +198,7 @@ async function version(path: string, version: string): Promise if (original !== undefined) { write(original); } - } + }, }; } diff --git a/src/commands/validate.ts b/src/commands/validate.ts index 37fc110..d198276 100644 --- a/src/commands/validate.ts +++ b/src/commands/validate.ts @@ -1,4 +1,5 @@ import { resolve } from "node:path"; + import { command } from "../common/command"; import { StdoutError } from "../common/stdout"; import { store } from "../common/storage"; @@ -17,7 +18,7 @@ export const defaultOptions = { forceUpdateCheck: false, quietSuccess: false, path: process.cwd(), - updateCheck: true + updateCheck: true, } satisfies ValidateOptions; /** diff --git a/src/common/command.ts b/src/common/command.ts index eedb8e3..fa3fe3d 100644 --- a/src/common/command.ts +++ b/src/common/command.ts @@ -16,7 +16,11 @@ export function command( ...[defaultOptions]: OptionalWhenEmpty, never, Required>> ): (...[options]: OptionalWhenEmpty, GlobalOptions & T>) => Promise | void { return async (...[options]: OptionalWhenEmpty, GlobalOptions & T>) => { - const opts = _.merge({ quiet: false }, defaultOptions as Required>, options as GlobalOptions & PickRequired); + const opts = _.merge( + { quiet: false }, + defaultOptions as Required>, + options as GlobalOptions & PickRequired, + ); const { reduceMotion } = getConfig(); const output = opts.quiet ? createQuietConsole() : createConsole(reduceMotion); @@ -49,7 +53,8 @@ export type GlobalOptions = { /** * Determines whether {@template T} contains any properties, when it does not, {@template TOptional} is returned as an optional; otherwise {@template TRequired} is returned. */ -type OptionalWhenEmpty = T extends Record ? [TOptional?] : [TRequired]; +type OptionalWhenEmpty = + T extends Record ? [TOptional?] : [TRequired]; /** * Defines the complete set of (merged) options sent to a command. diff --git a/src/common/file-copier.ts b/src/common/file-copier.ts index 65b089b..f4cfd44 100644 --- a/src/common/file-copier.ts +++ b/src/common/file-copier.ts @@ -10,7 +10,7 @@ import { dirname, extname, resolve } from "path"; export function createCopier(options: Options): FileCopier { return new FileCopier({ data: {}, - ...options + ...options, }); } @@ -32,7 +32,7 @@ class FileCopier { public async copy(path: string, destination?: string): Promise { const opts = { source: resolve(this.options.source, path), - dest: resolve(this.options.dest, destination || path) + dest: resolve(this.options.dest, destination || path), }; if (fs.lstatSync(opts.source).isDirectory()) { @@ -61,7 +61,7 @@ class FileCopier { fs.cpSync(source, dest, { filter, - recursive: true + recursive: true, }); templates.forEach((opts) => this.copyFile(opts)); diff --git a/src/common/runner.ts b/src/common/runner.ts index 50b61cf..02b9582 100644 --- a/src/common/runner.ts +++ b/src/common/runner.ts @@ -1,4 +1,9 @@ -import child_process, { ChildProcessByStdio, SpawnOptionsWithStdioTuple, StdioNull, StdioPipe } from "node:child_process"; +import child_process, { + ChildProcessByStdio, + SpawnOptionsWithStdioTuple, + StdioNull, + StdioPipe, +} from "node:child_process"; import { Readable } from "node:stream"; /** @@ -54,16 +59,19 @@ function forget(command: string, args: string[], options: RunOptions): Promise(options: RunOptions, stderr: T): SpawnOptionsWithStdioTuple { +function mergeOptions( + options: RunOptions, + stderr: T, +): SpawnOptionsWithStdioTuple { return { ...{ shell: true, - windowsHide: true + windowsHide: true, }, ...options, ...{ - stdio: ["ignore", "ignore", stderr] - } + stdio: ["ignore", "ignore", stderr], + }, }; } diff --git a/src/common/stdout.ts b/src/common/stdout.ts index ff4469f..ce3f9ae 100644 --- a/src/common/stdout.ts +++ b/src/common/stdout.ts @@ -17,7 +17,7 @@ export function createConsole(reduceMotion: boolean): StdOut { return new ConsoleStdOut({ interactive, level: MessageLevel.LOG, - reduceMotion: reduceMotion || !interactive + reduceMotion: reduceMotion || !interactive, }); } @@ -29,7 +29,7 @@ export function createQuietConsole(): StdOut { return new ConsoleStdOut({ interactive: false, level: MessageLevel.WARN, - reduceMotion: true + reduceMotion: true, }); } @@ -388,5 +388,5 @@ enum MessageLevel { /** * Miscellaneous information that is not represented by a symbol. */ - LOG = 4 + LOG = 4, } diff --git a/src/common/storage.ts b/src/common/storage.ts index f24487a..0f564c8 100644 --- a/src/common/storage.ts +++ b/src/common/storage.ts @@ -1,4 +1,5 @@ import { readFileSync, writeFileSync } from "fs"; + import { relative } from "../system/path"; const storePath = relative("../.cli.cache"); @@ -33,7 +34,7 @@ export const store = { const cache = readStore(); cache[key] = value; writeFileSync(storePath, JSON.stringify(cache), { encoding: "utf-8", flag: "w" }); - } + }, }; /** diff --git a/src/config.ts b/src/config.ts index 0291fec..b8b9b37 100644 --- a/src/config.ts +++ b/src/config.ts @@ -17,15 +17,15 @@ export const defaultConfig: Config = Object.freeze({ npm: { cli: { mode: "prod" as const, - version: `^${JSON.parse(readFileSync(relative("../package.json"), { encoding: "utf-8" })).version}` + version: `^${JSON.parse(readFileSync(relative("../package.json"), { encoding: "utf-8" })).version}`, }, streamDeck: { mode: "prod" as const, - version: "^0.3.0" - } + version: "^0.3.0", + }, }, reduceMotion: false, - packageManager: "npm" + packageManager: "npm", }); /** @@ -129,7 +129,9 @@ function exitWithError(message: string, errors?: string[]): never { } console.log(); - console.log(`Please repair the configuration file or run ${chalk.cyan("streamdeck config reset")} to reset all configuration values.`); + console.log( + `Please repair the configuration file or run ${chalk.cyan("streamdeck config reset")} to reset all configuration values.`, + ); console.log(); console.log(getFilePath()); @@ -149,21 +151,21 @@ const validateSchema = new Ajv({ allErrors: true }).compile({ additionalProperties: true, optionalProperties: { mode: { enum: ["dev", "prod"] }, - version: { type: "string" } - } + version: { type: "string" }, + }, }, streamDeck: { additionalProperties: true, optionalProperties: { mode: { enum: ["dev", "prod"] }, - version: { type: "string" } - } - } - } + version: { type: "string" }, + }, + }, + }, }, reduceMotion: { type: "boolean" }, - packageManager: { enum: ["npm", "yarn", "pnpm", "bun"] } - } + packageManager: { enum: ["npm", "yarn", "pnpm", "bun"] }, + }, } satisfies JTDSchemaType>); /** @@ -180,7 +182,7 @@ function validate(config: unknown): never | void { validateSchema.errors?.map(({ instancePath, message, params: { allowedValues } }) => { const detail = allowedValues ? `: ${allowedValues.join(", ")}` : ""; return `[${instancePath.replaceAll("/", ".").substring(1)}] ${message}${detail}`; - }) + }), ); } diff --git a/src/index.ts b/src/index.ts index f217084..aec3efd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import { program } from "commander"; + import { config, create, link, pack, restart, setDeveloperMode, stop, validate } from "./commands"; import { packageManager } from "./package-manager"; diff --git a/src/json/file-context.ts b/src/json/file-context.ts index 523a380..ef1ffc5 100644 --- a/src/json/file-context.ts +++ b/src/json/file-context.ts @@ -1,5 +1,6 @@ import { existsSync, readFileSync } from "node:fs"; -import { JsonObjectMap, type JsonObject } from "./map"; + +import { type JsonObject, JsonObjectMap } from "./map"; import type { JsonSchema, JsonSchemaError } from "./schema"; /** @@ -23,7 +24,7 @@ export class JsonFileContext { */ constructor( public readonly path: string, - public readonly schema: JsonSchema + public readonly schema: JsonSchema, ) { if (existsSync(this.path)) { const json = readFileSync(this.path, { encoding: "utf-8" }); diff --git a/src/json/map.ts b/src/json/map.ts index b79bf03..18901ec 100644 --- a/src/json/map.ts +++ b/src/json/map.ts @@ -1,5 +1,14 @@ -import { type ArrayNode, type DocumentNode, type ElementNode, type MemberNode, type NullNode, type ObjectNode, type ValueNode } from "@humanwhocodes/momoa"; +import { + type ArrayNode, + type DocumentNode, + type ElementNode, + type MemberNode, + type NullNode, + type ObjectNode, + type ValueNode, +} from "@humanwhocodes/momoa"; import { type AnyValidateFunction } from "ajv/dist/types"; + import { type JsonLocation, type LocationRef } from "../common/location"; /** @@ -56,14 +65,14 @@ export class JsonObjectMap { private aggregate( node: ArrayNode | DocumentNode | ElementNode | NullNode | ObjectNode | ValueNode, pointer: string, - errors: AnyValidateFunction["errors"] + errors: AnyValidateFunction["errors"], ): JsonElement | JsonElement[] | JsonObject { const nodeRef: NodeRef = { location: { ...node.loc?.start, instancePath: pointer, - key: getPath(pointer) - } + key: getPath(pointer), + }, }; this.nodes.set(pointer, nodeRef); @@ -76,10 +85,13 @@ export class JsonObjectMap { // Object node, recursively reduce each member. if (node.type === "Object") { - return node.members.reduce((obj: Record, member: MemberNode) => { - obj[member.name.value] = this.aggregate(member.value, `${pointer}/${member.name.value}`, errors); - return obj; - }, {}); + return node.members.reduce( + (obj: Record, member: MemberNode) => { + obj[member.name.value] = this.aggregate(member.value, `${pointer}/${member.name.value}`, errors); + return obj; + }, + {}, + ); } // Array node, recursively reduce each element. @@ -99,7 +111,9 @@ export class JsonObjectMap { return nodeRef.node; } - throw new Error(`Encountered unhandled node type '${node.type}' when mapping abstract-syntax tree node to JSON object`); + throw new Error( + `Encountered unhandled node type '${node.type}' when mapping abstract-syntax tree node to JSON object`, + ); } } @@ -128,7 +142,8 @@ export type JsonObject = { /** * JSON property within a JSON string, including it's parsed value, and the location it was parsed from. */ -export type JsonElement = T extends Array ? JsonElement[] : T extends object | undefined ? JsonObject : JsonValueNode; +export type JsonElement = + T extends Array ? JsonElement[] : T extends object | undefined ? JsonObject : JsonValueNode; /** * Represents a node within a JSON structure. @@ -141,7 +156,7 @@ class JsonValueNode implements LocationRef { */ constructor( public readonly value: T, - public readonly location: JsonLocation + public readonly location: JsonLocation, ) {} /** @inheritdoc */ diff --git a/src/json/schema.ts b/src/json/schema.ts index 1220b6e..fae50e3 100644 --- a/src/json/schema.ts +++ b/src/json/schema.ts @@ -1,9 +1,10 @@ import { keywordDefinitions } from "@elgato/schemas"; import { parse } from "@humanwhocodes/momoa"; -import Ajv, { AnySchemaObject, ErrorObject, KeywordDefinition, type AnySchema, type DefinedError } from "ajv"; -import { DataValidationCxt, type AnyValidateFunction } from "ajv/dist/types"; +import Ajv, { type AnySchema, AnySchemaObject, type DefinedError, ErrorObject, KeywordDefinition } from "ajv"; +import { type AnyValidateFunction, DataValidationCxt } from "ajv/dist/types"; import { type LimitNumberError } from "ajv/dist/vocabularies/validation/limitNumber"; import { isEqual, uniqWith } from "lodash"; + import { type JsonLocation, type LocationRef } from "../common/location"; import { colorize } from "../common/stdout"; import { aggregate } from "../common/utils"; @@ -41,7 +42,7 @@ export class JsonSchema { const ajv = new Ajv({ allErrors: true, messages: false, - strict: false + strict: false, }); ajv.addKeyword(keywordDefinitions.markdownDescription); @@ -90,12 +91,12 @@ export class JsonSchema { keyword: "false schema", instancePath: "", schemaPath: "", - params: {} + params: {}, }, message: "Contents must be a valid JSON string", - location: { instancePath: "/", key: undefined } - } - ] + location: { instancePath: "/", key: undefined }, + }, + ], }; } @@ -110,8 +111,8 @@ export class JsonSchema { this.filter(this._validate.errors).map((source) => ({ location: map.find(source.instancePath)?.location, message: this.getMessage(source as DefinedError), - source: source as DefinedError - })) ?? [] + source: source as DefinedError, + })) ?? [], }; } @@ -130,7 +131,7 @@ export class JsonSchema { // Remove ignored keywords, and remove duplicate errors. return uniqWith( errors.filter(({ keyword }) => !ignoredKeywords.includes(keyword)), - (a, b) => a.instancePath === b.instancePath && a.keyword === b.keyword && isEqual(a.params, b.params) + (a, b) => a.instancePath === b.instancePath && a.keyword === b.keyword && isEqual(a.params, b.params), ); } @@ -143,7 +144,9 @@ export class JsonSchema { const { keyword, message, params, instancePath } = error; if (keyword === "additionalProperties") { - return params.additionalProperty !== undefined ? `must not contain property: ${params.additionalProperty}` : "must not contain additional properties"; + return params.additionalProperty !== undefined + ? `must not contain property: ${params.additionalProperty}` + : "must not contain additional properties"; } if (keyword === "enum") { @@ -199,13 +202,18 @@ function captureKeyword(def: KeywordDefinition, map: Map): Keyw return { keyword, schemaType, - validate: (schema: unknown, data: unknown, parentSchema?: AnySchemaObject, dataCtx?: DataValidationCxt): boolean => { + validate: ( + schema: unknown, + data: unknown, + parentSchema?: AnySchemaObject, + dataCtx?: DataValidationCxt, + ): boolean => { if (dataCtx?.instancePath !== undefined) { map.set(dataCtx.instancePath, schema); } return true; - } + }, }; } diff --git a/src/package-manager.ts b/src/package-manager.ts index f400a4d..fc6e75c 100644 --- a/src/package-manager.ts +++ b/src/package-manager.ts @@ -4,6 +4,7 @@ import { dirname, join } from "node:path"; import { Readable } from "node:stream"; import semver from "semver"; import { extract } from "tar"; + import { dependencies, version } from "../package.json"; import { moveSync } from "./system/fs"; import { relative } from "./system/path"; @@ -76,7 +77,7 @@ class PackageManager { await extract({ file, strip: 1, - cwd: installationPath + cwd: installationPath, }); } catch (err) { // When something goes wrong, fallback to the previous package. @@ -120,8 +121,8 @@ class PackageManager { public async search(name: string): Promise { const res = await fetch(`https://registry.npmjs.org/${name}`, { headers: { - Accept: "application/vnd.npm.install-v1+json" - } + Accept: "application/vnd.npm.install-v1+json", + }, }); if (res.status !== 200) { @@ -191,7 +192,7 @@ class PackageManager { return update; }, - undefined as PackageMetadataVersion | undefined + undefined as PackageMetadataVersion | undefined, ); } } diff --git a/src/stream-deck.ts b/src/stream-deck.ts index 40f9741..73ad611 100644 --- a/src/stream-deck.ts +++ b/src/stream-deck.ts @@ -101,7 +101,7 @@ export function getPluginId(path: string): string | undefined { export function generatePluginId(author: string | undefined, name: string | undefined): string | undefined { const sections = { author: getSafeValue(author), - name: getSafeValue(name) + name: getSafeValue(name), }; if (sections.author === undefined || sections.name === undefined) { @@ -174,7 +174,7 @@ class PluginInfo { constructor( private readonly path: string, private readonly entry: Dirent, - public readonly uuid: string + public readonly uuid: string, ) {} /** diff --git a/src/system/fs.ts b/src/system/fs.ts index c0441d1..7178c60 100644 --- a/src/system/fs.ts +++ b/src/system/fs.ts @@ -45,12 +45,12 @@ export async function* getFiles(path: string, ignorePatterns?: string[]): AsyncG name: basename(absolute), path: { absolute, - relative: entry + relative: entry, }, size: { bytes: stats.size, - text: sizeAsString(stats.size) - } + text: sizeAsString(stats.size), + }, }; } } @@ -61,7 +61,10 @@ export async function* getFiles(path: string, ignorePatterns?: string[]): AsyncG * @param defaultPatterns Collection of default ignore patterns. * @returns Predicate that determines whether the path should be ignored; returns `true` when the path should be ignored. */ -export async function getIgnores(path: string, defaultPatterns: string[] = defaultIgnorePatterns): Promise<(path: string) => boolean> { +export async function getIgnores( + path: string, + defaultPatterns: string[] = defaultIgnorePatterns, +): Promise<(path: string) => boolean> { const i = ignore().add(defaultPatterns); // When a ".sdignore" file is present, add the ignore patterns. @@ -71,7 +74,7 @@ export async function getIgnores(path: string, defaultPatterns: string[] = defau try { const rl = createInterface({ input: fileStream, - crlfDelay: Infinity + crlfDelay: Infinity, }); // Treat each line as a pattern, adding it to the ignore. diff --git a/src/system/path.ts b/src/system/path.ts index 359aa85..ae16db5 100644 --- a/src/system/path.ts +++ b/src/system/path.ts @@ -1,6 +1,7 @@ import { existsSync, readdirSync } from "node:fs"; import { delimiter, dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; + import { isDirectory, isFile } from "../system/fs"; /** @@ -10,7 +11,11 @@ import { isDirectory, isFile } from "../system/fs"; */ export function isExecutable(application: string): boolean { for (const path of process.env.PATH?.split(delimiter) || []) { - if (existsSync(path) && isDirectory(path) && readdirSync(path).find((entry) => entry === application && isFile(join(path, entry)))) { + if ( + existsSync(path) && + isDirectory(path) && + readdirSync(path).find((entry) => entry === application && isFile(join(path, entry))) + ) { return true; } } diff --git a/src/validation/entry-collection.ts b/src/validation/entry-collection.ts index 2addce7..1453442 100644 --- a/src/validation/entry-collection.ts +++ b/src/validation/entry-collection.ts @@ -1,4 +1,5 @@ import chalk from "chalk"; + import { OrderedArray } from "../common/ordered-array"; import { StdOut } from "../common/stdout"; import type { ValidationEntry } from "./entry"; @@ -14,7 +15,7 @@ export class ValidationEntryCollection { (x) => x.level, (x) => x.details?.location?.line ?? Infinity, (x) => x.details?.location?.column ?? Infinity, - (x) => x.message + (x) => x.message, ); /** diff --git a/src/validation/entry.ts b/src/validation/entry.ts index ba65496..9af0446 100644 --- a/src/validation/entry.ts +++ b/src/validation/entry.ts @@ -1,5 +1,6 @@ import chalk from "chalk"; import { EOL } from "node:os"; + import { LocationRef } from "../common/location"; /** @@ -20,7 +21,7 @@ export class ValidationEntry { constructor( public readonly level: ValidationLevel, public readonly message: string, - public readonly details?: ValidationEntryDetails + public readonly details?: ValidationEntryDetails, ) { if (message.endsWith(".")) { this.message = message.slice(0, -1); @@ -75,7 +76,7 @@ export enum ValidationLevel { /** * Warning. */ - warning = 1 + warning = 1, } /** diff --git a/src/validation/plugin/index.ts b/src/validation/plugin/index.ts index bec3289..87f59a0 100644 --- a/src/validation/plugin/index.ts +++ b/src/validation/plugin/index.ts @@ -26,6 +26,6 @@ export function validatePlugin(path: string): Promise { categoryMatchesName, layoutsExistAndSchemasAreValid, layoutItemKeysAreUnique, - layoutItemsAreWithinBoundsAndNoOverlap + layoutItemsAreWithinBoundsAndNoOverlap, ]); } diff --git a/src/validation/plugin/plugin.ts b/src/validation/plugin/plugin.ts index 0e68b79..7b88b2b 100644 --- a/src/validation/plugin/plugin.ts +++ b/src/validation/plugin/plugin.ts @@ -1,6 +1,7 @@ import type { Layout, Manifest } from "@elgato/schemas/streamdeck/plugins"; import { createRequire } from "node:module"; import { basename, dirname, join, resolve } from "node:path"; + import { JsonLocation, LocationRef } from "../../common/location"; import { JsonFileContext, JsonSchema } from "../../json"; import { isPredefinedLayoutLike, isValidPluginId } from "../../stream-deck"; @@ -23,7 +24,7 @@ export function createContext(path: string): PluginContext { return { hasValidId: isValidPluginId(id), manifest: new ManifestJsonFileContext(join(path, "manifest.json")), - id + id, }; } @@ -49,7 +50,7 @@ class ManifestJsonFileContext extends JsonFileContext { const filePath = resolve(dirname(path), action.Encoder.layout.value); this.layoutFiles.push({ location: action.Encoder.layout.location, - layout: new JsonFileContext(filePath, compiledLayoutSchema) + layout: new JsonFileContext(filePath, compiledLayoutSchema), }); } }); diff --git a/src/validation/plugin/rules/layout-item-bounds.ts b/src/validation/plugin/rules/layout-item-bounds.ts index a867fae..b6eb12e 100644 --- a/src/validation/plugin/rules/layout-item-bounds.ts +++ b/src/validation/plugin/rules/layout-item-bounds.ts @@ -1,5 +1,6 @@ import { type Layout } from "@elgato/schemas/streamdeck/plugins"; import chalk from "chalk"; + import { JsonElement, JsonObject } from "../../../json"; import { rule } from "../../rule"; import { type PluginContext } from "../plugin"; @@ -12,14 +13,14 @@ export const layoutItemsAreWithinBoundsAndNoOverlap = rule(functi for (let i = items.length - 1; i >= 0; i--) { const { node, - vertices: { x1, x2, y1, y2 } + vertices: { x1, x2, y1, y2 }, } = items[i]; // First check the bounds of the rectangle are within the canvas. if (x1 < 0 || x2 > 200 || y1 < 0 || y2 > 100) { this.addError(layout.path, "must not be outside of the canvas", { ...node, - suggestion: "Width and height, relative to the x and y, must be within the 200x100 px canvas" + suggestion: "Width and height, relative to the x and y, must be within the 200x100 px canvas", }); } @@ -48,8 +49,8 @@ function getItemBounds(layout: JsonObject): Bounds[] { x2: rect[0].value + rect[2].value, y1: rect[1].value, y2: rect[1].value + rect[3].value, - z: zOrder?.value ?? 0 - } + z: zOrder?.value ?? 0, + }, }); } diff --git a/src/validation/plugin/rules/layout-schema.ts b/src/validation/plugin/rules/layout-schema.ts index ef6c193..04d5c28 100644 --- a/src/validation/plugin/rules/layout-schema.ts +++ b/src/validation/plugin/rules/layout-schema.ts @@ -1,9 +1,9 @@ import { DefinedError } from "ajv"; +import { existsSync } from "node:fs"; + import { rule } from "../../rule"; import { type PluginContext } from "../plugin"; -import { existsSync } from "node:fs"; - /** * Validate the layout files referenced in the manifest exist, and log any JSON schema errors that were encountered when validating their content. */ diff --git a/src/validation/plugin/rules/manifest-category.ts b/src/validation/plugin/rules/manifest-category.ts index 0e0df2e..b79df44 100644 --- a/src/validation/plugin/rules/manifest-category.ts +++ b/src/validation/plugin/rules/manifest-category.ts @@ -7,18 +7,18 @@ import { type PluginContext } from "../plugin"; export const categoryMatchesName = rule(function (plugin) { const { manifest: { - value: { Category: category, Name: name } - } + value: { Category: category, Name: name }, + }, } = plugin; if (name?.value !== undefined && category?.value !== name?.value) { const val = category?.value === undefined ? undefined : `'${category}'`; this.addWarning(plugin.manifest.path, `should match plugin name`, { location: { - key: "Category" + key: "Category", }, ...category, - suggestion: `Expected '${name}', but was ${val}` + suggestion: `Expected '${name}', but was ${val}`, }); } }); diff --git a/src/validation/plugin/rules/manifest-files-exist.ts b/src/validation/plugin/rules/manifest-files-exist.ts index d996673..3326da4 100644 --- a/src/validation/plugin/rules/manifest-files-exist.ts +++ b/src/validation/plugin/rules/manifest-files-exist.ts @@ -1,5 +1,6 @@ import { existsSync } from "node:fs"; import { basename, extname, join, resolve } from "node:path"; + import { colorize } from "../../../common/stdout"; import { aggregate } from "../../../common/utils"; import { FilePathOptions } from "../../../json"; @@ -18,7 +19,7 @@ export const manifestFilesExist = rule(async function (plugin: Pl // Validate the manifest is flagged to be ignored. if (ignores(basename(plugin.manifest.path))) { this.addError(plugin.manifest.path, "Manifest file must not be ignored", { - suggestion: `Review ${streamDeckIgnoreFilename} file` + suggestion: `Review ${streamDeckIgnoreFilename} file`, }); } @@ -42,12 +43,18 @@ export const manifestFilesExist = rule(async function (plugin: Pl // When the value type is incorrect, or there is already a schema error, we can rely on schema validation. const { node } = nodeRef; - if (typeof node.value !== "string" || plugin.manifest.errors.find((e) => e.location?.instancePath === instancePath)) { + if ( + typeof node.value !== "string" || + plugin.manifest.errors.find((e) => e.location?.instancePath === instancePath) + ) { return; } // Determine the possible paths from the file path options. - const possiblePaths = typeof opts === "object" && !opts.includeExtension ? opts.extensions.map((ext) => `${node.value}${ext}`) : [node.value]; + const possiblePaths = + typeof opts === "object" && !opts.includeExtension + ? opts.extensions.map((ext) => `${node.value}${ext}`) + : [node.value]; // Attempt to resolve the possible paths the value can represent. let resolvedPath: string | undefined = undefined; @@ -56,7 +63,11 @@ export const manifestFilesExist = rule(async function (plugin: Pl if (existsSync(path)) { // If the path has already been resolved, there are files with duplicate names, e.g. "my-image.png" and "my-image.svg". Warn, and highlighted the resolved path. if (resolvedPath !== undefined) { - this.addWarning(plugin.manifest.path, `multiple files named ${colorize(node.value)} found, using ${colorize(resolvedPath)}`, node); + this.addWarning( + plugin.manifest.path, + `multiple files named ${colorize(node.value)} found, using ${colorize(resolvedPath)}`, + node, + ); break; } @@ -68,7 +79,7 @@ export const manifestFilesExist = rule(async function (plugin: Pl if (resolvedPath === undefined) { this.addError(plugin.manifest.path, `file not found, ${colorize(node.value)}`, { ...node, - suggestion: typeof opts === "object" ? `File must be ${aggregate(opts.extensions, "or")}` : undefined + suggestion: typeof opts === "object" ? `File must be ${aggregate(opts.extensions, "or")}` : undefined, }); return; @@ -78,7 +89,7 @@ export const manifestFilesExist = rule(async function (plugin: Pl if (ignores(resolvedPath)) { this.addError(plugin.manifest.path, `file must not be ignored, ${colorize(resolvedPath)}`, { ...node, - suggestion: `Review ${streamDeckIgnoreFilename} file` + suggestion: `Review ${streamDeckIgnoreFilename} file`, }); return; @@ -94,8 +105,8 @@ export const manifestFilesExist = rule(async function (plugin: Pl if (!existsSync(join(this.path, `${node.value}@2x.png`))) { this.addWarning(fullPath, "should have high-resolution (@2x) variant", { location: { - key: node.location.key - } + key: node.location.key, + }, }); missingHighRes.add(fullPath); } diff --git a/src/validation/plugin/rules/manifest-schema.ts b/src/validation/plugin/rules/manifest-schema.ts index e4c7e7d..91804c8 100644 --- a/src/validation/plugin/rules/manifest-schema.ts +++ b/src/validation/plugin/rules/manifest-schema.ts @@ -1,8 +1,8 @@ +import { existsSync } from "node:fs"; + import { rule } from "../../rule"; import { type PluginContext } from "../plugin"; -import { existsSync } from "node:fs"; - /** * Validates the JSON schema of the manifest; this validation fulfils the same role as if the JSON were validated in an IDE, i.e. custom keyword validation is not applied. */ @@ -22,7 +22,7 @@ export const manifestExistsAndSchemaIsValid = rule(function (plug if (plugin.hasValidId && location?.instancePath === "" && message === "must contain property: UUID") { this.addError(plugin.manifest.path, message, { location, - suggestion: `Expected: ${plugin.id}` + suggestion: `Expected: ${plugin.id}`, }); return; diff --git a/src/validation/plugin/rules/manifest-urls-exist.ts b/src/validation/plugin/rules/manifest-urls-exist.ts index e34877f..ee4f8c5 100644 --- a/src/validation/plugin/rules/manifest-urls-exist.ts +++ b/src/validation/plugin/rules/manifest-urls-exist.ts @@ -1,4 +1,5 @@ import chalk from "chalk"; + import { rule } from "../../rule"; import { type PluginContext } from "../plugin"; @@ -8,8 +9,8 @@ import { type PluginContext } from "../plugin"; export const manifestUrlsExist = rule(async function (plugin: PluginContext) { const { manifest: { - value: { URL: url } - } + value: { URL: url }, + }, } = plugin; if (url?.value == undefined) { @@ -23,7 +24,7 @@ export const manifestUrlsExist = rule(async function (plugin: Plu } catch { this.addError(plugin.manifest.path, "must be valid URL", { ...url, - suggestion: !url.value.toLowerCase().startsWith("http") ? "Protocol must be http or https" : undefined + suggestion: !url.value.toLowerCase().startsWith("http") ? "Protocol must be http or https" : undefined, }); return; @@ -42,12 +43,18 @@ export const manifestUrlsExist = rule(async function (plugin: Plu if (status < 200 || status >= 300) { this.addWarning(plugin.manifest.path, `should return success (received ${chalk.yellow(status)})`, { ...url, - suggestion: "Status code should be 2xx" + suggestion: "Status code should be 2xx", }); } } catch (err) { // Check if resolving the DNS failed. - if (err instanceof Error && typeof err.cause === "object" && err.cause && "code" in err.cause && err.cause.code === "ENOTFOUND") { + if ( + err instanceof Error && + typeof err.cause === "object" && + err.cause && + "code" in err.cause && + err.cause.code === "ENOTFOUND" + ) { this.addError(plugin.manifest.path, "must be resolvable", url); } else { throw err; diff --git a/src/validation/plugin/rules/manifest-uuids.ts b/src/validation/plugin/rules/manifest-uuids.ts index 53ba940..4d234f0 100644 --- a/src/validation/plugin/rules/manifest-uuids.ts +++ b/src/validation/plugin/rules/manifest-uuids.ts @@ -12,7 +12,7 @@ export const manifestUuids = rule(async function (plugin: PluginC if (plugin.hasValidId && manifest.UUID?.value !== undefined && plugin.id !== manifest.UUID.value) { this.addError(plugin.manifest.path, "must match parent directory name", { location: manifest.UUID.location, - suggestion: `Expected: ${plugin.id}` + suggestion: `Expected: ${plugin.id}`, }); } diff --git a/src/validation/plugin/rules/path-input.ts b/src/validation/plugin/rules/path-input.ts index 27c280c..c072d1e 100644 --- a/src/validation/plugin/rules/path-input.ts +++ b/src/validation/plugin/rules/path-input.ts @@ -1,5 +1,6 @@ import { existsSync, lstatSync } from "node:fs"; import { basename } from "node:path"; + import { colorize } from "../../../common/stdout"; import { isValidPluginId } from "../../../stream-deck"; import { rule } from "../../rule"; @@ -27,8 +28,12 @@ export const pathIsDirectoryAndUuid = rule(function (plugin: Plug // Directory name is a valid identifier. if (!isValidPluginId(plugin.id)) { - this.addError(this.path, "Name must be in reverse DNS format, and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), and periods (.)", { - suggestion: "Example: com.elgato.wave-link" - }); + this.addError( + this.path, + "Name must be in reverse DNS format, and must only contain lowercase alphanumeric characters (a-z, 0-9), hyphens (-), and periods (.)", + { + suggestion: "Example: com.elgato.wave-link", + }, + ); } }); diff --git a/src/validation/result.ts b/src/validation/result.ts index 72fcb23..094a790 100644 --- a/src/validation/result.ts +++ b/src/validation/result.ts @@ -1,11 +1,14 @@ import { StdOut } from "../common/stdout"; -import { ValidationLevel, type ValidationEntry } from "./entry"; +import { type ValidationEntry, ValidationLevel } from "./entry"; import { ValidationEntryCollection } from "./entry-collection"; /** * Validation result containing a collection of {@link ValidationEntryCollection} grouped by the directory or file path they're associated with. */ -export class ValidationResult extends Array implements ReadonlyArray { +export class ValidationResult + extends Array + implements ReadonlyArray +{ /** * Private backing field for {@link Result.errorCount}. */ @@ -66,7 +69,9 @@ export class ValidationResult extends Array implement // Both errors and warnings. if (this.hasErrors() && this.hasWarnings()) { - output.error(`${pluralize("problem", this.errorCount + this.warningCount)} (${pluralize("error", this.errorCount)}, ${pluralize("warning", this.warningCount)})`); + output.error( + `${pluralize("problem", this.errorCount + this.warningCount)} (${pluralize("error", this.errorCount)}, ${pluralize("warning", this.warningCount)})`, + ); return; } diff --git a/src/validation/validator.ts b/src/validation/validator.ts index 4dc30e8..e8fdef7 100644 --- a/src/validation/validator.ts +++ b/src/validation/validator.ts @@ -1,4 +1,4 @@ -import { ValidationEntry, ValidationLevel, type ValidationEntryDetails } from "./entry"; +import { ValidationEntry, type ValidationEntryDetails, ValidationLevel } from "./entry"; import { ValidationResult } from "./result"; import { type ValidationRule } from "./rule"; @@ -31,7 +31,7 @@ export class ValidationContext { */ constructor( public readonly path: string, - private readonly result: ValidationResult + private readonly result: ValidationResult, ) {} /**