From 78853ebd14b1a4d9be04828f9152c2adf9fabc0e Mon Sep 17 00:00:00 2001 From: Thomas Lindner <5178550+tclindner@users.noreply.github.com> Date: Sun, 27 Feb 2022 12:34:15 -0600 Subject: [PATCH] Ts part 14 (#576) * Update esbuild.config.js * Add @types/node * Reporter * Update tsconfig.json * Tests * Define more types * Add build to github workflows * Update tsconfig.json --- .github/workflows/ci.yml | 1 + .github/workflows/prerelease.yml | 1 + .github/workflows/release.yml | 1 + esbuild.config.js | 60 +++-- package-lock.json | 9 +- package.json | 7 +- src/Config.ts | 32 ++- src/NpmPackageJsonLint.ts | 53 ++-- src/Parser.ts | 93 ------- src/Reporter.ts | 63 ++--- src/Rules.ts | 64 ++--- src/cli.ts | 4 +- src/config/ConfigValidator.ts | 7 +- src/config/applyExtendsIfSpecified.ts | 6 +- src/file-parser.ts | 83 ++++++ src/linter/linter.ts | 236 ++++++++++++------ src/linter/results-helper.ts | 42 ++-- src/rules/bin-type.ts | 3 +- src/rules/description-format.ts | 9 +- src/rules/no-absolute-version-dependencies.ts | 10 +- src/rules/no-duplicate-properties.ts | 4 +- src/rules/valid-values-author.ts | 13 +- src/rules/valid-values-engines.ts | 15 +- src/rules/valid-values-license.ts | 10 +- src/rules/valid-values-name-scope.ts | 9 +- src/rules/valid-values-private.ts | 11 +- src/rules/valid-values-publishConfig.ts | 15 +- src/types/lint-function.ts | 29 +++ src/types/lint-result.ts | 3 + src/types/package-json-linting-result.ts | 24 ++ src/utils/getIgnorer.ts | 11 +- src/validators/dependency-audit.ts | 6 +- src/validators/valid-values.ts | 14 +- test/integration/cli.test.js | 4 +- test/unit/Reporter.test.ts | 22 +- test/unit/Rules.test.ts | 8 +- test/unit/config.test.ts | 2 +- test/unit/config/ConfigValidator.test.ts | 2 +- .../{Parser.test.ts => file-parser.test.ts} | 10 +- test/unit/linter/linter.test.ts | 91 +++++-- test/unit/linter/results-helper.test.ts | 15 +- .../rules/no-duplicate-properties.test.ts | 4 +- test/unit/rules/valid-values-license.test.ts | 2 +- test/unit/rules/valid-values-private.test.ts | 2 +- tsconfig.json | 7 +- 45 files changed, 687 insertions(+), 430 deletions(-) delete mode 100755 src/Parser.ts create mode 100755 src/file-parser.ts create mode 100644 src/types/lint-function.ts create mode 100644 src/types/lint-result.ts create mode 100644 src/types/package-json-linting-result.ts rename test/unit/{Parser.test.ts => file-parser.test.ts} (72%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6df70dce..3ea1d022 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,4 +38,5 @@ jobs: with: node-version: ${{ matrix.node }} - run: npm ci --no-progress + - run: npm run build - run: npm run test:ci diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index bdb323f7..c1062cba 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -14,6 +14,7 @@ jobs: node-version: '14' registry-url: 'https://registry.npmjs.org' - run: npm ci --no-progress --production + - run: npm run build - run: npm version --no-push --no-git-tag-version --yes ${{ github.event.release.tag_name }} - run: npm publish --tag next env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dc86ff2b..97e3af88 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ jobs: node-version: '14' registry-url: 'https://registry.npmjs.org' - run: npm ci --no-progress --production + - run: npm run build - run: npm version --no-push --no-git-tag-version --yes ${{ github.event.release.tag_name }} - run: npm publish --tag latest env: diff --git a/esbuild.config.js b/esbuild.config.js index 66a5977e..8ba2bd91 100644 --- a/esbuild.config.js +++ b/esbuild.config.js @@ -1,20 +1,50 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies -const esbuild = require('esbuild'); +/* eslint-disable @typescript-eslint/no-var-requires, import/no-extraneous-dependencies */ +const esbuild = require('esbuild'); // Automatically exclude all node_modules from the bundled version -// eslint-disable-next-line import/no-extraneous-dependencies, @typescript-eslint/no-var-requires const {nodeExternalsPlugin} = require('esbuild-node-externals'); +const {readdirSync} = require('fs'); +const path = require('path'); + +const rulesDirectory = path.join(__dirname, 'src', 'rules'); +const bundle = true; +const minify = true; +const platform = 'node'; +const sourcemap = true; +const target = 'node12'; +const plugins = [nodeExternalsPlugin()]; + +readdirSync(rulesDirectory).forEach((file) => { + const ruleFilePath = path.join(rulesDirectory, file); + const beginIndex = 0; + const endIndex = -3; + const ruleFileNameWithoutExtension = file.slice(beginIndex, endIndex); + + esbuild + .build({ + entryPoints: [ruleFilePath], + outfile: `dist/rules/${ruleFileNameWithoutExtension}.js`, + bundle, + minify, + platform, + sourcemap: false, + target, + plugins, + }) + // eslint-disable-next-line unicorn/no-process-exit + .catch(() => process.exit(1)); +}); esbuild .build({ entryPoints: ['./src/api.ts'], outfile: 'dist/api.js', - bundle: true, - minify: true, - platform: 'node', - sourcemap: true, - target: 'node14', - plugins: [nodeExternalsPlugin()], + bundle, + minify, + platform, + sourcemap, + target, + plugins, }) // eslint-disable-next-line unicorn/no-process-exit .catch(() => process.exit(1)); @@ -23,12 +53,12 @@ esbuild .build({ entryPoints: ['./src/cli.ts'], outfile: 'dist/cli.js', - bundle: true, - minify: true, - platform: 'node', - sourcemap: true, - target: 'node14', - plugins: [nodeExternalsPlugin()], + bundle, + minify, + platform, + sourcemap, + target, + plugins, }) // eslint-disable-next-line unicorn/no-process-exit .catch(() => process.exit(1)); diff --git a/package-lock.json b/package-lock.json index 3c66c1ae..4971d7c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -949,9 +949,9 @@ "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=" }, "@types/node": { - "version": "17.0.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.16.tgz", - "integrity": "sha512-ydLaGVfQOQ6hI1xK2A5nVh8bl0OGoIfYMxPWHqqYe9bTkWCfqiVvZoh2I/QF2sNSkZzZyROBoTefIEI+PB6iIA==", + "version": "17.0.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz", + "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==", "dev": true }, "@types/normalize-package-data": { @@ -5156,8 +5156,7 @@ "type-fest": { "version": "2.12.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.12.0.tgz", - "integrity": "sha512-Qe5GRT+n/4GoqCNGGVp5Snapg1Omq3V7irBJB3EaKsp7HWDo5Gv2d/67gfNyV+d5EXD+x/RF5l1h4yJ7qNkcGA==", - "dev": true + "integrity": "sha512-Qe5GRT+n/4GoqCNGGVp5Snapg1Omq3V7irBJB3EaKsp7HWDo5Gv2d/67gfNyV+d5EXD+x/RF5l1h4yJ7qNkcGA==" }, "typedarray-to-buffer": { "version": "3.1.5", diff --git a/package.json b/package.json index df6d93a0..109a6275 100644 --- a/package.json +++ b/package.json @@ -30,12 +30,14 @@ "main": "dist/api.js", "types": "index.d.ts", "scripts": { - "build": "node esbuild.config.js", + "build": "npm run esbuild && npm run tsc", + "esbuild": "node esbuild.config.js", "eslint": "eslint . --format=node_modules/eslint-formatter-pretty", "npmpackagejsonlint": "node src/cli.js ./package.json", "lint": "npm run eslint && npm run npmpackagejsonlint", "test": "jest", - "test:ci": "jest --runInBand" + "test:ci": "jest --runInBand", + "tsc": "tsc --project tsconfig.json" }, "dependencies": { "ajv": "^8.10.0", @@ -58,6 +60,7 @@ }, "devDependencies": { "@types/jest": "^27.4.1", + "@types/node": "^17.0.21", "@typescript-eslint/eslint-plugin": "^5.12.1", "esbuild": "^0.14.23", "esbuild-node-externals": "^1.4.1", diff --git a/src/Config.ts b/src/Config.ts index bf350685..381e513f 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -3,6 +3,7 @@ import {validateRules} from './config/ConfigValidator'; import {transform} from './config/cosmicConfigTransformer'; import {applyExtendsIfSpecified} from './config/applyExtendsIfSpecified'; import {applyOverrides} from './config/applyOverrides'; +import {Rules} from './rules'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:Config'); @@ -14,30 +15,37 @@ const noRules = 0; * @class */ export class Config { + /** + * The user passed config object. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any config: any; + /** + * The current working directory. + */ cwd: string; + /** + * The user passed configFile path. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any configFile: any; + /** + * The base directory that config should be pulled from. + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any configBaseDirectory: any; + /** + * Rules object + */ // eslint-disable-next-line @typescript-eslint/no-explicit-any rules: any; - /** - * Constructor - * - * @param {string} cwd The current working directory. - * @param {Object} config The user passed config object. - * @param {string} configFile The user passed configFile path. - * @param {string} configBaseDirectory The base directory that config should be pulled from. - * @param {Object} rules Rules object - */ - constructor(cwd, config, configFile, configBaseDirectory, rules) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + constructor(cwd: string, config: any, configFile: any, configBaseDirectory: any, rules: Rules) { if (config) { this.config = applyExtendsIfSpecified(config, 'PassedConfig'); } @@ -51,12 +59,12 @@ export class Config { /** * Gets the config for a file. * - * @param {string} filePath File path of the file being linted. + * @param filePath File path of the file being linted. * @returns {Object} A config object. * @memberof Config */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - getConfigForFile(filePath) { + getConfigForFile(filePath: string) { debug(`Getting config for ${filePath}`); const filePathToSearch = filePath; diff --git a/src/NpmPackageJsonLint.ts b/src/NpmPackageJsonLint.ts index 70ff49e3..15f6c6a9 100644 --- a/src/NpmPackageJsonLint.ts +++ b/src/NpmPackageJsonLint.ts @@ -1,11 +1,14 @@ import isPlainObj from 'is-plain-obj'; import slash from 'slash'; +import {PackageJson} from 'type-fest'; import {Config} from './Config'; -import {Rules} from './Rules'; -import {executeOnPackageJsonFiles, executeOnPackageJsonObject} from './linter/linter'; +import {Rules} from './rules'; +import {executeOnPackageJsonFiles, executeOnPackageJsonObject, OverallLintingResult} from './linter/linter'; import {getFileList} from './utils/getFileList'; import {getIgnorer} from './utils/getIgnorer'; import {Severity} from './types/severity'; +import {PackageJsonFileLintingResult} from './types/package-json-linting-result'; +import {LintIssue} from './lint-issue'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:NpmPackageJsonLint'); @@ -17,29 +20,27 @@ const noIssues = 0; /** * Checks if the given issue is an error issue. * - * @param {LintIssue} issue npm-package-json-lint issue - * @returns {boolean} True if error, false if warning. + * @param issue A {@link LintIssue} object + * @returns True if error, false if warning. * @private */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const isIssueAnError = (issue) => issue.severity === Severity.Error; +const isIssueAnError = (issue: LintIssue): boolean => issue.severity === Severity.Error; -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const isPackageJsonObjectValid = (packageJsonObject) => isPlainObj(packageJsonObject); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const isPackageJsonObjectValid = (packageJsonObject: PackageJson | any): boolean => isPlainObj(packageJsonObject); -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const areRequiredOptionsValid = (packageJsonObject, patterns) => +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const areRequiredOptionsValid = (packageJsonObject: PackageJson | any, patterns: string[]): boolean => (!patterns && !isPackageJsonObjectValid(packageJsonObject)) || (patterns && (packageJsonObject || isPackageJsonObjectValid(packageJsonObject))); /** * Filters results to only include errors. * - * @param {LintResult[]} results The results to filter. - * @returns {LintResult[]} The filtered results. + * @param results The results to filter. + * @returns The filtered results. */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const getErrorResults = (results) => { +const getErrorResults = (results: PackageJsonFileLintingResult[]): PackageJsonFileLintingResult[] => { const filtered = []; results.forEach((result) => { @@ -60,16 +61,6 @@ const getErrorResults = (results) => { return filtered; }; -/** - * CLIEngine configuration object - * - * @typedef {Object} NpmPackageJsonLint - * @property {string} configFile The configuration file to use. - * @property {string} cwd The value to use for the current working directory. - * @property {boolean} useConfigFiles False disables use of .npmpackagejsonlintrc.json files, npmpackagejsonlint.config.js files, and npmPackageJsonLintConfig object in package.json file. - * @property {Object} rules An object of rules to use. - */ - export interface NpmPackageJsonLintOptions { cwd?: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -88,10 +79,6 @@ export interface NpmPackageJsonLintOptions { fix?: boolean; } -/** - * Public CLIEngine class - * @class - */ export class NpmPackageJsonLint { cwd: string; @@ -117,7 +104,7 @@ export class NpmPackageJsonLint { /** * constructor - * @param {NpmPackageJsonLint} options The options for the CLIEngine. + * @param options An instance of the {@link NpmPackageJsonLintOptions} options object. * @constructor */ constructor(options: NpmPackageJsonLintOptions) { @@ -154,11 +141,9 @@ export class NpmPackageJsonLint { /** * Runs the linter using the config specified in the constructor * - * @returns {LinterResult} The results {@link LinterResult} from linting a collection of package.json files. - * @memberof NpmPackageJsonLint + * @returns The results {@link OverallLintingResult} from linting a collection of package.json files. */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - lint() { + lint(): OverallLintingResult { debug('Starting lint'); if (areRequiredOptionsValid(this.packageJsonObject, this.patterns)) { @@ -168,7 +153,7 @@ export class NpmPackageJsonLint { } const ignorer = getIgnorer(this.cwd, this.ignorePath); - let linterOutput; + let linterOutput: OverallLintingResult; if (this.patterns) { debug('Linting using patterns'); diff --git a/src/Parser.ts b/src/Parser.ts deleted file mode 100755 index 4d711b43..00000000 --- a/src/Parser.ts +++ /dev/null @@ -1,93 +0,0 @@ -import fs from 'fs'; -import stripComments from 'strip-json-comments'; - -/** - * Require JavaScript file - * - * @param {String} fileName String file path of file to load - * @return {Object} Config object from file. - * @throws {Error} If the file cannot be read. - */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, import/no-dynamic-require, global-require -const requireFile = (fileName) => require(fileName); - -/** - * Sychronously reads file from file system - * - * @param {String} fileName String file path of file to load - * @return {String} File contents with BOM removed. - * @throws {Error} If the file cannot be read. - */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const readFile = (fileName) => fs.readFileSync(fileName, 'utf8').replace(/^\uFEFF/, ''); - -/** - * Helper method for throwing errors when file fails to load. - * - * @param {String} fileName Name of the file that failed to load. - * @param {Object} err Error object - * @returns {Undefined} No return - * @throws {Error} - */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const handleError = (fileName, err) => { - throw new Error(`Failed to read config file: ${fileName}. \nError: ${err.message}`); -}; - -/** - * Public Parser class - * @class - */ -export const Parser = { - sourceSymbol: Symbol('JSON source'), - - /** - * Parse a JSON file - * - * @param {String} fileName String file path of file to load - * @return {Object} Valid JavaScript object - * @static - */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - parseJsonFile(fileName) { - let json = {}; - let fileContents = ''; - - try { - fileContents = readFile(fileName); - - json = JSON.parse(stripComments(fileContents)); - } catch (error) { - handleError(fileName, error); - } - - Object.defineProperty(json, Parser.sourceSymbol, { - value: fileContents, - enumerable: false, - writable: false, - configurable: false, - }); - - return json; - }, - - /** - * Parse a JavaScript file - * - * @param {String} fileName String file path of file to load - * @return {Object} Valid JavaScript object - * @static - */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - parseJavaScriptFile(fileName) { - let obj = {}; - - try { - obj = requireFile(fileName); - } catch (error) { - handleError(fileName, error); - } - - return obj; - }, -}; diff --git a/src/Reporter.ts b/src/Reporter.ts index 0e0b4b9d..d9c2ae00 100755 --- a/src/Reporter.ts +++ b/src/Reporter.ts @@ -1,5 +1,6 @@ import chalk from 'chalk'; import plur from 'plur'; +import {LintIssue} from './lint-issue'; const zeroIssues = 0; const oneFile = 1; @@ -7,12 +8,10 @@ const oneFile = 1; /** * Prints issues to console * - * @param {LintIssue[]} issues An array of LintIssues - * @returns {Undefined} No return - * @private + * @param issues An array of LintIssues + * @internal */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const printResultSetIssues = (issues) => { +const printResultSetIssues = (issues: LintIssue[]): void => { // eslint-disable-next-line no-restricted-syntax for (const issue of issues) { // eslint-disable-next-line no-console @@ -23,13 +22,12 @@ const printResultSetIssues = (issues) => { /** * Print results for an individual package.json file linting * - * @param {LintResult} resultSet Result object from a given file's lint result - * @param {Boolean} quiet True suppress warnings, false show warnings - * @returns {Undefined} No results - * @private + * @param resultSet Result object from a given file's lint result + * @param quiet True suppress warnings, false show warnings + * @internal */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const printIndividualResultSet = (resultSet, quiet) => { +const printIndividualResultSet = (resultSet, quiet: boolean): void => { const {filePath, issues, ignored, errorCount, warningCount} = resultSet; if (ignored) { @@ -57,13 +55,12 @@ const printIndividualResultSet = (resultSet, quiet) => { /** * Prints the overall total counts section * - * @param {Object} linterOutput Full results from linting. Includes an array of results and overall counts - * @param {Boolean} quiet True suppress warnings, false show warnings - * @returns {Undefined} No results - * @private + * @param linterOutput Full results from linting. Includes an array of results and overall counts + * @param quiet True suppress warnings, false show warnings + * @internal */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const printTotals = (linterOutput, quiet) => { +const printTotals = (linterOutput, quiet: boolean): void => { const {errorCount, warningCount, ignoreCount} = linterOutput; if (errorCount > zeroIssues || warningCount > zeroIssues) { @@ -83,28 +80,20 @@ const printTotals = (linterOutput, quiet) => { }; /** - * Public Reporter class - * @class + * Print results to console + * + * @param linterOutput An array of LintIssues + * @param quiet Flag indicating whether to print warnings. + * @internal */ -// eslint-disable-next-line unicorn/no-static-only-class -export class Reporter { - /** - * Print CLIEngine Output - * - * @param {Object} linterOutput An array of LintIssues - * @param {boolean} quiet Flag indicating whether to print warnings. - * @return {undefined} No return - * @static - */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - static write(linterOutput, quiet) { - // eslint-disable-next-line no-restricted-syntax - for (const result of linterOutput.results) { - printIndividualResultSet(result, quiet); - } +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export const write = (linterOutput, quiet: boolean): void => { + // eslint-disable-next-line no-restricted-syntax + for (const result of linterOutput.results) { + printIndividualResultSet(result, quiet); + } - if (linterOutput.results.length > oneFile) { - printTotals(linterOutput, quiet); - } + if (linterOutput.results.length > oneFile) { + printTotals(linterOutput, quiet); } -} +}; diff --git a/src/Rules.ts b/src/Rules.ts index 7ef7d575..4b1bf994 100755 --- a/src/Rules.ts +++ b/src/Rules.ts @@ -1,35 +1,39 @@ import chalk from 'chalk'; -import fs from 'fs'; +import {readdirSync} from 'fs'; import path from 'path'; +import {LintFunction} from './types/lint-function'; +import {RuleType} from './types/rule-type'; + +export interface Rule { + lint: LintFunction; + ruleType: RuleType; + minItems?: number; +} export class Rules { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - rules: any; + rules: Record; - /** - * Constructor - */ constructor() { this.rules = {}; } /** * Loads rules - * @return {Object} Set of rules + * + * @return Set of rules */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - load() { + load(): Record { const rulesDirectory = path.join(__dirname, 'rules'); try { - fs.readdirSync(rulesDirectory).forEach((file) => { + readdirSync(rulesDirectory).forEach((filePath) => { const beginIndex = 0; const endIndex = -3; - const ruleId = file.slice(beginIndex, endIndex); - const ruleModule = path.join(rulesDirectory, file); + // remove the file extension, e.g. `.js` + const ruleId = filePath.slice(beginIndex, endIndex); + const filePathToRuleModule = path.join(rulesDirectory, filePath); - // eslint-disable-next-line no-underscore-dangle - this._registerRule(ruleId, ruleModule); + this.registerRule(ruleId, filePathToRuleModule); }); return this.rules; @@ -39,12 +43,12 @@ export class Rules { } /** - * Loads a rule - * @param {String} ruleId Name of the rule - * @return {Object} Rule + * Loads a rule module + * + * @param ruleId Name of the rule + * @return Rule module */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - get(ruleId) { + get(ruleId: string): Rule { const rule = this.rules[ruleId]; if (typeof rule === 'undefined') { @@ -53,28 +57,28 @@ export class Rules { throw new Error(chalk.bold.red(errorMsg)); } - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(this.rules[ruleId]); + // eslint-disable-next-line import/no-dynamic-require, global-require, @typescript-eslint/no-var-requires + const ruleModule = require(this.rules[ruleId]); + + return ruleModule; } /** * Gets entire rule set * - * @returns {Object} Rule set + * @returns Rule set */ - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - getRules() { + getRules(): Record { return this.rules; } /** * Registers a rule in the rules object - * @param {String} ruleId Name of the rule - * @param {String} ruleModule Path to rule - * @return {undefined} No return + * + * @param ruleId Name of the rule + * @param filePathToRuleModule File path to rule */ - // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/explicit-function-return-type - _registerRule(ruleId, ruleModule) { - this.rules[ruleId] = ruleModule; + registerRule(ruleId: string, filePathToRuleModule: string): void { + this.rules[ruleId] = filePathToRuleModule; } } diff --git a/src/cli.ts b/src/cli.ts index 0b8b4333..24655e46 100755 --- a/src/cli.ts +++ b/src/cli.ts @@ -3,7 +3,7 @@ import chalk from 'chalk'; import meow from 'meow'; import {NpmPackageJsonLint} from './NpmPackageJsonLint'; -import {Reporter} from './Reporter'; +import {write} from './reporter'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:cli'); @@ -113,7 +113,7 @@ try { debug(`NpmPackageJsonLint.lint complete`); debug(`Reporter.write starting`); - Reporter.write(linterOutput, flags.quiet); + write(linterOutput, flags.quiet); debug(`Reporter.write complete`); if (linterOutput.warningCount > flags.maxWarnings) { diff --git a/src/config/ConfigValidator.ts b/src/config/ConfigValidator.ts index 27496a3f..1e1165bb 100755 --- a/src/config/ConfigValidator.ts +++ b/src/config/ConfigValidator.ts @@ -1,3 +1,4 @@ +import {RuleType} from '../types/rule-type'; import { isArrayRuleSchemaValid, isConfigObjectSchemaValid, @@ -93,17 +94,17 @@ const validateRule = (ruleModule: any, ruleName: any, userConfig: any, source: a if (ruleModule) { try { switch (ruleModule.ruleType) { - case 'array': { + case RuleType.Array: { isArrayRuleConfigValid(userConfig, ruleModule.minItems); break; } - case 'object': { + case RuleType.Object: { isObjectRuleConfigValid(userConfig); break; } - case 'optionalObject': { + case RuleType.OptionalObject: { isOptionalObjRuleConfigValid(userConfig); break; diff --git a/src/config/applyExtendsIfSpecified.ts b/src/config/applyExtendsIfSpecified.ts index 85ce412d..de7e7c08 100644 --- a/src/config/applyExtendsIfSpecified.ts +++ b/src/config/applyExtendsIfSpecified.ts @@ -1,5 +1,5 @@ import path from 'path'; -import {Parser} from '../Parser'; +import {parseJavaScriptFile, parseJsonFile} from '../file-parser'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:applyExtendsIfSpecified'); @@ -111,11 +111,11 @@ const loadConfigFile = (filePath: any): any => { switch (path.extname(filePath)) { case '.js': - config = Parser.parseJavaScriptFile(filePath); + config = parseJavaScriptFile(filePath); break; case '.json': - config = Parser.parseJsonFile(filePath); + config = parseJsonFile(filePath); break; default: diff --git a/src/file-parser.ts b/src/file-parser.ts new file mode 100755 index 00000000..e0c0853d --- /dev/null +++ b/src/file-parser.ts @@ -0,0 +1,83 @@ +import fs from 'fs'; +import stripComments from 'strip-json-comments'; + +/** + * Require JavaScript file + * + * @param fileName String file path of file to load + * @internal + */ +// eslint-disable-next-line import/no-dynamic-require, global-require +const requireFile = (fileName: string): NodeRequire => require(fileName); + +/** + * Sychronously reads file from file system + * + * @param fileName String file path of file to load + * @internal + */ +const readFile = (fileName: string): string => fs.readFileSync(fileName, 'utf8').replace(/^\uFEFF/, ''); + +/** + * Helper method for throwing errors when file fails to load. + * + * @param fileName Name of the file that failed to load. + * @param err Error object + * @throws {Error} + * @internal + */ +const handleError = (fileName: string, err: Error): void => { + throw new Error(`Failed to read config file: ${fileName}. \nError: ${err.message}`); +}; + +export const sourceSymbol = Symbol('JSON source'); + +/** + * Parse a JSON file + * + * @param fileName String file path of file to load + * @return Valid JSON + * @internal + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const parseJsonFile = (fileName: string): Record => { + let json = {}; + let fileContents = ''; + + try { + fileContents = readFile(fileName); + + json = JSON.parse(stripComments(fileContents)); + } catch (error) { + handleError(fileName, error); + } + + Object.defineProperty(json, sourceSymbol, { + value: fileContents, + enumerable: false, + writable: false, + configurable: false, + }); + + return json; +}; + +/** + * Parse a JavaScript file + * + * @param fileName String file path of file to load + * @return Valid JavaScript object + * @internal + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const parseJavaScriptFile = (fileName: string): Record => { + let obj = {}; + + try { + obj = requireFile(fileName); + } catch (error) { + handleError(fileName, error); + } + + return obj; +}; diff --git a/src/linter/linter.ts b/src/linter/linter.ts index 87f8be7a..62745c40 100644 --- a/src/linter/linter.ts +++ b/src/linter/linter.ts @@ -1,54 +1,75 @@ import path from 'path'; -import {Parser} from '../Parser'; +import {PackageJson} from 'type-fest'; +import {Ignore} from 'ignore'; +import {Rules} from '../rules'; +import {parseJsonFile} from '../file-parser'; +import {LintIssue} from '../lint-issue'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; -import {aggregateCountsPerFile, aggregateOverallCounts} from './results-helper'; +import {aggregateCountsPerFile, aggregateOverallCounts, OverallAggregatedResultCounts} from './results-helper'; +import {Config} from '../Config'; +import {PackageJsonFileLintingResult} from '../types/package-json-linting-result'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:linter'); -/** - * A package.json file linting result. - * @typedef {Object} FileLintResult - * @property {string} filePath The path to the file that was linted. - * @property {LintIssue[]} issues An array of LintIssues from the run. - * @property {boolean} ignored A flag indicated whether the file was ignored or not. - * @property {number} errorCount Number of errors for the package.json file. - * @property {number} warningCount Number of warnings for the package.json file. - */ +export interface CreateResultObjectOptions { + /** + * The current working directory. + */ + cwd: string; + /** + * An optional string representing the package.json file. + */ + fileName: string; + /** + * A flag indicating that the file was skipped. + */ + ignored: boolean; + /** + * A list of issues. + */ + issues: LintIssue[]; + /** + * Number of errors. + */ + errorCount: number; + /** + * Number of warnings. + */ + warningCount: number; +} /** * Creates a results object. * - * @param {string} cwd The current working directory. - * @param {string} fileName An optional string representing the package.json file. - * @param {boolean} ignored A flag indicating that the file was skipped. - * @param {LintIssue[]} issues A list of issues. - * @param {number} errorCount Number of errors. - * @param {number} warningCount Number of warnings. - * @returns {FileLintResult} The lint results {@link FileLintResult} for the package.json file. - * @private + * @param options A {@link CreateResultObjectOptions} object + * @returns The lint results {@link PackageJsonFileLintingResult} for the package.json file. + * @internal */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const createResultObject = ({cwd, fileName, ignored, issues, errorCount, warningCount}) => ({ - filePath: `./${path.relative(cwd, fileName)}`, - issues, - ignored, - errorCount, - warningCount, -}); +const createResultObject = (options: CreateResultObjectOptions): PackageJsonFileLintingResult => { + const {cwd, fileName, ignored, issues, errorCount, warningCount} = options; + + return { + filePath: `./${path.relative(cwd, fileName)}`, + issues, + ignored, + errorCount, + warningCount, + }; +}; /** * Runs configured rules against the provided package.json object. * - * @param {Object} packageJsonData Valid package.json data - * @param {Object} configObj Configuration object - * @param {Object} rules Object of rule definitions - * @return {LintIssue[]} An array of {@link LintIssue} objects. - * @private + * @param packageJsonData Valid package.json data + * @param configObj Configuration object + * @param rules Object of rule definitions + * @return An array of {@link LintIssue} objects. + * @internal */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const lint = (packageJsonData, configObj, rules) => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const lint = (packageJsonData: any, configObj, rules: Rules): LintIssue[] => { const lintIssues = []; // eslint-disable-next-line no-restricted-syntax, guard-for-in @@ -56,7 +77,7 @@ const lint = (packageJsonData, configObj, rules) => { const ruleModule = rules.get(rule); let severity = Severity.Off; - let ruleConfig = {}; + let ruleConfig; if (ruleModule.ruleType === RuleType.Array || ruleModule.ruleType === RuleType.Object) { severity = typeof configObj[rule] === 'string' && configObj[rule] === 'off' ? configObj[rule] : configObj[rule][0]; @@ -90,16 +111,23 @@ const lint = (packageJsonData, configObj, rules) => { /** * Processes package.json object * - * @param {string} cwd The current working directory. - * @param {Object} packageJsonObj An object representation of a package.json file. - * @param {Object} config A config object. - * @param {String} fileName An optional string representing the package.json file. - * @param {Object} rules An instance of `Rules`. - * @returns {FileLintResult} A {@link FileLintResult} object with the result of linting a package.json file. - * @private + * @param cwd The current working directory. + * @param packageJsonObj An object representation of a package.json file. + * @param config A config object. + * @param fileName An optional string representing the package.json file. + * @param rules An instance of `Rules`. + * @returns A {@link PackageJsonFileLintingResult} object with the result of linting a package.json file. + * @internal */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const processPackageJsonObject = (cwd, packageJsonObj, config, fileName, rules) => { +const processPackageJsonObject = ( + cwd: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonObj: PackageJson | any, + // TODO: Type + config, + fileName: string, + rules: Rules +): PackageJsonFileLintingResult => { const lintIssues = lint(packageJsonObj, config, rules); const counts = aggregateCountsPerFile(lintIssues); const result = createResultObject({ @@ -117,42 +145,74 @@ const processPackageJsonObject = (cwd, packageJsonObj, config, fileName, rules) /** * Processes a package.json file. * - * @param {string} cwd The current working directory. - * @param {string} fileName The filename of the file being linted. - * @param {Object} config A config object. - * @param {Object} rules An instance of `Rules`. - * @returns {FileLintResult} A {@link FileLintResult} object with the result of linting a package.json file. - * @private + * @param cwd The current working directory. + * @param fileName The filename of the file being linted. + * @param config A config object. + * @param rules An instance of `Rules`. + * @returns A {@link PackageJsonFileLintingResult} object with the result of linting a package.json file. + * @internal */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -const processPackageJsonFile = (cwd, fileName, config, rules) => { - const packageJsonObj = Parser.parseJsonFile(path.resolve(fileName)); +// TODO: Type +const processPackageJsonFile = (cwd: string, fileName: string, config, rules: Rules): PackageJsonFileLintingResult => { + const packageJsonObj = parseJsonFile(path.resolve(fileName)); return processPackageJsonObject(cwd, packageJsonObj, config, fileName, rules); }; -/** - * Linting results for a collection of package.json files. - * @typedef {Object} LinterResult - * @property {FileLintResult[]} results An array of LintIssues from the run. - * @property {number} ignoreCount A flag indicated whether the file was ignored or not. - * @property {number} errorCount Number of errors for the package.json file. - * @property {number} warningCount Number of warnings for the package.json file. - */ +export interface LinterResult { + results: LintIssue[]; + ignoreCount: number; + /** + * Number of errors for the package.json file. + */ + errorCount: number; + /** + * Number of warnings for the package.json file. + */ + warningCount: number; +} + +export interface ExecuteOnPackageJsonObjectOptions { + /** + * The current working directory. + */ + cwd: string; + /** + * An object representation of a package.json file. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonObject: PackageJson | any; + /** + * An optional string representing the texts filename. + */ + filename?: string; + /** + * An instance of the `ignore` module. + */ + ignorer: Ignore; + /** + * An instance of {@Config}. + */ + configHelper: Config; + /** + * An instance of {@link Rules} + */ + rules: Rules; +} + +export interface OverallLintingResult extends OverallAggregatedResultCounts { + results: PackageJsonFileLintingResult[]; +} /** * Executes linter on package.json object * - * @param {string} cwd The current working directory. - * @param {Object} packageJsonObj An object representation of a package.json file. - * @param {string} filename An optional string representing the texts filename. - * @param {Object} ignorer An instance of the `ignore` module. - * @param {Object} configHelper An instance of `Config`. - * @param {Object} rules An instance of `Rules`. - * @returns {LinterResult} The results {@link LinterResult} from linting a collection of package.json files. + * @param options A {@link ExecuteOnPackageJsonObjectOptions} object + * @returns The results {@link OverallLintingResult} from linting a collection of package.json files. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const executeOnPackageJsonObject = ({cwd, packageJsonObject, filename, ignorer, configHelper, rules}): any => { +export const executeOnPackageJsonObject = (options: ExecuteOnPackageJsonObjectOptions): OverallLintingResult => { + const {cwd, packageJsonObject, filename, ignorer, configHelper, rules} = options; + debug('executing on package.json object'); const results = []; @@ -197,17 +257,37 @@ export const executeOnPackageJsonObject = ({cwd, packageJsonObject, filename, ig }; }; +export interface ExecuteOnPackageJsonFilesOptions { + /** + * The current working directory. + */ + cwd: string; + /** + * An array of files and directory names. + */ + fileList: string[]; + /** + * An instance of the `ignore` module. + */ + ignorer: Ignore; + /** + * An instance of {@Config}. + */ + configHelper: Config; + /** + * An instance of {@link Rules} + */ + rules: Rules; +} + /** * Executes the current configuration on an array of file and directory names. - * @param {string} cwd The current working directory. - * @param {string[]} fileList An array of files and directory names. - * @param {Object} ignorer An instance of the `ignore` module. - * @param {Object} configHelper An instance of `Config`. - * @param {Object} rules An instance of `Rules`. - * @returns {LinterResult} The results {@link LinterResult} from linting a collection of package.json files. + * @param options A {@link ExecuteOnPackageJsonFilesOptions} object + * @returns The results {@link OverallLintingResult} from linting a collection of package.json files. */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const executeOnPackageJsonFiles = ({cwd, fileList, ignorer, configHelper, rules}): any => { +export const executeOnPackageJsonFiles = (options: ExecuteOnPackageJsonFilesOptions): OverallLintingResult => { + const {cwd, fileList, ignorer, configHelper, rules} = options; + debug('executing on package.json files'); const results = fileList.map((filePath) => { const relativeFilePath = path.relative(cwd, filePath); diff --git a/src/linter/results-helper.ts b/src/linter/results-helper.ts index 9ba97726..eddea519 100644 --- a/src/linter/results-helper.ts +++ b/src/linter/results-helper.ts @@ -1,4 +1,6 @@ +import {LintIssue} from '../lint-issue'; import {Severity} from '../types/severity'; +import {PackageJsonFileLintingResult} from '../types/package-json-linting-result'; /** * A result count object for a files. @@ -7,14 +9,18 @@ import {Severity} from '../types/severity'; * @property {number} warningCount Number of warnings for a file result. */ +export interface PackageJsonFileAggregatedResultCounts { + errorCount: number; + warningCount: number; +} + /** * Aggregates the count of errors and warning for a package.json file. * - * @param {LintIssue[]} issues Array of {@link LintIssue} objects from a package.json file. - * @returns {FileResultCounts} Counts object {@link FileResultCounts}. + * @param issues Array of {@link LintIssue} objects from a package.json file. + * @returns Counts object {@link PackageJsonFileAggregatedResultCounts}. */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const aggregateCountsPerFile = (issues) => { +export const aggregateCountsPerFile = (issues: LintIssue[]): PackageJsonFileAggregatedResultCounts => { const incrementOne = 1; // eslint-disable-next-line unicorn/no-array-reduce @@ -36,22 +42,28 @@ export const aggregateCountsPerFile = (issues) => { ); }; -/** - * A result count object for all files. - * @typedef {Object} OverallResultCounts - * @property {number} ignoreCount Total number of ignored files. - * @property {number} errorCount Total number of errors. - * @property {number} warningCount Total number of warnings. - */ +export interface OverallAggregatedResultCounts { + /** + * Total number of ignored files. + */ + ignoreCount: number; + /** + * Total number of errors. + */ + errorCount: number; + /** + * Total number of warnings. + */ + warningCount: number; +} /** * Aggregates the count of errors and warnings for all package.json files. * - * @param {FileLintResult[]} results Array of {@link FileLintResult} objects from all package.json files. - * @returns {OverallResultCounts} Counts object {@link OverallResultCounts} + * @param results Array of {@link PackageJsonFileLintingResult} objects from all package.json files. + * @returns Counts object {@link OverallAggregatedResultCounts} */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const aggregateOverallCounts = (results) => +export const aggregateOverallCounts = (results: PackageJsonFileLintingResult[]): OverallAggregatedResultCounts => // eslint-disable-next-line unicorn/no-array-reduce results.reduce( (counts, result) => ({ diff --git a/src/rules/bin-type.ts b/src/rules/bin-type.ts index 2fb3a2e4..c1e25e2c 100755 --- a/src/rules/bin-type.ts +++ b/src/rules/bin-type.ts @@ -3,6 +3,7 @@ import {isObject, isString} from '../validators/type'; import {LintIssue} from '../lint-issue'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; +import {LintResult} from '../types/lint-result'; const lintId = 'bin-type'; const nodeName = 'bin'; @@ -11,7 +12,7 @@ const message = 'Type should be either a string or an Object'; export const ruleType = RuleType.Standard; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity): LintIssue | null => { +export const lint = (packageJsonData: PackageJson | any, severity: Severity): LintResult => { if (!isString(packageJsonData, nodeName) && !isObject(packageJsonData, nodeName)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/description-format.ts b/src/rules/description-format.ts index 18089521..e69e581b 100755 --- a/src/rules/description-format.ts +++ b/src/rules/description-format.ts @@ -3,14 +3,19 @@ import {isString} from '../validators/type'; import {LintIssue} from '../lint-issue'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; +import {LintResult} from '../types/lint-result'; const lintId = 'description-format'; const nodeName = 'description'; export const ruleType = RuleType.Object; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, config: any): LintIssue | null => { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + config: Record +): LintResult => { if (!packageJsonData.hasOwnProperty(nodeName)) { return null; } diff --git a/src/rules/no-absolute-version-dependencies.ts b/src/rules/no-absolute-version-dependencies.ts index 739615c8..d4cac56d 100755 --- a/src/rules/no-absolute-version-dependencies.ts +++ b/src/rules/no-absolute-version-dependencies.ts @@ -3,6 +3,8 @@ import {areVersionsAbsolute} from '../validators/dependency-audit'; import {LintIssue} from '../lint-issue'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; +import {OptionalObjectRuleConfig} from '../types/lint-function'; +import {LintResult} from '../types/lint-result'; const lintId = 'no-absolute-version-dependencies'; const nodeName = 'dependencies'; @@ -10,8 +12,12 @@ const message = 'You are using an invalid version range. Please do not use absol export const ruleType = RuleType.OptionalObject; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, config: any): LintIssue | null => { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + config: OptionalObjectRuleConfig +): LintResult => { if (packageJsonData.hasOwnProperty(nodeName) && areVersionsAbsolute(packageJsonData, nodeName, config)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/no-duplicate-properties.ts b/src/rules/no-duplicate-properties.ts index 8ad00e62..c3416177 100644 --- a/src/rules/no-duplicate-properties.ts +++ b/src/rules/no-duplicate-properties.ts @@ -1,5 +1,5 @@ import {PackageJson} from 'type-fest'; -import {Parser} from '../Parser'; +import {sourceSymbol} from '../file-parser'; import {findDuplicatePropNames} from '../validators/property'; import {LintIssue} from '../lint-issue'; import {RuleType} from '../types/rule-type'; @@ -16,7 +16,7 @@ export const lint = (packageJsonData: PackageJson | any, severity: Severity): Li * If we send package json straight to npm-package-json-lint, fallback to empty string. * Because we already lose information about duplicate properties. */ - const source = packageJsonData[Parser.sourceSymbol] || ''; + const source = packageJsonData[sourceSymbol] || ''; const dupProps = findDuplicatePropNames(source); if (dupProps.length > 0) { diff --git a/src/rules/valid-values-author.ts b/src/rules/valid-values-author.ts index 51708000..3cd2c709 100755 --- a/src/rules/valid-values-author.ts +++ b/src/rules/valid-values-author.ts @@ -1,5 +1,6 @@ import {PackageJson} from 'type-fest'; import {LintIssue} from '../lint-issue'; +import {LintResult} from '../types/lint-result'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; import {isString, isObject} from '../validators/type'; @@ -13,8 +14,14 @@ export const ruleType = RuleType.Array; export const minItems = 1; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { +export const arrayType = 'string'; + +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: T[] +): LintResult => { let value; if (isString(packageJsonData, nodeName)) { @@ -29,7 +36,7 @@ export const lint = (packageJsonData: PackageJson | any, severity: Severity, val return new LintIssue(lintId, severity, nodeName, 'author node has invalid data type'); } - if (!isValidValue(packageJsonData, nodeName, value, validValues)) { + if (!isValidValue(packageJsonData, nodeName, value, validValues)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/valid-values-engines.ts b/src/rules/valid-values-engines.ts index 99a4b6a8..72ced403 100755 --- a/src/rules/valid-values-engines.ts +++ b/src/rules/valid-values-engines.ts @@ -1,6 +1,7 @@ import semver from 'semver'; import {PackageJson} from 'type-fest'; import {LintIssue} from '../lint-issue'; +import {LintResult} from '../types/lint-result'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; import {isObject} from '../validators/type'; @@ -14,14 +15,18 @@ export const ruleType = RuleType.Array; export const minItems = 1; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: object[] +): LintResult => { if (packageJsonData.hasOwnProperty(nodeName)) { if (isObject(packageJsonData, nodeName)) { - const validValuesAsJson = validValues.map((validValue) => JSON.stringify(validValue)); - const valueAsJson = JSON.stringify(packageJsonData[nodeName]); + const validValuesAsJsonString = validValues.map((validValue) => JSON.stringify(validValue)); + const valueAsJsonString = JSON.stringify(packageJsonData[nodeName]); - if (!isValidValue(packageJsonData, nodeName, valueAsJson, validValuesAsJson)) { + if (!isValidValue(packageJsonData, nodeName, valueAsJsonString, validValuesAsJsonString)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/valid-values-license.ts b/src/rules/valid-values-license.ts index 0bea47a7..87d69170 100644 --- a/src/rules/valid-values-license.ts +++ b/src/rules/valid-values-license.ts @@ -20,9 +20,13 @@ export const minItems = 1; * @param {Array} validValues An array of valid values * @return {Object|Boolean} LintIssue object if invalid. True if valid */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { - if (!isValidValue(packageJsonData, nodeName, packageJsonData[nodeName], validValues)) { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: string[] +): LintIssue | null => { + if (!isValidValue(packageJsonData, nodeName, packageJsonData[nodeName], validValues)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/valid-values-name-scope.ts b/src/rules/valid-values-name-scope.ts index d90c2ac9..9ff696e8 100755 --- a/src/rules/valid-values-name-scope.ts +++ b/src/rules/valid-values-name-scope.ts @@ -1,5 +1,6 @@ import {PackageJson} from 'type-fest'; import {LintIssue} from '../lint-issue'; +import {LintResult} from '../types/lint-result'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; import {matchValidValue} from '../validators/valid-values'; @@ -20,8 +21,12 @@ export const minItems = 1; * @param {Array} validValues An array of valid values * @return {Object|Boolean} LintIssue object if invalid. True if valid */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: string[] +): LintResult => { const validRegexes = validValues.map((scope) => new RegExp(`^${scope}/`)); if (!matchValidValue(packageJsonData, nodeName, packageJsonData[nodeName], validRegexes)) { diff --git a/src/rules/valid-values-private.ts b/src/rules/valid-values-private.ts index e31b479d..fb552e86 100755 --- a/src/rules/valid-values-private.ts +++ b/src/rules/valid-values-private.ts @@ -1,5 +1,6 @@ import {PackageJson} from 'type-fest'; import {LintIssue} from '../lint-issue'; +import {LintResult} from '../types/lint-result'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; import {isValidValue} from '../validators/valid-values'; @@ -19,9 +20,13 @@ export const minItems = 1; * @param {Array} validValues An array of valid values * @return {Object|Boolean} LintIssue object if invalid. True if valid */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { - if (!isValidValue(packageJsonData, nodeName, packageJsonData[nodeName], validValues)) { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: boolean[] +): LintResult => { + if (!isValidValue(packageJsonData, nodeName, packageJsonData[nodeName], validValues)) { return new LintIssue(lintId, severity, nodeName, message); } diff --git a/src/rules/valid-values-publishConfig.ts b/src/rules/valid-values-publishConfig.ts index 26248b19..5ef7a983 100755 --- a/src/rules/valid-values-publishConfig.ts +++ b/src/rules/valid-values-publishConfig.ts @@ -1,5 +1,6 @@ import {PackageJson} from 'type-fest'; import {LintIssue} from '../lint-issue'; +import {LintResult} from '../types/lint-result'; import {RuleType} from '../types/rule-type'; import {Severity} from '../types/severity'; import {isObject} from '../validators/type'; @@ -13,14 +14,18 @@ export const ruleType = RuleType.Array; export const minItems = 1; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const lint = (packageJsonData: PackageJson | any, severity: Severity, validValues: any): LintIssue | null => { +export const lint = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + packageJsonData: PackageJson | any, + severity: Severity, + validValues: object[] +): LintResult => { if (packageJsonData.hasOwnProperty(nodeName)) { if (isObject(packageJsonData, nodeName)) { - const validValuesAsJson = validValues.map((validValue) => JSON.stringify(validValue)); - const valueAsJson = JSON.stringify(packageJsonData[nodeName]); + const validValuesAsJsonString = validValues.map((validValue) => JSON.stringify(validValue)); + const valueAsJsonString = JSON.stringify(packageJsonData[nodeName]); - if (!isValidValue(packageJsonData, nodeName, valueAsJson, validValuesAsJson)) { + if (!isValidValue(packageJsonData, nodeName, valueAsJsonString, validValuesAsJsonString)) { return new LintIssue(lintId, severity, nodeName, message); } } else { diff --git a/src/types/lint-function.ts b/src/types/lint-function.ts new file mode 100644 index 00000000..7b300421 --- /dev/null +++ b/src/types/lint-function.ts @@ -0,0 +1,29 @@ +import {PackageJson} from 'type-fest'; +import {LintResult} from './lint-result'; +import {Severity} from './severity'; + +export interface StandardLintFunction { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (packageJsonData: PackageJson | any, severity: Severity): LintResult; +} + +export interface ArrayLintFunction { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (packageJsonData: PackageJson | any, severity: Severity, ruleConfig: T[]): LintResult; +} + +export interface ObjectLintFunction { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (packageJsonData: PackageJson | any, severity: Severity, ruleConfig: Record): LintResult; +} + +export interface OptionalObjectRuleConfig { + exceptions?: string[]; +} + +export interface OptionalObjectLintFunction { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (packageJsonData: PackageJson | any, severity: Severity, ruleConfig: OptionalObjectRuleConfig): LintResult; +} + +export type LintFunction = StandardLintFunction | ArrayLintFunction | ObjectLintFunction | OptionalObjectLintFunction; diff --git a/src/types/lint-result.ts b/src/types/lint-result.ts new file mode 100644 index 00000000..8407b6cb --- /dev/null +++ b/src/types/lint-result.ts @@ -0,0 +1,3 @@ +import {LintIssue} from '../lint-issue'; + +export type LintResult = LintIssue | null; diff --git a/src/types/package-json-linting-result.ts b/src/types/package-json-linting-result.ts new file mode 100644 index 00000000..c2eba354 --- /dev/null +++ b/src/types/package-json-linting-result.ts @@ -0,0 +1,24 @@ +import {LintIssue} from '../lint-issue'; + +export interface PackageJsonFileLintingResult { + /** + * File path to the package.json file + */ + filePath: string; + /** + * A list of issues. + */ + issues: LintIssue[]; + /** + * A flag indicating that the file was skipped. + */ + ignored: boolean; + /** + * Number of errors. + */ + errorCount: number; + /** + * Number of warnings. + */ + warningCount: number; +} diff --git a/src/utils/getIgnorer.ts b/src/utils/getIgnorer.ts index 3f55eb18..314bfc5f 100644 --- a/src/utils/getIgnorer.ts +++ b/src/utils/getIgnorer.ts @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import ignore from 'ignore'; +import ignore, {Ignore} from 'ignore'; // eslint-disable-next-line @typescript-eslint/no-var-requires const debug = require('debug')('npm-package-json-lint:getIgnorer'); @@ -11,12 +11,11 @@ const FILE_NOT_FOUND_ERROR_CODE = 'ENOENT'; /** * Generates ignorer based on ignore file content. * - * @param {string} cwd Current work directory. - * @param {string} ignorePath Ignore path. - * @returns {Object} Ignorer + * @param cwd Current work directory. + * @param ignorePath Ignore path. + * @returns An instance of an Ignorer */ -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export const getIgnorer = (cwd, ignorePath) => { +export const getIgnorer = (cwd: string, ignorePath: string): Ignore => { const ignoreFilePath = ignorePath || DEFAULT_IGNORE_FILENAME; debug(`ignoreFilePath: ${ignoreFilePath}`); diff --git a/src/validators/dependency-audit.ts b/src/validators/dependency-audit.ts index a5615cca..ca134a36 100755 --- a/src/validators/dependency-audit.ts +++ b/src/validators/dependency-audit.ts @@ -1,4 +1,5 @@ import {PackageJson} from 'type-fest'; +import {OptionalObjectRuleConfig} from '../types/lint-function'; // eslint-disable-next-line @typescript-eslint/no-var-requires const semver = require('semver'); @@ -211,8 +212,7 @@ const absoluteVersionChecker = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any packageJsonData: PackageJson | any, nodeName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - config: any + config: OptionalObjectRuleConfig ): AbsoluteVersionCheckerResult => { const notFound = -1; const firstCharOfStr = 0; @@ -226,7 +226,7 @@ const absoluteVersionChecker = ( continue; } - const dependencyVersion = packageJsonData[nodeName][dependencyName]; + const dependencyVersion: string = packageJsonData[nodeName][dependencyName]; if ( dependencyVersion.startsWith('^', firstCharOfStr) || diff --git a/src/validators/valid-values.ts b/src/validators/valid-values.ts index 0205f928..d56dca07 100755 --- a/src/validators/valid-values.ts +++ b/src/validators/valid-values.ts @@ -9,14 +9,12 @@ import {PackageJson} from 'type-fest'; * @param validValues Array of valid values to validate against * @return True if the node is equal to one of the valid values or is missing. False if it is not. */ -export const isValidValue = ( +export const isValidValue = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any packageJsonData: PackageJson | any, nodeName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - validValues: any + value: T, + validValues: T[] ): boolean => { if (!packageJsonData.hasOwnProperty(nodeName)) { return true; @@ -38,10 +36,8 @@ export const matchValidValue = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any packageJsonData: PackageJson | any, nodeName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: any, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - validRegexes: any + value: string, + validRegexes: RegExp[] ): boolean => { if (!packageJsonData.hasOwnProperty(nodeName)) { return true; diff --git a/test/integration/cli.test.js b/test/integration/cli.test.js index 0ecb8ca1..578f7d18 100644 --- a/test/integration/cli.test.js +++ b/test/integration/cli.test.js @@ -315,7 +315,7 @@ Totals describe('when the cli is run against a monorepo with overrides', () => { test('each file results and totals will be output', () => { - const cli = spawnSync('../../../src/cli.js', [`**/package.json`], { + const cli = spawnSync('../../../dist/cli.js', [`**/package.json`], { env, cwd: './test/fixtures/monorepo', }); @@ -350,7 +350,7 @@ Totals describe('when the cli is run against a monorepo without directory input', () => { test('each file results and totals will be output', () => { - const cli = spawnSync('../../../src/cli.js', [`.`], { + const cli = spawnSync('../../../dist/cli.js', [`.`], { env, cwd: './test/fixtures/monorepo', }); diff --git a/test/unit/Reporter.test.ts b/test/unit/Reporter.test.ts index eb0b4cb9..fa41beae 100755 --- a/test/unit/Reporter.test.ts +++ b/test/unit/Reporter.test.ts @@ -1,5 +1,5 @@ import chalk from 'chalk'; -import {Reporter} from '../../src/Reporter'; +import {write} from '../../src/reporter'; describe('Reporter Unit Tests', () => { describe('write method', () => { @@ -23,7 +23,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); consoleMock.mockRestore(); @@ -55,7 +55,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, chalk.underline('dummyText')); @@ -97,7 +97,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, chalk.underline('dummyText')); @@ -133,7 +133,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, true); + write(results, true); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, chalk.underline('dummyText')); @@ -186,7 +186,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, chalk.underline('dummyText')); @@ -215,7 +215,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, `${chalk.yellow.underline('dummyText')} - ignored`); @@ -265,7 +265,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); @@ -325,7 +325,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, true); + write(results, true); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, chalk.underline('dummyText')); @@ -373,7 +373,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, false); + write(results, false); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, `${chalk.yellow.underline('dummyText')} - ignored`); @@ -423,7 +423,7 @@ describe('Reporter Unit Tests', () => { const consoleMock = jest.spyOn(console, 'log'); - Reporter.write(results, true); + write(results, true); expect(console.log).toHaveBeenCalledTimes(expectedCallCount); expect(console.log).toHaveBeenNthCalledWith(1, ''); expect(console.log).toHaveBeenNthCalledWith(2, `${chalk.yellow.underline('dummyText')} - ignored`); diff --git a/test/unit/Rules.test.ts b/test/unit/Rules.test.ts index 553d92f8..b98c4441 100755 --- a/test/unit/Rules.test.ts +++ b/test/unit/Rules.test.ts @@ -1,19 +1,19 @@ import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; -import {Rules} from '../../src/Rules'; +import {Rules} from '../../src/rules'; jest.mock('fs'); jest.mock('path'); describe('Rules Unit Tests', () => { - describe('_registerRule method', () => { + describe('registerRule method', () => { describe('when a ruleId and ruleModule are passed in', () => { test('the rules object contains the rule as a key and the module path as a value', () => { const rules = new Rules(); const firstIndex = 0; - rules._registerRule('key', 'c/git/key.js'); + rules.registerRule('key', 'c/git/key.js'); expect(Object.keys(rules.rules)[firstIndex]).toStrictEqual('key'); expect(rules.rules.key).toStrictEqual('c/git/key.js'); }); @@ -81,7 +81,7 @@ describe('Rules Unit Tests', () => { describe('when getRules is called', () => { test('the rules object should be returned', () => { const rules = new Rules(); - rules._registerRule('ruleId', 'ruleModule'); + rules.registerRule('ruleId', 'ruleModule'); expect(rules.getRules()).toStrictEqual({ruleId: 'ruleModule'}); }); diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 4efbb030..81b66ef2 100755 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -2,7 +2,7 @@ import * as cosmiconfig from 'cosmiconfig'; import {Config} from '../../src/Config'; import * as applyOverrides from '../../src/config/applyOverrides'; import * as applyExtendsIfSpecified from '../../src/config/applyExtendsIfSpecified'; -import {Rules} from '../../src/Rules'; +import {Rules} from '../../src/rules'; const rules = new Rules(); rules.load(); diff --git a/test/unit/config/ConfigValidator.test.ts b/test/unit/config/ConfigValidator.test.ts index 0ee2ca9f..0c5b27f2 100755 --- a/test/unit/config/ConfigValidator.test.ts +++ b/test/unit/config/ConfigValidator.test.ts @@ -1,5 +1,5 @@ import * as configValidator from '../../../src/config/ConfigValidator'; -import {Rules} from '../../../src/Rules'; +import {Rules} from '../../../src/rules'; const rules = new Rules(); rules.load(); diff --git a/test/unit/Parser.test.ts b/test/unit/file-parser.test.ts similarity index 72% rename from test/unit/Parser.test.ts rename to test/unit/file-parser.test.ts index 304c2d34..9ececbfb 100755 --- a/test/unit/Parser.test.ts +++ b/test/unit/file-parser.test.ts @@ -1,5 +1,5 @@ import fs from 'fs'; -import {Parser} from '../../src/Parser'; +import {parseJsonFile, sourceSymbol} from '../../src/file-parser'; jest.mock('fs'); @@ -14,9 +14,11 @@ describe('Parser Unit Tests', () => { // @ts-expect-error-error fs.readFileSync.mockReturnValue(json); - const parsedJson = Parser.parseJsonFile('dummyFile.txt'); + const parsedJson = parseJsonFile('dummyFile.txt'); expect(parsedJson).toStrictEqual(obj); - expect(parsedJson[Parser.sourceSymbol]).toStrictEqual(json); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + expect(parsedJson[sourceSymbol]).toStrictEqual(json); }); }); @@ -28,7 +30,7 @@ describe('Parser Unit Tests', () => { }); expect(() => { - Parser.parseJsonFile('missing.json'); + parseJsonFile('missing.json'); }).toThrow('Failed to read config file: missing.json. \nError: Error'); }); }); diff --git a/test/unit/linter/linter.test.ts b/test/unit/linter/linter.test.ts index 9b492401..6618a97c 100755 --- a/test/unit/linter/linter.test.ts +++ b/test/unit/linter/linter.test.ts @@ -1,3 +1,5 @@ +import {Ignore} from 'ignore'; +import {Config} from 'cosmiconfig/dist/types'; import {executeOnPackageJsonFiles, executeOnPackageJsonObject} from '../../../src/linter/linter'; import {Rules} from '../../../src/Rules'; import {LintIssue} from '../../../src/lint-issue'; @@ -7,10 +9,14 @@ describe('linter Unit Tests', () => { describe('executeOnPackageJsonFiles method tests', () => { test('files not ignored', () => { const patterns = ['./test/fixtures/valid/package.json', './test/fixtures/errors/package.json']; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest .fn() .mockReturnValueOnce({'name-format': 'error'}) @@ -55,10 +61,14 @@ describe('linter Unit Tests', () => { test('files ignored', () => { const patterns = ['./test/fixtures/valid/package.json', './test/fixtures/errors/package.json']; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => true, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn(), }; const rules = new Rules(); @@ -102,10 +112,14 @@ describe('linter Unit Tests', () => { const packageJsonObj = { name: 'my-test-module', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn(), }; const rules = new Rules(); @@ -141,10 +155,14 @@ describe('linter Unit Tests', () => { const packageJsonObj = { name: 'my-test-module', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => true, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn(), }; const rules = new Rules(); @@ -180,10 +198,14 @@ describe('linter Unit Tests', () => { const packageJsonObj = { name: 'my-test-module', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => true, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn(), }; const rules = new Rules(); @@ -219,10 +241,14 @@ describe('linter Unit Tests', () => { const packageJsonObj = { name: 'my-test-module', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn(), }; const rules = new Rules(); @@ -242,7 +268,6 @@ describe('linter Unit Tests', () => { warningCount: 0, }; - // @ts-expect-error-error const results = executeOnPackageJsonObject({ cwd: process.cwd(), packageJsonObject: packageJsonObj, @@ -259,10 +284,14 @@ describe('linter Unit Tests', () => { name: 'my-test-module', author: 'Spiderman', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn().mockReturnValue({'valid-values-author': ['error', ['Peter Parker']]}), }; const rules = new Rules(); @@ -301,10 +330,14 @@ describe('linter Unit Tests', () => { name: 'my-test-module', author: 'Spiderman', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn().mockReturnValue({'valid-values-author': 'off'}), }; const rules = new Rules(); @@ -342,10 +375,14 @@ describe('linter Unit Tests', () => { name: 'my-test-module', description: 'Spiderman', }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn().mockReturnValue({ 'description-format': [ 'error', @@ -399,10 +436,14 @@ describe('linter Unit Tests', () => { myModule: '^1.0.0', }, }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn().mockReturnValue({ 'no-caret-version-dependencies': 'error', }), @@ -450,10 +491,14 @@ describe('linter Unit Tests', () => { myModule: '^1.0.0', }, }; - const mockIgnorer = { + const mockIgnorer: Ignore = { ignores: (): boolean => false, + add: jest.fn(), + createFilter: jest.fn(), + filter: jest.fn(), + test: jest.fn(), }; - const mockConfigHelper = { + const mockConfigHelper: Config = { getConfigForFile: jest.fn().mockReturnValue({ 'no-caret-version-dependencies': [ 'error', diff --git a/test/unit/linter/results-helper.test.ts b/test/unit/linter/results-helper.test.ts index 33be458d..832cbfb3 100755 --- a/test/unit/linter/results-helper.test.ts +++ b/test/unit/linter/results-helper.test.ts @@ -1,18 +1,21 @@ +import {LintIssue} from '../../../src/lint-issue'; import {aggregateCountsPerFile, aggregateOverallCounts} from '../../../src/linter/results-helper'; +import {PackageJsonFileLintingResult} from '../../../src/types/package-json-linting-result'; +import {Severity} from '../../../src/types/severity'; describe('resultsHelper Unit Tests', () => { describe('aggregateCountsPerFile', () => { test('multiple issues', () => { - const issues = [ + const issues: LintIssue[] = [ { lintId: 'require-name', - severity: 'error', + severity: Severity.Error, node: 'name', lintMessage: 'dummyText', }, { lintId: 'require-name', - severity: 'warning', + severity: Severity.Warning, node: 'name', lintMessage: 'dummyText', }, @@ -34,27 +37,31 @@ describe('resultsHelper Unit Tests', () => { describe('aggregateOverallCounts', () => { test('multiple issues', () => { - const results = [ + const results: PackageJsonFileLintingResult[] = [ { issues: [], + filePath: '', ignored: true, errorCount: 0, warningCount: 0, }, { issues: [], + filePath: '', ignored: false, errorCount: 1, warningCount: 1, }, { issues: [], + filePath: '', ignored: false, errorCount: 9, warningCount: 0, }, { issues: [], + filePath: '', ignored: true, errorCount: 0, warningCount: 0, diff --git a/test/unit/rules/no-duplicate-properties.test.ts b/test/unit/rules/no-duplicate-properties.test.ts index a81c8538..1382942e 100644 --- a/test/unit/rules/no-duplicate-properties.test.ts +++ b/test/unit/rules/no-duplicate-properties.test.ts @@ -1,12 +1,12 @@ import {lint, ruleType} from '../../../src/rules/no-duplicate-properties'; -import {Parser} from '../../../src/Parser'; +import {sourceSymbol} from '../../../src/file-parser'; import {Severity} from '../../../src/types/severity'; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type const parsePackageJson = (source) => { const json = JSON.parse(source); - json[Parser.sourceSymbol] = source; + json[sourceSymbol] = source; return json; }; diff --git a/test/unit/rules/valid-values-license.test.ts b/test/unit/rules/valid-values-license.test.ts index 7af46546..13893f28 100755 --- a/test/unit/rules/valid-values-license.test.ts +++ b/test/unit/rules/valid-values-license.test.ts @@ -44,7 +44,7 @@ describe('valid-values-license Unit Tests', () => { describe('when package.json does not have node', () => { test('true should be returned', () => { const packageJsonData = {}; - const response = lint(packageJsonData, Severity.Error, 'valid'); + const response = lint(packageJsonData, Severity.Error, []); expect(response).toBeNull(); }); diff --git a/test/unit/rules/valid-values-private.test.ts b/test/unit/rules/valid-values-private.test.ts index 1544d469..7aab5f7c 100755 --- a/test/unit/rules/valid-values-private.test.ts +++ b/test/unit/rules/valid-values-private.test.ts @@ -44,7 +44,7 @@ describe('valid-values-private Unit Tests', () => { describe('when package.json does not have node', () => { test('true should be returned', () => { const packageJsonData = {}; - const response = lint(packageJsonData, Severity.Error, 'valid'); + const response = lint(packageJsonData, Severity.Error, []); expect(response).toBeNull(); }); diff --git a/tsconfig.json b/tsconfig.json index 16957571..fd6b33b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,13 @@ { "compilerOptions": { + // See https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping#node-12 + "lib": ["ES2019"], + "module": "commonjs", + "target": "ES2019", "downlevelIteration": true, "emitDeclarationOnly": true, "declaration": true, - "esModuleInterop": true + "esModuleInterop": true, + "outDir": "dist", } }