Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ab8f8be

Browse files
committedMar 10, 2020
Merge branch 'master' into no-unused-modules-dynamic-imports
2 parents 479604b + efd6be1 commit ab8f8be

18 files changed

+160
-18
lines changed
 

‎CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
55
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
66

77
## [Unreleased]
8+
### Fixed
9+
- [`order`]: fix `isExternalModule` detect on windows ([#1651], thanks [@fisker])
10+
- [`order`]: recognize ".." as a "parent" path ([#1658], thanks [@golopot])
11+
- [`no-duplicates`]: fix fixer on cases with default import ([#1666], thanks [@golopot])
12+
- [`no-unused-modules`]: Handle `export { default } from` syntax ([#1631], thanks [@richardxia])
813

914
## [2.20.1] - 2020-02-01
1015
### Fixed
@@ -652,7 +657,11 @@ for info on changes for earlier releases.
652657

653658
[`memo-parser`]: ./memo-parser/README.md
654659

660+
[#1666]: https://github.com/benmosher/eslint-plugin-import/pull/1666
661+
[#1658]: https://github.com/benmosher/eslint-plugin-import/pull/1658
662+
[#1651]: https://github.com/benmosher/eslint-plugin-import/pull/1651
655663
[#1635]: https://github.com/benmosher/eslint-plugin-import/issues/1635
664+
[#1631]: https://github.com/benmosher/eslint-plugin-import/issues/1631
656665
[#1625]: https://github.com/benmosher/eslint-plugin-import/pull/1625
657666
[#1620]: https://github.com/benmosher/eslint-plugin-import/pull/1620
658667
[#1619]: https://github.com/benmosher/eslint-plugin-import/pull/1619
@@ -1105,3 +1114,5 @@ for info on changes for earlier releases.
11051114
[@kentcdodds]: https://github.com/kentcdodds
11061115
[@IvanGoncharov]: https://github.com/IvanGoncharov
11071116
[@wschurman]: https://github.com/wschurman
1117+
[@fisker]: https://github.com/fisker
1118+
[@richardxia]: https://github.com/richardxia

‎appveyor.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ environment:
88
# - nodejs_version: "4"
99

1010
matrix:
11-
fast_finish: true
11+
fast_finish: false
1212

1313
# allow_failures:
1414
# - nodejs_version: "4" # for eslint 5
@@ -27,6 +27,9 @@ install:
2727
if ($env:nodejs_version -eq "4") {
2828
npm install -g npm@3;
2929
}
30+
if ($env:nodejs_version -in @("8", "10", "12")) {
31+
npm install -g npm@6.10.3;
32+
}
3033
- npm install
3134

3235
# fix symlinks

‎resolvers/node/CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
55

66
## Unreleased
7+
### Added
8+
- add `.node` extension ([#1663])
79

810
## v0.3.2 - 2018-01-05
911
### Added
10-
- `.mjs` extension detected by default to support `experimental-modules` (#939)
12+
- `.mjs` extension detected by default to support `experimental-modules` ([#939])
1113

1214
### Deps
1315
- update `debug`, `resolve`
@@ -42,6 +44,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
4244

4345
[#438]: https://github.com/benmosher/eslint-plugin-import/pull/438
4446

47+
[#1663]: https://github.com/benmosher/eslint-plugin-import/issues/1663
4548
[#939]: https://github.com/benmosher/eslint-plugin-import/issues/939
4649
[#531]: https://github.com/benmosher/eslint-plugin-import/issues/531
4750
[#437]: https://github.com/benmosher/eslint-plugin-import/issues/437

‎resolvers/node/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ function opts(file, config) {
2828
return Object.assign({
2929
// more closely matches Node (#333)
3030
// plus 'mjs' for native modules! (#939)
31-
extensions: ['.mjs', '.js', '.json'],
31+
extensions: ['.mjs', '.js', '.json', '.node'],
3232
},
3333
config,
3434
{

‎resolvers/node/test/dot-node.node

Whitespace-only changes.

‎resolvers/node/test/native.node

Whitespace-only changes.

‎resolvers/node/test/paths.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,21 @@ describe("default options", function () {
4040
.equal(path.resolve(__dirname, './native.mjs'))
4141
})
4242

43+
it("finds .node modules, with lowest precedence", function () {
44+
expect(node.resolve('./native.node', './test/file.js'))
45+
.to.have.property('path')
46+
.equal(path.resolve(__dirname, './native.node'))
47+
})
48+
49+
it("finds .node modules", function () {
50+
expect(node.resolve('./dot-node', './test/file.js'))
51+
.to.have.property('path')
52+
.equal(path.resolve(__dirname, './dot-node.node'))
53+
})
54+
4355
it("still finds .js if explicit", function () {
4456
expect(node.resolve('./native.js', './test/file.js'))
4557
.to.have.property('path')
4658
.equal(path.resolve(__dirname, './native.js'))
4759
})
48-
4960
})

‎src/core/importType.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ function isExternalPath(path, name, settings) {
2929
}
3030

3131
function isSubpath(subpath, path) {
32-
const normSubpath = subpath.replace(/[/]$/, '')
32+
const normPath = path.replace(/\\/g, '/')
33+
const normSubpath = subpath.replace(/\\/g, '/').replace(/\/$/, '')
3334
if (normSubpath.length === 0) {
3435
return false
3536
}
36-
const left = path.indexOf(normSubpath)
37+
const left = normPath.indexOf(normSubpath)
3738
const right = left + normSubpath.length
3839
return left !== -1 &&
39-
(left === 0 || normSubpath[0] !== '/' && path[left - 1] === '/') &&
40-
(right >= path.length || path[right] === '/')
40+
(left === 0 || normSubpath[0] !== '/' && normPath[left - 1] === '/') &&
41+
(right >= normPath.length || normPath[right] === '/')
4142
}
4243

4344
const externalModuleRegExp = /^\w/
@@ -67,7 +68,7 @@ function isInternalModule(name, settings, path) {
6768
}
6869

6970
function isRelativeToParent(name) {
70-
return /^\.\.[\\/]/.test(name)
71+
return/^\.\.$|^\.\.[\\/]/.test(name)
7172
}
7273

7374
const indexFiles = ['.', './', './index', './index.js']

‎src/rules/no-duplicates.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,13 @@ function getFix(first, rest, sourceCode) {
136136
fixes.push(fixer.insertTextBefore(closeBrace, specifiersText))
137137
}
138138
} else if (!shouldAddDefault && openBrace == null && shouldAddSpecifiers) {
139-
// `import './foo'` → `import {...} from './foo'`
140-
fixes.push(fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`))
139+
if (first.specifiers.length === 0) {
140+
// `import './foo'` → `import {...} from './foo'`
141+
fixes.push(fixer.insertTextAfter(firstToken, ` {${specifiersText}} from`))
142+
} else {
143+
// `import def from './foo'` → `import def, {...} from './foo'`
144+
fixes.push(fixer.insertTextAfter(first.specifiers[0], `, {${specifiersText}}`))
145+
}
141146
} else if (!shouldAddDefault && openBrace != null && closeBrace != null) {
142147
// `import {...} './foo'` → `import {..., ...} from './foo'`
143148
fixes.push(fixer.insertTextBefore(closeBrace, specifiersText))

‎src/rules/no-unused-modules.js

+51-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,54 @@ const CLASS_DECLARATION = 'ClassDeclaration'
6666
const DEFAULT = 'default'
6767
const TYPE_ALIAS = 'TypeAlias'
6868

69+
/**
70+
* List of imports per file.
71+
*
72+
* Represented by a two-level Map to a Set of identifiers. The upper-level Map
73+
* keys are the paths to the modules containing the imports, while the
74+
* lower-level Map keys are the paths to the files which are being imported
75+
* from. Lastly, the Set of identifiers contains either names being imported
76+
* or a special AST node name listed above (e.g ImportDefaultSpecifier).
77+
*
78+
* For example, if we have a file named foo.js containing:
79+
*
80+
* import { o2 } from './bar.js';
81+
*
82+
* Then we will have a structure that looks like:
83+
*
84+
* Map { 'foo.js' => Map { 'bar.js' => Set { 'o2' } } }
85+
*
86+
* @type {Map<string, Map<string, Set<string>>>}
87+
*/
6988
const importList = new Map()
89+
90+
/**
91+
* List of exports per file.
92+
*
93+
* Represented by a two-level Map to an object of metadata. The upper-level Map
94+
* keys are the paths to the modules containing the exports, while the
95+
* lower-level Map keys are the specific identifiers or special AST node names
96+
* being exported. The leaf-level metadata object at the moment only contains a
97+
* `whereUsed` propoerty, which contains a Set of paths to modules that import
98+
* the name.
99+
*
100+
* For example, if we have a file named bar.js containing the following exports:
101+
*
102+
* const o2 = 'bar';
103+
* export { o2 };
104+
*
105+
* And a file named foo.js containing the following import:
106+
*
107+
* import { o2 } from './bar.js';
108+
*
109+
* Then we will have a structure that looks like:
110+
*
111+
* Map { 'bar.js' => Map { 'o2' => { whereUsed: Set { 'foo.js' } } } }
112+
*
113+
* @type {Map<string, Map<string, object>>}
114+
*/
70115
const exportList = new Map()
116+
71117
const ignoredFiles = new Set()
72118
const filesOutsideSrc = new Set()
73119

@@ -453,9 +499,12 @@ module.exports = {
453499
}
454500
}
455501

456-
const exportStatement = exports.get(exportedValue)
502+
// exportsList will always map any imported value of 'default' to 'ImportDefaultSpecifier'
503+
const exportsKey = exportedValue === DEFAULT ? IMPORT_DEFAULT_SPECIFIER : exportedValue
504+
505+
const exportStatement = exports.get(exportsKey)
457506

458-
const value = exportedValue === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportedValue
507+
const value = exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey
459508

460509
if (typeof exportStatement !== 'undefined'){
461510
if (exportStatement.whereUsed.size < 1) {

‎tests/files/no-unused-modules/file-0.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ import {q} from './file-q'
1111
export * from './file-n'
1212
export { default, o0, o3 } from './file-o'
1313
export { p } from './file-p'
14+
import s from './file-s'
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './file-o'

‎tests/src/core/importType.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { expect } from 'chai'
22
import * as path from 'path'
33

4-
import importType from 'core/importType'
4+
import importType, {isExternalModule} from 'core/importType'
55

66
import { testContext, testFilePath } from '../utils'
77

@@ -180,6 +180,12 @@ describe('importType(name)', function () {
180180
})
181181

182182
it('returns "external" for a scoped module from a symlinked directory which partial path is contained in "external-module-folders" (webpack resolver)', function() {
183+
const originalFoldersContext = testContext({
184+
'import/resolver': 'webpack',
185+
'import/external-module-folders': [],
186+
})
187+
expect(importType('@test-scope/some-module', originalFoldersContext)).to.equal('internal')
188+
183189
const foldersContext = testContext({
184190
'import/resolver': 'webpack',
185191
'import/external-module-folders': ['files/symlinked-module'],
@@ -224,4 +230,11 @@ describe('importType(name)', function () {
224230
})
225231
expect(importType('@test-scope/some-module', foldersContext)).to.equal('external')
226232
})
233+
234+
it('`isExternalModule` works with windows directory separator', function() {
235+
expect(isExternalModule('foo', {}, 'E:\\path\\to\\node_modules\\foo')).to.equal(true)
236+
expect(isExternalModule('foo', {
237+
'import/external-module-folders': ['E:\\path\\to\\node_modules'],
238+
}, 'E:\\path\\to\\node_modules\\foo')).to.equal(true)
239+
})
227240
})

‎tests/src/rules/no-duplicates.js

+6
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,12 @@ ruleTester.run('no-duplicates', rule, {
168168
errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'],
169169
}),
170170

171+
test({
172+
code: "import def from './foo'; import {x} from './foo'",
173+
output: "import def, {x} from './foo'; ",
174+
errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'],
175+
}),
176+
171177
test({
172178
code: "import {x} from './foo'; import def from './foo'",
173179
output: "import def, {x} from './foo'; ",

‎tests/src/rules/no-unused-modules.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ ruleTester.run('no-unused-modules', rule, {
117117
import * as l from './file-l'
118118
export * from './file-n'
119119
export { default, o0, o3 } from './file-o'
120-
export { p } from './file-p'`,
120+
export { p } from './file-p'
121+
import s from './file-s'`,
121122
filename: testFilePath('./no-unused-modules/file-0.js'),
122123
errors: [
123124
error(`exported declaration 'default' not used within other modules`),
@@ -206,7 +207,11 @@ typescriptRuleTester.run('no-unused-modules', rule, {
206207

207208
// // test for export from
208209
ruleTester.run('no-unused-modules', rule, {
209-
valid: [],
210+
valid: [
211+
test({ options: unusedExportsOptions,
212+
code: `export { default } from './file-o'`,
213+
filename: testFilePath('./no-unused-modules/file-s.js')}),
214+
],
210215
invalid: [
211216
test({ options: unusedExportsOptions,
212217
code: `export { k } from '${testFilePath('./no-unused-modules/file-k.js')}'`,

‎tests/src/rules/order.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ ruleTester.run('order', rule, {
1919
var relParent1 = require('../foo');
2020
var relParent2 = require('../foo/bar');
2121
var relParent3 = require('../');
22+
var relParent4 = require('..');
2223
var sibling = require('./foo');
2324
var index = require('./');`,
2425
}),
@@ -196,7 +197,13 @@ ruleTester.run('order', rule, {
196197
import { Input } from '-/components/Input';
197198
import { Button } from '-/components/Button';
198199
199-
import { add } from './helper';`,
200+
import p from '..';
201+
import q from '../';
202+
203+
import { add } from './helper';
204+
205+
import i from '.';
206+
import j from './';`,
200207
options: [
201208
{
202209
'newlines-between': 'always',
@@ -2002,6 +2009,25 @@ ruleTester.run('order', rule, {
20022009
message: '`foo` import should occur before import of `Bar`',
20032010
}],
20042011
}),
2012+
// Alphabetize with parent paths
2013+
test({
2014+
code: `
2015+
import a from '../a';
2016+
import p from '..';
2017+
`,
2018+
output: `
2019+
import p from '..';
2020+
import a from '../a';
2021+
`,
2022+
options: [{
2023+
groups: ['external', 'index'],
2024+
alphabetize: {order: 'asc'},
2025+
}],
2026+
errors: [{
2027+
ruleID: 'order',
2028+
message: '`..` import should occur before import of `../a`',
2029+
}],
2030+
}),
20052031
// Alphabetize with require
20062032
test({
20072033
code: `

‎utils/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
55

66
## Unreleased
77

8+
### Added
9+
[New] Print more helpful info if parsing fails ([#1671], thanks [@kaiyoma])
10+
811
## v2.5.2 - 2020-01-12
912

1013
### Fixed
@@ -70,6 +73,7 @@ Yanked due to critical issue with cache key resulting from #839.
7073
### Fixed
7174
- `unambiguous.test()` regex is now properly in multiline mode
7275

76+
[#1671]: https://github.com/benmosher/eslint-plugin-import/pull/1671
7377
[#1606]: https://github.com/benmosher/eslint-plugin-import/pull/1606
7478
[#1602]: https://github.com/benmosher/eslint-plugin-import/pull/1602
7579
[#1591]: https://github.com/benmosher/eslint-plugin-import/pull/1591
@@ -94,3 +98,4 @@ Yanked due to critical issue with cache key resulting from #839.
9498
[@arcanis]: https://github.com/arcanis
9599
[@sompylasar]: https://github.com/sompylasar
96100
[@iamnapo]: https://github.com/iamnapo
101+
[@kaiyoma]: https://github.com/kaiyoma

‎utils/parse.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ exports.default = function parse(path, content, context) {
7676
visitorKeys: keysFromParser(parserPath, parser, parserRaw),
7777
}
7878
} catch (e) {
79-
//
79+
console.warn()
80+
console.warn('Error while parsing ' + parserOptions.filePath)
81+
console.warn('Line ' + e.lineNumber + ', column ' + e.column + ': ' + e.message)
8082
}
8183
if (!ast || typeof ast !== 'object') {
8284
console.warn(

0 commit comments

Comments
 (0)
Please sign in to comment.