From 37bd5423276abbe6ee2245a3adc5d178fe16121b Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Fri, 6 Apr 2018 22:53:19 -0500 Subject: [PATCH 01/11] Adds no-useless-path-segments documentation. --- README.md | 2 ++ docs/rules/no-useless-path-segments.md | 48 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 docs/rules/no-useless-path-segments.md diff --git a/README.md b/README.md index 529525363..6fab76861 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Forbid webpack loader syntax in imports ([`no-webpack-loader-syntax`]) * Forbid a module from importing itself ([`no-self-import`]) * Forbid a module from importing a module with a dependency path back to itself ([`no-cycle`]) +* Prevent unnecessary path segemnts in import and require statements ([`no-useless-path-segments`]) [`no-unresolved`]: ./docs/rules/no-unresolved.md [`named`]: ./docs/rules/named.md @@ -37,6 +38,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`no-webpack-loader-syntax`]: ./docs/rules/no-webpack-loader-syntax.md [`no-self-import`]: ./docs/rules/no-self-import.md [`no-cycle`]: ./docs/rules/no-cycle.md +[`no-useless-path-segments`]: ./docs/rules/no-useless-path-segments.md ### Helpful warnings diff --git a/docs/rules/no-useless-path-segments.md b/docs/rules/no-useless-path-segments.md new file mode 100644 index 000000000..d0891ee18 --- /dev/null +++ b/docs/rules/no-useless-path-segments.md @@ -0,0 +1,48 @@ +# import/no-useless-path-segments + +Use this rule to prevent unnecessary path segemnts in import and require statements. + +## Rule Details + +Given the following folder structure: + +``` +my-project +├── app.js +├── footer.js +├── header.js +└── pages + ├── about.js + ├── contact.js + └── index.js +``` + +The following patterns are considered problems: + +```js +/** + * in my-project/app.js + */ + +import "./../pages/about.js"; // should be "./pages/about.js" +import "./../pages/about"; // should be "./pages/about" +import "../pages/about.js"; // should be "./pages/about.js" +import "../pages/about"; // should be "./pages/about" +import "./pages//about"; // should be "./pages/about" +import "./pages/"; // should be "./pages" +``` + +The following patterns are NOT considered problems: + +```js +/** + * in my-project/app.js + */ + +import "./header.js"; +import "./pages"; +import "./pages/about"; +import "."; +import ".."; +import fs from "fs"; +``` From 5569a8cc6b463515a9b97f1036b9d92b6dcba86e Mon Sep 17 00:00:00 2001 From: Michael Novotny Date: Fri, 6 Apr 2018 22:59:35 -0500 Subject: [PATCH 02/11] Updates changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45891d9fe..24ba4b565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] +- Add documentation for [`no-useless-path-segments`] rule ([#1068], thanks [@manovotny]) - Fixer for [`first`] ([#1046], thanks [@fengkfengk]) ## [2.10.0] - 2018-03-29 @@ -452,6 +453,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#1068]: https://github.com/benmosher/eslint-plugin-import/pull/1068 [#1046]: https://github.com/benmosher/eslint-plugin-import/pull/1046 [#944]: https://github.com/benmosher/eslint-plugin-import/pull/944 [#891]: https://github.com/benmosher/eslint-plugin-import/pull/891 @@ -694,3 +696,4 @@ for info on changes for earlier releases. [@graingert]: https://github.com/graingert [@danny-andrews]: https://github.com/dany-andrews [@fengkfengk]: https://github.com/fengkfengk +[@manovotny]: https://github.com/manovotny From 72ed0b851d1c8510247c7558043e884d01e2e64d Mon Sep 17 00:00:00 2001 From: Kim Strauch Date: Sun, 8 Apr 2018 21:39:09 -0700 Subject: [PATCH 03/11] add dynamic import webpackChunkName comment rule --- docs/rules/dynamic-import-chunkname.md | 66 +++++ src/index.js | 3 +- src/rules/dynamic-import-chunkname.js | 71 ++++++ tests/src/rules/dynamic-import-chunkname.js | 251 ++++++++++++++++++++ 4 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 docs/rules/dynamic-import-chunkname.md create mode 100644 src/rules/dynamic-import-chunkname.js create mode 100644 tests/src/rules/dynamic-import-chunkname.js diff --git a/docs/rules/dynamic-import-chunkname.md b/docs/rules/dynamic-import-chunkname.md new file mode 100644 index 000000000..0ccc5697d --- /dev/null +++ b/docs/rules/dynamic-import-chunkname.md @@ -0,0 +1,66 @@ +# dynamic imports require a leading comment with a webpackChunkName (dynamic-import-chunkname) + +This rule reports any dynamic imports without a webpackChunkName specified in a leading block comment in the proper format. + +This is a useful rule because if the webpackChunkName is not defined in a dynamic import, Webpack will autogenerate the chunk name. + +## Rule Details +This rule runs against `import` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.' +You can also configure the regex format you'd like to accept for the webpackChunkName - for example, we don't want the number 6 to show up in our chunk names. + ```javascript +{ + "dynamic-import-chunkname": [2, { + importFunction: "dynamicImport", + webpackChunknameFormat: "[a-zA-Z0-57-9-/_]" + }] +} +``` + +### invalid +The following patterns are invalid: + +```javascript +// no leading comment +import('someModule'); + +// incorrectly formatted comment +import( + /*webpackChunkName:"someModule"*/ + 'someModule', +); + +// chunkname contains a 6 (forbidden by rule config) +import( + /* webpackChunkName: "someModule6" */ + 'someModule', +); + +// using single quotes instead of double quotes +import( + /* webpackChunkName: 'someModule' */ + 'someModule', +); + +// single-line comment, not a block-style comment +import( + // webpackChunkName: "someModule" + 'someModule', +); +``` +### valid +The following patterns are valid: + +```javascript + import( + /* webpackChunkName: "someModule" */ + 'someModule', + ); + import( + /* webpackChunkName: "someOtherModule12345789" */ + 'someModule', + ); +``` + +## When Not To Use It + +If you don't care that Webpack will autogenerate chunk names and may blow up browser caches and bundle size reports. diff --git a/src/index.js b/src/index.js index 2d6352b83..61c64214b 100644 --- a/src/index.js +++ b/src/index.js @@ -35,7 +35,8 @@ export const rules = { 'unambiguous': require('./rules/unambiguous'), 'no-unassigned-import': require('./rules/no-unassigned-import'), 'no-useless-path-segments': require('./rules/no-useless-path-segments'), - + 'dynamic-import-chunkname': require('./rules/dynamic-import-chunkname'), + // export 'exports-last': require('./rules/exports-last'), diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js new file mode 100644 index 000000000..50b85508a --- /dev/null +++ b/src/rules/dynamic-import-chunkname.js @@ -0,0 +1,71 @@ +import docsUrl from '../docsUrl' + +module.exports = { + meta: { + docs: { + url: docsUrl('dynamic-import-chunkname'), + }, + schema: [{ + type: 'object', + properties: { + importFunction: { + type: 'string', + }, + webpackChunknameFormat: { + type: 'string', + }, + }, + }], + }, + + create: function (context) { + const config = context.options[0] + let importFunction + if (config) { + ({ importFunction } = config) + } + + let webpackChunknameFormat = '[0-9a-zA-Z-_/.]+' + if (config && config.webpackChunknameFormat) { + ({ webpackChunknameFormat } = config) + } + const commentFormat = ` webpackChunkName: "${webpackChunknameFormat}" ` + const commentRegex = new RegExp(commentFormat) + + return { + CallExpression(node) { + if (node.callee.name !== importFunction && node.callee.type !== 'Import') { + return + } + const sourceCode = context.getSourceCode() + const arg = node.arguments[0] + const leadingComments = sourceCode.getComments(arg).leading + + if (!leadingComments || leadingComments.length !== 1) { + context.report({ + node, + message: 'dynamic imports require a leading comment with the webpack chunkname', + }) + return + } + + const comment = leadingComments[0] + if (comment.type !== 'Block') { + context.report({ + node, + message: 'dynamic imports require a /* foo */ style comment, not a // foo comment', + }) + return + } + + const webpackChunkDefinition = comment.value + if (!webpackChunkDefinition.match(commentRegex)) { + context.report({ + node, + message: `dynamic imports require a leading comment in the form /*${commentFormat}*/`, + }) + } + }, + } + }, +} diff --git a/tests/src/rules/dynamic-import-chunkname.js b/tests/src/rules/dynamic-import-chunkname.js new file mode 100644 index 000000000..c56f2fae6 --- /dev/null +++ b/tests/src/rules/dynamic-import-chunkname.js @@ -0,0 +1,251 @@ +import { SYNTAX_CASES } from '../utils' +import { RuleTester } from 'eslint' + +const rule = require('rules/dynamic-import-chunkname') +const ruleTester = new RuleTester() + +const commentFormat = '[0-9a-zA-Z-_/.]+' +const pickyCommentFormat = '[a-zA-Z-_/.]+' +const options = [{ importFunction: 'dynamicImport' }] +const pickyCommentOptions = [{ + importFunction: 'dynamicImport', + webpackChunknameFormat: pickyCommentFormat, +}] +const parser = 'babel-eslint' + +const noLeadingCommentError = 'dynamic imports require a leading comment with the webpack chunkname' +const nonBlockCommentError = 'dynamic imports require a /* foo */ style comment, not a // foo comment' +const commentFormatError = `dynamic imports require a leading comment in the form /* webpackChunkName: "${commentFormat}" */` +const pickyCommentFormatError = `dynamic imports require a leading comment in the form /* webpackChunkName: "${pickyCommentFormat}" */` + +ruleTester.run('dynamic-import-chunkname', rule, { + valid: [ + { + code: `dynamicImport( + /* webpackChunkName: "someModule" */ + 'test' + )`, + options, + }, + { + code: `dynamicImport( + /* webpackChunkName: "Some_Other_Module" */ + "test" + )`, + options, + }, + { + code: `dynamicImport( + /* webpackChunkName: "SomeModule123" */ + "test" + )`, + options, + }, + { + code: `dynamicImport( + /* webpackChunkName: "someModule" */ + 'someModule' + )`, + options: pickyCommentOptions, + errors: [{ + message: pickyCommentFormatError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName: "someModule" */ + 'test' + )`, + options, + parser, + }, + { + code: `import( + /* webpackChunkName: "Some_Other_Module" */ + "test" + )`, + options, + parser, + }, + { + code: `import( + /* webpackChunkName: "SomeModule123" */ + "test" + )`, + options, + parser, + }, + { + code: `import( + /* webpackChunkName: "someModule" */ + 'someModule' + )`, + options: pickyCommentOptions, + parser, + errors: [{ + message: pickyCommentFormatError, + type: 'CallExpression', + }], + }, + ...SYNTAX_CASES, + ], + + invalid: [ + { + code: `import( + // webpackChunkName: "someModule" + 'someModule' + )`, + options, + parser, + errors: [{ + message: nonBlockCommentError, + type: 'CallExpression', + }], + }, + { + code: 'import(\'test\')', + options, + parser, + errors: [{ + message: noLeadingCommentError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName: someModule */ + 'someModule' + )`, + options, + parser, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName: 'someModule' */ + 'someModule' + )`, + options, + parser, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName "someModule" */ + 'someModule' + )`, + options, + parser, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName:"someModule" */ + 'someModule' + )`, + options, + parser, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `import( + /* webpackChunkName: "someModule123" */ + 'someModule' + )`, + options: pickyCommentOptions, + parser, + errors: [{ + message: pickyCommentFormatError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + // webpackChunkName: "someModule" + 'someModule' + )`, + options, + errors: [{ + message: nonBlockCommentError, + type: 'CallExpression', + }], + }, + { + code: 'dynamicImport(\'test\')', + options, + errors: [{ + message: noLeadingCommentError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + /* webpackChunkName: someModule */ + 'someModule' + )`, + options, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + /* webpackChunkName: 'someModule' */ + 'someModule' + )`, + options, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + /* webpackChunkName "someModule" */ + 'someModule' + )`, + options, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + /* webpackChunkName:"someModule" */ + 'someModule' + )`, + options, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `dynamicImport( + /* webpackChunkName: "someModule123" */ + 'someModule' + )`, + options: pickyCommentOptions, + errors: [{ + message: pickyCommentFormatError, + type: 'CallExpression', + }], + }, + ], +}) From c9d7d660b43f91e490a692d82fb655f983bc4e99 Mon Sep 17 00:00:00 2001 From: Kim Strauch Date: Sun, 8 Apr 2018 21:40:37 -0700 Subject: [PATCH 04/11] update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 529525363..e28a407ec 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a * Forbid default exports ([`no-default-export`]) * Forbid anonymous values as default exports ([`no-anonymous-default-export`]) * Prefer named exports to be grouped together in a single export declaration ([`group-exports`]) +* Enforce a leading comment with the webpackChunkName for dynamic imports ([`dynamic-import-chunkname`]) [`first`]: ./docs/rules/first.md [`exports-last`]: ./docs/rules/exports-last.md @@ -99,6 +100,7 @@ This plugin intends to support linting of ES2015+ (ES6+) import/export syntax, a [`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md [`group-exports`]: ./docs/rules/group-exports.md [`no-default-export`]: ./docs/rules/no-default-export.md +[`dynamic-import-chunkname`]: ./docs/rules/dynamic-import-chunkname.md ## Installation From 121b9e1f61a4a2c0b8bbeaf8a03f9548c2825056 Mon Sep 17 00:00:00 2001 From: Brian Di Palma Date: Mon, 9 Apr 2018 17:11:25 +0100 Subject: [PATCH 05/11] Update recommended.js (#1066) Closes #1063 --- config/recommended.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/recommended.js b/config/recommended.js index 831c5bc29..a72a8b13d 100644 --- a/config/recommended.js +++ b/config/recommended.js @@ -16,8 +16,7 @@ module.exports = { // red flags (thus, warnings) 'import/no-named-as-default': 'warn', 'import/no-named-as-default-member': 'warn', - 'import/no-duplicates': 'warn', - 'import/unambiguous': 'warn', + 'import/no-duplicates': 'warn' }, // need all these for parsing dependencies (even if _your_ code doesn't need From 55ee74c2df0634fdd3b0d77186edf7fe4a42d485 Mon Sep 17 00:00:00 2001 From: Stefan Wrobel Date: Mon, 9 Apr 2018 15:25:54 -0700 Subject: [PATCH 06/11] Fix link to order autofixer in CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45891d9fe..9bc5ed698 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [2.10.0] - 2018-03-29 ### Added -- Autofixer for [`order`] rule ([#711], thanks [@tihonove]) +- Autofixer for [`order`] rule ([#908], thanks [@tihonove]) - Add [`no-cycle`] rule: reports import cycles. ## [2.9.0] - 2018-02-21 @@ -454,6 +454,7 @@ for info on changes for earlier releases. [#1046]: https://github.com/benmosher/eslint-plugin-import/pull/1046 [#944]: https://github.com/benmosher/eslint-plugin-import/pull/944 +[#908]: https://github.com/benmosher/eslint-plugin-import/pull/908 [#891]: https://github.com/benmosher/eslint-plugin-import/pull/891 [#889]: https://github.com/benmosher/eslint-plugin-import/pull/889 [#858]: https://github.com/benmosher/eslint-plugin-import/pull/858 From e6e4e98d328ab54b7dbd2a5e3119cd560a6bb3bb Mon Sep 17 00:00:00 2001 From: Kim Strauch Date: Mon, 9 Apr 2018 22:05:29 -0700 Subject: [PATCH 07/11] respond to PR feedback --- docs/rules/dynamic-import-chunkname.md | 10 ++++----- src/index.js | 2 +- src/rules/dynamic-import-chunkname.js | 23 ++++++++++----------- tests/src/rules/dynamic-import-chunkname.js | 4 ++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/rules/dynamic-import-chunkname.md b/docs/rules/dynamic-import-chunkname.md index 0ccc5697d..98b98871e 100644 --- a/docs/rules/dynamic-import-chunkname.md +++ b/docs/rules/dynamic-import-chunkname.md @@ -2,15 +2,15 @@ This rule reports any dynamic imports without a webpackChunkName specified in a leading block comment in the proper format. -This is a useful rule because if the webpackChunkName is not defined in a dynamic import, Webpack will autogenerate the chunk name. +This rule enforces naming of webpack chunks in dynamic imports. When you don't explicitly name chunks, webpack will autogenerate chunk names that are not consistent across builds, which prevents long-term browser caching. ## Rule Details -This rule runs against `import` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.' -You can also configure the regex format you'd like to accept for the webpackChunkName - for example, we don't want the number 6 to show up in our chunk names. +This rule runs against `import()` by default, but can be configured to also run against an alternative dynamic-import function, e.g. 'dynamicImport.' +You can also configure the regex format you'd like to accept for the webpackChunkName - for example, if we don't want the number 6 to show up in our chunk names: ```javascript { "dynamic-import-chunkname": [2, { - importFunction: "dynamicImport", + importFunctions: ["dynamicImport"], webpackChunknameFormat: "[a-zA-Z0-57-9-/_]" }] } @@ -63,4 +63,4 @@ The following patterns are valid: ## When Not To Use It -If you don't care that Webpack will autogenerate chunk names and may blow up browser caches and bundle size reports. +If you don't care that webpack will autogenerate chunk names and may blow up browser caches and bundle size reports. diff --git a/src/index.js b/src/index.js index 61c64214b..5b55527b2 100644 --- a/src/index.js +++ b/src/index.js @@ -36,7 +36,7 @@ export const rules = { 'no-unassigned-import': require('./rules/no-unassigned-import'), 'no-useless-path-segments': require('./rules/no-useless-path-segments'), 'dynamic-import-chunkname': require('./rules/dynamic-import-chunkname'), - + // export 'exports-last': require('./rules/exports-last'), diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index 50b85508a..0b64b26b6 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -8,8 +8,11 @@ module.exports = { schema: [{ type: 'object', properties: { - importFunction: { - type: 'string', + importFunctions: { + type: 'array', + items: { + type: 'string', + }, }, webpackChunknameFormat: { type: 'string', @@ -20,23 +23,19 @@ module.exports = { create: function (context) { const config = context.options[0] - let importFunction - if (config) { - ({ importFunction } = config) - } + const { importFunctions = [] } = config || {} + const { webpackChunknameFormat = '[0-9a-zA-Z-_/.]+' } = config || {} - let webpackChunknameFormat = '[0-9a-zA-Z-_/.]+' - if (config && config.webpackChunknameFormat) { - ({ webpackChunknameFormat } = config) - } const commentFormat = ` webpackChunkName: "${webpackChunknameFormat}" ` const commentRegex = new RegExp(commentFormat) return { - CallExpression(node) { - if (node.callee.name !== importFunction && node.callee.type !== 'Import') { + [`CallExpression[callee.type="Import"],CallExpression[callee.name]`](node) { + const { callee: { name }} = node + if (name && !importFunctions.includes(name)) { return } + const sourceCode = context.getSourceCode() const arg = node.arguments[0] const leadingComments = sourceCode.getComments(arg).leading diff --git a/tests/src/rules/dynamic-import-chunkname.js b/tests/src/rules/dynamic-import-chunkname.js index c56f2fae6..eb76ce91a 100644 --- a/tests/src/rules/dynamic-import-chunkname.js +++ b/tests/src/rules/dynamic-import-chunkname.js @@ -6,9 +6,9 @@ const ruleTester = new RuleTester() const commentFormat = '[0-9a-zA-Z-_/.]+' const pickyCommentFormat = '[a-zA-Z-_/.]+' -const options = [{ importFunction: 'dynamicImport' }] +const options = [{ importFunctions: ['dynamicImport'] }] const pickyCommentOptions = [{ - importFunction: 'dynamicImport', + importFunctions: ['dynamicImport'], webpackChunknameFormat: pickyCommentFormat, }] const parser = 'babel-eslint' From 9be016f64c154547e8cd3b2dade4e1c3d0a875a4 Mon Sep 17 00:00:00 2001 From: Kim Strauch Date: Mon, 9 Apr 2018 22:41:32 -0700 Subject: [PATCH 08/11] replace includes() with an indexOf() call, add test cases for multiple import functions --- src/rules/dynamic-import-chunkname.js | 3 ++- tests/src/rules/dynamic-import-chunkname.js | 25 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index 0b64b26b6..dbd1bf790 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -10,6 +10,7 @@ module.exports = { properties: { importFunctions: { type: 'array', + uniqueItems: true, items: { type: 'string', }, @@ -32,7 +33,7 @@ module.exports = { return { [`CallExpression[callee.type="Import"],CallExpression[callee.name]`](node) { const { callee: { name }} = node - if (name && !importFunctions.includes(name)) { + if (name && importFunctions.indexOf(name) < 0) { return } diff --git a/tests/src/rules/dynamic-import-chunkname.js b/tests/src/rules/dynamic-import-chunkname.js index eb76ce91a..329401106 100644 --- a/tests/src/rules/dynamic-import-chunkname.js +++ b/tests/src/rules/dynamic-import-chunkname.js @@ -11,6 +11,9 @@ const pickyCommentOptions = [{ importFunctions: ['dynamicImport'], webpackChunknameFormat: pickyCommentFormat, }] +const multipleImportFunctionOptions = [{ + importFunctions: ['dynamicImport', 'definitelyNotStaticImport'], +}] const parser = 'babel-eslint' const noLeadingCommentError = 'dynamic imports require a leading comment with the webpack chunkname' @@ -173,6 +176,28 @@ ruleTester.run('dynamic-import-chunkname', rule, { type: 'CallExpression', }], }, + { + code: `dynamicImport( + /* webpackChunkName "someModule" */ + 'someModule' + )`, + options: multipleImportFunctionOptions, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, + { + code: `definitelyNotStaticImport( + /* webpackChunkName "someModule" */ + 'someModule' + )`, + options: multipleImportFunctionOptions, + errors: [{ + message: commentFormatError, + type: 'CallExpression', + }], + }, { code: `dynamicImport( // webpackChunkName: "someModule" From 115b6fb3c8ab4845bbe4d079320a4f7354ff82a2 Mon Sep 17 00:00:00 2001 From: Kim Strauch Date: Mon, 9 Apr 2018 23:18:20 -0700 Subject: [PATCH 09/11] remove AST selectors --- src/rules/dynamic-import-chunkname.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index dbd1bf790..867808f0b 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -31,9 +31,8 @@ module.exports = { const commentRegex = new RegExp(commentFormat) return { - [`CallExpression[callee.type="Import"],CallExpression[callee.name]`](node) { - const { callee: { name }} = node - if (name && importFunctions.indexOf(name) < 0) { + CallExpression(node) { + if (node.callee.type !== 'Import' && importFunctions.indexOf(node.callee.name) < 0) { return } From d5d8b358924d7a0d5cb79cb9d7a824651d26586c Mon Sep 17 00:00:00 2001 From: Ben Mosher Date: Thu, 12 Apr 2018 07:21:14 -0400 Subject: [PATCH 10/11] memo-parser cautionary note --- memo-parser/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/memo-parser/README.md b/memo-parser/README.md index 545ad999a..8a2a3cb5c 100644 --- a/memo-parser/README.md +++ b/memo-parser/README.md @@ -1,5 +1,13 @@ # eslint-plugin-import/memo-parser + +## NOTE! + +This used to improve performance, but as of ESLint 5 and v2 of this plugin, it seems to just consume a bunch of memory and slightly increase lint times. + +**Not recommended for use at this time!** + + This parser is just a memoizing wrapper around some actual parser. To configure, just add your _actual_ parser to the `parserOptions`, like so: From ec87b6410d651b327df2b2ee20ea337b7de09e8c Mon Sep 17 00:00:00 2001 From: Mattijs Bliek Date: Fri, 13 Apr 2018 12:38:42 +0200 Subject: [PATCH 11/11] Ignore type imports for named rule. (#1057) --- CHANGELOG.md | 1 + docs/rules/named.md | 3 ++- src/rules/named.js | 3 ++- tests/src/rules/named.js | 24 +++--------------------- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7467a8f62..a13962d23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] +- Ignore type imports for named rule ([#931], thanks [@mattijsbliek]) - Add documentation for [`no-useless-path-segments`] rule ([#1068], thanks [@manovotny]) - Fixer for [`first`] ([#1046], thanks [@fengkfengk]) diff --git a/docs/rules/named.md b/docs/rules/named.md index 6dc7c60e5..0830af5e4 100644 --- a/docs/rules/named.md +++ b/docs/rules/named.md @@ -8,10 +8,11 @@ Note: for packages, the plugin will find exported names from [`jsnext:main`], if present in `package.json`. Redux's npm module includes this key, and thereby is lintable, for example. -A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported. +A module path that is [ignored] or not [unambiguously an ES module] will not be reported when imported. Note that type imports, as used by [Flow], are always ignored. [ignored]: ../../README.md#importignore [unambiguously an ES module]: https://github.com/bmeck/UnambiguousJavaScriptGrammar +[Flow]: https://flow.org/ ## Rule Details diff --git a/src/rules/named.js b/src/rules/named.js index aa17dfb40..8c2acd714 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -11,7 +11,8 @@ module.exports = { create: function (context) { function checkSpecifiers(key, type, node) { - if (node.source == null) return // local export, ignore + // ignore local exports and type imports + if (node.source == null || node.importKind === 'type') return if (!node.specifiers .some(function (im) { return im.type === type })) { diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 8bd78f6eb..4fdd3434f 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -67,18 +67,10 @@ ruleTester.run('named', rule, { test({ code: 'import { deepProp } from "./named-exports"' }), test({ code: 'import { deepSparseElement } from "./named-exports"' }), - // flow types + // should ignore imported flow types, even if they don’t exist test({ - code: 'import type { MyType } from "./flowtypes"', - 'parser': 'babel-eslint', - }), - test({ - code: 'import type { MyInterface } from "./flowtypes"', - 'parser': 'babel-eslint', - }), - test({ - code: 'import type { MyClass } from "./flowtypes"', - 'parser': 'babel-eslint', + code: 'import type { MissingType } from "./flowtypes"', + parser: 'babel-eslint', }), // TypeScript @@ -244,16 +236,6 @@ ruleTester.run('named', rule, { // }], // }), - // flow types - test({ - code: 'import type { MissingType } from "./flowtypes"', - parser: 'babel-eslint', - errors: [{ - message: "MissingType not found in './flowtypes'", - type: 'Identifier', - }], - }), - // TypeScript test({ code: 'import { MissingType } from "./typescript"',