Skip to content

Commit

Permalink
New: no-extraneous-* rules (refs #71)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed Jun 2, 2017
1 parent deb4f55 commit a75c638
Show file tree
Hide file tree
Showing 28 changed files with 610 additions and 8 deletions.
18 changes: 10 additions & 8 deletions README.md
Expand Up @@ -11,7 +11,7 @@ Additional ESLint's rules for Node.js
## :cd: Install & Usage

```
> npm install --save-dev eslint eslint-plugin-node
$ npm install --save-dev eslint eslint-plugin-node
```

- Requires Node.js `^4.0.0 || >=6.0.0`
Expand Down Expand Up @@ -45,22 +45,24 @@ Additional ESLint's rules for Node.js

## :bulb: Rules

- :star: - the mark of recommended rules.
- :pencil: - the mark of fixable rules.

| | | Rule ID | Description |
|:------:|:--------:|:-----------------------------------------------------------------|:------------|
| | | [exports-style](docs/rules/exports-style.md) | Enforce either `module.exports` or `exports`.
| :star: | | [no-deprecated-api](docs/rules/no-deprecated-api.md) | Disallow deprecated API.
| | | [no-missing-import](docs/rules/no-missing-import.md) | Disallow `import` declarations for files that don't exist. :warning:
| :star: | | [no-missing-require](docs/rules/no-missing-require.md) | Disallow `require()`s for files that don't exist.
| | | [no-extraneous-import](docs/rules/no-extraneous-import.md) | Disallow `import` declarations of extraneous packages. :warning:
| :star: | | [no-extraneous-require](docs/rules/no-extraneous-require.md) | Disallow `require()` expressions of extraneous packages.
| | | [no-missing-import](docs/rules/no-missing-import.md) | Disallow `import` declarations of files that don't exist. :warning:
| :star: | | [no-missing-require](docs/rules/no-missing-require.md) | Disallow `require()` expressions of files that don't exist.
| :star: | | [no-unpublished-bin](docs/rules/no-unpublished-bin.md) | Disallow `bin` files that npm ignores.
| | | [no-unpublished-import](docs/rules/no-unpublished-import.md) | Disallow `import` declarations for files that npm ignores. :warning:
| :star: | | [no-unpublished-require](docs/rules/no-unpublished-require.md) | Disallow `require()`s for files that npm ignores.
| | | [no-unpublished-import](docs/rules/no-unpublished-import.md) | Disallow `import` declarations of files that npm ignores. :warning:
| :star: | | [no-unpublished-require](docs/rules/no-unpublished-require.md) | Disallow `require()` expressions of files that npm ignores.
| :star: | | [no-unsupported-features](docs/rules/no-unsupported-features.md) | Disallow unsupported ECMAScript features on the specified version.
| :star: | | [process-exit-as-throw](docs/rules/process-exit-as-throw.md) | Make the same code path as throw at `process.exit()`.
| :star: | :pencil: | [shebang](docs/rules/shebang.md) | Suggest correct usage of shebang.

- :star: - the mark of a recommended rule.
- :pencil: - the mark of a fixable rule.

## :wrench: Configs

This plugin provides `plugin:node/recommended` preset config.
Expand Down
60 changes: 60 additions & 0 deletions docs/rules/no-extraneous-import.md
@@ -0,0 +1,60 @@
# Disallow `import` declarations which import extraneous modules (no-extraneous-import)

If a `import` declaration's source is extraneous (it's not written in `package.json`), the program works in local, but will not work after dependencies are re-installed. It will cause troubles to your team/contributors.
This rule disallows `import` declarations of extraneous modules.

## Rule Details

This rule warns `import` declarations of extraneous modules.

## Options

```json
{
"rules": {
"node/no-extraneous-import": ["error", {
"allowModules": []
}]
}
}
```

### allowModules

Some platforms have additional embedded modules.
For example, Electron has `electron` module.

We can specify additional embedded modules with this option.
This option is an array of strings as module names.

```json
{
"rules": {
"node/no-extraneous-import": ["error", {
"allowModules": ["electron"]
}]
}
}
```

## Shared Settings

The following options can be set by [shared settings](http://eslint.org/docs/user-guide/configuring.html#adding-shared-settings).
Several rules have the same option, but we can set this option at once.

- `allowModules`

For Example:

```json
{
"settings": {
"node": {
"allowModules": ["electron"]
}
},
"rules": {
"node/no-extraneous-import": "error"
}
}
```
60 changes: 60 additions & 0 deletions docs/rules/no-extraneous-require.md
@@ -0,0 +1,60 @@
# Disallow `require()`s which import extraneous modules (no-extraneous-require)

If a `require()`'s target is extraneous (it's not written in `package.json`), the program works in local, but will not work after dependencies are re-installed. It will cause troubles to your team/contributors.
This rule disallows `require()` of extraneous modules.

