From 69d77d699ec53c5ced609a47cf98031d5dbe84da Mon Sep 17 00:00:00 2001 From: Sergey Kupletsky Date: Sun, 10 Nov 2024 15:42:45 +0100 Subject: [PATCH] chore: add new linters and fix eslint warnings --- .eslintrc | 86 ----------- .eslintrc.cjs | 96 ++++++++++++ ava.config.js | 3 + package-lock.json | 142 +++++++++++++++++- package.json | 5 +- src/cli/cli.ts | 44 +++--- src/config/config.ts | 3 +- src/errors/compose-validation-error.ts | 12 +- src/errors/config-validation-error.ts | 13 ++ src/formatters/codeclimate.ts | 9 +- src/formatters/json.ts | 1 + src/formatters/junit.ts | 2 +- src/formatters/stylish.ts | 20 +-- src/index.ts | 4 +- src/linter/linter.ts | 30 ++-- src/rules/no-build-and-image-rule.ts | 6 +- .../no-duplicate-container-names-rule.ts | 6 +- src/rules/no-duplicate-exported-ports-rule.ts | 6 +- src/rules/no-quotes-in-volumes-rule.ts | 18 +-- src/rules/require-quotes-in-ports-rule.ts | 18 +-- .../service-container-name-regex-rule.ts | 6 +- ...ce-dependencies-alphabetical-order-rule.ts | 12 +- ...service-image-require-explicit-tag-rule.ts | 6 +- src/rules/service-keys-order-rule.ts | 18 +-- .../service-ports-alphabetical-order-rule.ts | 12 +- src/rules/services-alphabetical-order-rule.ts | 14 +- src/rules/top-level-properties-order-rule.ts | 10 +- src/util/check-for-updates.ts | 25 --- src/util/files-finder.ts | 24 +-- src/util/formatter-loader.ts | 6 +- src/util/line-finder.ts | 20 +-- src/util/load-schema.ts | 2 +- src/util/logger.ts | 16 +- src/util/rules-loader.ts | 12 +- src/util/service-ports-parser.ts | 4 +- tests/linter.spec.ts | 11 +- tests/rules/no-build-and-image-rule.spec.ts | 16 +- .../no-duplicate-container-names-rule.spec.ts | 7 +- .../no-duplicate-exported-ports-rule.spec.ts | 10 +- tests/rules/no-quotes-in-volumes-rule.spec.ts | 15 +- tests/rules/no-version-field-rule.spec.ts | 13 +- .../require-project-name-field-rule.spec.ts | 7 +- .../require-quotes-in-ports-rule.spec.ts | 22 ++- .../service-container-name-regex-rule.spec.ts | 7 +- ...pendencies-alphabetical-order-rule.spec.ts | 21 ++- ...ce-image-require-explicit-tag-rule.spec.ts | 19 ++- tests/rules/service-keys-order-rule.spec.ts | 12 +- ...vice-ports-alphabetical-order-rule.spec.ts | 12 +- .../services-alphabetical-order-rule.spec.ts | 12 +- .../top-level-properties-order-rule.spec.ts | 18 ++- tests/util/files-finder.spec.ts | 17 ++- tests/util/line-finder.spec.ts | 19 ++- tests/util/service-ports-parser.spec.ts | 25 ++- 53 files changed, 610 insertions(+), 364 deletions(-) delete mode 100644 .eslintrc create mode 100644 .eslintrc.cjs create mode 100644 src/errors/config-validation-error.ts delete mode 100644 src/util/check-for-updates.ts diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 4d11206..0000000 --- a/.eslintrc +++ /dev/null @@ -1,86 +0,0 @@ -{ - "root": true, - "env": { - "node": true, - "es2024": true - }, - "globals": { - "process": true, - "import": true - }, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "./tsconfig.eslint.json", - "sourceType": "module", - "ecmaVersion": "latest" - }, - "extends": [ - "eslint:recommended", - "plugin:import/recommended", - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:sonarjs/recommended-legacy", - "airbnb-base", - "airbnb-typescript/base", - "plugin:import/typescript", - "plugin:prettier/recommended" - ], - "plugins": [ - "@typescript-eslint", - "sonarjs", - "import", - "@stylistic", - "prettier" - ], - "settings": { - "import/resolver": { - "typescript": { - "alwaysTryTypes": true, - "project": "./tsconfig.eslint.json", - "extensions": [".ts", ".tsx", ".d.ts"] - }, - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx", ".d.ts"] - } - } - }, - "rules": { - "no-unused-vars": 0, - "no-console": 0, - "@typescript-eslint/no-unused-vars": [ - 2, - { - "args": "none" - } - ], - "prettier/prettier": 2, - "@stylistic/indent": ["error", 2], - "@stylistic/indent-binary-ops": ["error", 2], - "arrow-body-style": 0, - "prefer-arrow-callback": 0, - "prefer-rest-params": 0, - "sonarjs/cognitive-complexity": 1, - "@typescript-eslint/triple-slash-reference": [ - 2, - { - "path": "never", - "types": "always", - "lib": "always" - } - ], - "import/prefer-default-export": 0, - "import/no-default-export": 0, - "import/no-unresolved": [2, { "commonjs": true }], - "import/extensions": 0, - "import/order": [ - 2, - { - "groups": [ - "builtin", - "external", - "internal" - ] - } - ] - }, - "ignorePatterns": ["node_modules", "dist", ".tsimp", "coverage", "bin"] -} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..802886b --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,96 @@ +module.exports = { + 'root': true, + 'env': { + 'node': true, + 'es2024': true, + }, + 'globals': { + 'process': true, + 'import': true, + }, + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'project': './tsconfig.eslint.json', + 'sourceType': 'module', + 'ecmaVersion': 'latest', + }, + 'extends': [ + 'eslint:recommended', + 'plugin:import/recommended', + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:sonarjs/recommended-legacy', + 'airbnb-base', + 'airbnb-typescript/base', + 'plugin:import/typescript', + 'plugin:ava/recommended', + 'plugin:unicorn/recommended', + 'plugin:prettier/recommended', + ], + 'plugins': [ + '@typescript-eslint', + 'sonarjs', + 'import', + '@stylistic', + 'prettier', + ], + 'settings': { + 'import/resolver': { + 'typescript': { + 'alwaysTryTypes': true, + 'project': './tsconfig.eslint.json', + 'extensions': ['.ts', '.tsx', '.d.ts'], + }, + 'node': { + 'extensions': ['.js', '.jsx', '.ts', '.tsx', '.d.ts'], + }, + }, + }, + 'rules': { + 'no-unused-vars': 0, + 'no-console': 0, + '@typescript-eslint/no-unused-vars': [ + 2, + { + 'args': 'none', + }, + ], + 'prettier/prettier': 2, + '@stylistic/indent': ['error', 2], + '@stylistic/indent-binary-ops': ['error', 2], + 'arrow-body-style': 0, + 'prefer-arrow-callback': 0, + 'prefer-rest-params': 0, + 'sonarjs/cognitive-complexity': 1, + '@typescript-eslint/triple-slash-reference': [ + 2, + { + 'path': 'never', + 'types': 'always', + 'lib': 'always', + }, + ], + '@typescript-eslint/ban-ts-comment': 0, + 'import/prefer-default-export': 0, + 'import/no-default-export': 0, + 'import/no-unresolved': [2, { 'commonjs': true }], + 'import/extensions': 0, + 'import/order': [ + 2, + { + 'groups': [ + 'builtin', + 'external', + 'internal', + ], + }, + ], + 'no-restricted-syntax': 0, + 'unicorn/no-array-for-each': 0, + 'unicorn/consistent-function-scoping': 0, + 'unicorn/no-null': 0, + 'unicorn/no-await-expression-member': 0, + 'unicorn/switch-case-braces': [2, 'avoid'], + 'unicorn/import-style': [2, {"styles": {"node:path": {"named": true, "default": false}}}] + }, + 'ignorePatterns': ['node_modules', 'dist', '.tsimp', 'coverage', 'bin'], +}; diff --git a/ava.config.js b/ava.config.js index 1acc21f..cf20b29 100644 --- a/ava.config.js +++ b/ava.config.js @@ -2,6 +2,9 @@ export default { files: ['tests/**/*.spec.ts'], extensions: { ts: 'module', + mjs: true, + cjs: true, + js: true, }, nodeArguments: ['--import=tsimp/import', '--no-warnings'], timeout: '2m', diff --git a/package-lock.json b/package-lock.json index 9eafaf8..36d52a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "ajv": "^8.17.1", "chalk": "^5.3.0", "cosmiconfig": "^9.0.0", - "yaml": "^2.5.1", + "yaml": "^2.6.0", "yargs": "^17.7.2" }, "bin": { @@ -40,12 +40,13 @@ "eslint-config-airbnb-typescript": "18.0.0", "eslint-config-prettier": "9.1.0", "eslint-import-resolver-typescript": "3.6.3", + "eslint-plugin-ava": "14.0.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-prettier": "5.2.1", "eslint-plugin-sonarjs": "1.0.3", "eslint-plugin-unicorn": "56.0.0", "esmock": "2.6.9", - "markdownlint-cli2": "^0.15.0", + "markdownlint-cli2": "0.15.0", "semantic-release": "24.2.0", "tap-xunit": "2.4.1", "tsimp": "2.0.12", @@ -3232,6 +3233,19 @@ "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", "dev": true }, + "node_modules/enhance-visitors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/enhance-visitors/-/enhance-visitors-1.0.0.tgz", + "integrity": "sha512-+29eJLiUixTEDRaZ35Vu8jP3gPLNcQQkQkOQjLp2X+6cZGGPDD/uasbFzvLsJKnGZnvmyZ0srxudwOtskHeIDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.13.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", @@ -3775,6 +3789,60 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-ava": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-ava/-/eslint-plugin-ava-14.0.0.tgz", + "integrity": "sha512-XmKT6hppaipwwnLVwwvQliSU6AF1QMHiNoLD5JQfzhUhf0jY7CO0O624fQrE+Y/fTb9vbW8r77nKf7M/oHulxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhance-visitors": "^1.0.0", + "eslint-utils": "^3.0.0", + "espree": "^9.0.0", + "espurify": "^2.1.1", + "import-modules": "^2.1.0", + "micro-spelling-correcter": "^1.1.1", + "pkg-dir": "^5.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=14.17 <15 || >=16.4" + }, + "peerDependencies": { + "eslint": ">=8.26.0" + } + }, + "node_modules/eslint-plugin-ava/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-ava/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint-plugin-import": { "version": "2.31.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", @@ -3907,6 +3975,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.0.tgz", "integrity": "sha512-aXpddVz/PQMmd69uxO98PA4iidiVNvA0xOtbpUoz1WhBd4RxOQQYqN618v68drY0hmy5uU2jy1bheKEVWBjlPw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "@eslint-community/eslint-utils": "^4.4.0", @@ -3963,6 +4032,35 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-visitor-keys": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", @@ -4157,6 +4255,13 @@ "node": ">=4" } }, + "node_modules/espurify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-2.1.1.tgz", + "integrity": "sha512-zttWvnkhcDyGOhSH4vO2qCBILpdCMv/MX8lp4cqgRkQoDRGK2oZxi2GfWhlP2dIXmk7BaKeOTuzbHhyC68o8XQ==", + "dev": true, + "license": "MIT" + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -5112,6 +5217,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/import-modules": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-modules/-/import-modules-2.1.0.tgz", + "integrity": "sha512-8HEWcnkbGpovH9yInoisxaSoIg9Brbul+Ju3Kqe2UsYDUBJD/iQjSgEj0zPcTDPKfPp2fs5xlv1i+JSye/m1/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6098,6 +6216,13 @@ "node": ">= 8" } }, + "node_modules/micro-spelling-correcter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/micro-spelling-correcter/-/micro-spelling-correcter-1.1.1.tgz", + "integrity": "sha512-lkJ3Rj/mtjlRcHk6YyCbvZhyWTOzdBvTHsxMmZSk5jxN1YyVSQ+JETAom55mdzfcyDrY/49Z7UCW760BK30crg==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -9472,6 +9597,19 @@ "node": ">=4" } }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/plur": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", diff --git a/package.json b/package.json index e79868e..265be0d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "eslint": "eslint .", "eslint:fix": "eslint . --fix", "lint": "npm run eslint && npm run markdownlint", - "markdownlint": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#**/node_modules\" \"#CHANGELOG.md\"", + "markdownlint": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#**/node_modules\"", "markdownlint:fix": "markdownlint-cli2 --fix \"**/*.md\" \"#node_modules\" \"#**/node_modules\"", "markdownlint:fix-changelog": "markdownlint-cli2 --fix \"CHANGELOG.md\" && prettier --write \"CHANGELOG.md\"", "prettier": "prettier --write \"**/*.md\"", @@ -77,12 +77,13 @@ "eslint-config-airbnb-typescript": "18.0.0", "eslint-config-prettier": "9.1.0", "eslint-import-resolver-typescript": "3.6.3", + "eslint-plugin-ava": "14.0.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-prettier": "5.2.1", "eslint-plugin-sonarjs": "1.0.3", "eslint-plugin-unicorn": "56.0.0", "esmock": "2.6.9", - "markdownlint-cli2": "^0.15.0", + "markdownlint-cli2": "0.15.0", "semantic-release": "24.2.0", "tap-xunit": "2.4.1", "tsimp": "2.0.12", diff --git a/src/cli/cli.ts b/src/cli/cli.ts index 879a8c6..99925e2 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env node + import { readFileSync, writeFileSync } from 'node:fs'; import { resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -10,7 +12,7 @@ import { Logger, LOG_SOURCE } from '../util/logger.js'; import { loadFormatter } from '../util/formatter-loader.js'; const packageJson = JSON.parse( - readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'), 'utf-8'), + readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'), 'utf8'), ) as Record; const { argv } = yargsLib(hideBin(process.argv)) @@ -87,41 +89,43 @@ const { argv } = yargsLib(hideBin(process.argv)) .alias('version', 'v'); export default async function cli() { - const args = (await argv) as unknown as CLIConfig; + const cliArguments = (await argv) as unknown as CLIConfig; // Initialize the logger with the final debug and color options - Logger.init(args.debug); + Logger.init(cliArguments.debug); const logger = Logger.getInstance(); logger.debug(LOG_SOURCE.CLI, 'Debug mode is ON'); - logger.debug(LOG_SOURCE.CLI, 'Arguments:', args); + logger.debug(LOG_SOURCE.CLI, 'Arguments:', cliArguments); - const config = await loadConfig(args.config); + const config = await loadConfig(cliArguments.config).catch((error) => { + process.exit(1); + }); // Override config values with CLI arguments if they are provided - if (args.quiet) { - config.quiet = args.quiet; + if (cliArguments.quiet) { + config.quiet = cliArguments.quiet; } - if (args.debug) { - config.debug = args.debug; + if (cliArguments.debug) { + config.debug = cliArguments.debug; } - if (args.exclude.length > 0) { - config.exclude = args.exclude; + if (cliArguments.exclude.length > 0) { + config.exclude = cliArguments.exclude; } logger.debug(LOG_SOURCE.CLI, 'Final config:', config); const linter = new DCLinter(config); // Handle the `fix` and `fix-dry-run` flags - if (args.fix || args.fixDryRun) { - await linter.fixFiles(args.files, args.recursive, args.fixDryRun); + if (cliArguments.fix || cliArguments.fixDryRun) { + await linter.fixFiles(cliArguments.files, cliArguments.recursive, cliArguments.fixDryRun); } // Always run the linter after attempting to fix issues - let lintResults = await linter.lintFiles(args.files, args.recursive); + let lintResults = await linter.lintFiles(cliArguments.files, cliArguments.recursive); // Filter out warnings if `--quiet` is enabled - if (args.quiet) { + if (cliArguments.quiet) { // Keep only files with errors lintResults = lintResults .map((result) => ({ @@ -138,12 +142,12 @@ export default async function cli() { const totalWarnings = lintResults.reduce((count, result) => count + result.warningCount, 0); // Choose and apply the formatter - const formatter = await loadFormatter(args.formatter); + const formatter = await loadFormatter(cliArguments.formatter); const formattedResults = formatter(lintResults); // Output results - if (args.outputFile) { - writeFileSync(args.outputFile, formattedResults); + if (cliArguments.outputFile) { + writeFileSync(cliArguments.outputFile, formattedResults); } else { console.log(formattedResults); } @@ -152,10 +156,10 @@ export default async function cli() { if (totalErrors > 0) { logger.debug(LOG_SOURCE.CLI, `${totalErrors} errors found`); process.exit(1); - } else if (args.maxWarnings && args.maxWarnings >= 0 && totalWarnings > args.maxWarnings) { + } else if (cliArguments.maxWarnings && cliArguments.maxWarnings >= 0 && totalWarnings > cliArguments.maxWarnings) { logger.debug( LOG_SOURCE.CLI, - `Warning threshold exceeded: ${totalWarnings} warnings (max allowed: ${args.maxWarnings})`, + `Warning threshold exceeded: ${totalWarnings} warnings (max allowed: ${cliArguments.maxWarnings})`, ); process.exit(1); } diff --git a/src/config/config.ts b/src/config/config.ts index 7e0ab9d..ec3d241 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -3,6 +3,7 @@ import { Ajv } from 'ajv'; import type { Config } from './config.types.js'; import { Logger, LOG_SOURCE } from '../util/logger.js'; import { loadSchema } from '../util/load-schema.js'; +import { ConfigValidationError } from '../errors/config-validation-error.js'; function getDefaultConfig(): Config { return { @@ -23,7 +24,7 @@ async function validateConfig(config: Config): Promise { if (!validate(config)) { logger.error('Invalid configuration:', validate.errors); - process.exit(1); + throw new ConfigValidationError(validate.errors); } logger.debug(LOG_SOURCE.CONFIG, 'Validation complete'); return config; diff --git a/src/errors/compose-validation-error.ts b/src/errors/compose-validation-error.ts index e0e6cd2..8e6adc3 100644 --- a/src/errors/compose-validation-error.ts +++ b/src/errors/compose-validation-error.ts @@ -9,13 +9,13 @@ class ComposeValidationError extends Error { details: ErrorObject; - constructor(e: ErrorObject) { - super(`Validation error: ${e?.message}`); + constructor(error: ErrorObject) { + super(`Validation error: ${error?.message}`); this.name = 'ComposeValidationError'; - this.keyword = e.keyword; - this.instancePath = e.instancePath; - this.schemaPath = e.schemaPath; - this.details = e; + this.keyword = error.keyword; + this.instancePath = error.instancePath; + this.schemaPath = error.schemaPath; + this.details = error; } toString(): string { diff --git a/src/errors/config-validation-error.ts b/src/errors/config-validation-error.ts new file mode 100644 index 0000000..ff87111 --- /dev/null +++ b/src/errors/config-validation-error.ts @@ -0,0 +1,13 @@ +import { ErrorObject } from 'ajv'; + +class ConfigValidationError extends Error { + constructor(validationErrors?: ErrorObject[] | null | undefined) { + super(); + this.message = `Invalid configuration: ${ + validationErrors?.map((error) => error.message).join(', ') || 'No details' + }`; + this.name = 'ConfigValidationError'; + } +} + +export { ConfigValidationError }; diff --git a/src/formatters/codeclimate.ts b/src/formatters/codeclimate.ts index 840fb0e..8ae67ce 100644 --- a/src/formatters/codeclimate.ts +++ b/src/formatters/codeclimate.ts @@ -5,9 +5,12 @@ const generateFingerprint = (data: (string | null)[], hashes: Set): stri const hash = createHash('md5'); // Filter out null values and update the hash - data.filter(Boolean).forEach((part) => { - hash.update(part!.toString()); // Using non-null assertion since filter removed null values - }); + for (const part of data.filter(Boolean)) { + hash.update(part!.toString()); + } + // data.filter(Boolean).forEach((part) => { + // hash.update(part!.toString()); // Using non-null assertion since filter removed null values + // }); // Hash collisions should not happen, but if they do, a random hash will be generated. const hashCopy = hash.copy(); diff --git a/src/formatters/json.ts b/src/formatters/json.ts index 9e492e4..d24f319 100644 --- a/src/formatters/json.ts +++ b/src/formatters/json.ts @@ -1,5 +1,6 @@ import type { LintResult } from '../linter/linter.types.js'; export default function jsonFormatter(results: LintResult[]): string { + // eslint-disable-next-line unicorn/no-null return JSON.stringify(results, null, 2); } diff --git a/src/formatters/junit.ts b/src/formatters/junit.ts index eed9259..c1d1593 100644 --- a/src/formatters/junit.ts +++ b/src/formatters/junit.ts @@ -1,7 +1,7 @@ import type { LintResult } from '../linter/linter.types.js'; function escapeXml(unsafe: string): string { - return unsafe.replace(/[<>&'"]/g, (c) => { + return unsafe.replaceAll(/[<>&'"]/g, (c) => { switch (c) { case '<': return '<'; diff --git a/src/formatters/stylish.ts b/src/formatters/stylish.ts index 117bfbd..0723313 100644 --- a/src/formatters/stylish.ts +++ b/src/formatters/stylish.ts @@ -1,4 +1,4 @@ -import path from 'node:path'; +import { resolve } from 'node:path'; import chalk from 'chalk'; import type { LintResult } from '../linter/linter.types.js'; @@ -15,31 +15,31 @@ export default function stylishFormatter(results: LintResult[]): string { } // Format the file path header without nested template literals - const filePath = chalk.underline(path.resolve(result.filePath)); + const filePath = chalk.underline(resolve(result.filePath)); output += `\n${filePath}\n`; - result.messages.forEach((msg) => { - const { type } = msg; + result.messages.forEach((message) => { + const { type } = message; const color = type === 'error' ? chalk.red : chalk.yellow; - const line = msg.line.toString().padStart(4, ' '); - const column = msg.column.toString().padEnd(4, ' '); + const line = message.line.toString().padStart(4, ' '); + const column = message.column.toString().padEnd(4, ' '); // Break down message formatting into separate parts const position = chalk.dim(`${line}:${column}`); const formattedType = color(type); - const ruleInfo = chalk.dim(msg.rule); + const ruleInfo = chalk.dim(message.rule); - output += `${position} ${formattedType} ${msg.message} ${ruleInfo}\n`; + output += `${position} ${formattedType} ${message.message} ${ruleInfo}\n`; // Increment counts without using the ++ operator if (type === 'error') { errorCount += 1; - if (msg.fixable) { + if (message.fixable) { fixableErrorCount += 1; } } else { warningCount += 1; - if (msg.fixable) { + if (message.fixable) { fixableWarningCount += 1; } } diff --git a/src/index.ts b/src/index.ts index 39c8f5b..5062148 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1 @@ -import { DCLinter } from './linter/linter.js'; - -export { DCLinter }; +export { DCLinter } from './linter/linter.js'; diff --git a/src/linter/linter.ts b/src/linter/linter.ts index ed18b55..100e4a2 100644 --- a/src/linter/linter.ts +++ b/src/linter/linter.ts @@ -43,20 +43,22 @@ class DCLinter { try { context.sourceCode = fs.readFileSync(file, 'utf8'); - const doc = parseDocument(context.sourceCode, { merge: true }); + const parsedDocument = parseDocument(context.sourceCode, { merge: true }); - if (doc.errors && doc.errors.length > 0) { - doc.errors.forEach((error) => { + if (parsedDocument.errors && parsedDocument.errors.length > 0) { + parsedDocument.errors.forEach((error) => { throw error; }); } // Use Record to type the parsed content safely - context.content = doc.toJS() as Record; + context.content = parsedDocument.toJS() as Record; validationComposeSchema(context.content); - } catch (e: unknown) { - if (e instanceof YAMLError) { - const startPos: { line: number; col: number } | undefined = Array.isArray(e.linePos) ? e.linePos[0] : e.linePos; + } catch (error: unknown) { + if (error instanceof YAMLError) { + const startPos: { line: number; col: number } | undefined = Array.isArray(error.linePos) + ? error.linePos[0] + : error.linePos; messages.push({ rule: 'invalid-yaml', category: 'style', @@ -67,13 +69,13 @@ class DCLinter { type: 'error', fixable: false, }); - } else if (e instanceof ComposeValidationError) { + } else if (error instanceof ComposeValidationError) { messages.push({ rule: 'invalid-schema', type: 'error', category: 'style', severity: 'critical', - message: e.toString(), + message: error.toString(), line: 1, column: 1, fixable: false, @@ -89,7 +91,7 @@ class DCLinter { type: 'error', fixable: false, }); - logger.debug(LOG_SOURCE.LINTER, `Error while processing file ${file}`, e); + logger.debug(LOG_SOURCE.LINTER, `Error while processing file ${file}`, error); } return { context: null, messages }; @@ -114,10 +116,10 @@ class DCLinter { messages.push(...fileMessages); } - const errorCount = messages.filter((msg) => msg.type === 'error').length; - const warningCount = messages.filter((msg) => msg.type === 'warning').length; - const fixableErrorCount = messages.filter((msg) => msg.fixable && msg.type === 'error').length; - const fixableWarningCount = messages.filter((msg) => msg.fixable && msg.type === 'warning').length; + const errorCount = messages.filter((message) => message.type === 'error').length; + const warningCount = messages.filter((message) => message.type === 'warning').length; + const fixableErrorCount = messages.filter((message) => message.fixable && message.type === 'error').length; + const fixableWarningCount = messages.filter((message) => message.fixable && message.type === 'warning').length; lintResults.push({ filePath: file, diff --git a/src/rules/no-build-and-image-rule.ts b/src/rules/no-build-and-image-rule.ts index 559abed..07cad8a 100644 --- a/src/rules/no-build-and-image-rule.ts +++ b/src/rules/no-build-and-image-rule.ts @@ -44,8 +44,8 @@ export default class NoBuildAndImageRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -62,7 +62,7 @@ export default class NoBuildAndImageRule implements LintRule { const hasPullPolicy = service.has('pull_policy'); if (hasBuild && hasImage && (!this.checkPullPolicy || !hasPullPolicy)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'build'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'build'); errors.push({ rule: this.name, type: this.type, diff --git a/src/rules/no-duplicate-container-names-rule.ts b/src/rules/no-duplicate-container-names-rule.ts index eb17309..d589f6b 100644 --- a/src/rules/no-duplicate-container-names-rule.ts +++ b/src/rules/no-duplicate-container-names-rule.ts @@ -42,8 +42,8 @@ export default class NoDuplicateContainerNamesRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -59,7 +59,7 @@ export default class NoDuplicateContainerNamesRule implements LintRule { const containerName = String(service.get('container_name')); if (containerNames.has(containerName)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'container_name'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'container_name'); errors.push({ rule: this.name, type: this.type, diff --git a/src/rules/no-duplicate-exported-ports-rule.ts b/src/rules/no-duplicate-exported-ports-rule.ts index e1f1c5f..447cfa0 100644 --- a/src/rules/no-duplicate-exported-ports-rule.ts +++ b/src/rules/no-duplicate-exported-ports-rule.ts @@ -43,8 +43,8 @@ export default class NoDuplicateExportedPortsRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -67,7 +67,7 @@ export default class NoDuplicateExportedPortsRule implements LintRule { currentPortRange.some((port) => { if (exportedPortsMap.has(port)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'ports'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'ports'); errors.push({ rule: this.name, type: this.type, diff --git a/src/rules/no-quotes-in-volumes-rule.ts b/src/rules/no-quotes-in-volumes-rule.ts index 98f232e..dcd614b 100644 --- a/src/rules/no-quotes-in-volumes-rule.ts +++ b/src/rules/no-quotes-in-volumes-rule.ts @@ -31,17 +31,17 @@ export default class NoQuotesInVolumesRule implements LintRule { return 'Quotes should not be used in volume names.'; } - private static extractVolumes(doc: ParsedNode | null, callback: (volume: Scalar) => void) { - if (!doc || !isMap(doc)) return; + private static extractVolumes(document: ParsedNode | null, callback: (volume: Scalar) => void) { + if (!document || !isMap(document)) return; - doc.items.forEach((item) => { + document.items.forEach((item) => { if (!isMap(item.value)) return; const serviceMap = item.value; serviceMap.items.forEach((service) => { if (!isMap(service.value)) return; - const volumes = service.value.items.find((i) => isScalar(i.key) && i.key.value === 'volumes'); + const volumes = service.value.items.find((node) => isScalar(node.key) && node.key.value === 'volumes'); if (!volumes || !isSeq(volumes.value)) return; volumes.value.items.forEach((volume) => { @@ -55,9 +55,9 @@ export default class NoQuotesInVolumesRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); + const parsedDocument = parseDocument(context.sourceCode); - NoQuotesInVolumesRule.extractVolumes(doc.contents, (volume) => { + NoQuotesInVolumesRule.extractVolumes(parsedDocument.contents, (volume) => { if (volume.type !== 'PLAIN') { errors.push({ rule: this.name, @@ -78,15 +78,15 @@ export default class NoQuotesInVolumesRule implements LintRule { // eslint-disable-next-line class-methods-use-this public fix(content: string): string { - const doc = parseDocument(content); + const parsedDocument = parseDocument(content); - NoQuotesInVolumesRule.extractVolumes(doc.contents, (volume) => { + NoQuotesInVolumesRule.extractVolumes(parsedDocument.contents, (volume) => { if (volume.type !== 'PLAIN') { // eslint-disable-next-line no-param-reassign volume.type = 'PLAIN'; } }); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/require-quotes-in-ports-rule.ts b/src/rules/require-quotes-in-ports-rule.ts index 646584f..f31deeb 100644 --- a/src/rules/require-quotes-in-ports-rule.ts +++ b/src/rules/require-quotes-in-ports-rule.ts @@ -46,17 +46,17 @@ export default class RequireQuotesInPortsRule implements LintRule { } // Static method to extract and process ports - private static extractPorts(doc: ParsedNode | null, callback: (port: Scalar) => void) { - if (!doc || !isMap(doc)) return; + private static extractPorts(document: ParsedNode | null, callback: (port: Scalar) => void) { + if (!document || !isMap(document)) return; - doc.items.forEach((item) => { + document.items.forEach((item) => { if (!isMap(item.value)) return; const serviceMap = item.value; serviceMap.items.forEach((service) => { if (!isMap(service.value)) return; - const ports = service.value.items.find((i) => isScalar(i.key) && i.key.value === 'ports'); + const ports = service.value.items.find((node) => isScalar(node.key) && node.key.value === 'ports'); if (!ports || !isSeq(ports.value)) return; ports.value.items.forEach((port) => { @@ -70,9 +70,9 @@ export default class RequireQuotesInPortsRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); + const parsedDocument = parseDocument(context.sourceCode); - RequireQuotesInPortsRule.extractPorts(doc.contents, (port) => { + RequireQuotesInPortsRule.extractPorts(parsedDocument.contents, (port) => { if (port.type !== this.getQuoteType()) { errors.push({ rule: this.name, @@ -92,15 +92,15 @@ export default class RequireQuotesInPortsRule implements LintRule { } public fix(content: string): string { - const doc = parseDocument(content); + const parsedDocument = parseDocument(content); - RequireQuotesInPortsRule.extractPorts(doc.contents, (port) => { + RequireQuotesInPortsRule.extractPorts(parsedDocument.contents, (port) => { if (port.type !== this.getQuoteType()) { // eslint-disable-next-line no-param-reassign port.type = this.getQuoteType(); } }); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/service-container-name-regex-rule.ts b/src/rules/service-container-name-regex-rule.ts index f2a1a44..ac2c273 100644 --- a/src/rules/service-container-name-regex-rule.ts +++ b/src/rules/service-container-name-regex-rule.ts @@ -39,8 +39,8 @@ export default class ServiceContainerNameRegexRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -55,7 +55,7 @@ export default class ServiceContainerNameRegexRule implements LintRule { const containerName = String(service.get('container_name')); if (!ServiceContainerNameRegexRule.containerNameRegex.test(containerName)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'container_name'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'container_name'); errors.push({ rule: this.name, type: this.type, diff --git a/src/rules/service-dependencies-alphabetical-order-rule.ts b/src/rules/service-dependencies-alphabetical-order-rule.ts index af6cd2b..1cfe09b 100644 --- a/src/rules/service-dependencies-alphabetical-order-rule.ts +++ b/src/rules/service-dependencies-alphabetical-order-rule.ts @@ -45,8 +45,8 @@ export default class ServiceDependenciesAlphabeticalOrderRule implements LintRul public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -67,7 +67,7 @@ export default class ServiceDependenciesAlphabeticalOrderRule implements LintRul const sortedDependencies = [...extractedDependencies].sort((a, b) => a.localeCompare(b)); if (JSON.stringify(extractedDependencies) !== JSON.stringify(sortedDependencies)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'depends_on'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'depends_on'); errors.push({ rule: this.name, type: this.type, @@ -87,8 +87,8 @@ export default class ServiceDependenciesAlphabeticalOrderRule implements LintRul // eslint-disable-next-line class-methods-use-this public fix(content: string): string { - const doc = parseDocument(content); - const services = doc.get('services'); + const parsedDocument = parseDocument(content); + const services = parsedDocument.get('services'); if (!isMap(services)) return content; @@ -106,6 +106,6 @@ export default class ServiceDependenciesAlphabeticalOrderRule implements LintRul }); }); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/service-image-require-explicit-tag-rule.ts b/src/rules/service-image-require-explicit-tag-rule.ts index f526237..7d7ff9a 100644 --- a/src/rules/service-image-require-explicit-tag-rule.ts +++ b/src/rules/service-image-require-explicit-tag-rule.ts @@ -62,8 +62,8 @@ export default class ServiceImageRequireExplicitTagRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -78,7 +78,7 @@ export default class ServiceImageRequireExplicitTagRule implements LintRule { const image = String(service.get('image')); if (!this.isImageTagExplicit(image)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'image'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'image'); errors.push({ rule: this.name, type: this.type, diff --git a/src/rules/service-keys-order-rule.ts b/src/rules/service-keys-order-rule.ts index c33575c..d614736 100644 --- a/src/rules/service-keys-order-rule.ts +++ b/src/rules/service-keys-order-rule.ts @@ -104,13 +104,13 @@ export default class ServiceKeysOrderRule implements LintRule { private getCorrectOrder(keys: string[]): string[] { const otherKeys = keys.filter((key) => !Object.values(this.groups).flat().includes(key)).sort(); - return this.groupOrder.flatMap((group) => this.groups[group]).concat(otherKeys); + return [...this.groupOrder.flatMap((group) => this.groups[group]), ...otherKeys]; } public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -133,7 +133,7 @@ export default class ServiceKeysOrderRule implements LintRule { if (expectedIndex === -1) return; if (expectedIndex < lastSeenIndex) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, key); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, key); errors.push({ rule: this.name, type: this.type, @@ -155,8 +155,8 @@ export default class ServiceKeysOrderRule implements LintRule { } public fix(content: string): string { - const doc = parseDocument(content); - const services = doc.get('services'); + const parsedDocument = parseDocument(content); + const services = parsedDocument.get('services'); if (!isMap(services)) return content; @@ -174,7 +174,7 @@ export default class ServiceKeysOrderRule implements LintRule { const orderedService = new YAMLMap(); correctOrder.forEach((key) => { - const item = service.items.find((i) => isScalar(i.key) && i.key.value === key); + const item = service.items.find((node) => isScalar(node.key) && node.key.value === key); if (item) { orderedService.add(item); } @@ -182,7 +182,7 @@ export default class ServiceKeysOrderRule implements LintRule { keys.forEach((key) => { if (!correctOrder.includes(key)) { - const item = service.items.find((i) => isScalar(i.key) && i.key.value === key); + const item = service.items.find((node) => isScalar(node.key) && node.key.value === key); if (item) { orderedService.add(item); } @@ -192,6 +192,6 @@ export default class ServiceKeysOrderRule implements LintRule { services.set(serviceName, orderedService); }); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/service-ports-alphabetical-order-rule.ts b/src/rules/service-ports-alphabetical-order-rule.ts index 69f90f9..a62b4d0 100644 --- a/src/rules/service-ports-alphabetical-order-rule.ts +++ b/src/rules/service-ports-alphabetical-order-rule.ts @@ -34,8 +34,8 @@ export default class ServicePortsAlphabeticalOrderRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -54,7 +54,7 @@ export default class ServicePortsAlphabeticalOrderRule implements LintRule { const sortedPorts = [...extractedPorts].sort((a, b) => a.localeCompare(b, undefined, { numeric: true })); if (JSON.stringify(extractedPorts) !== JSON.stringify(sortedPorts)) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName, 'ports'); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName, 'ports'); errors.push({ rule: this.name, type: this.type, @@ -74,8 +74,8 @@ export default class ServicePortsAlphabeticalOrderRule implements LintRule { // eslint-disable-next-line class-methods-use-this public fix(content: string): string { - const doc = parseDocument(content); - const services = doc.get('services'); + const parsedDocument = parseDocument(content); + const services = parsedDocument.get('services'); if (!isMap(services)) return content; @@ -95,6 +95,6 @@ export default class ServicePortsAlphabeticalOrderRule implements LintRule { }); }); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/services-alphabetical-order-rule.ts b/src/rules/services-alphabetical-order-rule.ts index a2b20b1..f4abe38 100644 --- a/src/rules/services-alphabetical-order-rule.ts +++ b/src/rules/services-alphabetical-order-rule.ts @@ -50,8 +50,8 @@ export default class ServicesAlphabeticalOrderRule implements LintRule { public check(context: LintContext): LintMessage[] { const errors: LintMessage[] = []; - const doc = parseDocument(context.sourceCode); - const services = doc.get('services'); + const parsedDocument = parseDocument(context.sourceCode); + const services = parsedDocument.get('services'); if (!isMap(services)) return []; @@ -64,7 +64,7 @@ export default class ServicesAlphabeticalOrderRule implements LintRule { const misplacedBefore = ServicesAlphabeticalOrderRule.findMisplacedService(processedServices, serviceName); if (misplacedBefore) { - const line = findLineNumberForService(doc, context.sourceCode, serviceName); + const line = findLineNumberForService(parsedDocument, context.sourceCode, serviceName); errors.push({ rule: this.name, @@ -87,8 +87,8 @@ export default class ServicesAlphabeticalOrderRule implements LintRule { // eslint-disable-next-line class-methods-use-this public fix(content: string): string { - const doc = parseDocument(content); - const services = doc.get('services'); + const parsedDocument = parseDocument(content); + const services = parsedDocument.get('services'); if (!isMap(services)) return content; @@ -104,8 +104,8 @@ export default class ServicesAlphabeticalOrderRule implements LintRule { sortedServices.add(item); }); - doc.set('services', sortedServices); + parsedDocument.set('services', sortedServices); - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/rules/top-level-properties-order-rule.ts b/src/rules/top-level-properties-order-rule.ts index 884f627..f628cbb 100644 --- a/src/rules/top-level-properties-order-rule.ts +++ b/src/rules/top-level-properties-order-rule.ts @@ -106,8 +106,8 @@ export default class TopLevelPropertiesOrderRule implements LintRule { } public fix(content: string): string { - const doc = parseDocument(content); - const { contents } = doc; + const parsedDocument = parseDocument(content); + const { contents } = parsedDocument; if (!isMap(contents)) return content; @@ -124,14 +124,14 @@ export default class TopLevelPropertiesOrderRule implements LintRule { const reorderedMap = new YAMLMap(); correctOrder.forEach((key) => { - const item = contents.items.find((i) => isScalar(i.key) && String(i.key.value) === key); + const item = contents.items.find((node) => isScalar(node.key) && String(node.key.value) === key); if (item) { reorderedMap.items.push(item); } }); - doc.contents = reorderedMap as unknown as typeof doc.contents; + parsedDocument.contents = reorderedMap as unknown as typeof parsedDocument.contents; - return doc.toString(); + return parsedDocument.toString(); } } diff --git a/src/util/check-for-updates.ts b/src/util/check-for-updates.ts deleted file mode 100644 index e94aabe..0000000 --- a/src/util/check-for-updates.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* -import { version as currentVersion } from '../../package.json'; -import { LOG_SOURCE, Logger } from './logger.js'; - -async function checkForUpdates() { - const logger = Logger.getInstance(); - try { - const response = await fetch('https://registry.npmjs.org/docker-compose-linter'); - const data = await response.json(); - - const latestVersion = data['dist-tags'].latest; - - if (currentVersion !== latestVersion) { - logger.info(`A new release of docker-compose-linter is available: v${currentVersion} -> v${latestVersion}`); - logger.info(`Update it by running: npm install -g docker-compose-linter`); - } else { - logger.debug(LOG_SOURCE.UTIL, 'You are using the latest version of docker-compose-linter.'); - } - } catch (error) { - logger.debug(LOG_SOURCE.UTIL, 'Failed to check for updates:', error); - } -} - -export { checkForUpdates } -*/ diff --git a/src/util/files-finder.ts b/src/util/files-finder.ts index 8edbd61..cc199f5 100644 --- a/src/util/files-finder.ts +++ b/src/util/files-finder.ts @@ -17,27 +17,27 @@ export function findFilesForLinting(paths: string[], recursive: boolean, exclude if (excludePaths && excludePaths.length > 0) { excludePaths.forEach((p) => excludeSet.add(p)); } - const exclude = Array.from(excludeSet); + const exclude = [...excludeSet]; logger.debug('UTIL', `Paths to exclude: ${exclude.toString()}`); // Regular expression to match [compose*.yml, compose*.yaml, docker-compose*.yml, docker-compose*.yaml] files const dockerComposePattern = /^(docker-)?compose.*\.ya?ml$/; - paths.forEach((fileOrDir) => { - if (!fs.existsSync(fileOrDir)) { - logger.debug('UTIL', `File or directory not found: ${fileOrDir}`); - throw new FileNotFoundError(fileOrDir); + paths.forEach((fileOrDirectory) => { + if (!fs.existsSync(fileOrDirectory)) { + logger.debug('UTIL', `File or directory not found: ${fileOrDirectory}`); + throw new FileNotFoundError(fileOrDirectory); } let allPaths: string[] = []; - const fileOrDirStats = fs.statSync(fileOrDir); + const fileOrDirectoryStats = fs.statSync(fileOrDirectory); - if (fileOrDirStats.isDirectory()) { + if (fileOrDirectoryStats.isDirectory()) { try { - allPaths = fs.readdirSync(resolve(fileOrDir)).map((f) => join(fileOrDir, f)); + allPaths = fs.readdirSync(resolve(fileOrDirectory)).map((f) => join(fileOrDirectory, f)); } catch (error) { - logger.debug('UTIL', `Error reading directory: ${fileOrDir}`, error); + logger.debug('UTIL', `Error reading directory: ${fileOrDirectory}`, error); allPaths = []; } @@ -55,15 +55,15 @@ export function findFilesForLinting(paths: string[], recursive: boolean, exclude // If recursive search is enabled, search within the directory logger.debug('UTIL', `Recursive search is enabled, search within the directory: ${path}`); const nestedFiles = findFilesForLinting([path], recursive, exclude); - filesToCheck = filesToCheck.concat(nestedFiles); + filesToCheck = [...filesToCheck, ...nestedFiles]; } } else if (pathStats.isFile() && dockerComposePattern.test(basename(path))) { // Add the file to the list if it matches the pattern filesToCheck.push(path); } }); - } else if (fileOrDirStats.isFile()) { - filesToCheck.push(fileOrDir); + } else if (fileOrDirectoryStats.isFile()) { + filesToCheck.push(fileOrDirectory); } }); diff --git a/src/util/formatter-loader.ts b/src/util/formatter-loader.ts index 032ec46..036b465 100644 --- a/src/util/formatter-loader.ts +++ b/src/util/formatter-loader.ts @@ -1,4 +1,4 @@ -import path from 'node:path'; +import { resolve } from 'node:path'; import type { LintResult } from '../linter/linter.types.js'; import { Logger } from './logger.js'; @@ -9,7 +9,7 @@ async function importFormatter(modulePath: string): Promise { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access const Formatter = (await import(modulePath)).default; return Formatter as FormatterFunction; - } catch (error) { + } catch { throw new Error(`Module at ${modulePath} does not export a default formatter.`); } } @@ -18,7 +18,7 @@ export async function loadFormatter(formatterName: string): Promise { return JSON.parse( - readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), `../../schemas/${name}.schema.json`), 'utf-8'), + readFileSync(resolve(dirname(fileURLToPath(import.meta.url)), `../../schemas/${name}.schema.json`), 'utf8'), ) as Record; } diff --git a/src/util/logger.ts b/src/util/logger.ts index cb13a29..e00472f 100644 --- a/src/util/logger.ts +++ b/src/util/logger.ts @@ -55,29 +55,29 @@ class Logger { } } - public debug(source: LogSource, ...args: unknown[]): void { + public debug(source: LogSource, ...options: unknown[]): void { if (this.debugMode) { const message = Logger.formatMessage('DEBUG', source); - console.debug(message, ...args); + console.debug(message, ...options); } } // eslint-disable-next-line class-methods-use-this - public info(...args: unknown[]): void { + public info(...options: unknown[]): void { const message = Logger.formatMessage('INFO'); - console.info(message, ...args); + console.info(message, ...options); } // eslint-disable-next-line class-methods-use-this - public warn(...args: unknown[]): void { + public warn(...options: unknown[]): void { const message = Logger.formatMessage('WARN'); - console.warn(message, ...args); + console.warn(message, ...options); } // eslint-disable-next-line class-methods-use-this - public error(...args: unknown[]): void { + public error(...options: unknown[]): void { const message = Logger.formatMessage('ERROR'); - console.error(message, ...args); + console.error(message, ...options); } } diff --git a/src/util/rules-loader.ts b/src/util/rules-loader.ts index 1e6358d..73ecf1c 100644 --- a/src/util/rules-loader.ts +++ b/src/util/rules-loader.ts @@ -1,15 +1,15 @@ -import path from 'node:path'; +import { join, resolve, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import fs from 'node:fs'; import type { LintRule, LintMessageType } from '../linter/linter.types.js'; import type { Config, ConfigRuleLevel, ConfigRule } from '../config/config.types.js'; import { Logger } from './logger.js'; -async function importRule(file: string, rulesDir: string): Promise { +async function importRule(file: string, rulesDirectory: string): Promise { const logger = Logger.getInstance(); try { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const RuleClass = (await import(path.join(rulesDir, file))).default; + const RuleClass = (await import(join(rulesDirectory, file))).default; if (typeof RuleClass === 'function') { return new (RuleClass as new () => LintRule)(); @@ -22,15 +22,15 @@ async function importRule(file: string, rulesDir: string): Promise { - const rulesDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../rules'); + const rulesDirectory = resolve(dirname(fileURLToPath(import.meta.url)), '../rules'); const ruleFiles = fs - .readdirSync(rulesDir) + .readdirSync(rulesDirectory) .filter((file) => file.endsWith('.js') || (file.endsWith('.ts') && !file.endsWith('d.ts'))); // Parallel import with Promise.all const ruleInstances: (LintRule | null)[] = await Promise.all( - ruleFiles.map(async (file) => importRule(file, rulesDir)), + ruleFiles.map(async (file) => importRule(file, rulesDirectory)), ); const activeRules: LintRule[] = []; diff --git a/src/util/service-ports-parser.ts b/src/util/service-ports-parser.ts index 148fab4..682a29a 100644 --- a/src/util/service-ports-parser.ts +++ b/src/util/service-ports-parser.ts @@ -1,4 +1,4 @@ -import net from 'net'; +import net from 'node:net'; import { isMap, isScalar } from 'yaml'; function extractPublishedPortValue(yamlNode: unknown): string { @@ -38,7 +38,7 @@ function parsePortsRange(port: string): string[] { } const ports: string[] = []; - // eslint-disable-next-line no-plusplus + // eslint-disable-next-line no-plusplus,unicorn/prevent-abbreviations for (let i = start; i <= end; i++) { ports.push(i.toString()); } diff --git a/tests/linter.spec.ts b/tests/linter.spec.ts index db6dbb9..9a40e93 100644 --- a/tests/linter.spec.ts +++ b/tests/linter.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import esmock from 'esmock'; import { Logger } from '../src/util/logger.js'; import type { Config } from '../src/config/config.types.js'; @@ -51,11 +52,13 @@ services: image: nginx `; +// @ts-ignore TS2339 test.beforeEach(() => { Logger.init(false); // Initialize logger }); -test('DCLinter: should lint files correctly', async (t) => { +// @ts-ignore TS2349 +test('DCLinter: should lint files correctly', async (t: ExecutionContext) => { const mockFindFiles = (): string[] => [mockFilePath]; const mockLoadLintRules = (): LintRule[] => [mockRule]; const mockReadFileSync = (): string => mockFileContent; @@ -83,7 +86,8 @@ test('DCLinter: should lint files correctly', async (t) => { t.is(result[0].warningCount, 0, 'There should be no warnings'); }); -test('DCLinter: should lint multiple files correctly', async (t) => { +// @ts-ignore TS2349 +test('DCLinter: should lint multiple files correctly', async (t: ExecutionContext) => { const mockFindFiles = (): string[] => mockFilePaths; const mockLoadLintRules = (): LintRule[] => [mockRule]; const mockReadFileSync = (filePath: string): string => mockFileContent; @@ -109,7 +113,8 @@ test('DCLinter: should lint multiple files correctly', async (t) => { t.is(result[1].messages.length, 1, 'There should be one lint message for the second file'); }); -test('DCLinter: should fix files', async (t) => { +// @ts-ignore TS2349 +test('DCLinter: should fix files', async (t: ExecutionContext) => { const mockFindFiles = (): string[] => [mockFilePath]; const mockLoadLintRules = (): LintRule[] => [mockRule]; const mockReadFileSync = (): string => mockFileContent; diff --git a/tests/rules/no-build-and-image-rule.spec.ts b/tests/rules/no-build-and-image-rule.spec.ts index 26d2e26..73606d6 100644 --- a/tests/rules/no-build-and-image-rule.spec.ts +++ b/tests/rules/no-build-and-image-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import NoBuildAndImageRule from '../../src/rules/no-build-and-image-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -47,7 +48,8 @@ services: const filePath = '/docker-compose.yml'; -test('NoBuildAndImageRule: should return a warning when both "build" and "image" are used in a service', (t) => { +// @ts-ignore TS2349 +test('NoBuildAndImageRule: should return a warning when both "build" and "image" are used in a service', (t: ExecutionContext) => { const rule = new NoBuildAndImageRule(); const context: LintContext = { path: filePath, @@ -72,7 +74,8 @@ test('NoBuildAndImageRule: should return a warning when both "build" and "image" }); }); -test('NoBuildAndImageRule: should return a warning when both "build" and "image" are used in a service and checkPullPolicy is false', (t) => { +// @ts-ignore TS2349 +test('NoBuildAndImageRule: should return a warning when both "build" and "image" are used in a service and checkPullPolicy is false', (t: ExecutionContext) => { const rule = new NoBuildAndImageRule({ checkPullPolicy: false }); const context: LintContext = { path: filePath, @@ -97,7 +100,8 @@ test('NoBuildAndImageRule: should return a warning when both "build" and "image" }); }); -test('NoBuildAndImageRule: should not return warnings when "build" and "image" are used with pull_policy and checkPullPolicy is true', (t) => { +// @ts-ignore TS2349 +test('NoBuildAndImageRule: should not return warnings when "build" and "image" are used with pull_policy and checkPullPolicy is true', (t: ExecutionContext) => { const rule = new NoBuildAndImageRule({ checkPullPolicy: true }); const context: LintContext = { path: filePath, @@ -113,7 +117,8 @@ test('NoBuildAndImageRule: should not return warnings when "build" and "image" a ); }); -test('NoBuildAndImageRule: should not return warnings when only "build" is used', (t) => { +// @ts-ignore TS2349 +test('NoBuildAndImageRule: should not return warnings when only "build" is used', (t: ExecutionContext) => { const rule = new NoBuildAndImageRule(); const context: LintContext = { path: filePath, @@ -125,7 +130,8 @@ test('NoBuildAndImageRule: should not return warnings when only "build" is used' t.is(errors.length, 0, 'There should be no warnings when only "build" is used.'); }); -test('NoBuildAndImageRule: should not return warnings when only "image" is used', (t) => { +// @ts-ignore TS2349 +test('NoBuildAndImageRule: should not return warnings when only "image" is used', (t: ExecutionContext) => { const rule = new NoBuildAndImageRule(); const context: LintContext = { path: filePath, diff --git a/tests/rules/no-duplicate-container-names-rule.spec.ts b/tests/rules/no-duplicate-container-names-rule.spec.ts index f5f2683..9c7c382 100644 --- a/tests/rules/no-duplicate-container-names-rule.spec.ts +++ b/tests/rules/no-duplicate-container-names-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import NoDuplicateContainerNamesRule from '../../src/rules/no-duplicate-container-names-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -25,7 +26,8 @@ services: container_name: db_container `; -test('NoDuplicateContainerNamesRule: should return an error when duplicate container names are found', (t) => { +// @ts-ignore TS2349 +test('NoDuplicateContainerNamesRule: should return an error when duplicate container names are found', (t: ExecutionContext) => { const rule = new NoDuplicateContainerNamesRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -41,7 +43,8 @@ test('NoDuplicateContainerNamesRule: should return an error when duplicate conta t.true(errors[0].message.includes(expectedMessage)); }); -test('NoDuplicateContainerNamesRule: should not return errors when container names are unique', (t) => { +// @ts-ignore TS2349 +test('NoDuplicateContainerNamesRule: should not return errors when container names are unique', (t: ExecutionContext) => { const rule = new NoDuplicateContainerNamesRule(); const context: LintContext = { path: '/docker-compose.yml', diff --git a/tests/rules/no-duplicate-exported-ports-rule.spec.ts b/tests/rules/no-duplicate-exported-ports-rule.spec.ts index 45dacd5..4b01ce3 100644 --- a/tests/rules/no-duplicate-exported-ports-rule.spec.ts +++ b/tests/rules/no-duplicate-exported-ports-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import NoDuplicateExportedPortsRule from '../../src/rules/no-duplicate-exported-ports-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -101,7 +102,8 @@ services: const filePath = '/docker-compose.yml'; -test('NoDuplicateExportedPortsRule: should return multiple errors when duplicate exported ports are found', (t) => { +// @ts-ignore TS2349 +test('NoDuplicateExportedPortsRule: should return multiple errors when duplicate exported ports are found', (t: ExecutionContext) => { const rule = new NoDuplicateExportedPortsRule(); const context: LintContext = { path: filePath, @@ -125,7 +127,8 @@ test('NoDuplicateExportedPortsRule: should return multiple errors when duplicate }); }); -test('NoDuplicateExportedPortsRule: should not return errors when exported ports are unique', (t) => { +// @ts-ignore TS2349 +test('NoDuplicateExportedPortsRule: should not return errors when exported ports are unique', (t: ExecutionContext) => { const rule = new NoDuplicateExportedPortsRule(); const context: LintContext = { path: filePath, @@ -137,7 +140,8 @@ test('NoDuplicateExportedPortsRule: should not return errors when exported ports t.is(errors.length, 0, 'There should be no errors when exported ports are unique.'); }); -test('NoDuplicateExportedPortsRule: should return an error when range overlap is detected', (t) => { +// @ts-ignore TS2349 +test('NoDuplicateExportedPortsRule: should return an error when range overlap is detected', (t: ExecutionContext) => { const rule = new NoDuplicateExportedPortsRule(); const context: LintContext = { path: filePath, diff --git a/tests/rules/no-quotes-in-volumes-rule.spec.ts b/tests/rules/no-quotes-in-volumes-rule.spec.ts index e02e111..6b42030 100644 --- a/tests/rules/no-quotes-in-volumes-rule.spec.ts +++ b/tests/rules/no-quotes-in-volumes-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import NoQuotesInVolumesRule from '../../src/rules/no-quotes-in-volumes-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -17,7 +18,8 @@ services: - "data" `; -test('NoQuotesInVolumesRule: should not return errors for YAML without quotes in volumes', (t) => { +// @ts-ignore TS2349 +test('NoQuotesInVolumesRule: should not return errors for YAML without quotes in volumes', (t: ExecutionContext) => { const rule = new NoQuotesInVolumesRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -26,10 +28,11 @@ test('NoQuotesInVolumesRule: should not return errors for YAML without quotes in }; const errors = rule.check(context); - t.deepEqual(errors.length, 0, 'There should be no errors for correct YAML.'); + t.is(errors.length, 0, 'There should be no errors for correct YAML.'); }); -test('NoQuotesInVolumesRule: should return errors for YAML with quotes in volumes', (t) => { +// @ts-ignore TS2349 +test('NoQuotesInVolumesRule: should return errors for YAML with quotes in volumes', (t: ExecutionContext) => { const rule = new NoQuotesInVolumesRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -44,7 +47,8 @@ test('NoQuotesInVolumesRule: should return errors for YAML with quotes in volume t.is(errors[0].severity, 'info'); }); -test('NoQuotesInVolumesRule: should fix YAML with quotes in volumes', (t) => { +// @ts-ignore TS2349 +test('NoQuotesInVolumesRule: should fix YAML with quotes in volumes', (t: ExecutionContext) => { const rule = new NoQuotesInVolumesRule(); const fixedYAML = rule.fix(incorrectYAML); @@ -52,7 +56,8 @@ test('NoQuotesInVolumesRule: should fix YAML with quotes in volumes', (t) => { t.false(fixedYAML.includes('"data"'), 'The volume name should no longer have quotes.'); }); -test('NoQuotesInVolumesRule: should not modify YAML without quotes in volumes', (t) => { +// @ts-ignore TS2349 +test('NoQuotesInVolumesRule: should not modify YAML without quotes in volumes', (t: ExecutionContext) => { const rule = new NoQuotesInVolumesRule(); const fixedYAML = rule.fix(correctYAML); diff --git a/tests/rules/no-version-field-rule.spec.ts b/tests/rules/no-version-field-rule.spec.ts index 7aae094..e5c9537 100644 --- a/tests/rules/no-version-field-rule.spec.ts +++ b/tests/rules/no-version-field-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import NoVersionFieldRule from '../../src/rules/no-version-field-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -16,7 +17,8 @@ services: image: nginx `; -test('NoVersionFieldRule: should return an error when "version" field is present', (t) => { +// @ts-ignore TS2349 +test('NoVersionFieldRule: should return an error when "version" field is present', (t: ExecutionContext) => { const rule = new NoVersionFieldRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -38,7 +40,8 @@ test('NoVersionFieldRule: should return an error when "version" field is present t.is(errors[0].severity, 'minor'); }); -test('NoVersionFieldRule: should not return errors when "version" field is not present', (t) => { +// @ts-ignore TS2349 +test('NoVersionFieldRule: should not return errors when "version" field is not present', (t: ExecutionContext) => { const rule = new NoVersionFieldRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -56,14 +59,16 @@ test('NoVersionFieldRule: should not return errors when "version" field is not p t.is(errors.length, 0, 'There should be no errors when the "version" field is absent.'); }); -test('NoVersionFieldRule: should fix by removing the "version" field', (t) => { +// @ts-ignore TS2349 +test('NoVersionFieldRule: should fix by removing the "version" field', (t: ExecutionContext) => { const rule = new NoVersionFieldRule(); const fixedYAML = rule.fix(yamlWithVersion); t.false(fixedYAML.includes('version:'), 'The "version" field should be removed.'); }); -test('NoVersionFieldRule: should not modify YAML without "version" field', (t) => { +// @ts-ignore TS2349 +test('NoVersionFieldRule: should not modify YAML without "version" field', (t: ExecutionContext) => { const rule = new NoVersionFieldRule(); const fixedYAML = rule.fix(yamlWithoutVersion); diff --git a/tests/rules/require-project-name-field-rule.spec.ts b/tests/rules/require-project-name-field-rule.spec.ts index fbea128..7b8e725 100644 --- a/tests/rules/require-project-name-field-rule.spec.ts +++ b/tests/rules/require-project-name-field-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import RequireProjectNameFieldRule from '../../src/rules/require-project-name-field-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -16,7 +17,8 @@ services: image: nginx `; -test('RequiredProjectNameFieldRule: should return a warning when "name" field is missing', (t) => { +// @ts-ignore TS2349 +test('RequiredProjectNameFieldRule: should return a warning when "name" field is missing', (t: ExecutionContext) => { const rule = new RequireProjectNameFieldRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -37,7 +39,8 @@ test('RequiredProjectNameFieldRule: should return a warning when "name" field is t.is(errors[0].severity, 'minor'); }); -test('RequiredProjectNameFieldRule: should not return warnings when "name" field is present', (t) => { +// @ts-ignore TS2349 +test('RequiredProjectNameFieldRule: should not return warnings when "name" field is present', (t: ExecutionContext) => { const rule = new RequireProjectNameFieldRule(); const context: LintContext = { path: '/docker-compose.yml', diff --git a/tests/rules/require-quotes-in-ports-rule.spec.ts b/tests/rules/require-quotes-in-ports-rule.spec.ts index bd36dd3..490fddc 100644 --- a/tests/rules/require-quotes-in-ports-rule.spec.ts +++ b/tests/rules/require-quotes-in-ports-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import RequireQuotesInPortsRule from '../../src/rules/require-quotes-in-ports-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -26,7 +27,8 @@ services: const pathToFile = '/docker-compose.yml'; -test('RequireQuotesInPortsRule: should return a warning when ports are not quoted', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should return a warning when ports are not quoted', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'single' }); const context: LintContext = { path: pathToFile, @@ -41,7 +43,8 @@ test('RequireQuotesInPortsRule: should return a warning when ports are not quote t.is(errors[0].severity, 'minor'); }); -test('RequireQuotesInPortsRule: should not return warnings when ports are quoted with single quotes', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should not return warnings when ports are quoted with single quotes', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'single' }); const context: LintContext = { path: pathToFile, @@ -53,7 +56,8 @@ test('RequireQuotesInPortsRule: should not return warnings when ports are quoted t.is(errors.length, 0, 'There should be no warnings when ports are quoted with single quotes.'); }); -test('RequireQuotesInPortsRule: should not return warnings when ports are quoted with double quotes', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should not return warnings when ports are quoted with double quotes', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'double' }); const context: LintContext = { path: pathToFile, @@ -65,7 +69,8 @@ test('RequireQuotesInPortsRule: should not return warnings when ports are quoted t.is(errors.length, 0, 'There should be no warnings when ports are quoted with double quotes.'); }); -test('RequireQuotesInPortsRule: should fix unquoted ports by adding single quotes and not modify already quoted ports', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should fix unquoted ports by adding single quotes and not modify already quoted ports', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'single' }); const fixedYAML = rule.fix(yamlWithoutQuotes); @@ -73,7 +78,8 @@ test('RequireQuotesInPortsRule: should fix unquoted ports by adding single quote t.false(fixedYAML.includes('ports:\n - 8080:80'), 'The unquoted ports should no longer exist.'); }); -test('RequireQuotesInPortsRule: should fix double quotes ports by changing them to single quotes', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should fix double quotes ports by changing them to single quotes', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'single' }); const fixedYAML = rule.fix(yamlWithSingleQuotes); @@ -81,7 +87,8 @@ test('RequireQuotesInPortsRule: should fix double quotes ports by changing them t.false(fixedYAML.includes(`"8080:80"`), 'The ports should not have double quotes.'); }); -test('RequireQuotesInPortsRule: should fix unquoted ports by adding double quotes and not modify already quoted ports', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should fix unquoted ports by adding double quotes and not modify already quoted ports', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'double' }); const fixedYAML = rule.fix(yamlWithoutQuotes); @@ -89,7 +96,8 @@ test('RequireQuotesInPortsRule: should fix unquoted ports by adding double quote t.false(fixedYAML.includes('ports:\n - 8080:80'), 'The unquoted ports should no longer exist.'); }); -test('RequireQuotesInPortsRule: should fix single quotes ports by changing them to double quotes', (t) => { +// @ts-ignore TS2349 +test('RequireQuotesInPortsRule: should fix single quotes ports by changing them to double quotes', (t: ExecutionContext) => { const rule = new RequireQuotesInPortsRule({ quoteType: 'double' }); const fixedYAML = rule.fix(yamlWithSingleQuotes); diff --git a/tests/rules/service-container-name-regex-rule.spec.ts b/tests/rules/service-container-name-regex-rule.spec.ts index c06e6e4..2668d31 100644 --- a/tests/rules/service-container-name-regex-rule.spec.ts +++ b/tests/rules/service-container-name-regex-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServiceContainerNameRegexRule from '../../src/rules/service-container-name-regex-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -19,7 +20,8 @@ services: container_name: "my-app-123" `; -test('ServiceContainerNameRegexRule: should return an error for invalid container name', (t) => { +// @ts-ignore TS2349 +test('ServiceContainerNameRegexRule: should return an error for invalid container name', (t: ExecutionContext) => { const rule = new ServiceContainerNameRegexRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -35,7 +37,8 @@ test('ServiceContainerNameRegexRule: should return an error for invalid containe t.true(errors[0].message.includes(expectedMessage)); }); -test('ServiceContainerNameRegexRule: should not return an error for valid container name', (t) => { +// @ts-ignore TS2349 +test('ServiceContainerNameRegexRule: should not return an error for valid container name', (t: ExecutionContext) => { const rule = new ServiceContainerNameRegexRule(); const context: LintContext = { path: '/docker-compose.yml', diff --git a/tests/rules/service-dependencies-alphabetical-order-rule.spec.ts b/tests/rules/service-dependencies-alphabetical-order-rule.spec.ts index 77fdcba..da75ebd 100644 --- a/tests/rules/service-dependencies-alphabetical-order-rule.spec.ts +++ b/tests/rules/service-dependencies-alphabetical-order-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServiceDependenciesAlphabeticalOrderRule from '../../src/rules/service-dependencies-alphabetical-order-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -48,12 +49,13 @@ services: `; // Helper function to normalize YAML -const normalizeYAML = (yaml: string) => yaml.replace(/\s+/g, ' ').trim(); +const normalizeYAML = (yaml: string) => yaml.replaceAll(/\s+/g, ' ').trim(); const filePath = '/docker-compose.yml'; // Short syntax tests -test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when short syntax services are not in alphabetical order', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when short syntax services are not in alphabetical order', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const context: LintContext = { path: filePath, @@ -66,7 +68,8 @@ test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when sho t.true(errors[0].message.includes(`Services in "depends_on" for service "web" should be in alphabetical order.`)); }); -test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when short syntax services are in alphabetical order', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when short syntax services are in alphabetical order', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const context: LintContext = { path: filePath, @@ -78,7 +81,8 @@ test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when t.is(errors.length, 0, 'There should be no warnings when short syntax services are in alphabetical order.'); }); -test('ServiceDependenciesAlphabeticalOrderRule: should fix the order of short syntax services', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should fix the order of short syntax services', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectShortSyntax); @@ -90,7 +94,8 @@ test('ServiceDependenciesAlphabeticalOrderRule: should fix the order of short sy }); // Long syntax tests -test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when long syntax services are not in alphabetical order', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when long syntax services are not in alphabetical order', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const context: LintContext = { path: filePath, @@ -103,7 +108,8 @@ test('ServiceDependenciesAlphabeticalOrderRule: should return a warning when lon t.true(errors[0].message.includes(`Services in "depends_on" for service "web" should be in alphabetical order.`)); }); -test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when long syntax services are in alphabetical order', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when long syntax services are in alphabetical order', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const context: LintContext = { path: filePath, @@ -115,7 +121,8 @@ test('ServiceDependenciesAlphabeticalOrderRule: should not return warnings when t.is(errors.length, 0, 'There should be no warnings when long syntax services are in alphabetical order.'); }); -test('ServiceDependenciesAlphabeticalOrderRule: should fix the order of long syntax services', (t) => { +// @ts-ignore TS2349 +test('ServiceDependenciesAlphabeticalOrderRule: should fix the order of long syntax services', (t: ExecutionContext) => { const rule = new ServiceDependenciesAlphabeticalOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectLongSyntax); diff --git a/tests/rules/service-image-require-explicit-tag-rule.spec.ts b/tests/rules/service-image-require-explicit-tag-rule.spec.ts index 6c9d4a4..3155c48 100644 --- a/tests/rules/service-image-require-explicit-tag-rule.spec.ts +++ b/tests/rules/service-image-require-explicit-tag-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServiceImageRequireExplicitTagRule from '../../src/rules/service-image-require-explicit-tag-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -93,7 +94,8 @@ services: const filePath = '/docker-compose.yml'; -test('ServiceImageRequireExplicitTagRule: should return a warning when no tag is specified', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should return a warning when no tag is specified', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule(); const context: LintContext = { path: filePath, @@ -116,7 +118,8 @@ test('ServiceImageRequireExplicitTagRule: should return a warning when no tag is }); }); -test('ServiceImageRequireExplicitTagRule: should return a warning when using latest tag', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should return a warning when using latest tag', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule(); const context: LintContext = { path: filePath, @@ -139,7 +142,8 @@ test('ServiceImageRequireExplicitTagRule: should return a warning when using lat }); }); -test('ServiceImageRequireExplicitTagRule: should return a warning when using stable tag', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should return a warning when using stable tag', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule(); const context: LintContext = { path: filePath, @@ -162,7 +166,8 @@ test('ServiceImageRequireExplicitTagRule: should return a warning when using sta }); }); -test('ServiceImageRequireExplicitTagRule: should return a warning when using prohibited tags', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should return a warning when using prohibited tags', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule(); const context: LintContext = { path: filePath, @@ -187,7 +192,8 @@ test('ServiceImageRequireExplicitTagRule: should return a warning when using pro }); }); -test('ServiceImageRequireExplicitTagRule: should use custom prohibitedTags when provided in the constructor', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should use custom prohibitedTags when provided in the constructor', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule({ prohibitedTags: ['unstable', 'preview'] }); const context: LintContext = { @@ -209,7 +215,8 @@ test('ServiceImageRequireExplicitTagRule: should use custom prohibitedTags when }); }); -test('ServiceImageRequireExplicitTagRule: should not return warnings when a specific version tag or digest is used', (t) => { +// @ts-ignore TS2349 +test('ServiceImageRequireExplicitTagRule: should not return warnings when a specific version tag or digest is used', (t: ExecutionContext) => { const rule = new ServiceImageRequireExplicitTagRule(); const context: LintContext = { path: filePath, diff --git a/tests/rules/service-keys-order-rule.spec.ts b/tests/rules/service-keys-order-rule.spec.ts index 84508f2..54f2860 100644 --- a/tests/rules/service-keys-order-rule.spec.ts +++ b/tests/rules/service-keys-order-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServiceKeysOrderRule from '../../src/rules/service-keys-order-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -37,9 +38,10 @@ services: `; // Helper function to strip spaces and normalize strings for comparison -const normalizeYAML = (yaml: string) => yaml.replace(/\s+/g, ' ').trim(); +const normalizeYAML = (yaml: string) => yaml.replaceAll(/\s+/g, ' ').trim(); -test('ServiceKeysOrderRule: should return a warning when service keys are in the wrong order', (t) => { +// @ts-ignore TS2349 +test('ServiceKeysOrderRule: should return a warning when service keys are in the wrong order', (t: ExecutionContext) => { const rule = new ServiceKeysOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -62,7 +64,8 @@ test('ServiceKeysOrderRule: should return a warning when service keys are in the }); }); -test('ServiceKeysOrderRule: should not return warnings when service keys are in the correct order', (t) => { +// @ts-ignore TS2349 +test('ServiceKeysOrderRule: should not return warnings when service keys are in the correct order', (t: ExecutionContext) => { const rule = new ServiceKeysOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -74,7 +77,8 @@ test('ServiceKeysOrderRule: should not return warnings when service keys are in t.is(errors.length, 0, 'There should be no warnings when service keys are in the correct order.'); }); -test('ServiceKeysOrderRule: should fix the order of service keys', (t) => { +// @ts-ignore TS2349 +test('ServiceKeysOrderRule: should fix the order of service keys', (t: ExecutionContext) => { const rule = new ServiceKeysOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectOrder); diff --git a/tests/rules/service-ports-alphabetical-order-rule.spec.ts b/tests/rules/service-ports-alphabetical-order-rule.spec.ts index 912f3f6..e91804f 100644 --- a/tests/rules/service-ports-alphabetical-order-rule.spec.ts +++ b/tests/rules/service-ports-alphabetical-order-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServicePortsAlphabeticalOrderRule from '../../src/rules/service-ports-alphabetical-order-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -47,9 +48,10 @@ services: `; // Helper function to strip spaces and normalize strings for comparison -const normalizeYAML = (yaml: string) => yaml.replace(/\s+/g, ' ').trim(); +const normalizeYAML = (yaml: string) => yaml.replaceAll(/\s+/g, ' ').trim(); -test('ServicePortsAlphabeticalOrderRule: should return a warning when ports are not alphabetically ordered', (t) => { +// @ts-ignore TS2349 +test('ServicePortsAlphabeticalOrderRule: should return a warning when ports are not alphabetically ordered', (t: ExecutionContext) => { const rule = new ServicePortsAlphabeticalOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -63,7 +65,8 @@ test('ServicePortsAlphabeticalOrderRule: should return a warning when ports are t.true(errors[0].message.includes(`Ports in service "web" should be in alphabetical order.`)); }); -test('ServicePortsAlphabeticalOrderRule: should not return warnings when ports are alphabetically ordered', (t) => { +// @ts-ignore TS2349 +test('ServicePortsAlphabeticalOrderRule: should not return warnings when ports are alphabetically ordered', (t: ExecutionContext) => { const rule = new ServicePortsAlphabeticalOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -75,7 +78,8 @@ test('ServicePortsAlphabeticalOrderRule: should not return warnings when ports a t.is(errors.length, 0, 'There should be no warnings when ports are in alphabetical order.'); }); -test('ServicePortsAlphabeticalOrderRule: should fix the order of ports', (t) => { +// @ts-ignore TS2349 +test('ServicePortsAlphabeticalOrderRule: should fix the order of ports', (t: ExecutionContext) => { const rule = new ServicePortsAlphabeticalOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectPortOrder); diff --git a/tests/rules/services-alphabetical-order-rule.spec.ts b/tests/rules/services-alphabetical-order-rule.spec.ts index 83ace3c..3298cdc 100644 --- a/tests/rules/services-alphabetical-order-rule.spec.ts +++ b/tests/rules/services-alphabetical-order-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import ServicesAlphabeticalOrderRule from '../../src/rules/services-alphabetical-order-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -33,9 +34,10 @@ services: `; // Helper function to normalize strings for comparison -const normalizeYAML = (yaml: string) => yaml.replace(/\s+/g, ' ').trim(); +const normalizeYAML = (yaml: string) => yaml.replaceAll(/\s+/g, ' ').trim(); -test('ServicesAlphabeticalOrderRule: should return a warning when services are out of order', (t) => { +// @ts-ignore TS2349 +test('ServicesAlphabeticalOrderRule: should return a warning when services are out of order', (t: ExecutionContext) => { const rule = new ServicesAlphabeticalOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -52,7 +54,8 @@ test('ServicesAlphabeticalOrderRule: should return a warning when services are o t.true(errors[2].message.includes('Service "cache" should be before "database".')); }); -test('ServicesAlphabeticalOrderRule: should not return warnings when services are in alphabetical order', (t) => { +// @ts-ignore TS2349 +test('ServicesAlphabeticalOrderRule: should not return warnings when services are in alphabetical order', (t: ExecutionContext) => { const rule = new ServicesAlphabeticalOrderRule(); const context: LintContext = { path: '/docker-compose.yml', @@ -64,7 +67,8 @@ test('ServicesAlphabeticalOrderRule: should not return warnings when services ar t.is(errors.length, 0, 'There should be no warnings when services are in alphabetical order.'); }); -test('ServicesAlphabeticalOrderRule: should fix the order of services', (t) => { +// @ts-ignore TS2349 +test('ServicesAlphabeticalOrderRule: should fix the order of services', (t: ExecutionContext) => { const rule = new ServicesAlphabeticalOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectOrder); diff --git a/tests/rules/top-level-properties-order-rule.spec.ts b/tests/rules/top-level-properties-order-rule.spec.ts index 96f0754..cef34dd 100644 --- a/tests/rules/top-level-properties-order-rule.spec.ts +++ b/tests/rules/top-level-properties-order-rule.spec.ts @@ -1,4 +1,5 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { parseDocument } from 'yaml'; import TopLevelPropertiesOrderRule, { TopLevelKeys } from '../../src/rules/top-level-properties-order-rule.js'; import type { LintContext } from '../../src/linter/linter.types.js'; @@ -41,9 +42,10 @@ volumes: const filePath = '/docker-compose.yml'; // Helper function to normalize YAML strings for comparison -const normalizeYAML = (yaml: string) => yaml.replace(/\s+/g, ' ').trim(); +const normalizeYAML = (yaml: string) => yaml.replaceAll(/\s+/g, ' ').trim(); -test('TopLevelPropertiesOrderRule: should return a warning when top-level properties are out of order', (t) => { +// @ts-ignore TS2349 +test('TopLevelPropertiesOrderRule: should return a warning when top-level properties are out of order', (t: ExecutionContext) => { const rule = new TopLevelPropertiesOrderRule(); const context: LintContext = { path: filePath, @@ -60,7 +62,8 @@ test('TopLevelPropertiesOrderRule: should return a warning when top-level proper t.true(errors[2].message.includes('Property "networks" is out of order.')); }); -test('TopLevelPropertiesOrderRule: should not return warnings when top-level properties are in the correct order', (t) => { +// @ts-ignore TS2349 +test('TopLevelPropertiesOrderRule: should not return warnings when top-level properties are in the correct order', (t: ExecutionContext) => { const rule = new TopLevelPropertiesOrderRule(); const context: LintContext = { path: filePath, @@ -72,7 +75,8 @@ test('TopLevelPropertiesOrderRule: should not return warnings when top-level pro t.is(errors.length, 0, 'There should be no warnings when top-level properties are in the correct order.'); }); -test('TopLevelPropertiesOrderRule: should fix the order of top-level properties', (t) => { +// @ts-ignore TS2349 +test('TopLevelPropertiesOrderRule: should fix the order of top-level properties', (t: ExecutionContext) => { const rule = new TopLevelPropertiesOrderRule(); const fixedYAML = rule.fix(yamlWithIncorrectOrder); @@ -118,7 +122,8 @@ x-b: some-key: some-value `; -test('TopLevelPropertiesOrderRule: should return warnings based on custom order', (t) => { +// @ts-ignore TS2349 +test('TopLevelPropertiesOrderRule: should return warnings based on custom order', (t: ExecutionContext) => { const customOrder = [ TopLevelKeys.Version, TopLevelKeys.Services, @@ -144,7 +149,8 @@ test('TopLevelPropertiesOrderRule: should return warnings based on custom order' t.true(errors[3].message.includes('Property "networks" is out of order.')); }); -test('TopLevelPropertiesOrderRule: should fix the order of top-level properties based on custom order', (t) => { +// @ts-ignore TS2349 +test('TopLevelPropertiesOrderRule: should fix the order of top-level properties based on custom order', (t: ExecutionContext) => { const customOrder = [ TopLevelKeys.Version, TopLevelKeys.Services, diff --git a/tests/util/files-finder.spec.ts b/tests/util/files-finder.spec.ts index 93542b5..d92f0e1 100644 --- a/tests/util/files-finder.spec.ts +++ b/tests/util/files-finder.spec.ts @@ -1,6 +1,7 @@ /* eslint-disable sonarjs/no-duplicate-string, @stylistic/indent */ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import esmock from 'esmock'; import { Logger } from '../../src/util/logger.js'; import { FileNotFoundError } from '../../src/errors/file-not-found-error.js'; @@ -19,15 +20,16 @@ const mockFilesInDirectory = ['docker-compose.yml', 'compose.yaml', 'another-fil const mockDirectoriesInDirectory = ['another_dir', 'node_modules']; const mockFilesInSubDirectory = ['docker-compose.yml', 'another-file.yaml', 'example.txt']; +// @ts-ignore TS2339 test.beforeEach(() => { Logger.init(false); // Initialize logger }); -const mockReaddirSync = (dir: string): string[] => { - if (dir === mockDirectory) { +const mockReaddirSync = (directory: string): string[] => { + if (directory === mockDirectory) { return [...mockFilesInDirectory, ...mockDirectoriesInDirectory]; } - if (dir === mockNodeModulesDirectory || dir === mockFolderDirectory) { + if (directory === mockNodeModulesDirectory || directory === mockFolderDirectory) { return mockFilesInSubDirectory; } return []; @@ -43,7 +45,8 @@ const mockStatSync = (filePath: string) => { }; const mockExistsSync = () => true; -test('findFilesForLinting: should handle recursive search and find only compose files in directory and exclude node_modules', async (t) => { +// @ts-ignore TS2349 +test('findFilesForLinting: should handle recursive search and find only compose files in directory and exclude node_modules', async (t: ExecutionContext) => { // Use esmock to mock fs module const { findFilesForLinting } = await esmock( '../../src/util/files-finder.js', @@ -65,7 +68,8 @@ test('findFilesForLinting: should handle recursive search and find only compose ); }); -test('findFilesForLinting: should return file directly if file is passed and search only compose in directory', async (t) => { +// @ts-ignore TS2349 +test('findFilesForLinting: should return file directly if file is passed and search only compose in directory', async (t: ExecutionContext) => { // Use esmock to mock fs module const { findFilesForLinting } = await esmock( '../../src/util/files-finder.js', @@ -87,7 +91,8 @@ test('findFilesForLinting: should return file directly if file is passed and sea ); }); -test('findFilesForLinting: should throw error if path does not exist', async (t) => { +// @ts-ignore TS2349 +test('findFilesForLinting: should throw error if path does not exist', async (t: ExecutionContext) => { // Use esmock to mock fs module const { findFilesForLinting } = await esmock( '../../src/util/files-finder.js', diff --git a/tests/util/line-finder.spec.ts b/tests/util/line-finder.spec.ts index d52731d..2cfc1ad 100644 --- a/tests/util/line-finder.spec.ts +++ b/tests/util/line-finder.spec.ts @@ -1,7 +1,9 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { findLineNumberByKey, findLineNumberByValue } from '../../src/util/line-finder.js'; -test('findLineNumberByKey: should return the correct line number when the key exists', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByKey: should return the correct line number when the key exists', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: @@ -15,7 +17,8 @@ services: t.is(line, 5, 'Should return the correct line number for the key "image"'); }); -test('findLineNumberByKey: should return 1 when the key does not exist', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByKey: should return 1 when the key does not exist', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: @@ -29,7 +32,8 @@ services: t.is(line, 1, 'Should return 1 when the key does not exist'); }); -test('findLineNumberByKey: should work for nested keys', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByKey: should work for nested keys', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: @@ -45,7 +49,8 @@ services: t.is(line, 6, 'Should return the correct line number for the nested key "ports"'); }); -test('findLineNumberByValue: should return the correct line number when the value exists', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByValue: should return the correct line number when the value exists', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: @@ -59,7 +64,8 @@ services: t.is(line, 5, 'Should return the correct line number for the value "nginx"'); }); -test('findLineNumberByValue: should return 0 when the value does not exist', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByValue: should return 0 when the value does not exist', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: @@ -73,7 +79,8 @@ services: t.is(line, 1, 'Should return 1 when the value does not exist'); }); -test('findLineNumberByValue: should return the correct line number for a value inside an array', (t) => { +// @ts-ignore TS2349 +test('findLineNumberByValue: should return the correct line number for a value inside an array', (t: ExecutionContext) => { const yamlContent = ` version: '3' services: diff --git a/tests/util/service-ports-parser.spec.ts b/tests/util/service-ports-parser.spec.ts index 2d65f60..32d3885 100644 --- a/tests/util/service-ports-parser.spec.ts +++ b/tests/util/service-ports-parser.spec.ts @@ -1,42 +1,50 @@ import test from 'ava'; +import type { ExecutionContext } from 'ava'; import { Scalar, YAMLMap } from 'yaml'; import { extractPublishedPortValue, parsePortsRange } from '../../src/util/service-ports-parser.js'; -test('extractPublishedPortValue should return port from scalar value with no IP', (t) => { +// @ts-ignore TS2349 +test('extractPublishedPortValue should return port from scalar value with no IP', (t: ExecutionContext) => { const scalarNode = new Scalar('8080:9000'); const result = extractPublishedPortValue(scalarNode); t.is(result, '8080'); }); -test('extractPublishedPortValue should return correct port from scalar value with IP', (t) => { +// @ts-ignore TS2349 +test('extractPublishedPortValue should return correct port from scalar value with IP', (t: ExecutionContext) => { const scalarNode = new Scalar('127.0.0.1:3000'); const result = extractPublishedPortValue(scalarNode); t.is(result, '3000'); }); -test('extractPublishedPortValue should return published port from map node', (t) => { +// @ts-ignore TS2349 +test('extractPublishedPortValue should return published port from map node', (t: ExecutionContext) => { const mapNode = new YAMLMap(); mapNode.set('published', '8080'); const result = extractPublishedPortValue(mapNode); t.is(result, '8080'); }); -test('extractPublishedPortValue should return empty string for unknown node type', (t) => { +// @ts-ignore TS2349 +test('extractPublishedPortValue should return empty string for unknown node type', (t: ExecutionContext) => { const result = extractPublishedPortValue({}); t.is(result, ''); }); -test('parsePortsRange should return array of ports for a range', (t) => { +// @ts-ignore TS2349 +test('parsePortsRange should return array of ports for a range', (t: ExecutionContext) => { t.deepEqual(parsePortsRange('3000-3002'), ['3000', '3001', '3002']); t.deepEqual(parsePortsRange('3000-3000'), ['3000']); }); -test('parsePortsRange should return single port when no range is specified', (t) => { +// @ts-ignore TS2349 +test('parsePortsRange should return single port when no range is specified', (t: ExecutionContext) => { const result = parsePortsRange('8080'); t.deepEqual(result, ['8080']); }); -test('parsePortsRange should return empty array for invalid range', (t) => { +// @ts-ignore TS2349 +test('parsePortsRange should return empty array for invalid range', (t: ExecutionContext) => { t.deepEqual(parsePortsRange('$TEST'), []); t.deepEqual(parsePortsRange('$TEST-3002'), []); t.deepEqual(parsePortsRange('3000-$TEST'), []); @@ -44,6 +52,7 @@ test('parsePortsRange should return empty array for invalid range', (t) => { t.deepEqual(parsePortsRange('3000-$TEST-$TEST-5000'), []); }); -test('parsePortsRange should return empty array when start port is greater than end port', (t) => { +// @ts-ignore TS2349 +test('parsePortsRange should return empty array when start port is greater than end port', (t: ExecutionContext) => { t.deepEqual(parsePortsRange('3005-3002'), []); });