node_modules

This commit is contained in:
saeid
2019-07-15 13:07:31 +04:30
parent d3735c6d30
commit ce1a3ebf9c
35713 changed files with 3639219 additions and 1 deletions
+297
View File
@@ -0,0 +1,297 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
/*istanbul ignore next */
/**
* This function is copied from https://github.com/eslint/eslint/blob/2355f8d0de1d6732605420d15ddd4f1eee3c37b6/lib/ast-utils.js#L648-L684
*
* @param {ASTNode} node - The node to get.
* @returns {string|null} The property name if static. Otherwise, null.
* @private
*/
function getStaticPropertyName(node) {
let prop = null
switch (node && node.type) {
case "Property":
case "MethodDefinition":
prop = node.key
break
case "MemberExpression":
prop = node.property
break
// no default
}
switch (prop && prop.type) {
case "Literal":
return String(prop.value)
case "TemplateLiteral":
if (prop.expressions.length === 0 && prop.quasis.length === 1) {
return prop.quasis[0].value.cooked
}
break
case "Identifier":
if (!node.computed) {
return prop.name
}
break
// no default
}
return null
}
/**
* Checks whether the given node is assignee or not.
*
* @param {ASTNode} node - The node to check.
* @returns {boolean} `true` if the node is assignee.
*/
function isAssignee(node) {
return (
node.parent.type === "AssignmentExpression" && node.parent.left === node
)
}
/**
* Gets the top assignment expression node if the given node is an assignee.
*
* This is used to distinguish 2 assignees belong to the same assignment.
* If the node is not an assignee, this returns null.
*
* @param {ASTNode} leafNode - The node to get.
* @returns {ASTNode|null} The top assignment expression node, or null.
*/
function getTopAssignment(leafNode) {
let node = leafNode
// Skip MemberExpressions.
while (
node.parent.type === "MemberExpression" &&
node.parent.object === node
) {
node = node.parent
}
// Check assignments.
if (!isAssignee(node)) {
return null
}
// Find the top.
while (node.parent.type === "AssignmentExpression") {
node = node.parent
}
return node
}
/**
* Gets top assignment nodes of the given node list.
*
* @param {ASTNode[]} nodes - The node list to get.
* @returns {ASTNode[]} Gotten top assignment nodes.
*/
function createAssignmentList(nodes) {
return nodes.map(getTopAssignment).filter(Boolean)
}
/**
* Gets the reference of `module.exports` from the given scope.
*
* @param {escope.Scope} scope - The scope to get.
* @returns {ASTNode[]} Gotten MemberExpression node list.
*/
function getModuleExportsNodes(scope) {
const variable = scope.set.get("module")
if (variable == null) {
return []
}
return variable.references
.map(reference => reference.identifier.parent)
.filter(
node =>
node.type === "MemberExpression" &&
getStaticPropertyName(node) === "exports"
)
}
/**
* Gets the reference of `exports` from the given scope.
*
* @param {escope.Scope} scope - The scope to get.
* @returns {ASTNode[]} Gotten Identifier node list.
*/
function getExportsNodes(scope) {
const variable = scope.set.get("exports")
if (variable == null) {
return []
}
return variable.references.map(reference => reference.identifier)
}
module.exports = {
meta: {
docs: {
description: "enforce either `module.exports` or `exports`",
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/exports-style.md",
},
type: "suggestion",
fixable: null,
schema: [
{
//
enum: ["module.exports", "exports"],
},
{
type: "object",
properties: { allowBatchAssign: { type: "boolean" } },
additionalProperties: false,
},
],
},
create(context) {
const mode = context.options[0] || "module.exports"
const batchAssignAllowed = Boolean(
context.options[1] != null && context.options[1].allowBatchAssign
)
const sourceCode = context.getSourceCode()
/**
* Gets the location info of reports.
*
* exports = foo
* ^^^^^^^^^
*
* module.exports = foo
* ^^^^^^^^^^^^^^^^
*
* @param {ASTNode} node - The node of `exports`/`module.exports`.
* @returns {Location} The location info of reports.
*/
function getLocation(node) {
const token = sourceCode.getTokenAfter(node)
return {
start: node.loc.start,
end: token.loc.end,
}
}
/**
* Enforces `module.exports`.
* This warns references of `exports`.
*
* @returns {void}
*/
function enforceModuleExports() {
const globalScope = context.getScope()
const exportsNodes = getExportsNodes(globalScope)
const assignList = batchAssignAllowed
? createAssignmentList(getModuleExportsNodes(globalScope))
: []
for (const node of exportsNodes) {
// Skip if it's a batch assignment.
if (
assignList.length > 0 &&
assignList.indexOf(getTopAssignment(node)) !== -1
) {
continue
}
// Report.
context.report({
node,
loc: getLocation(node),
message:
"Unexpected access to 'exports'. Use 'module.exports' instead.",
})
}
}
/**
* Enforces `exports`.
* This warns references of `module.exports`.
*
* @returns {void}
*/
function enforceExports() {
const globalScope = context.getScope()
const exportsNodes = getExportsNodes(globalScope)
const moduleExportsNodes = getModuleExportsNodes(globalScope)
const assignList = batchAssignAllowed
? createAssignmentList(exportsNodes)
: []
const batchAssignList = []
for (const node of moduleExportsNodes) {
// Skip if it's a batch assignment.
if (assignList.length > 0) {
const found = assignList.indexOf(getTopAssignment(node))
if (found !== -1) {
batchAssignList.push(assignList[found])
assignList.splice(found, 1)
continue
}
}
// Report.
context.report({
node,
loc: getLocation(node),
message:
"Unexpected access to 'module.exports'. Use 'exports' instead.",
})
}
// Disallow direct assignment to `exports`.
for (const node of exportsNodes) {
// Skip if it's not assignee.
if (!isAssignee(node)) {
continue
}
// Check if it's a batch assignment.
if (batchAssignList.indexOf(getTopAssignment(node)) !== -1) {
continue
}
// Report.
context.report({
node,
loc: getLocation(node),
message:
"Unexpected assignment to 'exports'. Don't modify 'exports' itself.",
})
}
}
return {
"Program:exit"() {
switch (mode) {
case "module.exports":
enforceModuleExports()
break
case "exports":
enforceExports()
break
// no default
}
},
}
},
}
+560
View File
@@ -0,0 +1,560 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { CALL, CONSTRUCT, READ, ReferenceTracker } = require("eslint-utils")
const enumeratePropertyNames = require("../util/enumerate-property-names")
const modules = {
_linklist: {
[READ]: { since: "5.0.0", replacedBy: null },
},
assert: {
deepEqual: {
[READ]: {
since: "10.0.0",
replacedBy:
"'assert.deepStrictEqual' or 'assert.strict.deepEqual'",
},
},
equal: {
[READ]: {
since: "10.0.0",
replacedBy: "'assert.strictEqual' or 'assert.strict.equal'",
},
},
notDeepEqual: {
[READ]: {
since: "10.0.0",
replacedBy:
"'assert.notDeepStrictEqual' or 'assert.strict.notDeepEqual'",
},
},
notEqual: {
[READ]: {
since: "10.0.0",
replacedBy:
"'assert.notStrictEqual' or 'assert.strict.notEqual'",
},
},
},
//eslint-disable-next-line camelcase
async_hooks: {
currentId: {
[READ]: {
since: "8.2.0",
replacedBy: "'async_hooks.executionAsyncId()'",
},
},
triggerId: {
[READ]: {
since: "8.2.0",
replacedBy: "'async_hooks.triggerAsyncId()'",
},
},
},
buffer: {
Buffer: {
[CONSTRUCT]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
},
[CALL]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
},
},
SlowBuffer: {
[READ]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.allocUnsafeSlow()'",
},
},
},
constants: {
[READ]: {
since: "6.3.0",
replacedBy: "'constants' property of each module",
},
},
crypto: {
_toBuf: {
[READ]: { since: "11.0.0", replacedBy: null },
},
Credentials: {
[READ]: { since: "0.12.0", replacedBy: "'tls.SecureContext'" },
},
DEFAULT_ENCODING: {
[READ]: { since: "10.0.0", replacedBy: null },
},
createCipher: {
[READ]: {
since: "10.0.0",
replacedBy: "'crypto.createCipheriv()'",
},
},
createCredentials: {
[READ]: {
since: "0.12.0",
replacedBy: "'tls.createSecureContext()'",
},
},
createDecipher: {
[READ]: {
since: "10.0.0",
replacedBy: "'crypto.createDecipheriv()'",
},
},
fips: {
[READ]: {
since: "10.0.0",
replacedBy: "'crypto.getFips()' and 'crypto.setFips()'",
},
},
prng: {
[READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
},
pseudoRandomBytes: {
[READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
},
rng: {
[READ]: { since: "11.0.0", replacedBy: "'crypto.randomBytes()'" },
},
},
domain: {
[READ]: { since: "4.0.0", replacedBy: null },
},
events: {
EventEmitter: {
listenerCount: {
[READ]: {
since: "4.0.0",
replacedBy: "'events.EventEmitter#listenerCount()'",
},
},
},
listenerCount: {
[READ]: {
since: "4.0.0",
replacedBy: "'events.EventEmitter#listenerCount()'",
},
},
},
freelist: {
[READ]: { since: "4.0.0", replacedBy: null },
},
fs: {
SyncWriteStream: {
[READ]: { since: "4.0.0", replacedBy: null },
},
exists: {
[READ]: {
since: "4.0.0",
replacedBy: "'fs.stat()' or 'fs.access()'",
},
},
lchmod: {
[READ]: { since: "0.4.0", replacedBy: null },
},
lchmodSync: {
[READ]: { since: "0.4.0", replacedBy: null },
},
lchown: {
[READ]: { since: "0.4.0", replacedBy: null },
},
lchownSync: {
[READ]: { since: "0.4.0", replacedBy: null },
},
},
http: {
createClient: {
[READ]: { since: "0.10.0", replacedBy: "'http.request()'" },
},
},
module: {
Module: {
requireRepl: {
[READ]: { since: "6.0.0", replacedBy: "'require(\"repl\")'" },
},
_debug: {
[READ]: { since: "9.0.0", replacedBy: null },
},
},
requireRepl: {
[READ]: { since: "6.0.0", replacedBy: "'require(\"repl\")'" },
},
_debug: {
[READ]: { since: "9.0.0", replacedBy: null },
},
},
os: {
getNetworkInterfaces: {
[READ]: { since: "0.6.0", replacedBy: "'os.networkInterfaces()'" },
},
tmpDir: {
[READ]: { since: "7.0.0", replacedBy: "'os.tmpdir()'" },
},
},
path: {
_makeLong: {
[READ]: { since: "9.0.0", replacedBy: "'path.toNamespacedPath()'" },
},
},
process: {
EventEmitter: {
[READ]: { since: "0.6.0", replacedBy: "'require(\"events\")'" },
},
assert: {
[READ]: { since: "10.0.0", replacedBy: "'require(\"assert\")'" },
},
binding: {
[READ]: { since: "10.9.0", replacedBy: null },
},
env: {
NODE_REPL_HISTORY_FILE: {
[READ]: { since: "4.0.0", replacedBy: "'NODE_REPL_HISTORY'" },
},
},
},
punycode: {
[READ]: {
since: "7.0.0",
replacedBy: "'https://www.npmjs.com/package/punycode'",
},
},
readline: {
codePointAt: {
[READ]: { since: "4.0.0", replacedBy: null },
},
getStringWidth: {
[READ]: { since: "6.0.0", replacedBy: null },
},
isFullWidthCodePoint: {
[READ]: { since: "6.0.0", replacedBy: null },
},
stripVTControlCharacters: {
[READ]: { since: "6.0.0", replacedBy: null },
},
},
// safe-buffer.Buffer function/constructror is just a re-export of buffer.Buffer
// and should be deprecated likewise.
"safe-buffer": {
Buffer: {
[CONSTRUCT]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
},
[CALL]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.alloc()' or 'buffer.Buffer.from()'",
},
},
SlowBuffer: {
[READ]: {
since: "6.0.0",
replacedBy: "'buffer.Buffer.allocUnsafeSlow()'",
},
},
},
sys: {
[READ]: { since: "0.3.0", replacedBy: "'util' module" },
},
timers: {
enroll: {
[READ]: {
since: "10.0.0",
replacedBy: "'setTimeout()' or 'setInterval()'",
},
},
unenroll: {
[READ]: {
since: "10.0.0",
replacedBy: "'clearTimeout()' or 'clearInterval()'",
},
},
},
tls: {
CleartextStream: {
[READ]: { since: "0.10.0", replacedBy: null },
},
CryptoStream: {
[READ]: { since: "0.12.0", replacedBy: "'tls.TLSSocket'" },
},
SecurePair: {
[READ]: { since: "6.0.0", replacedBy: "'tls.TLSSocket'" },
},
convertNPNProtocols: {
[READ]: { since: "10.0.0", replacedBy: null },
},
createSecurePair: {
[READ]: { since: "6.0.0", replacedBy: "'tls.TLSSocket'" },
},
parseCertString: {
[READ]: { since: "8.6.0", replacedBy: "'querystring.parse()'" },
},
},
tty: {
setRawMode: {
[READ]: {
since: "0.10.0",
replacedBy:
"'tty.ReadStream#setRawMode()' (e.g. 'process.stdin.setRawMode()')",
},
},
},
url: {
parse: {
[READ]: { since: "11.0.0", replacedBy: "'url.URL' constructor" },
},
resolve: {
[READ]: { since: "11.0.0", replacedBy: "'url.URL' constructor" },
},
},
util: {
debug: {
[READ]: { since: "0.12.0", replacedBy: "'console.error()'" },
},
error: {
[READ]: { since: "0.12.0", replacedBy: "'console.error()'" },
},
isArray: {
[READ]: { since: "4.0.0", replacedBy: "'Array.isArray()'" },
},
isBoolean: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isBuffer: {
[READ]: { since: "4.0.0", replacedBy: "'Buffer.isBuffer()'" },
},
isDate: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isError: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isFunction: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isNull: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isNullOrUndefined: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isNumber: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isObject: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isPrimitive: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isRegExp: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isString: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isSymbol: {
[READ]: { since: "4.0.0", replacedBy: null },
},
isUndefined: {
[READ]: { since: "4.0.0", replacedBy: null },
},
log: {
[READ]: { since: "6.0.0", replacedBy: "a third party module" },
},
print: {
[READ]: { since: "0.12.0", replacedBy: "'console.log()'" },
},
pump: {
[READ]: { since: "0.10.0", replacedBy: "'stream.Readable#pipe()'" },
},
puts: {
[READ]: { since: "0.12.0", replacedBy: "'console.log()'" },
},
_extend: {
[READ]: { since: "6.0.0", replacedBy: "'Object.assign()'" },
},
},
vm: {
runInDebugContext: {
[READ]: { since: "8.0.0", replacedBy: null },
},
},
}
const globals = {
Buffer: {
[CONSTRUCT]: {
since: "6.0.0",
replacedBy: "'Buffer.alloc()' or 'Buffer.from()'",
},
[CALL]: {
since: "6.0.0",
replacedBy: "'Buffer.alloc()' or 'Buffer.from()'",
},
},
COUNTER_NET_SERVER_CONNECTION: {
[READ]: { since: "11.0.0", replacedBy: null },
},
COUNTER_NET_SERVER_CONNECTION_CLOSE: {
[READ]: { since: "11.0.0", replacedBy: null },
},
COUNTER_HTTP_SERVER_REQUEST: {
[READ]: { since: "11.0.0", replacedBy: null },
},
COUNTER_HTTP_SERVER_RESPONSE: {
[READ]: { since: "11.0.0", replacedBy: null },
},
COUNTER_HTTP_CLIENT_REQUEST: {
[READ]: { since: "11.0.0", replacedBy: null },
},
COUNTER_HTTP_CLIENT_RESPONSE: {
[READ]: { since: "11.0.0", replacedBy: null },
},
GLOBAL: {
[READ]: { since: "6.0.0", replacedBy: "'global'" },
},
Intl: {
v8BreakIterator: {
[READ]: { since: "7.0.0", replacedBy: null },
},
},
require: {
extensions: {
[READ]: {
since: "0.12.0",
replacedBy: "compiling them ahead of time",
},
},
},
root: {
[READ]: { since: "6.0.0", replacedBy: "'global'" },
},
process: modules.process,
}
/**
* Makes a replacement message.
*
* @param {string|null} replacedBy - The text of substitute way.
* @returns {string} Replacement message.
*/
function toReplaceMessage(replacedBy) {
return replacedBy ? `. Use ${replacedBy} instead` : ""
}
/**
* Convert a given path to name.
* @param {symbol} type The report type.
* @param {string[]} path The property access path.
* @returns {string} The name.
*/
function toName(type, path) {
const baseName = path.join(".")
return type === ReferenceTracker.CALL
? `${baseName}()`
: type === ReferenceTracker.CONSTRUCT
? `new ${baseName}()`
: baseName
}
module.exports = {
meta: {
docs: {
description: "disallow deprecated APIs",
category: "Best Practices",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-deprecated-api.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
ignoreModuleItems: {
type: "array",
items: {
enum: Array.from(enumeratePropertyNames(modules)),
},
additionalItems: false,
uniqueItems: true,
},
ignoreGlobalItems: {
type: "array",
items: {
enum: Array.from(enumeratePropertyNames(globals)),
},
additionalItems: false,
uniqueItems: true,
},
// Deprecated since v4.2.0
ignoreIndirectDependencies: { type: "boolean" },
},
additionalProperties: false,
},
],
},
create(context) {
const options = context.options[0] || {}
const ignoredModuleItems = new Set(options.ignoreModuleItems || [])
const ignoredGlobalItems = new Set(options.ignoreGlobalItems || [])
/**
* Reports a use of a deprecated API.
*
* @param {ASTNode} node - A node to report.
* @param {string} name - The name of a deprecated API.
* @param {{since: number, replacedBy: string}} info - Information of the API.
* @returns {void}
*/
function reportItem(node, name, info) {
context.report({
node,
loc: node.loc,
message:
"{{name}} was deprecated since v{{version}}{{replace}}.",
data: {
name,
version: info.since,
replace: toReplaceMessage(info.replacedBy),
},
})
}
return {
"Program:exit"() {
const tracker = new ReferenceTracker(context.getScope(), {
mode: "legacy",
})
for (const report of tracker.iterateGlobalReferences(globals)) {
const { node, path, type, info } = report
const name = toName(type, path)
if (!ignoredGlobalItems.has(name)) {
reportItem(node, `'${name}'`, info)
}
}
for (const report of [
...tracker.iterateCjsReferences(modules),
...tracker.iterateEsmReferences(modules),
]) {
const { node, path, type, info } = report
const name = toName(type, path)
const suffix = path.length === 1 ? " module" : ""
if (!ignoredModuleItems.has(name)) {
reportItem(node, `'${name}'${suffix}`, info)
}
}
},
}
},
}
+53
View File
@@ -0,0 +1,53 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
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")
const getResolvePaths = require("../util/get-resolve-paths")
module.exports = {
meta: {
docs: {
description:
"disallow `import` declarations of extraneous packages",
category: "Possible Errors",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-extraneous-import.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
resolvePaths: getResolvePaths.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"(node) {
checkExtraneous(
context,
filePath,
getImportTargets(context, node, false)
)
},
}
},
}
+53
View File
@@ -0,0 +1,53 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
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")
const getResolvePaths = require("../util/get-resolve-paths")
module.exports = {
meta: {
docs: {
description:
"disallow `require()` expressions of extraneous packages",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-extraneous-require.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
resolvePaths: getResolvePaths.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"() {
checkExtraneous(
context,
filePath,
getRequireTargets(context, false)
)
},
}
},
}
+144
View File
@@ -0,0 +1,144 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*
* @deprecated since v4.2.0
* This rule was based on an invalid assumption.
* No meaning.
*/
"use strict"
const path = require("path")
const resolve = require("resolve")
const getPackageJson = require("../util/get-package-json")
const getRequireTargets = require("../util/get-require-targets")
const getImportExportTargets = require("../util/get-import-export-targets")
const CORE_MODULES = new Set([
"assert",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
/* "domain", */ "events",
"fs",
"http",
"https",
"module",
"net",
"os",
"path",
/* "punycode", */ "querystring",
"readline",
"repl",
"stream",
"string_decoder",
"timers",
"tls",
"tty",
"url",
"util",
"vm",
"zlib",
])
const BACK_SLASH = /\\/gu
module.exports = {
meta: {
docs: {
description:
"disallow third-party modules which are hiding core modules",
category: "Possible Errors",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-hide-core-modules.md",
},
type: "problem",
deprecated: true,
fixable: null,
schema: [
{
type: "object",
properties: {
allow: {
type: "array",
items: { enum: Array.from(CORE_MODULES) },
additionalItems: false,
uniqueItems: true,
},
ignoreDirectDependencies: { type: "boolean" },
ignoreIndirectDependencies: { type: "boolean" },
},
additionalProperties: false,
},
],
},
create(context) {
if (context.getFilename() === "<input>") {
return {}
}
const filePath = path.resolve(context.getFilename())
const dirPath = path.dirname(filePath)
const packageJson = getPackageJson(filePath)
const deps = new Set(
[].concat(
Object.keys((packageJson && packageJson.dependencies) || {}),
Object.keys((packageJson && packageJson.devDependencies) || {})
)
)
const options = context.options[0] || {}
const allow = options.allow || []
const ignoreDirectDependencies = Boolean(
options.ignoreDirectDependencies
)
const ignoreIndirectDependencies = Boolean(
options.ignoreIndirectDependencies
)
return {
"Program:exit"(node) {
const targets = []
.concat(
getRequireTargets(context, true),
getImportExportTargets(context, node, true)
)
.filter(t => CORE_MODULES.has(t.moduleName))
for (const target of targets) {
const name = target.moduleName
const allowed =
allow.indexOf(name) !== -1 ||
(ignoreDirectDependencies && deps.has(name)) ||
(ignoreIndirectDependencies && !deps.has(name))
if (allowed) {
continue
}
const resolved = resolve.sync(name, { basedir: dirPath })
const isCore = resolved === name
if (isCore) {
continue
}
context.report({
node: target.node,
loc: target.node.loc,
message:
"Unexpected import of third-party module '{{name}}'.",
data: {
name: path
.relative(dirPath, resolved)
.replace(BACK_SLASH, "/"),
},
})
}
},
}
},
}
+48
View File
@@ -0,0 +1,48 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const checkExistence = require("../util/check-existence")
const getAllowModules = require("../util/get-allow-modules")
const getImportExportTargets = require("../util/get-import-export-targets")
const getResolvePaths = require("../util/get-resolve-paths")
const getTryExtensions = require("../util/get-try-extensions")
module.exports = {
meta: {
docs: {
description: "disallow `import` declarations of missing files",
category: "Possible Errors",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-missing-import.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
tryExtensions: getTryExtensions.schema,
resolvePaths: getResolvePaths.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"(node) {
checkExistence(context, getImportExportTargets(context, node))
},
}
},
}
+48
View File
@@ -0,0 +1,48 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const checkExistence = require("../util/check-existence")
const getAllowModules = require("../util/get-allow-modules")
const getRequireTargets = require("../util/get-require-targets")
const getResolvePaths = require("../util/get-resolve-paths")
const getTryExtensions = require("../util/get-try-extensions")
module.exports = {
meta: {
docs: {
description: "disallow `require()` expressions of missing files",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-missing-require.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
tryExtensions: getTryExtensions.schema,
resolvePaths: getResolvePaths.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"() {
checkExistence(context, getRequireTargets(context))
},
}
},
}
+97
View File
@@ -0,0 +1,97 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const path = require("path")
const getConvertPath = require("../util/get-convert-path")
const getNpmignore = require("../util/get-npmignore")
const getPackageJson = require("../util/get-package-json")
/**
* Checks whether or not a given path is a `bin` file.
*
* @param {string} filePath - A file path to check.
* @param {string|object|undefined} binField - A value of the `bin` field of `package.json`.
* @param {string} basedir - A directory path that `package.json` exists.
* @returns {boolean} `true` if the file is a `bin` file.
*/
function isBinFile(filePath, binField, basedir) {
if (!binField) {
return false
}
if (typeof binField === "string") {
return filePath === path.resolve(basedir, binField)
}
return Object.keys(binField).some(
key => filePath === path.resolve(basedir, binField[key])
)
}
module.exports = {
meta: {
docs: {
description: "disallow 'bin' files which are ignored by npm",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unpublished-bin.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
//
convertPath: getConvertPath.schema,
},
},
],
},
create(context) {
return {
Program(node) {
// Check file path.
let rawFilePath = context.getFilename()
if (rawFilePath === "<input>") {
return
}
rawFilePath = path.resolve(rawFilePath)
// Find package.json
const p = getPackageJson(rawFilePath)
if (!p) {
return
}
// Convert by convertPath option
const basedir = path.dirname(p.filePath)
const relativePath = getConvertPath(context)(
path.relative(basedir, rawFilePath).replace(/\\/gu, "/")
)
const filePath = path.join(basedir, relativePath)
// Check this file is bin.
if (!isBinFile(filePath, p.bin, basedir)) {
return
}
// Check ignored or not
const npmignore = getNpmignore(filePath)
if (!npmignore.match(relativePath)) {
return
}
// Report.
context.report({
node,
message:
"npm ignores '{{name}}'. Check 'files' field of 'package.json' or '.npmignore'.",
data: { name: relativePath },
})
},
}
},
}
+54
View File
@@ -0,0 +1,54 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const checkPublish = require("../util/check-publish")
const getAllowModules = require("../util/get-allow-modules")
const getConvertPath = require("../util/get-convert-path")
const getImportExportTargets = require("../util/get-import-export-targets")
const getResolvePaths = require("../util/get-resolve-paths")
const getTryExtensions = require("../util/get-try-extensions")
module.exports = {
meta: {
docs: {
description: "disallow `import` declarations of private things",
category: "Possible Errors",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unpublished-import.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
resolvePaths: getResolvePaths.schema,
tryExtensions: getTryExtensions.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"(node) {
checkPublish(
context,
filePath,
getImportExportTargets(context, node)
)
},
}
},
}
+50
View File
@@ -0,0 +1,50 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const checkPublish = require("../util/check-publish")
const getAllowModules = require("../util/get-allow-modules")
const getConvertPath = require("../util/get-convert-path")
const getRequireTargets = require("../util/get-require-targets")
const getResolvePaths = require("../util/get-resolve-paths")
const getTryExtensions = require("../util/get-try-extensions")
module.exports = {
meta: {
docs: {
description: "disallow `require()` expressions of private things",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unpublished-require.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
allowModules: getAllowModules.schema,
convertPath: getConvertPath.schema,
resolvePaths: getResolvePaths.schema,
tryExtensions: getTryExtensions.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
return {
"Program:exit"() {
checkPublish(context, filePath, getRequireTargets(context))
},
}
},
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,167 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkUnsupportedBuiltins = require("../../util/check-unsupported-builtins")
const enumeratePropertyNames = require("../../util/enumerate-property-names")
const trackMap = {
globals: {
Array: {
from: { [READ]: { supported: "4.0.0" } },
of: { [READ]: { supported: "4.0.0" } },
},
Map: {
[READ]: { supported: "0.12.0" },
},
Math: {
acosh: { [READ]: { supported: "0.12.0" } },
asinh: { [READ]: { supported: "0.12.0" } },
atanh: { [READ]: { supported: "0.12.0" } },
cbrt: { [READ]: { supported: "0.12.0" } },
clz32: { [READ]: { supported: "0.12.0" } },
cosh: { [READ]: { supported: "0.12.0" } },
expm1: { [READ]: { supported: "0.12.0" } },
fround: { [READ]: { supported: "0.12.0" } },
hypot: { [READ]: { supported: "0.12.0" } },
imul: { [READ]: { supported: "0.12.0" } },
log10: { [READ]: { supported: "0.12.0" } },
log1p: { [READ]: { supported: "0.12.0" } },
log2: { [READ]: { supported: "0.12.0" } },
sign: { [READ]: { supported: "0.12.0" } },
sinh: { [READ]: { supported: "0.12.0" } },
tanh: { [READ]: { supported: "0.12.0" } },
trunc: { [READ]: { supported: "0.12.0" } },
},
Number: {
EPSILON: { [READ]: { supported: "0.12.0" } },
isFinite: { [READ]: { supported: "0.10.0" } },
isInteger: { [READ]: { supported: "0.12.0" } },
isNaN: { [READ]: { supported: "0.10.0" } },
isSafeInteger: { [READ]: { supported: "0.12.0" } },
MAX_SAFE_INTEGER: { [READ]: { supported: "0.12.0" } },
MIN_SAFE_INTEGER: { [READ]: { supported: "0.12.0" } },
parseFloat: { [READ]: { supported: "0.12.0" } },
parseInt: { [READ]: { supported: "0.12.0" } },
},
Object: {
assign: { [READ]: { supported: "4.0.0" } },
getOwnPropertySymbols: { [READ]: { supported: "0.12.0" } },
is: { [READ]: { supported: "0.10.0" } },
setPrototypeOf: { [READ]: { supported: "0.12.0" } },
values: { [READ]: { supported: "7.0.0" } },
entries: { [READ]: { supported: "7.0.0" } },
getOwnPropertyDescriptors: { [READ]: { supported: "7.0.0" } },
},
Promise: {
[READ]: { supported: "0.12.0" },
},
Proxy: {
[READ]: { supported: "6.0.0" },
},
Reflect: {
[READ]: { supported: "6.0.0" },
},
Set: {
[READ]: { supported: "0.12.0" },
},
String: {
fromCodePoint: { [READ]: { supported: "4.0.0" } },
raw: { [READ]: { supported: "4.0.0" } },
},
Symbol: {
[READ]: { supported: "0.12.0" },
},
Int8Array: {
[READ]: { supported: "0.10.0" },
},
Uint8Array: {
[READ]: { supported: "0.10.0" },
},
Uint8ClampedArray: {
[READ]: { supported: "0.10.0" },
},
Int16Array: {
[READ]: { supported: "0.10.0" },
},
Uint16Array: {
[READ]: { supported: "0.10.0" },
},
Int32Array: {
[READ]: { supported: "0.10.0" },
},
Uint32Array: {
[READ]: { supported: "0.10.0" },
},
Float32Array: {
[READ]: { supported: "0.10.0" },
},
Float64Array: {
[READ]: { supported: "0.10.0" },
},
DataView: {
[READ]: { supported: "0.10.0" },
},
WeakMap: {
[READ]: { supported: "0.12.0" },
},
WeakSet: {
[READ]: { supported: "0.12.0" },
},
Atomics: {
[READ]: { supported: "8.10.0" },
},
SharedArrayBuffer: {
[READ]: { supported: "8.10.0" },
},
},
}
module.exports = {
meta: {
docs: {
description:
"disallow unsupported ECMAScript built-ins on the specified version",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unsupported-features/es-builtins.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
version: {
type: "string",
},
ignores: {
type: "array",
items: {
enum: Array.from(
enumeratePropertyNames(trackMap.globals)
),
},
uniqueItems: true,
},
},
additionalProperties: false,
},
],
messages: {
unsupported:
"The '{{name}}' is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
},
},
create(context) {
return {
"Program:exit"() {
checkUnsupportedBuiltins(context, trackMap)
},
}
},
}
@@ -0,0 +1,628 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { rules: esRules } = require("eslint-plugin-es")
const { getInnermostScope } = require("eslint-utils")
const { Range } = require("semver") //eslint-disable-line no-unused-vars
const getConfiguredNodeVersion = require("../../util/get-configured-node-version")
const getSemverRange = require("../../util/get-semver-range")
const getOrSet = /^(?:g|s)et$/u
const features = {
//--------------------------------------------------------------------------
// ES2015
//--------------------------------------------------------------------------
arrowFunctions: {
ruleId: "no-arrow-functions",
cases: [
{
supported: "4.0.0",
messageId: "no-arrow-functions",
},
],
},
binaryNumericLiterals: {
ruleId: "no-binary-numeric-literals",
cases: [
{
supported: "4.0.0",
messageId: "no-binary-numeric-literals",
},
],
},
blockScopedFunctions: {
ruleId: "no-block-scoped-functions",
cases: [
{
supported: "6.0.0",
test: info => !info.isStrict,
messageId: "no-block-scoped-functions-sloppy",
},
{
supported: "4.0.0",
messageId: "no-block-scoped-functions-strict",
},
],
},
blockScopedVariables: {
ruleId: "no-block-scoped-variables",
cases: [
{
supported: "6.0.0",
test: info => !info.isStrict,
messageId: "no-block-scoped-variables-sloppy",
},
{
supported: "4.0.0",
messageId: "no-block-scoped-variables-strict",
},
],
},
classes: {
ruleId: "no-classes",
cases: [
{
supported: "6.0.0",
test: info => !info.isStrict,
messageId: "no-classes-sloppy",
},
{
supported: "4.0.0",
messageId: "no-classes-strict",
},
],
},
computedProperties: {
ruleId: "no-computed-properties",
cases: [
{
supported: "4.0.0",
messageId: "no-computed-properties",
},
],
},
defaultParameters: {
ruleId: "no-default-parameters",
cases: [
{
supported: "6.0.0",
messageId: "no-default-parameters",
},
],
},
destructuring: {
ruleId: "no-destructuring",
cases: [
{
supported: "6.0.0",
messageId: "no-destructuring",
},
],
},
forOfLoops: {
ruleId: "no-for-of-loops",
cases: [
{
supported: "0.12.0",
messageId: "no-for-of-loops",
},
],
},
generators: {
ruleId: "no-generators",
cases: [
{
supported: "4.0.0",
messageId: "no-generators",
},
],
},
modules: {
ruleId: "no-modules",
cases: [
{
supported: null,
messageId: "no-modules",
},
],
},
"new.target": {
ruleId: "no-new-target",
cases: [
{
supported: "5.0.0",
messageId: "no-new-target",
},
],
},
objectSuperProperties: {
ruleId: "no-object-super-properties",
cases: [
{
supported: "4.0.0",
messageId: "no-object-super-properties",
},
],
},
octalNumericLiterals: {
ruleId: "no-octal-numeric-literals",
cases: [
{
supported: "4.0.0",
messageId: "no-octal-numeric-literals",
},
],
},
propertyShorthands: {
ruleId: "no-property-shorthands",
cases: [
{
supported: "6.0.0",
test: info =>
info.node.shorthand && getOrSet.test(info.node.key.name),
messageId: "no-property-shorthands-getset",
},
{
supported: "4.0.0",
messageId: "no-property-shorthands",
},
],
},
regexpU: {
ruleId: "no-regexp-u-flag",
cases: [
{
supported: "6.0.0",
messageId: "no-regexp-u-flag",
},
],
},
regexpY: {
ruleId: "no-regexp-y-flag",
cases: [
{
supported: "6.0.0",
messageId: "no-regexp-y-flag",
},
],
},
restParameters: {
ruleId: "no-rest-parameters",
cases: [
{
supported: "6.0.0",
messageId: "no-rest-parameters",
},
],
},
spreadElements: {
ruleId: "no-spread-elements",
cases: [
{
supported: "5.0.0",
messageId: "no-spread-elements",
},
],
},
templateLiterals: {
ruleId: "no-template-literals",
cases: [
{
supported: "4.0.0",
messageId: "no-template-literals",
},
],
},
unicodeCodePointEscapes: {
ruleId: "no-unicode-codepoint-escapes",
cases: [
{
supported: "4.0.0",
messageId: "no-unicode-codepoint-escapes",
},
],
},
//--------------------------------------------------------------------------
// ES2016
//--------------------------------------------------------------------------
exponentialOperators: {
ruleId: "no-exponential-operators",
cases: [
{
supported: "7.0.0",
messageId: "no-exponential-operators",
},
],
},
//--------------------------------------------------------------------------
// ES2017
//--------------------------------------------------------------------------
asyncFunctions: {
ruleId: "no-async-functions",
cases: [
{
supported: "7.6.0",
messageId: "no-async-functions",
},
],
},
trailingCommasInFunctions: {
ruleId: "no-trailing-function-commas",
cases: [
{
supported: "8.0.0",
messageId: "no-trailing-function-commas",
},
],
},
//--------------------------------------------------------------------------
// ES2018
//--------------------------------------------------------------------------
asyncIteration: {
ruleId: "no-async-iteration",
cases: [
{
supported: "10.0.0",
messageId: "no-async-iteration",
},
],
},
malformedTemplateLiterals: {
ruleId: "no-malformed-template-literals",
cases: [
{
supported: "8.10.0",
messageId: "no-malformed-template-literals",
},
],
},
regexpLookbehind: {
ruleId: "no-regexp-lookbehind-assertions",
cases: [
{
supported: "8.10.0",
messageId: "no-regexp-lookbehind-assertions",
},
],
},
regexpNamedCaptureGroups: {
ruleId: "no-regexp-named-capture-groups",
cases: [
{
supported: "10.0.0",
messageId: "no-regexp-named-capture-groups",
},
],
},
regexpS: {
ruleId: "no-regexp-s-flag",
cases: [
{
supported: "8.10.0",
messageId: "no-regexp-s-flag",
},
],
},
regexpUnicodeProperties: {
ruleId: "no-regexp-unicode-property-escapes",
cases: [
{
supported: "10.0.0",
messageId: "no-regexp-unicode-property-escapes",
},
],
},
restSpreadProperties: {
ruleId: "no-rest-spread-properties",
cases: [
{
supported: "8.3.0",
messageId: "no-rest-spread-properties",
},
],
},
//--------------------------------------------------------------------------
// ES2019
//--------------------------------------------------------------------------
jsonSuperset: {
ruleId: "no-json-superset",
cases: [
{
supported: "10.0.0",
messageId: "no-json-superset",
},
],
},
optionalCatchBinding: {
ruleId: "no-optional-catch-binding",
cases: [
{
supported: "10.0.0",
messageId: "no-optional-catch-binding",
},
],
},
}
const keywords = Object.keys(features)
/**
* Parses the options.
* @param {RuleContext} context The rule context.
* @returns {{version:Range,ignores:Set<string>}} Parsed value.
*/
function parseOptions(context) {
const raw = context.options[0] || {}
const filePath = context.getFilename()
const version = getConfiguredNodeVersion(raw.version, filePath)
const ignores = new Set(raw.ignores || [])
return Object.freeze({ version, ignores })
}
/**
* Find the scope that a given node belongs to.
* @param {Scope} initialScope The initial scope to find.
* @param {Node} node The AST node.
* @returns {Scope} The scope that the node belongs to.
*/
function nomalizeScope(initialScope, node) {
let scope = getInnermostScope(initialScope, node)
while (scope && scope.block === node) {
scope = scope.upper
}
return scope
}
/**
* Merge two visitors.
* @param {Visitor} x The visitor which is assigned.
* @param {Visitor} y The visitor which is assigning.
* @returns {Visitor} `x`.
*/
function merge(x, y) {
for (const key of Object.keys(y)) {
if (typeof x[key] === "function") {
if (x[key]._handlers == null) {
const fs = [x[key], y[key]]
x[key] = dispatch.bind(null, fs)
x[key]._handlers = fs
} else {
x[key]._handlers.push(y[key])
}
} else {
x[key] = y[key]
}
}
return x
}
/**
* Dispatch all given functions with a node.
* @param {function[]} handlers The function list to call.
* @param {Node} node The AST node to be handled.
* @returns {void}
*/
function dispatch(handlers, node) {
for (const h of handlers) {
h(node)
}
}
/**
* Define the visitor object as merging the rules of eslint-plugin-es.
* @param {RuleContext} context The rule context.
* @param {{version:Range,ignores:Set<string>}} options The options.
* @returns {object} The defined visitor.
*/
function defineVisitor(context, options) {
const testInfoPrototype = {
get isStrict() {
return nomalizeScope(context.getScope(), this.node).isStrict
},
}
/**
* Check whether a given case object is full-supported on the configured node version.
* @param {{supported:string}} aCase The case object to check.
* @returns {boolean} `true` if it's supporting.
*/
function isNotSupportingVersion(aCase) {
return (
!aCase.supported ||
options.version.intersects(getSemverRange(`<${aCase.supported}`))
)
}
/**
* Define the predicate function to check whether a given case object is supported on the configured node version.
* @param {Node} node The node which is reported.
* @returns {function(aCase:{supported:string}):boolean} The predicate function.
*/
function isNotSupportingOn(node) {
return aCase =>
isNotSupportingVersion(aCase) &&
(!aCase.test || aCase.test({ node, __proto__: testInfoPrototype }))
}
return (
keywords
// Omit full-supported features and ignored features by options
// because this rule never reports those.
.filter(
keyword =>
!options.ignores.has(keyword) &&
features[keyword].cases.some(isNotSupportingVersion)
)
// Merge remaining features with overriding `context.report()`.
.reduce((visitor, keyword) => {
const { ruleId, cases } = features[keyword]
const rule = esRules[ruleId]
const thisContext = {
__proto__: context,
// Override `context.report()` then:
// - ignore if it's supported.
// - override reporting messages.
report(descriptor) {
// Set additional information.
if (descriptor.data) {
descriptor.data.version = options.version.raw
} else {
descriptor.data = { version: options.version.raw }
}
descriptor.fix = undefined
// Test and report.
const node = descriptor.node
const hitCase = cases.find(isNotSupportingOn(node))
if (hitCase) {
descriptor.messageId = hitCase.messageId
descriptor.data.supported = hitCase.supported
super.report(descriptor)
}
},
}
return merge(visitor, rule.create(thisContext))
}, {})
)
}
module.exports = {
meta: {
docs: {
description:
"disallow unsupported ECMAScript syntax on the specified version",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unsupported-features/es-syntax.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
version: {
type: "string",
},
ignores: {
type: "array",
items: {
enum: Object.keys(features),
},
uniqueItems: true,
},
},
additionalProperties: false,
},
],
messages: {
//------------------------------------------------------------------
// ES2015
//------------------------------------------------------------------
"no-arrow-functions":
"Arrow functions are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-binary-numeric-literals":
"Binary numeric literals are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-block-scoped-functions-strict":
"Block-scoped functions in strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-block-scoped-functions-sloppy":
"Block-scoped functions in non-strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-block-scoped-variables-strict":
"Block-scoped variables in strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-block-scoped-variables-sloppy":
"Block-scoped variables in non-strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-classes-strict":
"Classes in strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-classes-sloppy":
"Classes in non-strict mode are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-computed-properties":
"Computed properties are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-default-parameters":
"Default parameters are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-destructuring":
"Destructuring is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-for-of-loops":
"'for-of' loops are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-generators":
"Generator functions are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-modules":
"Import and export declarations are not supported yet.",
"no-new-target":
"'new.target' is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-object-super-properties":
"'super' in object literals is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-octal-numeric-literals":
"Octal numeric literals are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-property-shorthands":
"Property shorthands are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-property-shorthands-getset":
"Property shorthands of 'get' and 'set' are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-u-flag":
"RegExp 'u' flag is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-y-flag":
"RegExp 'y' flag is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-rest-parameters":
"Rest parameters are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-spread-elements":
"Spread elements are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-template-literals":
"Template literals are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-unicode-codepoint-escapes":
"Unicode code point escapes are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
//------------------------------------------------------------------
// ES2016
//------------------------------------------------------------------
"no-exponential-operators":
"Exponential operators are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
//------------------------------------------------------------------
// ES2017
//------------------------------------------------------------------
"no-async-functions":
"Async functions are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-trailing-function-commas":
"Trailing commas in function syntax are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
//------------------------------------------------------------------
// ES2018
//------------------------------------------------------------------
"no-async-iteration":
"Async iteration is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-malformed-template-literals":
"Malformed template literals are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-lookbehind-assertions":
"RegExp lookbehind assertions are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-named-capture-groups":
"RegExp named capture groups are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-s-flag":
"RegExp 's' flag is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-regexp-unicode-property-escapes":
"RegExp Unicode property escapes are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-rest-spread-properties":
"Rest/spread properties are not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
//------------------------------------------------------------------
// ES2019
//------------------------------------------------------------------
"no-json-superset":
"'\\u{{code}}' in string literals is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
"no-optional-catch-binding":
"The omission of 'catch' binding is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
},
},
create(context) {
return defineVisitor(context, parseOptions(context))
},
}
@@ -0,0 +1,276 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkUnsupportedBuiltins = require("../../util/check-unsupported-builtins")
const enumeratePropertyNames = require("../../util/enumerate-property-names")
/*eslint-disable camelcase */
const trackMap = {
globals: {
queueMicrotask: { [READ]: { supported: "11.0.0" } },
require: {
resolve: {
paths: { [READ]: { supported: "8.9.0" } },
},
},
},
modules: {
assert: {
strict: {
[READ]: { supported: "9.9.0" },
doesNotReject: { [READ]: { supported: "10.0.0" } },
rejects: { [READ]: { supported: "10.0.0" } },
},
deepStrictEqual: { [READ]: { supported: "4.0.0" } },
doesNotReject: { [READ]: { supported: "10.0.0" } },
notDeepStrictEqual: { [READ]: { supported: "4.0.0" } },
rejects: { [READ]: { supported: "10.0.0" } },
},
async_hooks: {
[READ]: { supported: "8.0.0" },
createHook: { [READ]: { supported: "8.1.0" } },
},
buffer: {
Buffer: {
alloc: { [READ]: { supported: "4.5.0" } },
allocUnsafe: { [READ]: { supported: "4.5.0" } },
allocUnsafeSlow: { [READ]: { supported: "4.5.0" } },
from: { [READ]: { supported: "4.5.0" } },
},
kMaxLength: { [READ]: { supported: "3.0.0" } },
transcode: { [READ]: { supported: "7.1.0" } },
constants: { [READ]: { supported: "8.2.0" } },
},
child_process: {
ChildProcess: { [READ]: { supported: "2.2.0" } },
},
console: {
clear: { [READ]: { supported: "8.3.0" } },
count: { [READ]: { supported: "8.3.0" } },
countReset: { [READ]: { supported: "8.3.0" } },
debug: { [READ]: { supported: "8.0.0" } },
dirxml: { [READ]: { supported: "8.0.0" } },
group: { [READ]: { supported: "8.5.0" } },
groupCollapsed: { [READ]: { supported: "8.5.0" } },
groupEnd: { [READ]: { supported: "8.5.0" } },
table: { [READ]: { supported: "10.0.0" } },
markTimeline: { [READ]: { supported: "8.0.0" } },
profile: { [READ]: { supported: "8.0.0" } },
profileEnd: { [READ]: { supported: "8.0.0" } },
timeLog: { [READ]: { supported: "10.7.0" } },
timeStamp: { [READ]: { supported: "8.0.0" } },
timeline: { [READ]: { supported: "8.0.0" } },
timelineEnd: { [READ]: { supported: "8.0.0" } },
},
crypto: {
Certificate: {
exportChallenge: { [READ]: { supported: "9.0.0" } },
exportPublicKey: { [READ]: { supported: "9.0.0" } },
verifySpkac: { [READ]: { supported: "9.0.0" } },
},
constants: { [READ]: { supported: "6.3.0" } },
fips: { [READ]: { supported: "6.0.0" } },
generateKeyPair: { [READ]: { supported: "10.12.0" } },
generateKeyPairSync: { [READ]: { supported: "10.12.0" } },
getCurves: { [READ]: { supported: "2.3.0" } },
getFips: { [READ]: { supported: "10.0.0" } },
privateEncrypt: { [READ]: { supported: "1.1.0" } },
publicDecrypt: { [READ]: { supported: "1.1.0" } },
randomFillSync: { [READ]: { supported: "7.10.0" } },
randomFill: { [READ]: { supported: "7.10.0" } },
scrypt: { [READ]: { supported: "10.5.0" } },
scryptSync: { [READ]: { supported: "10.5.0" } },
setFips: { [READ]: { supported: "10.0.0" } },
timingSafeEqual: { [READ]: { supported: "6.6.0" } },
},
dns: {
Resolver: { [READ]: { supported: "8.3.0" } },
resolvePtr: { [READ]: { supported: "6.0.0" } },
promises: {
[READ]: { supported: "10.6.0" },
},
},
fs: {
Dirent: { [READ]: { supported: "10.10.0" } },
copyFile: { [READ]: { supported: "8.5.0" } },
copyFileSync: { [READ]: { supported: "8.5.0" } },
mkdtemp: { [READ]: { supported: "5.10.0" } },
mkdtempSync: { [READ]: { supported: "5.10.0" } },
realpath: {
native: { [READ]: { supported: "9.2.0" } },
},
realpathSync: {
native: { [READ]: { supported: "9.2.0" } },
},
promises: {
[READ]: { supported: "10.1.0" },
},
},
http2: {
[READ]: { supported: "8.4.0" },
},
inspector: {
[READ]: { supported: "8.0.0" },
},
module: {
builtinModules: { [READ]: { supported: "9.3.0" } },
},
os: {
constants: {
[READ]: { supported: "6.3.0" },
priority: { [READ]: { supported: "10.10.0" } },
},
getPriority: { [READ]: { supported: "10.10.0" } },
homedir: { [READ]: { supported: "2.3.0" } },
setPriority: { [READ]: { supported: "10.10.0" } },
userInfo: { [READ]: { supported: "6.0.0" } },
},
path: {
toNamespacedPath: { [READ]: { supported: "9.0.0" } },
},
perf_hooks: {
[READ]: { supported: "8.5.0" },
},
process: {
allowedNodeEnvironmentFlags: { [READ]: { supported: "10.10.0" } },
argv0: { [READ]: { supported: "6.4.0" } },
channel: { [READ]: { supported: "7.1.0" } },
cpuUsage: { [READ]: { supported: "6.1.0" } },
emitWarning: { [READ]: { supported: "6.0.0" } },
getegid: { [READ]: { supported: "2.0.0" } },
geteuid: { [READ]: { supported: "2.0.0" } },
hasUncaughtExceptionCaptureCallback: {
[READ]: { supported: "9.3.0" },
},
hrtime: {
bigint: { [READ]: { supported: "10.7.0" } },
},
ppid: { [READ]: { supported: "9.2.0" } },
release: { [READ]: { supported: "3.0.0" } },
setegid: { [READ]: { supported: "2.0.0" } },
seteuid: { [READ]: { supported: "2.0.0" } },
setUncaughtExceptionCaptureCallback: {
[READ]: { supported: "9.3.0" },
},
},
stream: {
finished: { [READ]: { supported: "10.0.0" } },
pipeline: { [READ]: { supported: "10.0.0" } },
},
trace_events: {
[READ]: { supported: "10.0.0" },
},
url: {
URL: { [READ]: { supported: "7.0.0" } },
URLSearchParams: { [READ]: { supported: "7.5.0" } },
domainToASCII: { [READ]: { supported: "7.4.0" } },
domainToUnicode: { [READ]: { supported: "7.4.0" } },
},
util: {
callbackify: { [READ]: { supported: "8.2.0" } },
formatWithOptions: { [READ]: { supported: "10.0.0" } },
getSystemErrorName: { [READ]: { supported: "9.7.0" } },
inspect: {
custom: { [READ]: { supported: "6.6.0" } },
defaultOptions: { [READ]: { supported: "6.4.0" } },
},
isDeepStrictEqual: { [READ]: { supported: "9.0.0" } },
promisify: { [READ]: { supported: "8.0.0" } },
TextDecoder: { [READ]: { supported: "8.3.0" } },
TextEncoder: { [READ]: { supported: "8.3.0" } },
types: {
[READ]: { supported: "10.0.0" },
isBoxedPrimitive: { [READ]: { supported: "10.11.0" } },
},
},
v8: {
[READ]: { supported: "1.0.0" },
cachedDataVersionTag: { [READ]: { supported: "8.0.0" } },
getHeapSpaceStatistics: { [READ]: { supported: "6.0.0" } },
serialize: { [READ]: { supported: "8.0.0" } },
deserialize: { [READ]: { supported: "8.0.0" } },
Serializer: { [READ]: { supported: "8.0.0" } },
Deserializer: { [READ]: { supported: "8.0.0" } },
DefaultSerializer: { [READ]: { supported: "8.0.0" } },
DefaultDeserializer: { [READ]: { supported: "8.0.0" } },
},
vm: {
Module: { [READ]: { supported: "9.6.0" } },
compileFunction: { [READ]: { supported: "10.10.0" } },
},
worker_threads: {
[READ]: { supported: "10.5.0" },
},
},
}
Object.assign(trackMap.globals, {
Buffer: trackMap.modules.buffer.Buffer,
TextDecoder: Object.assign({}, trackMap.modules.util.TextDecoder, {
[READ]: { supported: "11.0.0" },
}),
TextEncoder: Object.assign({}, trackMap.modules.util.TextEncoder, {
[READ]: { supported: "11.0.0" },
}),
URL: Object.assign({}, trackMap.modules.url.URL, {
[READ]: { supported: "10.0.0" },
}),
URLSearchParams: Object.assign({}, trackMap.modules.url.URLSearchParams, {
[READ]: { supported: "10.0.0" },
}),
console: trackMap.modules.console,
process: trackMap.modules.process,
})
/*eslint-enable camelcase */
module.exports = {
meta: {
docs: {
description:
"disallow unsupported Node.js built-in APIs on the specified version",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/no-unsupported-features/node-builtins.md",
},
type: "problem",
fixable: null,
schema: [
{
type: "object",
properties: {
version: {
type: "string",
},
ignores: {
type: "array",
items: {
enum: Array.from(
new Set([
...enumeratePropertyNames(trackMap.globals),
...enumeratePropertyNames(trackMap.modules),
])
),
},
uniqueItems: true,
},
},
additionalProperties: false,
},
],
messages: {
unsupported:
"The '{{name}}' is not supported until Node.js {{supported}}. The configured version range is '{{version}}'.",
},
},
create(context) {
return {
"Program:exit"() {
checkUnsupportedBuiltins(context, trackMap)
},
}
},
}
+49
View File
@@ -0,0 +1,49 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
Buffer: { [READ]: true },
},
modules: {
buffer: {
Buffer: { [READ]: true },
},
},
}
module.exports = {
meta: {
docs: {
description:
'enforce either `Buffer` or `require("buffer").Buffer`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/buffer.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"buffer\").Buffer'. Use the global variable 'Buffer' instead.",
preferModule:
"Unexpected use of the global variable 'Buffer'. Use 'require(\"buffer\").Buffer' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
+46
View File
@@ -0,0 +1,46 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
console: { [READ]: true },
},
modules: {
console: { [READ]: true },
},
}
module.exports = {
meta: {
docs: {
description: 'enforce either `console` or `require("console")`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/console.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"console\")'. Use the global variable 'console' instead.",
preferModule:
"Unexpected use of the global variable 'console'. Use 'require(\"console\")' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
+46
View File
@@ -0,0 +1,46 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
process: { [READ]: true },
},
modules: {
process: { [READ]: true },
},
}
module.exports = {
meta: {
docs: {
description: 'enforce either `process` or `require("process")`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/process.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"process\")'. Use the global variable 'process' instead.",
preferModule:
"Unexpected use of the global variable 'process'. Use 'require(\"process\")' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
@@ -0,0 +1,49 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
TextDecoder: { [READ]: true },
},
modules: {
util: {
TextDecoder: { [READ]: true },
},
},
}
module.exports = {
meta: {
docs: {
description:
'enforce either `TextDecoder` or `require("util").TextDecoder`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/text-decoder.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"util\").TextDecoder'. Use the global variable 'TextDecoder' instead.",
preferModule:
"Unexpected use of the global variable 'TextDecoder'. Use 'require(\"util\").TextDecoder' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
@@ -0,0 +1,49 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
TextEncoder: { [READ]: true },
},
modules: {
util: {
TextEncoder: { [READ]: true },
},
},
}
module.exports = {
meta: {
docs: {
description:
'enforce either `TextEncoder` or `require("util").TextEncoder`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/text-encoder.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"util\").TextEncoder'. Use the global variable 'TextEncoder' instead.",
preferModule:
"Unexpected use of the global variable 'TextEncoder'. Use 'require(\"util\").TextEncoder' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
@@ -0,0 +1,49 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
URLSearchParams: { [READ]: true },
},
modules: {
url: {
URLSearchParams: { [READ]: true },
},
},
}
module.exports = {
meta: {
docs: {
description:
'enforce either `URLSearchParams` or `require("url").URLSearchParams`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/url-search-params.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"url\").URLSearchParams'. Use the global variable 'URLSearchParams' instead.",
preferModule:
"Unexpected use of the global variable 'URLSearchParams'. Use 'require(\"url\").URLSearchParams' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
+48
View File
@@ -0,0 +1,48 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const { READ } = require("eslint-utils")
const checkForPreferGlobal = require("../../util/check-prefer-global")
const trackMap = {
globals: {
URL: { [READ]: true },
},
modules: {
url: {
URL: { [READ]: true },
},
},
}
module.exports = {
meta: {
docs: {
description: 'enforce either `URL` or `require("url").URL`',
category: "Stylistic Issues",
recommended: false,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/prefer-global/url.md",
},
type: "suggestion",
fixable: null,
schema: [{ enum: ["always", "never"] }],
messages: {
preferGlobal:
"Unexpected use of 'require(\"url\").URL'. Use the global variable 'URL' instead.",
preferModule:
"Unexpected use of the global variable 'URL'. Use 'require(\"url\").URL' instead.",
},
},
create(context) {
return {
"Program:exit"() {
checkForPreferGlobal(context, trackMap)
},
}
},
}
+157
View File
@@ -0,0 +1,157 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const CodePathAnalyzer = safeRequire(
"eslint/lib/code-path-analysis/code-path-analyzer"
)
const CodePath = safeRequire("eslint/lib/code-path-analysis/code-path")
const CodePathSegment = safeRequire(
"eslint/lib/code-path-analysis/code-path-segment"
)
const originalLeaveNode =
CodePathAnalyzer && CodePathAnalyzer.prototype.leaveNode
/**
* Imports a specific module.
*
* @param {string} moduleName - A module name to import.
* @returns {object|null} The imported object, or null.
*/
function safeRequire(moduleName) {
try {
return require(moduleName)
} catch (_err) {
return null
}
}
/* istanbul ignore next */
/**
* Copied from https://github.com/eslint/eslint/blob/16fad5880bb70e9dddbeab8ed0f425ae51f5841f/lib/code-path-analysis/code-path-analyzer.js#L137
*
* @param {CodePathAnalyzer} analyzer - The instance.
* @param {ASTNode} node - The current AST node.
* @returns {void}
*/
function forwardCurrentToHead(analyzer, node) {
const codePath = analyzer.codePath
const state = CodePath.getState(codePath)
const currentSegments = state.currentSegments
const headSegments = state.headSegments
const end = Math.max(currentSegments.length, headSegments.length)
let i = 0
let currentSegment = null
let headSegment = null
// Fires leaving events.
for (i = 0; i < end; ++i) {
currentSegment = currentSegments[i]
headSegment = headSegments[i]
if (currentSegment !== headSegment && currentSegment) {
if (currentSegment.reachable) {
analyzer.emitter.emit(
"onCodePathSegmentEnd",
currentSegment,
node
)
}
}
}
// Update state.
state.currentSegments = headSegments
// Fires entering events.
for (i = 0; i < end; ++i) {
currentSegment = currentSegments[i]
headSegment = headSegments[i]
if (currentSegment !== headSegment && headSegment) {
CodePathSegment.markUsed(headSegment)
if (headSegment.reachable) {
analyzer.emitter.emit(
"onCodePathSegmentStart",
headSegment,
node
)
}
}
}
}
/**
* Checks whether a given node is `process.exit()` or not.
*
* @param {ASTNode} node - A node to check.
* @returns {boolean} `true` if the node is `process.exit()`.
*/
function isProcessExit(node) {
return (
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.computed === false &&
node.callee.object.type === "Identifier" &&
node.callee.object.name === "process" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "exit"
)
}
/**
* The function to override `CodePathAnalyzer.prototype.leaveNode` in order to
* address `process.exit()` as throw.
*
* @this CodePathAnalyzer
* @param {ASTNode} node - A node to be left.
* @returns {void}
*/
function overrideLeaveNode(node) {
if (isProcessExit(node)) {
this.currentNode = node
forwardCurrentToHead(this, node)
CodePath.getState(this.codePath).makeThrow()
this.original.leaveNode(node)
this.currentNode = null
} else {
originalLeaveNode.call(this, node)
}
}
const visitor =
CodePathAnalyzer == null
? {}
: {
Program: function installProcessExitAsThrow() {
CodePathAnalyzer.prototype.leaveNode = overrideLeaveNode
},
"Program:exit": function restoreProcessExitAsThrow() {
CodePathAnalyzer.prototype.leaveNode = originalLeaveNode
},
}
module.exports = {
meta: {
docs: {
description:
"make `process.exit()` expressions the same code path as `throw`",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/process-exit-as-throw.md",
},
type: "problem",
fixable: null,
schema: [],
supported: CodePathAnalyzer != null,
},
create() {
return visitor
},
}
+155
View File
@@ -0,0 +1,155 @@
/**
* @author Toru Nagashima
* See LICENSE file in root directory for full license.
*/
"use strict"
const path = require("path")
const getConvertPath = require("../util/get-convert-path")
const getPackageJson = require("../util/get-package-json")
const NODE_SHEBANG = "#!/usr/bin/env node\n"
const SHEBANG_PATTERN = /^(#!.+?)?(\r)?\n/u
const NODE_SHEBANG_PATTERN = /#!\/usr\/bin\/env node(?: [^\r\n]+?)?\n/u
/**
* Checks whether or not a given path is a `bin` file.
*
* @param {string} filePath - A file path to check.
* @param {string|object|undefined} binField - A value of the `bin` field of `package.json`.
* @param {string} basedir - A directory path that `package.json` exists.
* @returns {boolean} `true` if the file is a `bin` file.
*/
function isBinFile(filePath, binField, basedir) {
if (!binField) {
return false
}
if (typeof binField === "string") {
return filePath === path.resolve(basedir, binField)
}
return Object.keys(binField).some(
key => filePath === path.resolve(basedir, binField[key])
)
}
/**
* Gets the shebang line (includes a line ending) from a given code.
*
* @param {SourceCode} sourceCode - A source code object to check.
* @returns {{length: number, bom: boolean, shebang: string, cr: boolean}}
* shebang's information.
* `retv.shebang` is an empty string if shebang doesn't exist.
*/
function getShebangInfo(sourceCode) {
const m = SHEBANG_PATTERN.exec(sourceCode.text)
return {
bom: sourceCode.hasBOM,
cr: Boolean(m && m[2]),
length: (m && m[0].length) || 0,
shebang: (m && m[1] && `${m[1]}\n`) || "",
}
}
module.exports = {
meta: {
docs: {
description: "enforce the correct usage of shebang",
category: "Possible Errors",
recommended: true,
url:
"https://github.com/mysticatea/eslint-plugin-node/blob/v8.0.1/docs/rules/shebang.md",
},
type: "problem",
fixable: "code",
schema: [
{
type: "object",
properties: {
//
convertPath: getConvertPath.schema,
},
additionalProperties: false,
},
],
},
create(context) {
const sourceCode = context.getSourceCode()
let filePath = context.getFilename()
if (filePath === "<input>") {
return {}
}
filePath = path.resolve(filePath)
const p = getPackageJson(filePath)
if (!p) {
return {}
}
const basedir = path.dirname(p.filePath)
filePath = path.join(
basedir,
getConvertPath(context)(
path.relative(basedir, filePath).replace(/\\/gu, "/")
)
)
const needsShebang = isBinFile(filePath, p.bin, basedir)
const info = getShebangInfo(sourceCode)
return {
Program(node) {
if (
needsShebang
? NODE_SHEBANG_PATTERN.test(info.shebang)
: !info.shebang
) {
// Good the shebang target.
// Checks BOM and \r.
if (needsShebang && info.bom) {
context.report({
node,
message: "This file must not have Unicode BOM.",
fix(fixer) {
return fixer.removeRange([-1, 0])
},
})
}
if (needsShebang && info.cr) {
context.report({
node,
message:
"This file must have Unix linebreaks (LF).",
fix(fixer) {
const index = sourceCode.text.indexOf("\r")
return fixer.removeRange([index, index + 1])
},
})
}
} else if (needsShebang) {
// Shebang is lacking.
context.report({
node,
message:
'This file needs shebang "#!/usr/bin/env node".',
fix(fixer) {
return fixer.replaceTextRange(
[-1, info.length],
NODE_SHEBANG
)
},
})
} else {
// Shebang is extra.
context.report({
node,
message: "This file needs no shebang.",
fix(fixer) {
return fixer.removeRange([0, info.length])
},
})
}
},
}
},
}