Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add webpack option #375

Merged
merged 2 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 41 additions & 4 deletions lib/options-manager.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const foo = 1;
export default foo;
1 change: 1 addition & 0 deletions test/fixtures/webpack/no-config/file3.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const foo = 1;
export default foo;
9 changes: 9 additions & 0 deletions test/fixtures/webpack/with-config/webpack.config.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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: {}});
});

dflupu marked this conversation as resolved.
Show resolved Hide resolved
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