Skip to content

Commit

Permalink
Merge pull request #861 from benmosher/release-2.4.0
Browse files Browse the repository at this point in the history
Release 2.4.0
  • Loading branch information
benmosher committed Jun 2, 2017
2 parents b79e083 + 44ca158 commit b8e9a0b
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
@@ -1,7 +1,7 @@
language: node_js
node_js:
- 4
- 6
- 7

os:
- linux
Expand Down
13 changes: 12 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,6 +6,11 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
## [Unreleased]


## [2.4.0] - 2017-06-02
### Added
- Add `filePath` into `parserOptions` passed to `parser` ([#839], thanks [@sompylasar])
- Add `allow` option to [`no-unassigned-import`] to allow for files that match the globs ([#671], [#737], thanks [@kevin940726]).

## [2.3.0] - 2017-05-18
### Added
- [`no-anonymous-default-export`] rule: report anonymous default exports ([#712], thanks [@duncanbeevers]).
Expand Down Expand Up @@ -389,6 +394,7 @@ for info on changes for earlier releases.
[`no-anonymous-default-export`]: ./docs/rules/no-anonymous-default-export.md

[#742]: https://github.com/benmosher/eslint-plugin-import/pull/742
[#737]: https://github.com/benmosher/eslint-plugin-import/pull/737
[#712]: https://github.com/benmosher/eslint-plugin-import/pull/712
[#685]: https://github.com/benmosher/eslint-plugin-import/pull/685
[#680]: https://github.com/benmosher/eslint-plugin-import/pull/680
Expand Down Expand Up @@ -445,6 +451,8 @@ for info on changes for earlier releases.
[#157]: https://github.com/benmosher/eslint-plugin-import/pull/157
[#314]: https://github.com/benmosher/eslint-plugin-import/pull/314

[#839]: https://github.com/benmosher/eslint-plugin-import/issues/839
[#671]: https://github.com/benmosher/eslint-plugin-import/issues/671
[#660]: https://github.com/benmosher/eslint-plugin-import/issues/660
[#653]: https://github.com/benmosher/eslint-plugin-import/issues/653
[#627]: https://github.com/benmosher/eslint-plugin-import/issues/627
Expand Down Expand Up @@ -502,7 +510,8 @@ for info on changes for earlier releases.
[#119]: https://github.com/benmosher/eslint-plugin-import/issues/119
[#89]: https://github.com/benmosher/eslint-plugin-import/issues/89

[Unreleased]: https://github.com/benmosher/eslint-plugin-import/compare/v2.3.0...HEAD
[Unreleased]: https://github.com/benmosher/eslint-plugin-import/compare/v2.4.0...HEAD
[2.4.0]: https://github.com/benmosher/eslint-plugin-import/compare/v2.3.0...v2.4.0
[2.3.0]: https://github.com/benmosher/eslint-plugin-import/compare/v2.2.0...v2.3.0
[2.2.0]: https://github.com/benmosher/eslint-plugin-import/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/benmosher/eslint-plugin-import/compare/v2.0.1...v2.1.0
Expand Down Expand Up @@ -582,3 +591,5 @@ for info on changes for earlier releases.
[@giodamelio]: https://github.com/giodamelio
[@ntdb]: https://github.com/ntdb
[@ramasilveyra]: https://github.com/ramasilveyra
[@sompylasar]: https://github.com/sompylasar
[@kevin940726]: https://github.com/kevin940726
2 changes: 1 addition & 1 deletion appveyor.yml
@@ -1,7 +1,7 @@
# Test against this version of Node.js
environment:
matrix:
- nodejs_version: "4"
- nodejs_version: "7"
- nodejs_version: "6"

# platform:
Expand Down
22 changes: 22 additions & 0 deletions docs/rules/no-unassigned-import.md
Expand Up @@ -6,11 +6,24 @@ With both CommonJS' `require` and the ES6 modules' `import` syntax, it is possib

This rule aims to remove modules with side-effects by reporting when a module is imported but not assigned.

### Options

This rule supports the following option:

`allow`: An Array of globs. The files that match any of these patterns would be ignored/allowed by the linter. This can be useful for some build environments (e.g. css-loader in webpack).

Note that the globs start from the where the linter is executed (usually project root), but not from each file that includes the source. Learn more in both the pass and fail examples below.


## Fail

```js
import 'should'
require('should')

// In <PROJECT_ROOT>/src/app.js
import '../styles/app.css'
// {"allow": ["styles/*.css"]}
```


Expand All @@ -34,4 +47,13 @@ bar(require('foo'))
require('foo').bar
require('foo').bar()
require('foo')()

// With allow option set
import './style.css' // {"allow": ["**/*.css"]}
import 'babel-register' // {"allow": ["babel-register"]}

// In <PROJECT_ROOT>/src/app.js
import './styles/app.css'
import '../scripts/register.js'
// {"allow": ["src/styles/**", "**/scripts/*.js"]}
```
5 changes: 3 additions & 2 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-import",
"version": "2.3.0",
"version": "2.4.0",
"description": "Import with sanity.",
"engines": {
"node": ">=4"
Expand Down Expand Up @@ -69,6 +69,7 @@
"nyc": "^8.3.0",
"redux": "^3.0.4",
"rimraf": "2.5.2",
"sinon": "^2.3.2",
"typescript": "^2.0.3",
"typescript-eslint-parser": "^2.1.0"
},
Expand All @@ -81,7 +82,7 @@
"debug": "^2.2.0",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.2.0",
"eslint-module-utils": "^2.0.0",
"eslint-module-utils": "^2.1.0",
"has": "^1.0.1",
"lodash.cond": "^4.3.0",
"minimatch": "^3.0.3",
Expand Down
37 changes: 35 additions & 2 deletions src/rules/no-unassigned-import.js
@@ -1,4 +1,6 @@
import isStaticRequire from '../core/staticRequire'
import path from 'path'
import minimatch from 'minimatch'

function report(context, node) {
context.report({
Expand All @@ -7,15 +9,40 @@ function report(context, node) {
})
}

function testIsAllow(globs, filename, source) {
if (!Array.isArray(globs)) {
return false // default doesn't allow any patterns
}

let filePath

if (source[0] !== '.' && source[0] !== '/') { // a node module
filePath = source
} else {
filePath = path.resolve(path.dirname(filename), source) // get source absolute path
}

return globs.find(glob => (
minimatch(filePath, glob) ||
minimatch(filePath, path.join(process.cwd(), glob))
)) !== undefined
}

function create(context) {
const options = context.options[0] || {}
const filename = context.getFilename()
const isAllow = source => testIsAllow(options.allow, filename, source)

return {
ImportDeclaration(node) {
if (node.specifiers.length === 0) {
if (node.specifiers.length === 0 && !isAllow(node.source.value)) {
report(context, node)
}
},
ExpressionStatement(node) {
if (node.expression.type === 'CallExpression' && isStaticRequire(node.expression)) {
if (node.expression.type === 'CallExpression' &&
isStaticRequire(node.expression) &&
!isAllow(node.expression.arguments[0].value)) {
report(context, node.expression)
}
},
Expand All @@ -33,6 +60,12 @@ module.exports = {
'devDependencies': { 'type': ['boolean', 'array'] },
'optionalDependencies': { 'type': ['boolean', 'array'] },
'peerDependencies': { 'type': ['boolean', 'array'] },
'allow': {
'type': 'array',
'items': {
'type': 'string',
},
},
},
'additionalProperties': false,
},
Expand Down
36 changes: 36 additions & 0 deletions tests/src/core/parse.js
@@ -1,11 +1,14 @@
import * as fs from 'fs'
import { expect } from 'chai'
import sinon from 'sinon'
import parse from 'eslint-module-utils/parse'

import { getFilename } from '../utils'

describe('parse(content, { settings, ecmaFeatures })', function () {
const path = getFilename('jsx.js')
const parseStubParser = require('./parseStubParser')
const parseStubParserPath = require.resolve('./parseStubParser')
let content

before((done) =>
Expand All @@ -21,4 +24,37 @@ describe('parse(content, { settings, ecmaFeatures })', function () {
.not.to.throw(Error)
})

it('passes expected parserOptions to custom parser', function () {
const parseSpy = sinon.spy()
const parserOptions = { ecmaFeatures: { jsx: true } }
parseStubParser.parse = parseSpy
parse(path, content, { settings: {}, parserPath: parseStubParserPath, parserOptions: parserOptions })
expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1)
expect(parseSpy.args[0][0], 'custom parser to get content as its first argument').to.equal(content)
expect(parseSpy.args[0][1], 'custom parser to get an object as its second argument').to.be.an('object')
expect(parseSpy.args[0][1], 'custom parser to clone the parserOptions object').to.not.equal(parserOptions)
expect(parseSpy.args[0][1], 'custom parser to get ecmaFeatures in parserOptions which is a clone of ecmaFeatures passed in')
.to.have.property('ecmaFeatures')
.that.is.eql(parserOptions.ecmaFeatures)
.and.is.not.equal(parserOptions.ecmaFeatures)
expect(parseSpy.args[0][1], 'custom parser to get parserOptions.attachComment equal to true').to.have.property('attachComment', true)
expect(parseSpy.args[0][1], 'custom parser to get parserOptions.filePath equal to the full path of the source file').to.have.property('filePath', path)
})

it('should throw on context == null', function () {
expect(parse.bind(null, path, content, null)).to.throw(Error)
})

it('should throw on unable to resolve parserPath', function () {
expect(parse.bind(null, path, content, { settings: {}, parserPath: null })).to.throw(Error)
})

it('should take the alternate parser specified in settings', function () {
const parseSpy = sinon.spy()
const parserOptions = { ecmaFeatures: { jsx: true } }
parseStubParser.parse = parseSpy
expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: [ '.js' ] } }, parserPath: null, parserOptions: parserOptions })).not.to.throw(Error)
expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1)
})

})
4 changes: 4 additions & 0 deletions tests/src/core/parseStubParser.js
@@ -0,0 +1,4 @@
// this stub must be in a separate file to require from parse via moduleRequire
module.exports = {
parse: function () {},
}
67 changes: 67 additions & 0 deletions tests/src/rules/no-unassigned-import.js
Expand Up @@ -29,6 +29,52 @@ ruleTester.run('no-unassigned-import', rule, {
test({ code: 'require("lodash").foo'}),
test({ code: 'require("lodash").foo()'}),
test({ code: 'require("lodash")()'}),
test({
code: 'import "app.css"',
options: [{ 'allow': ['**/*.css'] }],
}),
test({
code: 'import "app.css";',
options: [{ 'allow': ['*.css'] }],
}),
test({
code: 'import "./app.css"',
options: [{ 'allow': ['**/*.css'] }],
}),
test({
code: 'import "foo/bar"',
options: [{ 'allow': ['foo/**'] }],
}),
test({
code: 'import "foo/bar"',
options: [{ 'allow': ['foo/bar'] }],
}),
test({
code: 'import "../dir/app.css"',
options: [{ 'allow': ['**/*.css'] }],
}),
test({
code: 'import "../dir/app.js"',
options: [{ 'allow': ['**/dir/**'] }],
}),
test({
code: 'require("./app.css")',
options: [{ 'allow': ['**/*.css'] }],
}),
test({
code: 'import "babel-register"',
options: [{ 'allow': ['babel-register'] }],
}),
test({
code: 'import "./styles/app.css"',
options: [{ 'allow': ['src/styles/**'] }],
filename: path.join(process.cwd(), 'src/app.js'),
}),
test({
code: 'import "../scripts/register.js"',
options: [{ 'allow': ['src/styles/**', '**/scripts/*.js'] }],
filename: path.join(process.cwd(), 'src/app.js'),
}),
],
invalid: [
test({
Expand All @@ -39,5 +85,26 @@ ruleTester.run('no-unassigned-import', rule, {
code: 'require("lodash")',
errors: [error],
}),
test({
code: 'import "./app.css"',
options: [{ 'allow': ['**/*.js'] }],
errors: [error],
}),
test({
code: 'import "./app.css"',
options: [{ 'allow': ['**/dir/**'] }],
errors: [error],
}),
test({
code: 'require("./app.css")',
options: [{ 'allow': ['**/*.js'] }],
errors: [error],
}),
test({
code: 'import "./styles/app.css"',
options: [{ 'allow': ['styles/*.css'] }],
filename: path.join(process.cwd(), 'src/app.js'),
errors: [error],
}),
],
})
11 changes: 9 additions & 2 deletions utils/CHANGELOG.md
Expand Up @@ -3,9 +3,16 @@ All notable changes to this module will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).

## v2 - 2016-11-07
## Unreleased


## v2.1.0 - 2017-06-02
### Added
- `parse` now additionally passes `filePath` to `parser` in `parserOptions` like `eslint` core does

## v2.0.0 - 2016-11-07
### Changed
- `unambiguous` no longer exposes fast test regex

### Fixed
- `unambiguous.test()` regex is now properly in multiline mode
- `unambiguous.test()` regex is now properly in multiline mode
2 changes: 1 addition & 1 deletion utils/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-module-utils",
"version": "2.0.0",
"version": "2.1.0",
"description": "Core utilities to support eslint-plugin-import and other module-related plugins.",
"engines": {
"node": ">=4"
Expand Down
4 changes: 4 additions & 0 deletions utils/parse.js
Expand Up @@ -22,6 +22,10 @@ exports.default = function parse(path, content, context) {
// always attach comments
parserOptions.attachComment = true

// provide the `filePath` like eslint itself does, in `parserOptions`
// https://github.com/eslint/eslint/blob/3ec436ee/lib/linter.js#L637
parserOptions.filePath = path

// require the parser relative to the main module (i.e., ESLint)
const parser = moduleRequire(parserPath)

Expand Down

0 comments on commit b8e9a0b

Please sign in to comment.