From cc147550cc197f4c6673376401fe473254523e74 Mon Sep 17 00:00:00 2001 From: Hans Bergren Date: Wed, 1 Apr 2020 09:43:50 -0400 Subject: [PATCH] Implement eslint-plugin-import (#1) This is the first draft of @xpring-eng/eslint-config-base. I'm tired of porting over the same ESLint configuration every time we spin up a new repo, and it's hard to keep the configuration in sync across so many different repos. A good lint configuration can help us write better TypeScript by avoiding code smells. Airbnb has the eslint-config-airbnb, and this repo implements the start of a @xpring-eng/eslint-config-base that can be used by all our TS projects. Then we can publish our ESLint configuration and import it across our repos. This will give us a single source of truth for lint configuration, and will ensure consistency across all our repos. I'm demoing this locally with the payid repo, and once I put all our shared ESLint config in here, I'll publish this to npm and we can start actually using it. --- .eslintrc.js | 11 +++ index.js | 16 +++- rules/imports.js | 219 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+), 4 deletions(-) create mode 100644 .eslintrc.js create mode 100644 rules/imports.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..b100ff2 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,11 @@ +// This is the ESLint file for _this_ package +// We don't need all the rules that + +module.exports = { + extends: './index.js', + rules: { + 'import/unambiguous': 'off', + 'import/no-commonjs': 'off', + 'import/no-unused-modules': 'off', + }, +} diff --git a/index.js b/index.js index ddd37f1..6f0b965 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,17 @@ module.exports = { - globals: { - MyGlobal: true, + parserOptions: { + sourceType: 'module', // Allow the use of imports / ES modules + ecmaFeatures: { + impliedStrict: true, // Enable global strict mode + }, }, - rules: { - semi: [2, 'always'], + // Specify global variables that are predefined + env: { + node: true, // Enable node global variables & Node.js scoping + es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020 }, + + extends: ['./rules/imports'].map(require.resolve), + rules: {}, } diff --git a/rules/imports.js b/rules/imports.js new file mode 100644 index 0000000..109ba23 --- /dev/null +++ b/rules/imports.js @@ -0,0 +1,219 @@ +module.exports = { + env: { + node: true, // Enable node global variables & Node.js scoping + es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020 + }, + parserOptions: { + sourceType: 'module', + }, + + plugins: ['import'], + extends: [ + 'plugin:import/warnings', + 'plugin:import/errors', + 'plugin:import/typescript', + ], + + // These rules are not captured in the standard warnings/errors/typescript configurations we extend above. + rules: { + /* STATIC ANALYSIS */ + // Forbid import of modules using absolute paths + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-absolute-path.md + 'import/no-absolute-path': 'error', + + // Forbid require() calls with expressions + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-dynamic-require.md + 'import/no-dynamic-require': 'error', + + // Forbid Webpack loader syntax in imports + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md + 'import/no-webpack-loader-syntax': 'error', + + // Forbid a module from importing itself + // https://github.com/benmosher/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/no-self-import.md + 'import/no-self-import': 'error', + + // Forbid cyclical dependencies between modules + // https://github.com/benmosher/eslint-plugin-import/blob/d81f48a2506182738409805f5272eff4d77c9348/docs/rules/no-cycle.md + 'import/no-cycle': 'error', + + // Ensures that there are no useless path segments + // https://github.com/benmosher/eslint-plugin-import/blob/ebafcbf59ec9f653b2ac2a0156ca3bcba0a7cf57/docs/rules/no-useless-path-segments.md + 'import/no-useless-path-segments': 'error', + + // Reports modules without any exports, or with unused exports + // https://github.com/benmosher/eslint-plugin-import/blob/f63dd261809de6883b13b6b5b960e6d7f42a7813/docs/rules/no-unused-modules.md + 'import/no-unused-modules': [ + 'error', + { + // TODO: I would like to enable missingExports, but it doesn't work for files that only export types: + // https://github.com/benmosher/eslint-plugin-import/issues/1680 + missingExports: false, + unusedExports: true, + }, + ], + + + + /* HELPFUL WARNINGS */ + // disallow use of jsdoc-marked-deprecated imports + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-deprecated.md + 'import/no-deprecated': 'warn', + + // Forbid the use of extraneous packages + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md + 'import/no-extraneous-dependencies': 'error', + + // Forbid mutable exports + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md + 'import/no-mutable-exports': 'error', + + /* MODULE SYSTEMS */ + // Warn if a module could be mistakenly parsed as a script by a consumer leveraging Unambiguous JavaScript Grammar + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/unambiguous.md + 'import/unambiguous': 'error', + + // disallow require() + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-commonjs.md + 'import/no-commonjs': 'error', + + // disallow AMD require/define + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-amd.md + 'import/no-amd': 'error', + + + + /* STYLE GUIDE */ + // disallow non-import statements appearing before import statements + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md + 'import/first': 'error', + + // Ensure consistent use of file extension within the import path + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'never', + mjs: 'never', + jsx: 'never', + ts: 'never', + tsx: 'never', + }, + ], + + // ensure absolute imports are above relative imports and that unassigned imports are ignored + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md + 'import/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + ], + 'newlines-between': 'always', + alphabetize: { order: 'asc', caseInsensitive: true }, + }, + ], + + // Require newlines after the last import/require in a group + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md + 'import/newline-after-import': ['error', { count: 1 }], + + // Require modules with a single export to use a default export + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md + 'import/prefer-default-export': 'error', + + // Warn when modules have too many dependencies (code smell) + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/max-dependencies.md + 'import/max-dependencies': ['warn', { max: 5 }], + + // Prevent unassigned imports + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-unassigned-import.md + // importing for side effects is perfectly acceptable, if you need side effects. + 'import/no-unassigned-import': 'warn', + + // Prevent importing the default as if it were named + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-default.md + 'import/no-named-default': 'error', + + // Reports if a module's default export is unnamed + // https://github.com/benmosher/eslint-plugin-import/blob/d9b712ac7fd1fddc391f7b234827925c160d956f/docs/rules/no-anonymous-default-export.md + 'import/no-anonymous-default-export': 'error', + + // Reports when named exports are not grouped together in a single export declaration + // or when multiple assignments to CommonJS module.exports or exports object are present + // in a single file. + // https://github.com/benmosher/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/group-exports.md + // TODO: Make this an error once we migrate our code + 'import/group-exports': 'warn', + + // dynamic imports require a leading comment with a webpackChunkName + // https://github.com/benmosher/eslint-plugin-import/blob/ebafcbf59ec9f653b2ac2a0156ca3bcba0a7cf57/docs/rules/dynamic-import-chunkname.md + 'import/dynamic-import-chunkname': 'warn', + + + + /* DISABLED RULES */ + // Enforces that all exports are declared at the bottom of the file + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/exports-last.md + 'import/exports-last': 'off', + + // Prohibit default exports. Mostly an inverse of prefer-default-export. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-default-export.md + 'import/no-default-export': 'off', + + // Note: This is a TERRIBLE rule. + // Prohibit named exports. Mostly an inverse of no-default-export. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-named-export.md + 'import/no-named-export': 'off', + + // Note: This is a TERRIBLE rule. + // Use this rule to prevent importing the submodules of other modules. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-internal-modules.md + 'import/no-internal-modules': 'off', + + // Note: Decent rule, but some JS libraries require using a wildcard import. + // Enforce a convention of not using namespace (a.k.a. "wildcard" *) imports. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-namespace.md + 'import/no-namespace': 'off', + + // Forbid the use of Node.js builtin modules. Can be useful for client-side web projects that do not have access to those modules. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-nodejs-modules.md + 'import/no-nodejs-modules': 'off', + + // Use this rule to prevent imports to folders in relative parent paths. + // This rule is useful for enforcing tree-like folder structures instead of complex graph-like folder structures. + // While this restriction might be a departure from Node's default resolution style, it can lead large, complex codebases to be easier to maintain. + // If you've ever had debates over "where to put files" this rule is for you. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-relative-parent-imports.md + // TODO: Enable this rule later to `warn`? + 'import/no-relative-parent-imports': 'off', + + // Some projects contain files which are not always meant to be executed in the same environment. + // For example consider a web application that contains specific code for the server and some specific code for the browser/client. + // In this case you don’t want to import server-only files in your client code. + // In order to prevent such scenarios this rule allows you to define restricted zones where you can forbid files from imported if they match a specific path. + // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-restricted-paths.md + 'import/no-restricted-paths': 'off', + }, + + overrides: [ + { + files: ['test/**/*.test.ts'], + rules: { + // Our Mocha test files never export anything. + 'import/no-unused-modules': 'off', + // Importing mocha is a side-effecting import + 'import/no-unassigned-import': ['error', { allow: ['mocha'] }], + // Warn when modules have too many dependencies (code smell) + // Increased the max for test files, since tests usually need to import more things + 'import/max-dependencies': ['warn', { max: 8 }], + }, + }, + ], +}