Skip to content

Commit

Permalink
add webpack resolver option
Browse files Browse the repository at this point in the history
  • Loading branch information
dflupu committed Mar 9, 2020
1 parent ba4efc9 commit 615b352
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 8 deletions.
45 changes: 41 additions & 4 deletions lib/options-manager.js
Expand Up @@ -8,6 +8,7 @@ const mergeWith = require('lodash/mergeWith');
const groupBy = require('lodash/groupBy');
const flow = require('lodash/flow');
const pathExists = require('path-exists');
const findUp = require('find-up');
const findCacheDir = require('find-cache-dir');
const resolveFrom = require('resolve-from');
const prettier = require('prettier');
Expand Down Expand Up @@ -313,14 +314,13 @@ const buildXOConfig = options => config => {
Object.assign(config.rules, options.rules);
}

if (options.settings) {
config.baseConfig.settings = options.settings;
}

if (options.parser) {
config.baseConfig.parser = options.parser;
}

config.baseConfig.settings = options.settings || {};
config.baseConfig.settings['import/resolver'] = gatherImportResolvers(options);

return config;
};

Expand Down Expand Up @@ -476,6 +476,43 @@ const findApplicableOverrides = (path, overrides) => {

const getIgnores = ({ignores}) => DEFAULT_IGNORES.concat(ignores || []);

const gatherImportResolvers = options => {
let resolvers = {};

const resolverSettings = options.settings && options.settings['import/resolver'];
if (resolverSettings) {
if (typeof resolverSettings === 'string') {
resolvers[resolverSettings] = {};
} else {
resolvers = {...resolverSettings};
}
}

let webpackResolverSettings;

if (options.webpack) {
webpackResolverSettings = options.webpack === true ? {} : options.webpack;
} else if (!(options.webpack === false || resolvers.webpack)) {
// If a webpack config file exists, add the import resolver automatically
const webpackConfigPath = findUp.sync('webpack.config.js', {cwd: options.cwd});
if (webpackConfigPath) {
webpackResolverSettings = {config: webpackConfigPath};
}
}

if (webpackResolverSettings) {
resolvers = {
...resolvers,
webpack: {
...resolvers.webpack,
...webpackResolverSettings
}
};
}

return resolvers;
};

module.exports = {
findApplicableOverrides,
mergeWithPrettierConfig,
Expand Down
5 changes: 4 additions & 1 deletion package.json
Expand Up @@ -61,6 +61,7 @@
"eslint-config-xo": "^0.29.0",
"eslint-config-xo-typescript": "^0.26.0",
"eslint-formatter-pretty": "^3.0.1",
"eslint-import-resolver-webpack": "^0.12.1",
"eslint-plugin-ava": "^10.0.1",
"eslint-plugin-eslint-comments": "^3.1.2",
"eslint-plugin-import": "^2.20.1",
Expand All @@ -70,6 +71,7 @@
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-unicorn": "^17.0.1",
"find-cache-dir": "^3.0.0",
"find-up": "^4.1.0",
"fs-extra": "^8.1.0",
"get-stdin": "^7.0.0",
"globby": "^9.0.0",
Expand Down Expand Up @@ -103,7 +105,8 @@
"nyc": "^15.0.0",
"pify": "^4.0.0",
"proxyquire": "^2.1.3",
"temp-write": "^4.0.0"
"temp-write": "^4.0.0",
"webpack": "^4.42.0"
},
"eslintConfig": {
"extends": "eslint-config-xo"
Expand Down
15 changes: 13 additions & 2 deletions readme.md
Expand Up @@ -252,7 +252,7 @@ Allow more extensions to be linted besides `.js` and `.jsx`. Make sure they're s

Type: `object`

[Shared ESLint settings](https://eslint.org/docs/user-guide/configuring#adding-shared-settings) exposed to rules. For example, to configure the [`import`](https://github.com/benmosher/eslint-plugin-import#settings) plugin to use your webpack configuration for determining search paths, you can put `{"import/resolver": "webpack"}` here.
[Shared ESLint settings](https://eslint.org/docs/user-guide/configuring#adding-shared-settings) exposed to rules.

### parser

Expand All @@ -269,6 +269,17 @@ Enforce ES2015+ rules. Disabling this will make it not *enforce* ES2015+ syntax

*ES2015+ is parsed even without this option. You can already use ES2017 features like [`async`/`await`](https://github.com/lukehoban/ecmascript-asyncawait).

### webpack

Type: `boolean | object`
Default: `false`

Use [eslint-import-resolver-webpack](https://github.com/benmosher/eslint-plugin-import/tree/master/resolvers/webpack) to resolve import search paths. This is enabled automatically if a `webpack.config.js` file is found.

Set this to a boolean to explicitly enable or disable the resolver.

Setting this to an object enables the resolver and passes the object as configuration. See the [resolver readme](https://github.com/benmosher/eslint-plugin-import/blob/master/resolvers/webpack/README.md) along with the [webpack documentation](https://webpack.js.org/configuration/resolve/) for more information.

## TypeScript and Flow

### TypeScript
Expand Down Expand Up @@ -368,7 +379,7 @@ For example, if your project targets Node.js 8 but you want to use the latest Ja
}
```

This way your `package.json` will contain the actual minimum Node.js version supported by your published code, but XO will lint your source code as if it targets Node.js 12.
This way your `package.json` will contain the actual minimum Node.js version supported by your published code, but XO will lint your source code as if it targets Node.js 12.

### Including files ignored by default

Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/webpack/no-config/file1.js
@@ -0,0 +1,2 @@
import _ from 'file2alias'; // eslint-disable-line no-unused-vars
import __ from 'inexistent'; // eslint-disable-line no-unused-vars
2 changes: 2 additions & 0 deletions test/fixtures/webpack/no-config/file2.js
@@ -0,0 +1,2 @@
const foo = 1;
export default foo;
1 change: 1 addition & 0 deletions test/fixtures/webpack/no-config/file3.js
@@ -0,0 +1 @@
import _ from '!./file2'; // eslint-disable-line no-unused-vars
2 changes: 2 additions & 0 deletions test/fixtures/webpack/with-config/file1.js
@@ -0,0 +1,2 @@
import _ from 'file2alias'; // eslint-disable-line no-unused-vars
import __ from 'inexistent'; // eslint-disable-line no-unused-vars
2 changes: 2 additions & 0 deletions test/fixtures/webpack/with-config/file2.js
@@ -0,0 +1,2 @@
const foo = 1;
export default foo;
9 changes: 9 additions & 0 deletions test/fixtures/webpack/with-config/webpack.config.js
@@ -0,0 +1,9 @@
const path = require('path')

module.exports = {
resolve: {
alias: {
file2alias: path.resolve(__dirname, './file2.js')
}
}
}
52 changes: 52 additions & 0 deletions test/lint-files.js
Expand Up @@ -196,6 +196,58 @@ test('typescript files', async t => {
);
});

test('webpack import resolver is used if webpack.config.js is found', async t => {
const cwd = 'fixtures/webpack/with-config/';
const {results} = await fn.lintFiles(path.resolve(cwd, 'file1.js'), {
cwd,
rules: {
'import/no-unresolved': 2
}
});

t.is(results[0].errorCount, 1);

const errorMessage = results[0].messages[0].message;
t.truthy(/Unable to resolve path to module 'inexistent'/.exec(errorMessage));
});

test('webpack import resolver config can be passed through webpack option', async t => {
const cwd = 'fixtures/webpack/no-config/';

const {results} = await fn.lintFiles(path.resolve(cwd, 'file1.js'), {
cwd,
webpack: {
config: {
resolve: {
alias: {
file2alias: path.resolve(__dirname, cwd, './file2.js')
}
}
}
},
rules: {
'import/no-unresolved': 2
}
});

t.is(results[0].errorCount, 1);
});

test('webpack import resolver is used if {webpack: true}', async t => {
const cwd = 'fixtures/webpack/no-config/';

const {results} = await fn.lintFiles(path.resolve(cwd, 'file3.js'), {
cwd,
webpack: true,
rules: {
'import/no-unresolved': 2,
'import/no-webpack-loader-syntax': 0
}
});

t.is(results[0].errorCount, 0);
});

async function configType(t, {dir}) {
const {results} = await fn.lintFiles('**/*', {cwd: path.resolve('fixtures', 'config-files', dir)});

Expand Down
31 changes: 30 additions & 1 deletion test/options-manager.js
Expand Up @@ -384,11 +384,40 @@ test('buildConfig: parser', t => {
});

test('buildConfig: settings', t => {
const settings = {'import/resolver': 'webpack'};
const settings = {'import/resolver': {webpack: {}}};
const config = manager.buildConfig({settings});
t.deepEqual(config.baseConfig.settings, settings);
});

test('buildConfig: finds webpack config file', t => {
const cwd = path.resolve('fixtures', 'webpack', 'with-config');
const config = manager.buildConfig({cwd});
const expected = {webpack: {config: path.resolve(cwd, 'webpack.config.js')}};
t.deepEqual(config.baseConfig.settings['import/resolver'], expected);
});

test('buildConfig: webpack option sets resolver', t => {
const config = manager.buildConfig({webpack: true, settings: {'import/resolver': 'node'}});
t.deepEqual(config.baseConfig.settings['import/resolver'], {webpack: {}, node: {}});
});

test('buildConfig: webpack option handles object values', t => {
const config = manager.buildConfig({webpack: {foo: 1}, settings: {'import/resolver': 'node'}});
t.deepEqual(config.baseConfig.settings['import/resolver'], {webpack: {foo: 1}, node: {}});
});

test('buildConfig: webpack resolver is not added automatically if webpack option is set to false', t => {
const cwd = path.resolve('fixtures', 'webpack', 'with-config');
const config = manager.buildConfig({cwd, webpack: false, settings: {}});
t.deepEqual(config.baseConfig.settings['import/resolver'], {});
});

test('buildConfig: webpack option is merged with import/resolver', t => {
const settings = {'import/resolver': {webpack: {bar: 1}}};
const config = manager.buildConfig({settings, webpack: {foo: 1}});
t.deepEqual(config.baseConfig.settings['import/resolver'], {webpack: {foo: 1, bar: 1}});
});

test('buildConfig: extends', t => {
const config = manager.buildConfig({extends: [
'plugin:foo/bar',
Expand Down

0 comments on commit 615b352

Please sign in to comment.