## Rule Details

This rule warns `require()` of extraneous modules.

## Options

```json
{
"rules": {
"node/no-extraneous-require": ["error", {
"allowModules": []
}]
}
}
```

### allowModules

Some platforms have additional embedded modules.
For example, Electron has `electron` module.

We can specify additional embedded modules with this option.
This option is an array of strings as module names.

```json
{
"rules": {
"node/no-extraneous-require": ["error", {
"allowModules": ["electron"]
}]
}
}
```

## Shared Settings

The following options can be set by [shared settings](http://eslint.org/docs/user-guide/configuring.html#adding-shared-settings).
Several rules have the same option, but we can set this option at once.

- `allowModules`

For Example:

```json
{
"settings": {
"node": {
"allowModules": ["electron"]
}
},
"rules": {
"node/no-extraneous-require": "error"
}
}
```
68 changes: 68 additions & 0 deletions lib/rules/no-extraneous-import.js
@@ -0,0 +1,68 @@
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const checkExtraneous = require("../util/check-extraneous")
const getAllowModules = require("../util/get-allow-modules")
const getConvertPath = require("../util/get-convert-path")
const getImportTargets = require("../util/get-import-export-targets")

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* The definition of this rule.
*
* @param {RuleContext} context - The rule context to check.
* @returns {object} The definition of this rule.
*/
function create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}

return {
"Program:exit"(node) {
checkExtraneous(
context,
filePath,
getImportTargets(context, node, false)
)
},
}
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
create,
meta: {
docs: {
description: "disallow `import` declarations of extraneous packages",
category: "Possible Errors",
recommended: false,
},
fixable: false,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
},
additionalProperties: false,
},
],
},
}
68 changes: 68 additions & 0 deletions lib/rules/no-extraneous-require.js
@@ -0,0 +1,68 @@
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const checkExtraneous = require("../util/check-extraneous")
const getAllowModules = require("../util/get-allow-modules")
const getConvertPath = require("../util/get-convert-path")
const getRequireTargets = require("../util/get-require-targets")

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* The definition of this rule.
*
* @param {RuleContext} context - The rule context to check.
* @returns {object} The definition of this rule.
*/
function create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}

return {
"Program:exit"() {
checkExtraneous(
context,
filePath,
getRequireTargets(context, false)
)
},
}
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
create,
meta: {
docs: {
description: "disallow `require()` expressions for extraneous packages",
category: "Possible Errors",
recommended: false,
},
fixable: false,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
},
additionalProperties: false,
},
],
},
}
95 changes: 95 additions & 0 deletions lib/util/check-extraneous.js
@@ -0,0 +1,95 @@
/**
* @author Toru Nagashima
* @copyright 2017 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
"use strict"

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

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

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Check whether the given package exists or not.
* @param {string} name The package name to check.
* @param {string} basedir The path to starting directory.
* @returns {boolean} `true` if the package was found.
*/
function exists(name, basedir) {
try {
resolve.sync(name, {basedir})
return true
}
catch (_err) {
return false
}
}

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
* Checks whether or not each requirement target is published via package.json.
*
* It reads package.json and checks the target exists in `dependencies`.
*
* @param {RuleContext} context - A context to report.
* @param {string} filePath - The current file path.
* @param {ImportTarget[]} targets - A list of target information to check.
* @returns {void}
*/
module.exports = function checkForExtraeous(context, filePath, targets) {
const packageInfo = getPackageJson(filePath)
if (!packageInfo) {
return
}

const allowed = new Set(getAllowModules(context))
const convertPath = getConvertPath(context)
const rootPath = path.dirname(packageInfo.filePath)
const convertedPath = path.resolve(
rootPath,
convertPath(
path.relative(rootPath, path.resolve(filePath))
)
)
const basedir = path.dirname(convertedPath)
const dependencies = new Set(
[].concat(
Object.keys(packageInfo.dependencies || {}),
Object.keys(packageInfo.devDependencies || {}),
Object.keys(packageInfo.peerDependencies || {}),
Object.keys(packageInfo.optionalDependencies || {})
)
)

for (const target of targets) {
const name = target.moduleName
const extraneous = (
name != null &&
!dependencies.has(name) &&
!allowed.has(name) &&
exists(name, basedir)
)

if (extraneous) {
context.report({
node: target.node,
loc: target.node.loc,
message: "\"{{moduleName}}\" is extraneous.",
data: target,
})
}
}
}
Empty file.
Empty file.
Empty file.

0 comments on commit a75c638

Please sign in to comment.