Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Implement Yarn 2 support #1630

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/airbnb-base/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const {

module.exports = ({ eslint = {}, ...opts } = {}) => (neutrino) => {
const baseConfig = eslint.baseConfig || {};
const usedPlugins = ['import'];
const usedResolvers = { node: {} };

neutrino.use(
lint({
...opts,
Expand Down Expand Up @@ -41,4 +44,19 @@ module.exports = ({ eslint = {}, ...opts } = {}) => (neutrino) => {
},
}),
);

lint.aliasPlugins(
{
plugins: usedPlugins,
},
__filename,
);
lint.aliasImportResolvers(
{
settings: {
'import/resolver': usedResolvers,
},
},
__filename,
);
};
3 changes: 2 additions & 1 deletion packages/airbnb-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"dependencies": {
"@neutrinojs/eslint": "9.4.0",
"eslint-config-airbnb-base": "^14.2.0",
"eslint-plugin-import": "^2.22.0"
"eslint-plugin-import": "^2.22.0",
"eslint-import-resolver-node": "0.3.4"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0",
Expand Down
18 changes: 18 additions & 0 deletions packages/airbnb/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const {

module.exports = ({ eslint = {}, ...opts } = {}) => (neutrino) => {
const baseConfig = eslint.baseConfig || {};
const usedPlugins = ['react', 'react-hooks', 'jsx-a11y', 'import'];
const usedResolvers = { node: {} };

neutrino.use(
lint({
...opts,
Expand Down Expand Up @@ -45,4 +48,19 @@ module.exports = ({ eslint = {}, ...opts } = {}) => (neutrino) => {
},
}),
);

lint.aliasPlugins(
{
plugins: usedPlugins,
},
__filename,
);
lint.aliasImportResolvers(
{
settings: {
'import/resolver': usedResolvers,
},
},
__filename,
);
};
3 changes: 2 additions & 1 deletion packages/airbnb/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-hooks": "^4.1.0"
"eslint-plugin-react-hooks": "^4.1.0",
"eslint-import-resolver-node": "0.3.4"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0",
Expand Down
136 changes: 136 additions & 0 deletions packages/eslint/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,142 @@ The following is a list of rules and their identifiers which can be overridden:
| ------ | ------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `lint` | By default, lints JS and JSX files from the `src` and `test` directories using ESLint. Contains a single loader named `eslint`. | all |

## Utility functions

### Plugins aliasing

When developing your custom ESLint preset, you may face a problem with sharable ESLint configs and plugins. This is due to ESLint plugins resolving system which searches for plugins packages in the project root. This may fail in some environments, especially in Plug'n'Play. This module provides `aliasPlugins()` function to resolve this issue in your package providing aliases from package names to absolute paths. You can import it in 2 ways: `require('@neutrinojs/eslint/alias-plugins')` or `require('@neutrinojs/eslint').aliasPlugins`. Example:

```js
const eslint = require('@neutrinojs/eslint');
const eslintBaseConfig = { plugins: ['node'] };

neutrino.use(eslint({
eslint: {
baseConfig: eslintBaseConfig
}
}))

lint.aliasPlugins(
// ESLint config that contains used plugins
eslintBaseConfig,
// Path to the current module file, so aliases can be correctly resolved from your package
// In most cases it is always `__filename`
__filename
);
```

If you use 3rd party configs, plugins will not be present in the configuration. So you have to list them manually just for aliasing. For example:

```js
const eslint = require('@neutrinojs/eslint');
const usedPlugins = ['react', 'react-hooks', 'jsx-a11y', 'import'];
const eslintBaseConfig = {
extends: [
require.resolve('eslint-config-airbnb'),
require.resolve('eslint-config-airbnb/hooks')
]
};

neutrino.use(eslint({
eslint: {
baseConfig: eslintBaseConfig
}
}))

lint.aliasPlugins(
// ESLint config that contains only used plugins
{ plugins: usedPlugins },
// Path to the current module file, so aliases can be correctly resolved from your package
// In most cases it is always `__filename`
__filename
);
```

**Important! Make sure all aliased plugins are present in your dependencies in package.json**

```json
{
"dependencies": {
"eslint-plugin-import": "latest",
"eslint-plugin-jsx-a11y": "latest",
"eslint-plugin-react": "latest",
"eslint-plugin-react-hooks": "latest",
}
}
```

### Import resolvers aliasing

Also you may have problems with allocation of import resolvers when `eslint-plugin-import` is used. This can happen in some environments, especially in Plug'n'Play. This module provides `aliasImportResolvers()` function to resolve this issue in your package providing aliases from package names to absolute paths. You can import it in 2 ways: `require('@neutrinojs/eslint/alias-import-resolvers')` or `require('@neutrinojs/eslint').aliasImportResolvers`. Example:

```js
const eslint = require('@neutrinojs/eslint');
const eslintBaseConfig = {
settings: {
'import/resolver': {
node: {
extensions: ['.js', '.json']
}
},
},
};

neutrino.use(eslint({
eslint: {
baseConfig: eslintBaseConfig
}
}))

lint.aliasImportResolvers(
// ESLint config that contains settings
eslintBaseConfig,
// Path to the current module file, so aliases can be correctly resolved from your package
// In most cases it is always `__filename`
__filename
);
```

If you use 3rd party configs, settings will not be present in the configuration. So you have to imitate them manually just for aliasing. For example:

```js
const eslint = require('@neutrinojs/eslint');
const usedResolvers = { node: {} };
const eslintBaseConfig = {
extends: [
require.resolve('eslint-config-standard')
]
};

neutrino.use(eslint({
eslint: {
baseConfig: eslintBaseConfig
}
}))

lint.aliasPlugins(
// ESLint config that contains only used resolvers
{
settings: {
'import/resolver': usedResolvers,
},
},
// Path to the current module file, so aliases can be correctly resolved from your package
// In most cases it is always `__filename`
__filename
);
```

**Important! Make sure all aliased import resolvers are present in your dependencies in package.json**

```json
{
"dependencies": {
"eslint-import-resolver-node": "latest"
}
}
```

## Contributing

This middleware is part of the
Expand Down
46 changes: 46 additions & 0 deletions packages/eslint/alias-import-resolvers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const moduleAlias = require('module-alias');
const pnpApi = process.versions.pnp ? require('pnpapi') : null; // eslint-disable-line import/no-unresolved

function toFullName(pluginName) {
const ESLINT_PREFIX = 'eslint-import-resolver-';
const ORGANIZATION_EXPRESSION = /^(@[\d.A-z-]+)\/(.+)$/;
const nameIsFull = pluginName.indexOf(ESLINT_PREFIX) === 0;
const nameIsOrganization = ORGANIZATION_EXPRESSION.test(pluginName);

if (nameIsOrganization) {
const [, organizationName, name] = pluginName.match(
ORGANIZATION_EXPRESSION,
);

return `${organizationName}/${toFullName(name)}`;
}

return nameIsFull ? pluginName : `${ESLINT_PREFIX}${pluginName}`;
}

function aliasModuleFrom(baseFilename = __filename) {
return function aliasImportResolver(importResolverName) {
let resolvedImportResolverPath;

if (pnpApi) {
resolvedImportResolverPath = pnpApi.resolveRequest(
importResolverName,
baseFilename,
);
} else {
resolvedImportResolverPath = require.resolve(importResolverName, {
paths: [baseFilename],
});
}

moduleAlias.addAlias(importResolverName, resolvedImportResolverPath);
};
}

module.exports = function aliasImportResolvers(eslintConfig, baseFilename) {
const { settings = {} } = eslintConfig;
const resolver = settings['import/resolver'] || {};
const resolversNames = Object.keys(resolver);

resolversNames.map(toFullName).forEach(aliasModuleFrom(baseFilename));
};
41 changes: 41 additions & 0 deletions packages/eslint/alias-plugins.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const moduleAlias = require('module-alias');
const pnpApi = process.versions.pnp ? require('pnpapi') : null; // eslint-disable-line import/no-unresolved

function toFullName(pluginName) {
const ESLINT_PREFIX = 'eslint-plugin-';
const ORGANIZATION_EXPRESSION = /^(@[\d.A-z-]+)\/(.+)$/;
const nameIsFull = pluginName.indexOf(ESLINT_PREFIX) === 0;
const nameIsOrganization = ORGANIZATION_EXPRESSION.test(pluginName);

if (nameIsOrganization) {
const [, organizationName, name] = pluginName.match(
ORGANIZATION_EXPRESSION,
);

return `${organizationName}/${toFullName(name)}`;
}

return nameIsFull ? pluginName : `${ESLINT_PREFIX}${pluginName}`;
}

function aliasModuleFrom(baseFilename = __filename) {
return function aliasPlugin(pluginName) {
let resolvedPluginPath;

if (pnpApi) {
resolvedPluginPath = pnpApi.resolveRequest(pluginName, baseFilename);
} else {
resolvedPluginPath = require.resolve(pluginName, {
paths: [baseFilename],
});
}

moduleAlias.addAlias(pluginName, resolvedPluginPath);
};
}

module.exports = function aliasPlugins(eslintConfig, baseFilename) {
const { plugins = [] } = eslintConfig;

plugins.map(toFullName).forEach(aliasModuleFrom(baseFilename));
};
11 changes: 9 additions & 2 deletions packages/eslint/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const { ConfigurationError, DuplicateRuleError } = require('neutrino/errors');
const aliasPlugins = require('./alias-plugins');
const aliasImportResolvers = require('./alias-import-resolvers');

const arrayToObject = (array) =>
array.reduce((obj, item) => Object.assign(obj, { [item]: true }), {});
Expand Down Expand Up @@ -125,7 +127,7 @@ module.exports = ({ test, include, exclude, eslint = {} } = {}) => {
}

const baseConfig = eslint.baseConfig || {};

const usedPlugins = ['babel'];
const loaderOptions = {
// For supported options, see:
// https://github.com/webpack-contrib/eslint-loader#options
Expand Down Expand Up @@ -172,10 +174,12 @@ module.exports = ({ test, include, exclude, eslint = {} } = {}) => {
// Unfortunately we can't `require.resolve('eslint-plugin-babel')` due to:
// https://github.com/eslint/eslint/issues/6237
// ...so we have no choice but to rely on it being hoisted.
plugins: ['babel', ...(baseConfig.plugins || [])],
plugins: [...usedPlugins, ...(baseConfig.plugins || [])],
},
};

aliasPlugins({ plugins: usedPlugins });

neutrino.config.module
.rule('lint')
.test(test || neutrino.regexFromExtensions())
Expand All @@ -192,3 +196,6 @@ module.exports = ({ test, include, exclude, eslint = {} } = {}) => {
neutrino.register('eslintrc', eslintrc);
};
};

module.exports.aliasPlugins = aliasPlugins;
module.exports.aliasImportResolvers = aliasImportResolvers;
10 changes: 8 additions & 2 deletions packages/eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@
"dependencies": {
"babel-eslint": "^10.1.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-babel": "^5.3.1"
"eslint-plugin-babel": "^5.3.1",
"module-alias": "^2.2.2"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0",
"neutrino": "^9.0.0",
"webpack": "^4.0.0"
}
},
"peerDependenciesMeta": {
"webpack": {
"optional": true
}
}
}
1 change: 1 addition & 0 deletions packages/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"dependencies": {
"@babel/core": "^7.11.4",
"@neutrinojs/eslint": "9.4.0",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^26.3.0",
"deepmerge": "^1.5.2",
Expand Down
Loading