Skip to content

Commit

Permalink
Merge pull request #116 from benmosher/webpack-resolver
Browse files Browse the repository at this point in the history
Webpack resolver!
  • Loading branch information
benmosher committed Nov 28, 2015
2 parents 4351987 + a749daf commit df78dfe
Show file tree
Hide file tree
Showing 44 changed files with 844 additions and 253 deletions.
4 changes: 1 addition & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ root = true
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.js]
indent_style = space
indent_width = 2
indent_size = 2
end_of_line = lf
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ rules:
curly: [2, "multi-line"]
comma-dangle: [2, always-multiline]
eqeqeq: [2, "allow-null"]
no-shadow: 1
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# files
src/
reports/
resolvers/

# config
.eslintrc
Expand Down
13 changes: 11 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ language: node_js
node_js:
- 0.10
- 0.12
- iojs
- 4
- stable
env:
- NODE_PATH=./lib

script: "npm run-script ci-test"
install:
- npm -g install npm@2
- npm install
# install all resolver deps
- "for resolver in ./resolvers/*; do cd $resolver && npm install && cd ../..; done"

script:
- "npm run-script ci-test"
- "cd resolvers/webpack && npm test"
- "cd ../.." # come back out of resolvers folder

after_success:
- npm run coveralls
115 changes: 85 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ rules:
```


## Rule Details
# Rule Details

### `no-unresolved`

Expand All @@ -79,6 +79,8 @@ This rule can also optionally report on unresolved modules in CommonJS `require(
To enable this, send `{ commonjs: true/false, amd: true/false }` as a rule option.
Both are disabled by default.

If you are using Webpack, see the section on [resolver plugins](#resolver-plugins).

### `named`

Verifies that all named imports are part of the set of named exports in the referenced module.
Expand Down Expand Up @@ -254,8 +256,85 @@ import * as _ from 'lodash' // <- reported

This rule is disabled by default.

# Resolver plugins

With the advent of module bundlers and the current state of modules and module
syntax specs, it's not always obvious where `import x from 'module'` should look
to find the file behind `module`.

Up through v0.10ish, this plugin has directly used substack's [`resolve`] plugin,
which implements Node's import behavior. This works pretty well in most cases.

However, Webpack allows a number of things in import module source strings that
Node does not, such as loaders (`import 'file!./whatever'`) and a number of
aliasing schemes, such as [`externals`]: mapping a module id to a global name at
runtime (allowing some modules to be included more traditionally via script tags).

In the interest of supporting both of these, v0.11 introduces resolver plugins.
At the moment, these are modules exporting a single function:

```js

exports.resolveImport = function (source, file, config) {
// return source's absolute path given
// - file: absolute path of importing module
// - config: optional config provided for this resolver

// return `null` if source is a "core" module (i.e. "fs", "crypto") that
// can't be found on the filesystem
}
```

The default `node` plugin that uses [`resolve`] is a handful of lines:

```js
var resolve = require('resolve')
, path = require('path')
, assign = require('object-assign')

exports.resolveImport = function resolveImport(source, file, config) {
if (resolve.isCore(source)) return null

return resolve.sync(source, opts(path.dirname(file), config))
}

function opts(basedir, config) {
return assign( {}
, config
, { basedir: basedir }
)
}
```

It essentially just uses the current file to get a reference base directory (`basedir`)
and then passes through any explicit config from the `.eslintrc`; things like
non-standard file extensions, module directories, etc.

Currently [Node] and [Webpack] resolution have been implemented, but the
resolvers are just npm packages, so third party packages are supported (and encouraged!).

Just install a resolver as `eslint-import-resolver-foo` and reference it as such:

```yaml
settings:
import/resolver: foo
```

or with a config object:

```yaml
settings:
import/resolver:
foo: { someConfigKey: value }
```

[`resolve`]: https://www.npmjs.com/package/resolve
[`externals`]: http://webpack.github.io/docs/library-and-externals.html

## Settings
[Node]: https://www.npmjs.com/package/eslint-import-resolver-node
[Webpack]: https://www.npmjs.com/package/eslint-import-resolver-webpack

# Settings

You may set the following settings in your `.eslintrc`:

Expand All @@ -265,11 +344,9 @@ A list of regex strings that, if matched by a path, will
not parse the matching module. In practice, this means rules other than
`no-unresolved` will not report on the `import` in question.

#### `import/resolve`

A passthrough to [resolve]'s `opts` parameter for `resolve.sync`.
#### `import/resolver`

[resolve]: https://www.npmjs.com/package/resolve#resolve-sync-id-opts
See [resolver plugins](#resolver-plugins).

#### `import/parser`

Expand Down Expand Up @@ -320,31 +397,9 @@ settings:
- 'node_modules' # this is the default, but must be included if overwritten
- '\\.es5$'

import/resolve:

extensions:
# if unset, default is just '.js', but it must be re-added explicitly if set
- .js
- .jsx
- .es6
- .coffee

paths:
# an array of absolute paths which will also be searched
# think NODE_PATH
- /usr/local/share/global_modules

# this is technically for identifying `node_modules` alternate names
moduleDirectory:

- node_modules # defaults to 'node_modules', but...
- bower_components

- project/src # can add a path segment here that will act like
# a source root, for in-project aliasing (i.e.
# `import MyStore from 'stores/my-store'`)
import/resolver: webpack # will use 'node' if not specified

import/parser: esprima-fb # default is 'babel-core'. change if needed.
import/parser: esprima-fb # default is 'babylon'. change if needed.
```

## SublimeLinter-eslint
Expand Down
5 changes: 5 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ environment:
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version

# update npm
- npm -g install npm@2
- set PATH=%APPDATA%\npm;%PATH%

# install modules
- npm install

Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"ci-test": "npm run-script pretest && mocha --recursive --reporter dot tests/lib/",
"debug": "mocha debug --recursive --reporter dot tests/lib/",
"compile": "rm -rf lib/ && babel -d lib/ src/",
"prepublish": "npm run compile",
"pretest": "eslint ./src && npm run compile && babel -d tests/lib/ tests/src/",
"prepublish": "eslint ./src && npm run compile",
"pretest": "npm run compile && babel -d tests/lib/ tests/src/",
"coveralls": "istanbul cover --report lcovonly --dir reports/coverage _mocha tests/lib/ -- --recursive --reporter dot; remap-istanbul -i reports/coverage/coverage.json -o reports/coverage/lcov.info --type lcovonly && cat ./reports/coverage/lcov.info | coveralls"
},
"repository": {
Expand Down Expand Up @@ -44,15 +44,17 @@
"eslint": ">=1.8.0",
"istanbul": "^0.4.0",
"mocha": "^2.2.1",
"remap-istanbul": "^0.4.0"
"remap-istanbul": "^0.4.0",
"eslint-import-resolver-webpack": "file:./resolvers/webpack",
"eslint-import-resolver-node": "file:./resolvers/node"
},
"peerDependencies": {
"eslint": ">=1.4.0"
},
"dependencies": {
"babel-runtime": "5.x",
"babylon": "^6.1.2",
"resolve": "1.1.6"
"eslint-import-resolver-node": "^0.1.0"
},
"greenkeeper": {
"ignore": [
Expand Down
3 changes: 3 additions & 0 deletions resolvers/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
env:
es6: false
44 changes: 44 additions & 0 deletions resolvers/node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# eslint-import-resolver-node

[![npm](https://img.shields.io/npm/v/eslint-import-resolver-node.svg)](https://www.npmjs.com/package/eslint-import-resolver-node)

Default Node-style module resolution plugin for [`eslint-plugin-import`](https://www.npmjs.com/package/eslint-plugin-import).

Published separately to allow pegging to a specific version in case of breaking
changes.

Config is passed directly through to [`resolve`](https://www.npmjs.com/package/resolve#resolve-sync-id-opts) as options:

```yaml
settings:
import/resolver:
node:
extensions:
# if unset, default is just '.js', but it must be re-added explicitly if set
- .js
- .jsx
- .es6
- .coffee

paths:
# an array of absolute paths which will also be searched
# think NODE_PATH
- /usr/local/share/global_modules

# this is technically for identifying `node_modules` alternate names
moduleDirectory:

- node_modules # defaults to 'node_modules', but...
- bower_components

- project/src # can add a path segment here that will act like
# a source root, for in-project aliasing (i.e.
# `import MyStore from 'stores/my-store'`)
```

or to use the default options:

```yaml
settings:
import/resolver: node
```
17 changes: 17 additions & 0 deletions resolvers/node/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var resolve = require('resolve')
, path = require('path')
, assign = require('object-assign')

exports.resolveImport = function resolveImport(source, file, config) {
if (resolve.isCore(source)) return null

return resolve.sync(source, opts(path.dirname(file), config))
}

function opts(basedir, config) {
return assign( {}
, config
, { basedir: basedir }
)
}

30 changes: 30 additions & 0 deletions resolvers/node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "eslint-import-resolver-node",
"version": "0.1.0",
"description": "Node default behavior import resolution plugin for eslint-plugin-import.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://github.com/benmosher/eslint-plugin-import"
},
"keywords": [
"eslint",
"eslintplugin",
"esnext",
"modules",
"eslint-plugin-import"
],
"author": "Ben Mosher (me@benmosher.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/benmosher/eslint-plugin-import/issues"
},
"homepage": "https://github.com/benmosher/eslint-plugin-import",
"dependencies": {
"object-assign": "^4.0.1",
"resolve": "^1.1.6"
}
}
1 change: 1 addition & 0 deletions resolvers/webpack/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "presets": ["es2015"] }
2 changes: 2 additions & 0 deletions resolvers/webpack/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test/
.babelrc
27 changes: 27 additions & 0 deletions resolvers/webpack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# eslint-import-resolver-webpack

[![npm](https://img.shields.io/npm/v/eslint-import-resolver-webpack.svg)](https://www.npmjs.com/package/eslint-import-resolver-webpack)

Webpack-literate module resolution plugin for [`eslint-plugin-import`](https://www.npmjs.com/package/eslint-plugin-import).

Published separately to allow pegging to a specific version in case of breaking
changes.

Will look for `webpack.config.js` as a sibling of the first ancestral `package.json`,
or a `config` parameter may be provided with another filename/path relative to the
`package.json`.

```yaml
---
settings:
import/resolver: webpack # take all defaults
```

or with explicit config file name:

```yaml
---
settings:
import/resolver:
webpack: { config: 'webpack.dev.config.js' }
```

0 comments on commit df78dfe

Please sign in to comment.