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 yarn workspaces support in no-extraneous-import #234

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
14 changes: 12 additions & 2 deletions docs/rules/no-extraneous-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ This rule warns `import` declarations of extraneous modules.
"node/no-extraneous-import": ["error", {
"allowModules": [],
"resolvePaths": [],
"tryExtensions": []
"tryExtensions": [],
"yarnWorkspaces": false
}]
}
}
Expand Down Expand Up @@ -55,6 +56,13 @@ When an import path does not exist, this rule checks whether or not any of `path

Default is `[".js", ".json", ".node"]`.

#### yarnWorkspaces

When using [yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces), the dependencies are
defined in the top level folder.

Default is `false`

### Shared Settings

The following options can be set by [shared settings](http://eslint.org/docs/user-guide/configuring.html#adding-shared-settings).
Expand All @@ -63,6 +71,7 @@ Several rules have the same option, but we can set this option at once.
- `allowModules`
- `resolvePaths`
- `tryExtensions`
- `yarnWorkspaces`

```js
// .eslintrc.js
Expand All @@ -71,7 +80,8 @@ module.exports = {
"node": {
"allowModules": ["electron"],
"resolvePaths": [__dirname],
"tryExtensions": [".js", ".json", ".node"]
"tryExtensions": [".js", ".json", ".node"],
"yarnWorkspaces": true
}
},
"rules": {
Expand Down
2 changes: 2 additions & 0 deletions lib/rules/no-extraneous-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const getAllowModules = require("../util/get-allow-modules")
const getConvertPath = require("../util/get-convert-path")
const getResolvePaths = require("../util/get-resolve-paths")
const getTryExtensions = require("../util/get-try-extensions")
const getYarnWorkspaces = require("../util/get-yarn-workspaces")
const visitImport = require("../util/visit-import")

module.exports = {
Expand All @@ -31,6 +32,7 @@ module.exports = {
convertPath: getConvertPath.schema,
resolvePaths: getResolvePaths.schema,
tryExtensions: getTryExtensions.schema,
yarnWorkspaces: getYarnWorkspaces.schema,
},
additionalProperties: false,
},
Expand Down
11 changes: 10 additions & 1 deletion lib/util/check-extraneous.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
"use strict"

const path = require("path")
const getAllowModules = require("./get-allow-modules")
const getPackageJson = require("./get-package-json")

Expand All @@ -18,7 +19,15 @@ const getPackageJson = require("./get-package-json")
* @returns {void}
*/
module.exports = function checkForExtraneous(context, filePath, targets) {
const packageInfo = getPackageJson(filePath)
const options = context.options[0] || {}

let packageInfo = getPackageJson(filePath)

// When using yarn workspaces, we should look in the parent folder
if (options.yarnWorkspaces && packageInfo && !packageInfo.workspaces) {
packageInfo = getPackageJson(path.dirname(packageInfo.filePath))
}

if (!packageInfo) {
return
}
Expand Down
42 changes: 42 additions & 0 deletions lib/util/get-yarn-workspaces.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"

const DEFAULT_VALUE = false

/**
* Gets `yarnWorkspaces` property from a given option object.
*
* @param {object|undefined} option - An option object to get.
* @returns {boolean|null} The `yarnWorkspaces` value, or `null`.
*/
function get(option) {
if (option && "yarnWorkspaces" in option) {
return option.yarnWorkspaces
}
return null
}

/**
* Gets "yarnWorkspaces" setting.
*
* 1. This checks `options` property, then returns it if exists.
* 2. This checks `settings.node` property, then returns it if exists.
* 3. This returns `false`.
*
* @param {RuleContext} context - The rule context.
* @returns {boolean}
*/
module.exports = function getTryExtensions(context, optionIndex = 0) {
return (
get(context.options && context.options[optionIndex]) ||
get(context.settings && context.settings.node) ||
DEFAULT_VALUE
)
}

module.exports.schema = {
type: "boolean",
}
4 changes: 4 additions & 0 deletions tests/fixtures/no-extraneous/yarnWorkspaces/aaa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "aaa",
"version": "0.0.0"
}
4 changes: 4 additions & 0 deletions tests/fixtures/no-extraneous/yarnWorkspaces/bbb/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "bbb",
"version": "0.0.0"
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
12 changes: 12 additions & 0 deletions tests/fixtures/no-extraneous/yarnWorkspaces/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"private": true,
"name": "test",
"version": "0.0.0",
"dependencies": {
"ccc": "*"
},
"workspaces": [
"aaa",
"bbb"
]
}
61 changes: 61 additions & 0 deletions tests/lib/rules/no-extraneous-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
"use strict"

const fs = require("fs")
const path = require("path")
const { Linter, RuleTester } = require("eslint")
const rule = require("../../../lib/rules/no-extraneous-import")
Expand Down Expand Up @@ -31,6 +32,16 @@ function fixture(name) {
return path.resolve(__dirname, "../../fixtures/no-extraneous", name)
}

// We need to simulate `yarn workspaces` by creating symlinks inside `node_modules`
fs.symlinkSync(
fixture("yarnWorkspaces/aaa"),
fixture("yarnWorkspaces/node_modules/aaa")
)
fs.symlinkSync(
fixture("yarnWorkspaces/bbb"),
fixture("yarnWorkspaces/node_modules/bbb")
)

const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 2015,
Expand Down Expand Up @@ -71,12 +82,51 @@ ruleTester.run("no-extraneous-import", rule, {
filename: fixture("optionalDependencies/a.js"),
code: "import aaa from 'aaa'",
},
{
filename: fixture("yarnWorkspaces/a.js"),
code: "import aaa from 'aaa'",
},
{
filename: fixture("yarnWorkspaces/b.js"),
code: "import bbb from 'bbb'",
},
{
filename: fixture("yarnWorkspaces/c.js"),
code: "import ccc from 'ccc'",
},

// missing packages are warned by no-missing-import
{
filename: fixture("dependencies/a.js"),
code: "import ccc from 'ccc'",
},

// yarnWorkspaces
{
filename: fixture("yarnWorkspaces/a.js"),
code: "import aaa from 'aaa'",
options: [{ yarnWorkspaces: true }],
},
{
filename: fixture("yarnWorkspaces/b.js"),
code: "import bbb from 'bbb'",
options: [{ yarnWorkspaces: true }],
},
{
filename: fixture("yarnWorkspaces/c.js"),
code: "import ccc from 'ccc'",
options: [{ yarnWorkspaces: true }],
},
{
filename: fixture("yarnWorkspaces/aaa/c.js"),
code: "import ccc from 'ccc'",
options: [{ yarnWorkspaces: true }],
},
{
filename: fixture("yarnWorkspaces/bbb/c.js"),
code: "import ccc from 'ccc'",
options: [{ yarnWorkspaces: true }],
},
],
invalid: [
{
Expand All @@ -100,6 +150,17 @@ ruleTester.run("no-extraneous-import", rule, {
errors: ['"bbb" is extraneous.'],
},

{
filename: fixture("yarnWorkspaces/aaa/c.js"),
code: "import ccc from 'ccc'",
errors: ['"ccc" is extraneous.'],
},
{
filename: fixture("yarnWorkspaces/bbb/c.js"),
code: "import ccc from 'ccc'",
errors: ['"ccc" is extraneous.'],
},

// import()
...(DynamicImportSupported
? [
Expand Down