From 2b50775066ec52fc7f978a75acfcea81d5f422cc Mon Sep 17 00:00:00 2001 From: Jon Cursi Date: Thu, 26 Sep 2024 15:49:33 -0400 Subject: [PATCH] [E4E-0]: Node 22 upgrade; Added examples; Dogfood the config onto itself; Updated library versions --- .vscode/settings.json | 6 +- LICENSE | 2 +- README.md | 73 +++++++-------- package.json | 2 +- src/commitlint.config.js | 10 ++- src/index.js | 190 ++++++++++++++++++++++++--------------- src/nest.js | 42 +++++++++ src/prettier.config.js | 12 ++- src/tsconfig.json | 7 ++ test/helpers.ts | 7 +- 10 files changed, 222 insertions(+), 129 deletions(-) create mode 100644 src/nest.js diff --git a/.vscode/settings.json b/.vscode/settings.json index cef3ee3..24f7a25 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,11 +3,7 @@ "editor.formatOnPaste": true, "git.ignoreLimitWarning": true, "editor.tabSize": 2, - "editor.codeActionsOnSave": [ - "source.organizeImports", - "source.fixAll", - "source.addMissingImports" - ], + "editor.codeActionsOnSave": ["source.organizeImports", "source.fixAll", "source.addMissingImports"], "eslint.codeActionsOnSave.mode": "problems", "typescript.tsdk": "node_modules/typescript/lib", "[javascript]": { diff --git a/LICENSE b/LICENSE index 8b5c840..694e360 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) ACT, Inc. and its affiliates. +Copyright (c) Encoura, LLC and its affiliates. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 131377e..db30ed1 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,6 @@ # ESLint Config - - -[![Version](https://img.shields.io/npm/v/@encoura/eslint-config)](https://www.npmjs.com/package/@encoura/eslint-config) [![Build Status](https://app.travis-ci.com/nrrccua/eslint-config.svg?branch=master)](https://app.travis-ci.com/nrrccua/eslint-config) [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/nrrccua/eslint-config/blob/master/LICENSE) [![Downloads](https://img.shields.io/npm/dw/@encoura/eslint-config?color=orange)](https://www.npmjs.com/package/@encoura/eslint-config) - - - -ACT's preferred configs for TypeScript, Prettier, ESLint, CommitLint, and +Encoura's preferred configs for TypeScript, Prettier, ESLint, CommitLint, and MarkdownLint. ## Getting Started @@ -22,10 +16,10 @@ Configure husky by adding the following to your `package.json` file: ```json ... -"husky": { - "hooks": { - "pre-commit": "lint-staged" - } +"scripts": { + ... + "prepare": "husky", + ... }, ... ``` @@ -43,45 +37,38 @@ module.exports = require('@encoura/eslint-config/commitlint.config'); This will allow CommitLint to discover the configuration this repository provides from within your `node_modules` folder. -Next, add the following to your `package.json` file so that CommitLint will -check for infractions in your commit messages every time you create a new -commit: +By default the Encoura commitlint expects a commit message in the following format: -```json -... -"husky": { - "hooks": { - ... - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", - ... - } -}, -... -``` +`[XXX-###]: Subject` where XXX-### is a jira ticket id, e.g., `E4E-1` + +The commit message may also be in the form of git's standard merge commit format. ## Configure ESLint To configure [ESLint](https://eslint.org/), add the following to your -`package.json` file. This will allow ESLint to discover the configuration this -repository provides from within your `node_modules` folder, and will check -your `*.js`, `*.ts`, and `*.tsx` files for infractions every time you create a -new commit: +`.eslintrc.js` and `package.json` files. This will allow ESLint to discover the +configuration this repository provides from within your `node_modules` folder, +and will check your `*.js`, `*.ts`, and `*.tsx` files for infractions every +time you create a new commit: -```json -... -"eslintConfig": { - ... - "extends": [ - ... - "@encoura/eslint-config", - ... - ], +```js +module.exports = { + extends: [ + // For front-end (React / Next.js) projects: + '@encoura/eslint-config' + // For back-end (Nest.js) projects: + '@encoura/eslint-config/nest' + ] ... -}, + // Add any custom rules/plugins/configuration here +} +``` + +```json ... "lint-staged": { ... - "*.{js,ts,tsx}": "eslint", + "*.{js,jsx,ts,tsx}": "eslint", ... }, ... @@ -107,11 +94,11 @@ a new commit: ## Configure Prettier -To configure [prettier](https://prettier.io/), create a `prettier.config.js` +To configure [prettier](https://prettier.io/), create a `.prettierrc.js` file in the root of your project that contains the following: ```js -module.exports = require('@encoura/eslint-config/prettier.config'); +module.exports = require('@encoura/eslint-config/.prettierrc'); ``` This will allow Prettier to discover the configuration this repository @@ -124,7 +111,7 @@ your files for infractions every time you create a new commit: ... "lint-staged": { ... - "*.{js,json,md,ts,tsx}": [ + "*.{js,jsx,json,md,ts,tsx}": [ "prettier --write", "git add" ] diff --git a/package.json b/package.json index 063d3c2..fcaa1de 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "markdownlint-cli": "^0.42.0", "prettier": "^3.3.3" }, - "description": "ACT's preferred configs for TypeScript, Prettier, ESLint, CommitLint, and MarkdownLint.", + "description": "Encoura's preferred configs for TypeScript, Prettier, ESLint, CommitLint, and MarkdownLint.", "devDependencies": { "@semantic-release/changelog": "6.0.3", "@semantic-release/exec": "6.0.3", diff --git a/src/commitlint.config.js b/src/commitlint.config.js index 536353c..d8f76a4 100644 --- a/src/commitlint.config.js +++ b/src/commitlint.config.js @@ -1,5 +1,5 @@ /** - * Copyright (c) ACT, Inc. and its affiliates. + * Copyright (c) Encoura, LLC and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -7,7 +7,15 @@ module.exports = { extends: ['@commitlint/config-conventional'], + parserPreset: { + parserOpts: { + headerCorrespondence: ['ticket', 'subject'], + headerPattern: /^(\[[A-Z0-9]*-[0-9]*\]):\s(.*)$/, + }, + }, rules: { 'subject-case': [2, 'always', 'sentence-case'], + 'subject-min-length': [2, 'always', 10], + 'type-empty': [0, 'never'], }, }; diff --git a/src/index.js b/src/index.js index e559355..7bd5292 100644 --- a/src/index.js +++ b/src/index.js @@ -1,47 +1,39 @@ /** - * Copyright (c) ACT, Inc. and its affiliates. + * Copyright (c) Encoura, LLC and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -module.exports = { +const prettierConfig = require('./prettier.config'); + +const overridePrintWidth = + prettierConfig && prettierConfig.overrides && prettierConfig.overrides.length > 0 + ? Math.max(...prettierConfig.overrides.map(o => o.options.printWidth)) + : 0; +const printWidth = Math.max(prettierConfig.printWidth, overridePrintWidth); + +const javascriptConfig = { env: { browser: true, es6: true, - 'jest/globals': true, }, extends: [ 'airbnb', - 'plugin:@typescript-eslint/recommended', 'plugin:import/errors', + 'plugin:import/recommended', 'plugin:import/warnings', - 'plugin:import/typescript', - 'plugin:jest/recommended', 'plugin:jsx-a11y/recommended', 'plugin:lodash/recommended', 'plugin:prettier/recommended', 'plugin:react/recommended', 'prettier', ], - overrides: [ - { - files: ['./**/*.test.tsx'], - rules: { - 'react/react-in-jsx-scope': 'off', - }, - }, - ], - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 2017, - }, + ignorePatterns: ['node_modules/**'], plugins: [ - '@typescript-eslint', 'disable', 'filenames', 'import', - 'jest', 'jsx-a11y', 'lodash', 'new-with-error', @@ -54,26 +46,11 @@ module.exports = { ], root: true, rules: { - '@typescript-eslint/explicit-function-return-type': 'error', - '@typescript-eslint/explicit-member-accessibility': 'off', - '@typescript-eslint/indent': 'off', - '@typescript-eslint/no-use-before-define': 'error', 'comma-dangle': ['error', 'only-multiline'], - 'filenames/match-exported': [ - 'error', - ['camel', 'pascal'], - '\\.(android|config|ios|test)$', - ], + 'filenames/match-exported': ['warn', [null, 'pascal', 'camel']], 'filenames/match-regex': 'off', 'function-paren-newline': 'off', - 'import/extensions': [ - 'error', - 'never', - { - css: 'always', - json: 'always', - }, - ], + 'import/extensions': ['error', 'never'], 'import/named': 'error', 'import/namespace': [ 'error', @@ -84,12 +61,7 @@ module.exports = { 'import/no-extraneous-dependencies': [ 'error', { - devDependencies: [ - '**/*.config.*', - '**/*.stories.*', - '**/*.test.*', - '**/test/**/*.*', - ], + devDependencies: ['**/*.config.*', '**/*.stories.*', '**/*.test.*', '**/test/**/*.*'], }, ], 'import/no-named-as-default': 'off', @@ -98,25 +70,28 @@ module.exports = { 'jsx-a11y/href-no-hash': 'off', 'lodash/import-scope': 'off', 'lodash/prefer-lodash-method': 'off', + 'lodash/preferred-alias': 'off', + 'max-len': [ + 'error', + { + code: printWidth, + }, + ], 'new-with-error/new-with-error': 'error', + 'no-console': 'error', 'no-loops/no-loops': 'error', + 'no-multi-spaces': ['error', { exceptions: { VariableDeclarator: true } }], + 'no-shadow': 'off', + 'no-throw-literal': 'error', 'no-underscore-dangle': [ 'error', { - allow: [ - '__DEV__', - '__ENV__', - '__typename', - '_cachedRowCount', - '_dataBlob', - '_ensureIndex', - '_id', - '_typename', - ], + allow: ['__DEV__', '__ENV__', '__typename', '_cachedRowCount', '_dataBlob', '_ensureIndex', '_id', '_typename'], allowAfterThis: true, }, ], 'no-use-before-define': 'off', + 'no-useless-constructor': 'error', 'prefer-destructuring': [ 'off', { @@ -143,13 +118,9 @@ module.exports = { 'react/boolean-prop-naming': 'error', 'react/destructuring-assignment': ['error', 'always'], 'react/function-component-definition': 'off', - 'react/jsx-filename-extension': [ - 'warn', - { - extensions: ['.js', '.ts', '.tsx'], - }, - ], + 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }], 'react/jsx-fragments': ['error', 'syntax'], + 'react/jsx-no-useless-fragment': 'warn', 'react/jsx-props-no-multi-spaces': 'warn', 'react/jsx-props-no-spreading': 'off', 'react/jsx-sort-props': [ @@ -165,6 +136,7 @@ module.exports = { ], 'react/jsx-wrap-multilines': 'off', 'react/no-deprecated': 'error', + 'react/no-invalid-html-attribute': 'off', 'react/no-multi-comp': ['error', { ignoreStateless: true }], 'react/no-this-in-sfc': 'error', 'react/no-typos': 'error', @@ -221,17 +193,95 @@ module.exports = { }, ], }, - settings: { - 'eslint-plugin-disable': { - paths: { - '@typescript-eslint': ['*.js', '**/*.js'], +}; + +const typescriptConfig = { + extends: [ + ...javascriptConfig.extends, + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:import/typescript', + ], + files: ['**/*.ts', '**/*.tsx'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: ['tsconfig.json'], + }, + plugins: ['@typescript-eslint', 'prettier'], + rules: { + ...javascriptConfig.rules, + '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'], + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/member-delimiter-style': 'error', + '@typescript-eslint/naming-convention': [ + 'warn', + { + custom: { + match: true, + regex: '^I[A-Z]', + }, + format: ['PascalCase'], + selector: 'interface', }, - }, - 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx'], - }, - react: { - version: 'detect', - }, + ], + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + '@typescript-eslint/no-shadow': ['error', { ignoreTypeValueShadow: true }], + '@typescript-eslint/no-throw-literal': 'error', + '@typescript-eslint/no-unsafe-assignment': 'warn', // Change this to 'error' in the future + '@typescript-eslint/no-unsafe-call': 'warn', // Change this to 'error' in the future + '@typescript-eslint/no-use-before-define': 'error', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + '@typescript-eslint/unified-signatures': 'error', + 'import/order': [ + 'warn', + { + alphabetize: { caseInsensitive: true, order: 'asc' }, + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroups: [ + { + group: 'internal', + pattern: '~/**', + }, + { + group: 'external', + pattern: '@**', + }, + { + group: 'external', + pattern: 'next/**', + }, + ], + warnOnUnassignedImports: true, + }, + ], + 'no-throw-literal': 'off', // Rely on @typescript-eslint/no-throw-literal instead + 'no-useless-constructor': 'off', // Rely on @typescript-eslint/no-useless-constructor instead }, }; + +const testConfig = { + env: { + 'jest/globals': true, + }, + extends: ['plugin:jest/recommended'], + files: ['test/**', '*.test.ts', '*.test.tsx', '*.spec.ts', '*.spec.tsx'], + plugins: ['jest'], + rules: { + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + 'react/react-in-jsx-scope': 'off', + }, +}; + +module.exports = { + ...javascriptConfig, + overrides: [typescriptConfig, testConfig], +}; diff --git a/src/nest.js b/src/nest.js new file mode 100644 index 0000000..7e37428 --- /dev/null +++ b/src/nest.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) Encoura, LLC and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const baseConfig = require('./index'); + +const controllerConfig = { + files: ['**/*.controller.ts'], +}; + +const entityConfig = { + files: ['**/*.entity.ts'], + rules: { + camel_case: 'off', + }, +}; + +const moduleConfig = { + files: ['**/*.module.ts'], + rules: { + '@typescript-eslint/no-extraneous-class': 'off', + }, +}; + +const serviceConfig = { + files: ['**/*.service.ts'], + rules: { + 'filenames/match-exported': 'off', + }, +}; + +module.exports = { + ...baseConfig, + overrides: [...baseConfig.overrides, controllerConfig, entityConfig, moduleConfig, serviceConfig], + rules: { + ...baseConfig.rules, + 'filenames/match-exported': 'off', + }, +}; diff --git a/src/prettier.config.js b/src/prettier.config.js index 65d4988..ae4d59f 100644 --- a/src/prettier.config.js +++ b/src/prettier.config.js @@ -1,5 +1,5 @@ /** - * Copyright (c) ACT, Inc. and its affiliates. + * Copyright (c) Encoura, LLC and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -9,7 +9,15 @@ module.exports = { arrowParens: 'avoid', bracketSpacing: true, insertPragma: false, - printWidth: 80, + overrides: [ + { + files: ['*.jsx', '*.tsx'], + options: { + printWidth: 160, + }, + }, + ], + printWidth: 120, proseWrap: 'preserve', requirePragma: false, semi: true, diff --git a/src/tsconfig.json b/src/tsconfig.json index 3f164f2..fffdc0d 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -26,5 +26,12 @@ "sourceMap": true, "strict": true, "target": "esnext" + }, + "ts-node": { + "transpileOnly": false, + "compilerOptions": { + "module": "commonjs", + "useUnknownInCatchVariables": true + } } } diff --git a/test/helpers.ts b/test/helpers.ts index 6cd83a2..e10c7a8 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -2,9 +2,4 @@ export const singleArg = ({ a, b }: { a: number; b: number }): number => a + b; export const multipleArgs = (a: number, b: number): number => a + b; -export const multipleArgsLongLine = ( - a: number, - b: number, - c: number, - d: number, -): number => a + b + c + d; +export const multipleArgsLongLine = (a: number, b: number, c: number, d: number): number => a + b + c + d;