import { join } from '@angular-devkit/core'; import { chain, Rule, Tree, SchematicContext } from '@angular-devkit/schematics'; import { addDepsToPackageJson } from './ast-utils'; import { offsetFromRoot } from './common'; import { eslintVersion, typescriptESLintVersion, eslintConfigPrettierVersion } from './versions'; export const enum Linter { TsLint = 'tslint', EsLint = 'eslint', None = 'none' } export function generateProjectLint( projectRoot: string, tsConfigPath: string, linter: Linter ) { if (linter === Linter.TsLint) { return { builder: '@angular-devkit/build-angular:tslint', options: { tsConfig: [tsConfigPath], exclude: ['**/node_modules/**', '!' + projectRoot + '/**'] } }; } else if (linter === Linter.EsLint) { return { builder: '@nrwl/linter:lint', options: { linter: 'eslint', config: projectRoot + '/.eslintrc', tsConfig: [tsConfigPath], exclude: ['**/node_modules/**', '!' + projectRoot + '/**'] } }; } else { return undefined; } } export function addLintFiles( projectRoot: string, linter: Linter, onlyGlobal = false ): Rule { return (host: Tree, context: SchematicContext) => { if (linter === 'tslint') { if (!host.exists('/tslint.json')) { host.create('/tslint.json', globalTsLint); } if (!onlyGlobal) { host.create( join(projectRoot as any, `tslint.json`), JSON.stringify({ extends: `${offsetFromRoot(projectRoot)}tslint.json`, rules: [] }) ); } return; } if (linter === 'eslint') { if (!host.exists('/.eslintrc')) { host.create('/.eslintrc', globalESLint); addDepsToPackageJson( {}, { '@typescript-eslint/parser': typescriptESLintVersion, '@typescript-eslint/eslint-plugin': typescriptESLintVersion, eslint: eslintVersion, 'eslint-config-prettier': eslintConfigPrettierVersion /** * REACT SPECIFIC PLUGINS USED IN CONFIG REACT SPECIFIC CONFIG BELOW * * NOTE: They will also need adding to the root package.json of this repo * when the time comes */ // "eslint-plugin-import": "2.18.2", // "eslint-plugin-jsx-a11y": "6.2.3", // "eslint-plugin-react": "7.14.3", // "eslint-plugin-react-hooks": "1.6.1", } )(host, context); } if (!onlyGlobal) { host.create( join(projectRoot as any, `.eslintrc`), JSON.stringify({ extends: `${offsetFromRoot(projectRoot)}.eslintrc`, rules: {} }) ); } } }; } const globalTsLint = ` { "rulesDirectory": ["node_modules/@nrwl/workspace/src/tslint"], "rules": { "arrow-return-shorthand": true, "callable-types": true, "class-name": true, "deprecation": { "severity": "warn" }, "forin": true, "import-blacklist": [true, "rxjs/Rx"], "interface-over-type-literal": true, "member-access": false, "member-ordering": [ true, { "order": [ "static-field", "instance-field", "static-method", "instance-method" ] } ], "no-arg": true, "no-bitwise": true, "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], "no-construct": true, "no-debugger": true, "no-duplicate-super": true, "no-empty": false, "no-empty-interface": true, "no-eval": true, "no-inferrable-types": [true, "ignore-params"], "no-misused-new": true, "no-non-null-assertion": true, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, "no-switch-case-fall-through": true, "no-unnecessary-initializer": true, "no-unused-expression": true, "no-var-keyword": true, "object-literal-sort-keys": false, "prefer-const": true, "radix": true, "triple-equals": [true, "allow-null-check"], "unified-signatures": true, "variable-name": false, "nx-enforce-module-boundaries": [ true, { "allow": [], "depConstraints": [ { "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] } ] } ] } } `; const globalESLint = ` { "root": true, "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2018, "sourceType": "module", "project": "./tsconfig.json" }, "plugins": ["@typescript-eslint"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier", "prettier/@typescript-eslint" ], "rules": { "@typescript-eslint/explicit-member-accessibility": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-parameter-properties": "off" }, "overrides": [ { "files": ["*.tsx"], "rules": { "@typescript-eslint/no-unused-vars": "off" } } ] } `; /** * CONFIG FOR REACT WORKSPACES * * ADAPTED FROM https://github.com/facebook/create-react-app/blob/567f36c9235f1e1fd4a76dc6d1ae00be754ca047/packages/eslint-config-react-app/index.js */ // { // "parser": "@typescript-eslint/parser", // "parserOptions": { // "ecmaVersion": 2018, // "sourceType": "module", // "project": "./tsconfig.json" // }, // "env": { // "browser": true, // "commonjs": true, // "es6": true, // "jest": true, // "node": true // }, // "settings": { // "react": { // "version": "detect" // } // }, // "plugins": ["@typescript-eslint", "import", "jsx-a11y", "react", "react-hooks"], // "extends": [ // "eslint:recommended", // "plugin:@typescript-eslint/eslint-recommended", // "plugin:@typescript-eslint/recommended", // "prettier", // "prettier/@typescript-eslint" // ], // /** // * Inspired by configuration originally found in create-react-app // * https://github.com/facebook/create-react-app // */ // "rules": { // /** // * Standard ESLint rule configurations // * https://eslint.org/docs/rules // */ // "array-callback-return": "warn", // "dot-location": ["warn", "property"], // "eqeqeq": ["warn", "smart"], // "new-parens": "warn", // "no-caller": "warn", // "no-cond-assign": ["warn", "except-parens"], // "no-const-assign": "warn", // "no-control-regex": "warn", // "no-delete-var": "warn", // "no-dupe-args": "warn", // "no-dupe-keys": "warn", // "no-duplicate-case": "warn", // "no-empty-character-class": "warn", // "no-empty-pattern": "warn", // "no-eval": "warn", // "no-ex-assign": "warn", // "no-extend-native": "warn", // "no-extra-bind": "warn", // "no-extra-label": "warn", // "no-fallthrough": "warn", // "no-func-assign": "warn", // "no-implied-eval": "warn", // "no-invalid-regexp": "warn", // "no-iterator": "warn", // "no-label-var": "warn", // "no-labels": ["warn", { "allowLoop": true, "allowSwitch": false }], // "no-lone-blocks": "warn", // "no-loop-func": "warn", // "no-mixed-operators": [ // "warn", // { // "groups": [ // ["&", "|", "^", "~", "<<", ">>", ">>>"], // ["==", "!=", "===", "!==", ">", ">=", "<", "<="], // ["&&", "||"], // ["in", "instanceof"], // ], // "allowSamePrecedence": false, // }, // ], // "no-multi-str": "warn", // "no-native-reassign": "warn", // "no-negated-in-lhs": "warn", // "no-new-func": "warn", // "no-new-object": "warn", // "no-new-symbol": "warn", // "no-new-wrappers": "warn", // "no-obj-calls": "warn", // "no-octal": "warn", // "no-octal-escape": "warn", // "no-redeclare": "warn", // "no-regex-spaces": "warn", // "no-restricted-syntax": ["warn", "WithStatement"], // "no-script-url": "warn", // "no-self-assign": "warn", // "no-self-compare": "warn", // "no-sequences": "warn", // "no-shadow-restricted-names": "warn", // "no-sparse-arrays": "warn", // "no-template-curly-in-string": "warn", // "no-this-before-super": "warn", // "no-throw-literal": "warn", // "no-restricted-globals": [ // "error", // [ // "addEventListener", // "blur", // "close", // "closed", // "confirm", // "defaultStatus", // "defaultstatus", // "event", // "external", // "find", // "focus", // "frameElement", // "frames", // "history", // "innerHeight", // "innerWidth", // "length", // "location", // "locationbar", // "menubar", // "moveBy", // "moveTo", // "name", // "onblur", // "onerror", // "onfocus", // "onload", // "onresize", // "onunload", // "open", // "opener", // "opera", // "outerHeight", // "outerWidth", // "pageXOffset", // "pageYOffset", // "parent", // "print", // "removeEventListener", // "resizeBy", // "resizeTo", // "screen", // "screenLeft", // "screenTop", // "screenX", // "screenY", // "scroll", // "scrollbars", // "scrollBy", // "scrollTo", // "scrollX", // "scrollY", // "self", // "status", // "statusbar", // "stop", // "toolbar", // "top", // ] // ], // "no-unexpected-multiline": "warn", // "no-unreachable": "warn", // "no-unused-expressions": [ // "error", // { // "allowShortCircuit": true, // "allowTernary": true, // "allowTaggedTemplates": true, // }, // ], // "no-unused-labels": "warn", // "no-useless-computed-key": "warn", // "no-useless-concat": "warn", // "no-useless-escape": "warn", // "no-useless-rename": [ // "warn", // { // "ignoreDestructuring": false, // "ignoreImport": false, // "ignoreExport": false, // }, // ], // "no-with": "warn", // "no-whitespace-before-property": "warn", // "react-hooks/exhaustive-deps": "warn", // "require-yield": "warn", // "rest-spread-spacing": ["warn", "never"], // "strict": ["warn", "never"], // "unicode-bom": ["warn", "never"], // "use-isnan": "warn", // "valid-typeof": "warn", // "no-restricted-properties": [ // "error", // { // "object": "require", // "property": "ensure", // "message": // "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting", // }, // { // "object": "System", // "property": "import", // "message": // "Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting", // }, // ], // "getter-return": "warn", // /** // * Import rule configurations // * https://github.com/benmosher/eslint-plugin-import // */ // "import/first": "error", // "import/no-amd": "error", // "import/no-webpack-loader-syntax": "error", // /** // * React-specific rule configurations // * https://github.com/yannickcr/eslint-plugin-react // */ // "react/forbid-foreign-prop-types": ["warn", { "allowInPropTypes": true }], // "react/jsx-no-comment-textnodes": "warn", // "react/jsx-no-duplicate-props": "warn", // "react/jsx-no-target-blank": "warn", // "react/jsx-no-undef": "error", // "react/jsx-pascal-case": [ // "warn", // { // "allowAllCaps": true, // "ignore": [], // }, // ], // "react/jsx-uses-react": "warn", // "react/jsx-uses-vars": "warn", // "react/no-danger-with-children": "warn", // "react/no-direct-mutation-state": "warn", // "react/no-is-mounted": "warn", // "react/no-typos": "error", // "react/react-in-jsx-scope": "error", // "react/require-render-return": "error", // "react/style-prop-object": "warn", // /** // * JSX Accessibility rule configurations // * https://github.com/evcohen/eslint-plugin-jsx-a11y // */ // "jsx-a11y/accessible-emoji": "warn", // "jsx-a11y/alt-text": "warn", // "jsx-a11y/anchor-has-content": "warn", // "jsx-a11y/anchor-is-valid": [ // "warn", // { // "aspects": ["noHref", "invalidHref"], // }, // ], // "jsx-a11y/aria-activedescendant-has-tabindex": "warn", // "jsx-a11y/aria-props": "warn", // "jsx-a11y/aria-proptypes": "warn", // "jsx-a11y/aria-role": "warn", // "jsx-a11y/aria-unsupported-elements": "warn", // "jsx-a11y/heading-has-content": "warn", // "jsx-a11y/iframe-has-title": "warn", // "jsx-a11y/img-redundant-alt": "warn", // "jsx-a11y/no-access-key": "warn", // "jsx-a11y/no-distracting-elements": "warn", // "jsx-a11y/no-redundant-roles": "warn", // "jsx-a11y/role-has-required-aria-props": "warn", // "jsx-a11y/role-supports-aria-props": "warn", // "jsx-a11y/scope": "warn", // /** // * React Hooks rule configurations // * https://github.com/facebook/react/tree/master/packages/eslint-plugin-react-hooks // */ // "react-hooks/rules-of-hooks": "error", // /** // * TypeScript-specific rule configurations (in addition to @typescript-eslint:recommended) // * https://github.com/typescript-eslint/typescript-eslint // */ // // TypeScript"s `noFallthroughCasesInSwitch` option is more robust (#6906) // "default-case": "off", // // "tsc" already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291) // "no-dupe-class-members": "off", // // "tsc" already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/477) // "no-undef": "off", // // Add TypeScript specific rules (and turn off ESLint equivalents) // "@typescript-eslint/consistent-type-assertions": "warn", // "no-array-constructor": "off", // "@typescript-eslint/no-array-constructor": "warn", // "@typescript-eslint/no-namespace": "error", // "no-use-before-define": "off", // "@typescript-eslint/no-use-before-define": [ // "warn", // { // "functions": false, // "classes": false, // "variables": false, // "typedefs": false, // }, // ], // "no-unused-vars": "off", // "@typescript-eslint/no-unused-vars": [ // "warn", // { // "args": "none", // "ignoreRestSiblings": true, // }, // ], // "no-useless-constructor": "off", // "@typescript-eslint/no-useless-constructor": "warn", // "@typescript-eslint/explicit-member-accessibility": "off", // "@typescript-eslint/explicit-function-return-type": "off" // } // }