eslint-plugin-n
Advanced tools
Comparing version 17.0.0-6 to 17.0.0-7
"use strict" | ||
module.exports = { | ||
commonRules: { | ||
"n/no-deprecated-api": "error", | ||
"n/no-extraneous-import": "error", | ||
"n/no-extraneous-require": "error", | ||
"n/no-exports-assign": "error", | ||
"n/no-missing-import": "error", | ||
"n/no-missing-require": "error", | ||
"n/no-process-exit": "error", | ||
"n/no-unpublished-bin": "error", | ||
"n/no-unpublished-import": "error", | ||
"n/no-unpublished-require": "error", | ||
"n/no-unsupported-features/es-builtins": "error", | ||
"n/no-unsupported-features/es-syntax": "error", | ||
"n/no-unsupported-features/node-builtins": "error", | ||
"n/process-exit-as-throw": "error", | ||
"n/hashbang": "error", | ||
}, | ||
} | ||
module.exports.commonRules = /** @type {const} */ ({ | ||
"n/no-deprecated-api": "error", | ||
"n/no-extraneous-import": "error", | ||
"n/no-extraneous-require": "error", | ||
"n/no-exports-assign": "error", | ||
"n/no-missing-import": "error", | ||
"n/no-missing-require": "error", | ||
"n/no-process-exit": "error", | ||
"n/no-unpublished-bin": "error", | ||
"n/no-unpublished-import": "error", | ||
"n/no-unpublished-require": "error", | ||
"n/no-unsupported-features/es-builtins": "error", | ||
"n/no-unsupported-features/es-syntax": "error", | ||
"n/no-unsupported-features/node-builtins": "error", | ||
"n/process-exit-as-throw": "error", | ||
"n/hashbang": "error", | ||
}) |
@@ -6,3 +6,6 @@ "use strict" | ||
// eslintrc config: https://eslint.org/docs/latest/use/configure/configuration-files | ||
/** | ||
* https://eslint.org/docs/latest/use/configure/configuration-files | ||
* @type {import('eslint').ESLint.ConfigData} | ||
*/ | ||
module.exports.eslintrc = { | ||
@@ -34,3 +37,6 @@ env: { | ||
// flat config: https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
/** | ||
* https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
* @type {import('eslint').Linter.FlatConfig} | ||
*/ | ||
module.exports.flat = { | ||
@@ -44,3 +50,5 @@ languageOptions: { | ||
}, | ||
rules: module.exports.eslintrc.rules, | ||
rules: | ||
/** @type {import('eslint').Linter.RulesRecord} */ | ||
(module.exports.eslintrc.rules), | ||
} |
@@ -6,3 +6,6 @@ "use strict" | ||
// eslintrc config: https://eslint.org/docs/latest/use/configure/configuration-files | ||
/** | ||
* https://eslint.org/docs/latest/use/configure/configuration-files | ||
* @type {import('eslint').ESLint.ConfigData} | ||
*/ | ||
module.exports.eslintrc = { | ||
@@ -31,3 +34,6 @@ env: { | ||
// https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
/** | ||
* https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
* @type {import('eslint').Linter.FlatConfig} | ||
*/ | ||
module.exports.flat = { | ||
@@ -41,3 +47,5 @@ languageOptions: { | ||
}, | ||
rules: module.exports.eslintrc.rules, | ||
rules: | ||
/** @type {import('eslint').Linter.RulesRecord} */ | ||
(module.exports.eslintrc.rules), | ||
} |
"use strict" | ||
const getPackageJson = require("../util/get-package-json") | ||
const { getPackageJson } = require("../util/get-package-json") | ||
const moduleConfig = require("./recommended-module") | ||
@@ -8,5 +8,14 @@ const scriptConfig = require("./recommended-script") | ||
const packageJson = getPackageJson() | ||
const isModule = (packageJson && packageJson.type) === "module" | ||
const isModule = | ||
packageJson != null && | ||
typeof packageJson === "object" && | ||
"type" in packageJson && | ||
packageJson.type === "module" | ||
const recommendedConfig = isModule ? moduleConfig : scriptConfig | ||
/** | ||
* https://eslint.org/docs/latest/use/configure/configuration-files | ||
* @type {import('eslint').ESLint.ConfigData} | ||
*/ | ||
module.exports.eslintrc = { | ||
@@ -13,0 +22,0 @@ ...recommendedConfig.eslintrc, |
138
lib/index.js
@@ -8,48 +8,16 @@ "use strict" | ||
const rules = { | ||
"callback-return": require("./rules/callback-return"), | ||
"exports-style": require("./rules/exports-style"), | ||
"file-extension-in-import": require("./rules/file-extension-in-import"), | ||
"global-require": require("./rules/global-require"), | ||
"handle-callback-err": require("./rules/handle-callback-err"), | ||
"no-callback-literal": require("./rules/no-callback-literal"), | ||
"no-deprecated-api": require("./rules/no-deprecated-api"), | ||
"no-exports-assign": require("./rules/no-exports-assign"), | ||
"no-extraneous-import": require("./rules/no-extraneous-import"), | ||
"no-extraneous-require": require("./rules/no-extraneous-require"), | ||
"no-missing-import": require("./rules/no-missing-import"), | ||
"no-missing-require": require("./rules/no-missing-require"), | ||
"no-mixed-requires": require("./rules/no-mixed-requires"), | ||
"no-new-require": require("./rules/no-new-require"), | ||
"no-path-concat": require("./rules/no-path-concat"), | ||
"no-process-env": require("./rules/no-process-env"), | ||
"no-process-exit": require("./rules/no-process-exit"), | ||
"no-restricted-import": require("./rules/no-restricted-import"), | ||
"no-restricted-require": require("./rules/no-restricted-require"), | ||
"no-sync": require("./rules/no-sync"), | ||
"no-unpublished-bin": require("./rules/no-unpublished-bin"), | ||
"no-unpublished-import": require("./rules/no-unpublished-import"), | ||
"no-unpublished-require": require("./rules/no-unpublished-require"), | ||
"no-unsupported-features/es-builtins": require("./rules/no-unsupported-features/es-builtins"), | ||
"no-unsupported-features/es-syntax": require("./rules/no-unsupported-features/es-syntax"), | ||
"no-unsupported-features/node-builtins": require("./rules/no-unsupported-features/node-builtins"), | ||
"prefer-global/buffer": require("./rules/prefer-global/buffer"), | ||
"prefer-global/console": require("./rules/prefer-global/console"), | ||
"prefer-global/process": require("./rules/prefer-global/process"), | ||
"prefer-global/text-decoder": require("./rules/prefer-global/text-decoder"), | ||
"prefer-global/text-encoder": require("./rules/prefer-global/text-encoder"), | ||
"prefer-global/url-search-params": require("./rules/prefer-global/url-search-params"), | ||
"prefer-global/url": require("./rules/prefer-global/url"), | ||
"prefer-node-protocol": require("./rules/prefer-node-protocol"), | ||
"prefer-promises/dns": require("./rules/prefer-promises/dns"), | ||
"prefer-promises/fs": require("./rules/prefer-promises/fs"), | ||
"process-exit-as-throw": require("./rules/process-exit-as-throw"), | ||
hashbang: require("./rules/hashbang"), | ||
/** | ||
* @typedef {{ | ||
'recommended-module': import('eslint').ESLint.ConfigData; | ||
'recommended-script': import('eslint').ESLint.ConfigData; | ||
'recommended': import('eslint').ESLint.ConfigData; | ||
'flat/recommended-module': import('eslint').Linter.FlatConfig; | ||
'flat/recommended-script': import('eslint').Linter.FlatConfig; | ||
'flat/recommended': import('eslint').Linter.FlatConfig; | ||
'flat/mixed-esm-and-cjs': import('eslint').Linter.FlatConfig[]; | ||
}} Configs | ||
*/ | ||
// Deprecated rules. | ||
"no-hide-core-modules": require("./rules/no-hide-core-modules"), | ||
shebang: require("./rules/shebang"), | ||
} | ||
const mod = { | ||
/** @type {import('eslint').ESLint.Plugin & { configs: Configs }} */ | ||
const plugin = { | ||
meta: { | ||
@@ -59,21 +27,69 @@ name: pkg.name, | ||
}, | ||
rules, | ||
rules: /** @type {Record<string, import('eslint').Rule.RuleModule>} */ ({ | ||
"callback-return": require("./rules/callback-return"), | ||
"exports-style": require("./rules/exports-style"), | ||
"file-extension-in-import": require("./rules/file-extension-in-import"), | ||
"global-require": require("./rules/global-require"), | ||
"handle-callback-err": require("./rules/handle-callback-err"), | ||
"no-callback-literal": require("./rules/no-callback-literal"), | ||
"no-deprecated-api": require("./rules/no-deprecated-api"), | ||
"no-exports-assign": require("./rules/no-exports-assign"), | ||
"no-extraneous-import": require("./rules/no-extraneous-import"), | ||
"no-extraneous-require": require("./rules/no-extraneous-require"), | ||
"no-missing-import": require("./rules/no-missing-import"), | ||
"no-missing-require": require("./rules/no-missing-require"), | ||
"no-mixed-requires": require("./rules/no-mixed-requires"), | ||
"no-new-require": require("./rules/no-new-require"), | ||
"no-path-concat": require("./rules/no-path-concat"), | ||
"no-process-env": require("./rules/no-process-env"), | ||
"no-process-exit": require("./rules/no-process-exit"), | ||
"no-restricted-import": require("./rules/no-restricted-import"), | ||
"no-restricted-require": require("./rules/no-restricted-require"), | ||
"no-sync": require("./rules/no-sync"), | ||
"no-unpublished-bin": require("./rules/no-unpublished-bin"), | ||
"no-unpublished-import": require("./rules/no-unpublished-import"), | ||
"no-unpublished-require": require("./rules/no-unpublished-require"), | ||
"no-unsupported-features/es-builtins": require("./rules/no-unsupported-features/es-builtins"), | ||
"no-unsupported-features/es-syntax": require("./rules/no-unsupported-features/es-syntax"), | ||
"no-unsupported-features/node-builtins": require("./rules/no-unsupported-features/node-builtins"), | ||
"prefer-global/buffer": require("./rules/prefer-global/buffer"), | ||
"prefer-global/console": require("./rules/prefer-global/console"), | ||
"prefer-global/process": require("./rules/prefer-global/process"), | ||
"prefer-global/text-decoder": require("./rules/prefer-global/text-decoder"), | ||
"prefer-global/text-encoder": require("./rules/prefer-global/text-encoder"), | ||
"prefer-global/url-search-params": require("./rules/prefer-global/url-search-params"), | ||
"prefer-global/url": require("./rules/prefer-global/url"), | ||
"prefer-node-protocol": require("./rules/prefer-node-protocol"), | ||
"prefer-promises/dns": require("./rules/prefer-promises/dns"), | ||
"prefer-promises/fs": require("./rules/prefer-promises/fs"), | ||
"process-exit-as-throw": require("./rules/process-exit-as-throw"), | ||
hashbang: require("./rules/hashbang"), | ||
// Deprecated rules. | ||
"no-hide-core-modules": require("./rules/no-hide-core-modules"), | ||
shebang: require("./rules/shebang"), | ||
}), | ||
configs: { | ||
"recommended-module": { plugins: ["n"], ...esmConfig.eslintrc }, | ||
"recommended-script": { plugins: ["n"], ...cjsConfig.eslintrc }, | ||
recommended: { plugins: ["n"], ...recommendedConfig.eslintrc }, | ||
"flat/recommended-module": { ...esmConfig.flat }, | ||
"flat/recommended-script": { ...cjsConfig.flat }, | ||
"flat/recommended": { ...recommendedConfig.flat }, | ||
"flat/mixed-esm-and-cjs": [ | ||
{ files: ["**/*.js"], ...recommendedConfig.flat }, | ||
{ files: ["**/*.mjs"], ...esmConfig.flat }, | ||
{ files: ["**/*.cjs"], ...cjsConfig.flat }, | ||
], | ||
}, | ||
} | ||
// set configs, e.g. mod.configs["recommended-module"] | ||
// do not defined in the mod obj - to avoid circular dependency | ||
mod.configs = { | ||
"recommended-module": { plugins: ["n"], ...esmConfig.eslintrc }, | ||
"recommended-script": { plugins: ["n"], ...cjsConfig.eslintrc }, | ||
recommended: { plugins: ["n"], ...recommendedConfig.eslintrc }, | ||
"flat/recommended-module": { plugins: { n: mod }, ...esmConfig.flat }, | ||
"flat/recommended-script": { plugins: { n: mod }, ...cjsConfig.flat }, | ||
"flat/recommended": { plugins: { n: mod }, ...recommendedConfig.flat }, | ||
"flat/mixed-esm-and-cjs": [ | ||
{ plugins: { n: mod }, files: ["**/*.js"], ...recommendedConfig.flat }, | ||
{ plugins: { n: mod }, files: ["**/*.mjs"], ...esmConfig.flat }, | ||
{ plugins: { n: mod }, files: ["**/*.cjs"], ...cjsConfig.flat }, | ||
], | ||
plugin.configs["flat/recommended-module"].plugins = { n: plugin } | ||
plugin.configs["flat/recommended-script"].plugins = { n: plugin } | ||
plugin.configs["flat/recommended"].plugins = { n: plugin } | ||
for (const config of plugin.configs["flat/mixed-esm-and-cjs"]) { | ||
config.plugins = { n: plugin } | ||
} | ||
module.exports = mod | ||
module.exports = plugin |
@@ -7,2 +7,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -34,5 +35,5 @@ meta: { | ||
* Find the closest parent matching a list of types. | ||
* @param {ASTNode} node The node whose parents we are searching | ||
* @param {Array} types The node types to match | ||
* @returns {ASTNode} The matched node or undefined. | ||
* @param {import('eslint').Rule.Node} node The node whose parents we are searching | ||
* @param {string[]} types The node types to match | ||
* @returns {import('eslint').Rule.Node | null} The matched node or undefined. | ||
*/ | ||
@@ -51,3 +52,3 @@ function findClosestParentOfType(node, types) { | ||
* Check to see if a node contains only identifers | ||
* @param {ASTNode} node The node to check | ||
* @param {import('estree').Expression | import('estree').Super} node The node to check | ||
* @returns {boolean} Whether or not the node contains only identifers | ||
@@ -74,3 +75,3 @@ */ | ||
* Check to see if a CallExpression is in our callback list. | ||
* @param {ASTNode} node The node to check against our callback names list. | ||
* @param {import('estree').CallExpression} node The node to check against our callback names list. | ||
* @returns {boolean} Whether or not this function matches our callback name. | ||
@@ -87,4 +88,4 @@ */ | ||
* Determines whether or not the callback is part of a callback expression. | ||
* @param {ASTNode} node The callback node | ||
* @param {ASTNode} parentNode The expression node | ||
* @param {import('eslint').Rule.Node} node The callback node | ||
* @param {import('estree').Statement} parentNode The expression node | ||
* @returns {boolean} Whether or not this is part of a callback expression | ||
@@ -124,11 +125,10 @@ */ | ||
// find the closest block, return or loop | ||
const closestBlock = | ||
findClosestParentOfType(node, [ | ||
"BlockStatement", | ||
"ReturnStatement", | ||
"ArrowFunctionExpression", | ||
]) || {} | ||
const closestBlock = findClosestParentOfType(node, [ | ||
"BlockStatement", | ||
"ReturnStatement", | ||
"ArrowFunctionExpression", | ||
]) | ||
// if our parent is a return we know we're ok | ||
if (closestBlock.type === "ReturnStatement") { | ||
if (closestBlock?.type === "ReturnStatement") { | ||
return | ||
@@ -138,3 +138,3 @@ } | ||
// arrow functions don't always have blocks and implicitly return | ||
if (closestBlock.type === "ArrowFunctionExpression") { | ||
if (closestBlock?.type === "ArrowFunctionExpression") { | ||
return | ||
@@ -144,3 +144,3 @@ } | ||
// block statements are part of functions and most if statements | ||
if (closestBlock.type === "BlockStatement") { | ||
if (closestBlock?.type === "BlockStatement") { | ||
// find the last item in the block | ||
@@ -147,0 +147,0 @@ const lastItem = |
@@ -7,2 +7,6 @@ /** | ||
/** | ||
* @typedef {import('estree').Node & { parent?: Node }} Node | ||
*/ | ||
/*istanbul ignore next */ | ||
@@ -12,10 +16,11 @@ /** | ||
* | ||
* @param {ASTNode} node - The node to get. | ||
* @returns {string|null} The property name if static. Otherwise, null. | ||
* @param {Node} node - The node to get. | ||
* @returns {string | null | undefined} The property name if static. Otherwise, null. | ||
* @private | ||
*/ | ||
function getStaticPropertyName(node) { | ||
/** @type {import('estree').Expression | import('estree').PrivateIdentifier | null} */ | ||
let prop = null | ||
switch (node && node.type) { | ||
switch (node?.type) { | ||
case "Property": | ||
@@ -33,3 +38,3 @@ case "MethodDefinition": | ||
switch (prop && prop.type) { | ||
switch (prop?.type) { | ||
case "Literal": | ||
@@ -45,3 +50,8 @@ return String(prop.value) | ||
case "Identifier": | ||
if (!node.computed) { | ||
if ( | ||
!( | ||
/** @type {import('estree').MemberExpression} */ (node) | ||
.computed | ||
) | ||
) { | ||
return prop.name | ||
@@ -60,3 +70,3 @@ } | ||
* | ||
* @param {ASTNode} node - The node to check. | ||
* @param {Node} node - The node to check. | ||
* @returns {boolean} `true` if the node is assignee. | ||
@@ -66,3 +76,4 @@ */ | ||
return ( | ||
node.parent.type === "AssignmentExpression" && node.parent.left === node | ||
node.parent?.type === "AssignmentExpression" && | ||
node.parent.left === node | ||
) | ||
@@ -77,4 +88,4 @@ } | ||
* | ||
* @param {ASTNode} leafNode - The node to get. | ||
* @returns {ASTNode|null} The top assignment expression node, or null. | ||
* @param {Node} leafNode - The node to get. | ||
* @returns {Node|null} The top assignment expression node, or null. | ||
*/ | ||
@@ -86,3 +97,3 @@ function getTopAssignment(leafNode) { | ||
while ( | ||
node.parent.type === "MemberExpression" && | ||
node.parent?.type === "MemberExpression" && | ||
node.parent.object === node | ||
@@ -99,3 +110,3 @@ ) { | ||
// Find the top. | ||
while (node.parent.type === "AssignmentExpression") { | ||
while (node.parent?.type === "AssignmentExpression") { | ||
node = node.parent | ||
@@ -110,7 +121,7 @@ } | ||
* | ||
* @param {ASTNode[]} nodes - The node list to get. | ||
* @returns {ASTNode[]} Gotten top assignment nodes. | ||
* @param {Node[]} nodes - The node list to get. | ||
* @returns {Node[]} Gotten top assignment nodes. | ||
*/ | ||
function createAssignmentList(nodes) { | ||
return nodes.map(getTopAssignment).filter(Boolean) | ||
return /** @type {Node[]} */ (nodes.map(getTopAssignment).filter(Boolean)) | ||
} | ||
@@ -121,4 +132,4 @@ | ||
* | ||
* @param {escope.Scope} scope - The scope to get. | ||
* @returns {ASTNode[]} Gotten MemberExpression node list. | ||
* @param {import('eslint').Scope.Scope} scope - The scope to get. | ||
* @returns {Node[]} Gotten MemberExpression node list. | ||
*/ | ||
@@ -131,6 +142,10 @@ function getModuleExportsNodes(scope) { | ||
return variable.references | ||
.map(reference => reference.identifier.parent) | ||
.map( | ||
reference => | ||
/** @type {Node & { parent: Node }} */ (reference.identifier) | ||
.parent | ||
) | ||
.filter( | ||
node => | ||
node.type === "MemberExpression" && | ||
node?.type === "MemberExpression" && | ||
getStaticPropertyName(node) === "exports" | ||
@@ -143,4 +158,4 @@ ) | ||
* | ||
* @param {escope.Scope} scope - The scope to get. | ||
* @returns {ASTNode[]} Gotten Identifier node list. | ||
* @param {import('eslint').Scope.Scope} scope - The scope to get. | ||
* @returns {import('estree').Identifier[]} Gotten Identifier node list. | ||
*/ | ||
@@ -152,5 +167,11 @@ function getExportsNodes(scope) { | ||
} | ||
return variable.references.map(reference => reference.identifier) | ||
} | ||
/** | ||
* @param {Node} property | ||
* @param {import('eslint').SourceCode} sourceCode | ||
* @returns {string | null} | ||
*/ | ||
function getReplacementForProperty(property, sourceCode) { | ||
@@ -169,3 +190,3 @@ if (property.type !== "Property" || property.kind !== "init") { | ||
let fixedValue = sourceCode.getText(property.value) | ||
if (property.method) { | ||
if (property.value.type === "FunctionExpression" && property.method) { | ||
fixedValue = `function${ | ||
@@ -180,2 +201,3 @@ property.value.generator ? "*" : "" | ||
.getCommentsBefore(property) | ||
// @ts-expect-error getText supports both BaseNode and BaseNodeWithoutComments | ||
.map(comment => sourceCode.getText(comment)) | ||
@@ -199,2 +221,3 @@ if (property.key.type === "Literal" || property.computed) { | ||
.getCommentsAfter(property) | ||
// @ts-expect-error getText supports both BaseNode and BaseNodeWithoutComments | ||
.map(comment => sourceCode.getText(comment)) | ||
@@ -205,8 +228,12 @@ ) | ||
// Check for a top level module.exports = { ... } | ||
/** | ||
* Check for a top level module.exports = { ... } | ||
* @param {Node} node | ||
* @returns {node is {parent: import('estree').AssignmentExpression & {parent: import('estree').ExpressionStatement, right: import('estree').ObjectExpression}}} | ||
*/ | ||
function isModuleExportsObjectAssignment(node) { | ||
return ( | ||
node.parent.type === "AssignmentExpression" && | ||
node.parent.parent.type === "ExpressionStatement" && | ||
node.parent.parent.parent.type === "Program" && | ||
node.parent?.type === "AssignmentExpression" && | ||
node.parent?.parent?.type === "ExpressionStatement" && | ||
node.parent.parent.parent?.type === "Program" && | ||
node.parent.right.type === "ObjectExpression" | ||
@@ -216,9 +243,19 @@ ) | ||
// Check for module.exports.foo or module.exports.bar reference or assignment | ||
/** | ||
* Check for module.exports.foo or module.exports.bar reference or assignment | ||
* @param {Node} node | ||
* @returns {node is import('estree').MemberExpression} | ||
*/ | ||
function isModuleExportsReference(node) { | ||
return ( | ||
node.parent.type === "MemberExpression" && node.parent.object === node | ||
node.parent?.type === "MemberExpression" && node.parent.object === node | ||
) | ||
} | ||
/** | ||
* @param {Node} node | ||
* @param {import('eslint').SourceCode} sourceCode | ||
* @param {import('eslint').Rule.RuleFixer} fixer | ||
* @returns {import('eslint').Rule.Fix | null} | ||
*/ | ||
function fixModuleExports(node, sourceCode, fixer) { | ||
@@ -245,2 +282,3 @@ if (isModuleExportsReference(node)) { | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -292,4 +330,4 @@ meta: { | ||
* | ||
* @param {ASTNode} node - The node of `exports`/`module.exports`. | ||
* @returns {Location} The location info of reports. | ||
* @param {Node} node - The node of `exports`/`module.exports`. | ||
* @returns {import('estree').SourceLocation} The location info of reports. | ||
*/ | ||
@@ -299,4 +337,6 @@ function getLocation(node) { | ||
return { | ||
start: node.loc.start, | ||
end: token.loc.end, | ||
start: /** @type {import('estree').SourceLocation} */ (node.loc) | ||
.start, | ||
end: /** @type {import('estree').SourceLocation} */ (token?.loc) | ||
?.end, | ||
} | ||
@@ -309,2 +349,3 @@ } | ||
* | ||
* @param {import('eslint').Scope.Scope} globalScope | ||
* @returns {void} | ||
@@ -320,5 +361,7 @@ */ | ||
// Skip if it's a batch assignment. | ||
const topAssignment = getTopAssignment(node) | ||
if ( | ||
topAssignment && | ||
assignList.length > 0 && | ||
assignList.indexOf(getTopAssignment(node)) !== -1 | ||
assignList.indexOf(topAssignment) !== -1 | ||
) { | ||
@@ -341,2 +384,3 @@ continue | ||
* | ||
* @param {import('eslint').Scope.Scope} globalScope | ||
* @returns {void} | ||
@@ -355,3 +399,6 @@ */ | ||
if (assignList.length > 0) { | ||
const found = assignList.indexOf(getTopAssignment(node)) | ||
const topAssignment = getTopAssignment(node) | ||
const found = topAssignment | ||
? assignList.indexOf(topAssignment) | ||
: -1 | ||
if (found !== -1) { | ||
@@ -382,4 +429,8 @@ batchAssignList.push(assignList[found]) | ||
const topAssignment = getTopAssignment(node) | ||
// Check if it's a batch assignment. | ||
if (batchAssignList.indexOf(getTopAssignment(node)) !== -1) { | ||
if ( | ||
topAssignment && | ||
batchAssignList.indexOf(topAssignment) !== -1 | ||
) { | ||
continue | ||
@@ -386,0 +437,0 @@ } |
@@ -9,3 +9,3 @@ /** | ||
const fs = require("fs") | ||
const mapTypescriptExtension = require("../util/map-typescript-extension") | ||
const { convertTsExtensionToJs } = require("../util/map-typescript-extension") | ||
const visitImport = require("../util/visit-import") | ||
@@ -28,3 +28,3 @@ | ||
.map(filename => path.extname(filename)) | ||
} catch (_error) { | ||
} catch { | ||
return [] | ||
@@ -34,2 +34,3 @@ } | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -75,3 +76,6 @@ meta: { | ||
// Ignore if it's not resolved to a file or it's a bare module. | ||
if (moduleType !== "relative" && moduleType !== "absolute") { | ||
if ( | ||
(moduleType !== "relative" && moduleType !== "absolute") || | ||
filePath == null | ||
) { | ||
return | ||
@@ -85,3 +89,3 @@ } | ||
const expectedExt = mapTypescriptExtension( | ||
const expectedExt = convertTsExtensionToJs( | ||
context, | ||
@@ -99,3 +103,4 @@ filePath, | ||
fix(fixer) { | ||
const index = node.range[1] - 1 | ||
const index = | ||
/** @type {[number, number]} */ (node.range)[1] - 1 | ||
return fixer.insertTextBeforeRange( | ||
@@ -117,13 +122,2 @@ [index, index], | ||
let fix = fixer => { | ||
const index = name.lastIndexOf(currentExt) | ||
const start = node.range[0] + 1 + index | ||
const end = start + currentExt.length | ||
return fixer.removeRange([start, end]) | ||
} | ||
if (otherExtensions.length > 1) { | ||
fix = undefined | ||
} | ||
context.report({ | ||
@@ -133,3 +127,16 @@ node, | ||
data: { ext: currentExt }, | ||
fix, | ||
fix: | ||
otherExtensions.length > 1 | ||
? undefined | ||
: fixer => { | ||
const index = name.lastIndexOf(currentExt) | ||
const start = | ||
/** @type {[number, number]} */ ( | ||
node.range | ||
)[0] + | ||
1 + | ||
index | ||
const end = start + currentExt.length | ||
return fixer.removeRange([start, end]) | ||
}, | ||
}) | ||
@@ -136,0 +143,0 @@ } |
@@ -20,18 +20,12 @@ /** | ||
* Finds the eslint-scope reference in the given scope. | ||
* @param {Object} scope The scope to search. | ||
* @param {ASTNode} node The identifier node. | ||
* @returns {Reference|null} Returns the found reference or null if none were found. | ||
* @param {import('eslint').Scope.Scope} scope The scope to search. | ||
* @param {import('estree').Node} node The identifier node. | ||
* @returns {import('eslint').Scope.Reference|undefined} Returns the found reference or null if none were found. | ||
*/ | ||
function findReference(scope, node) { | ||
const references = scope.references.filter( | ||
return scope.references.find( | ||
reference => | ||
reference.identifier.range[0] === node.range[0] && | ||
reference.identifier.range[1] === node.range[1] | ||
reference.identifier.range?.[0] === node.range?.[0] && | ||
reference.identifier.range?.[1] === node.range?.[1] | ||
) | ||
/* istanbul ignore else: correctly returns null */ | ||
if (references.length === 1) { | ||
return references[0] | ||
} | ||
return null | ||
} | ||
@@ -41,4 +35,4 @@ | ||
* Checks if the given identifier node is shadowed in the given scope. | ||
* @param {Object} scope The current scope. | ||
* @param {ASTNode} node The identifier node to check. | ||
* @param {import('eslint').Scope.Scope} scope The current scope. | ||
* @param {import('estree').Node} node The identifier node to check. | ||
* @returns {boolean} Whether or not the name is shadowed. | ||
@@ -49,5 +43,6 @@ */ | ||
return reference && reference.resolved && reference.resolved.defs.length > 0 | ||
return Boolean(reference?.resolved?.defs?.length) | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -78,3 +73,4 @@ meta: { | ||
if ( | ||
node.callee.name === "require" && | ||
/** @type {import('estree').Identifier} */ (node.callee) | ||
.name === "require" && | ||
!isShadowed(currentScope, node.callee) | ||
@@ -81,0 +77,0 @@ ) { |
@@ -7,2 +7,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -58,4 +59,4 @@ meta: { | ||
* Get the parameters of a given function scope. | ||
* @param {Object} scope The function scope. | ||
* @returns {Array} All parameters of the given scope. | ||
* @param {import('eslint').Scope.Scope} scope The function scope. | ||
* @returns {import('eslint').Scope.Variable[]} All parameters of the given scope. | ||
*/ | ||
@@ -71,3 +72,3 @@ function getParameters(scope) { | ||
* Check to see if we're handling the error object properly. | ||
* @param {ASTNode} node The AST node to check. | ||
* @param {import('estree').Node} node The AST node to check. | ||
* @returns {void} | ||
@@ -74,0 +75,0 @@ */ |
@@ -8,7 +8,8 @@ /** | ||
const path = require("path") | ||
const matcher = require("ignore") | ||
const matcher = require("ignore").default | ||
const getConvertPath = require("../util/get-convert-path") | ||
const getPackageJson = require("../util/get-package-json") | ||
const { getPackageJson } = require("../util/get-package-json") | ||
const getNpmignore = require("../util/get-npmignore") | ||
const { isBinFile } = require("../util/is-bin-file") | ||
@@ -20,41 +21,6 @@ const NODE_SHEBANG = "#!/usr/bin/env node\n" | ||
function simulateNodeResolutionAlgorithm(filePath, binField) { | ||
const possibilities = [filePath] | ||
let newFilePath = filePath.replace(/\.js$/u, "") | ||
possibilities.push(newFilePath) | ||
newFilePath = newFilePath.replace(/[/\\]index$/u, "") | ||
possibilities.push(newFilePath) | ||
return possibilities.includes(binField) | ||
} | ||
/** | ||
* 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 simulateNodeResolutionAlgorithm( | ||
filePath, | ||
path.resolve(basedir, binField) | ||
) | ||
} | ||
return Object.keys(binField).some(key => | ||
simulateNodeResolutionAlgorithm( | ||
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. | ||
* @param {import('eslint').SourceCode} sourceCode - A source code object to check. | ||
* @returns {{length: number, bom: boolean, shebang: string, cr: boolean}} | ||
@@ -114,8 +80,8 @@ * shebang's information. | ||
const p = getPackageJson(filePath) | ||
if (!p) { | ||
const packageJson = getPackageJson(filePath) | ||
if (typeof packageJson?.filePath !== "string") { | ||
return {} | ||
} | ||
const packageDirectory = path.dirname(p.filePath) | ||
const packageDirectory = path.dirname(packageJson.filePath) | ||
@@ -134,2 +100,3 @@ const originalAbsolutePath = path.resolve(filePath) | ||
/** @type {{ additionalExecutables: string[] }} */ | ||
const { additionalExecutables = [] } = context.options?.[0] ?? {} | ||
@@ -155,3 +122,3 @@ | ||
isExecutable.ignored === true || | ||
isBinFile(convertedAbsolutePath, p.bin, packageDirectory) | ||
isBinFile(convertedAbsolutePath, packageJson?.bin, packageDirectory) | ||
const info = getShebangInfo(sourceCode) | ||
@@ -163,3 +130,6 @@ | ||
start: { line: 1, column: 0 }, | ||
end: { line: 1, column: sourceCode.lines.at(0).length }, | ||
end: { | ||
line: 1, | ||
column: sourceCode.lines.at(0)?.length ?? 0, | ||
}, | ||
} | ||
@@ -166,0 +136,0 @@ |
@@ -7,2 +7,5 @@ /** | ||
const callbackNames = ["callback", "cb"] | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -26,12 +29,8 @@ meta: { | ||
create(context) { | ||
const callbackNames = ["callback", "cb"] | ||
function isCallback(name) { | ||
return callbackNames.indexOf(name) > -1 | ||
} | ||
return { | ||
CallExpression(node) { | ||
const errorArg = node.arguments[0] | ||
const calleeName = node.callee.name | ||
const calleeName = /** @type {import('estree').Identifier} */ ( | ||
node.callee | ||
).name | ||
@@ -41,3 +40,3 @@ if ( | ||
!couldBeError(errorArg) && | ||
isCallback(calleeName) | ||
callbackNames.includes(calleeName) | ||
) { | ||
@@ -56,3 +55,3 @@ context.report({ | ||
* Determine if a node has a possiblity to be an Error object | ||
* @param {ASTNode} node ASTNode to check | ||
* @param {import('estree').Node} node ASTNode to check | ||
* @returns {boolean} True if there is a chance it contains an Error obj | ||
@@ -59,0 +58,0 @@ */ |
@@ -19,2 +19,14 @@ /** | ||
/** | ||
* @typedef DeprecatedInfo | ||
* @property {string} since | ||
* @property {string|{ name: string, supported: string }[]|null} replacedBy | ||
*/ | ||
/** | ||
* @typedef ParsedOptions | ||
* @property {import('semver').Range} version | ||
* @property {Set<string>} ignoredGlobalItems | ||
* @property {Set<string>} ignoredModuleItems | ||
*/ | ||
/** @type {import('@eslint-community/eslint-utils').TraceMap<DeprecatedInfo>} */ | ||
const rawModules = { | ||
@@ -641,4 +653,4 @@ _linklist: { | ||
* | ||
* @param {string|array|null} replacedBy - The text of substitute way. | ||
* @param {Range} version - The configured version range | ||
* @param {DeprecatedInfo["replacedBy"]} replacedBy - The text of substitute way. | ||
* @param {import('semver').Range} version - The configured version range | ||
* @returns {string} Replacement message. | ||
@@ -653,3 +665,7 @@ */ | ||
({ supported }) => | ||
!version.intersects(getSemverRange(`<${supported}`)) | ||
!version.intersects( | ||
/** @type {import('semver').Range} */ ( | ||
getSemverRange(`<${supported}`) | ||
) | ||
) | ||
) | ||
@@ -680,5 +696,4 @@ .map(({ name }) => name) | ||
* Parses the options. | ||
* @param {RuleContext} context The rule context. | ||
* @returns {{version:Range,ignoredGlobalItems:Set<string>,ignoredModuleItems:Set<string>}} Parsed | ||
* value. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context. | ||
* @returns {ParsedOptions} Parsed options | ||
*/ | ||
@@ -694,2 +709,3 @@ function parseOptions(context) { | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -746,5 +762,5 @@ meta: { | ||
* | ||
* @param {ASTNode} node - A node to report. | ||
* @param {import('estree').Node} node - A node to report. | ||
* @param {string} name - The name of a deprecated API. | ||
* @param {{since: number, replacedBy: string}} info - Information of the API. | ||
* @param {DeprecatedInfo} info - Information of the API. | ||
* @returns {void} | ||
@@ -755,3 +771,5 @@ */ | ||
node, | ||
loc: node.loc, | ||
loc: /** @type {NonNullable<import('estree').Node["loc"]>} */ ( | ||
node.loc | ||
), | ||
messageId: "deprecated", | ||
@@ -758,0 +776,0 @@ data: { |
@@ -9,2 +9,7 @@ /** | ||
/** | ||
* @param {import('estree').Node} node | ||
* @param {import('eslint').Scope.Scope} scope | ||
* @returns {boolean} | ||
*/ | ||
function isExports(node, scope) { | ||
@@ -22,2 +27,7 @@ let variable = null | ||
/** | ||
* @param {import('estree').Node} node | ||
* @param {import('eslint').Scope.Scope} scope | ||
* @returns {boolean} | ||
*/ | ||
function isModuleExports(node, scope) { | ||
@@ -39,2 +49,3 @@ let variable = null | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -41,0 +52,0 @@ meta: { |
@@ -13,2 +13,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -15,0 +16,0 @@ meta: { |
@@ -14,2 +14,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -16,0 +17,0 @@ meta: { |
@@ -12,3 +12,3 @@ /** | ||
const path = require("path") | ||
const getPackageJson = require("../util/get-package-json") | ||
const { getPackageJson } = require("../util/get-package-json") | ||
const mergeVisitorsInPlace = require("../util/merge-visitors-in-place") | ||
@@ -18,2 +18,3 @@ const visitImport = require("../util/visit-import") | ||
/** @type {Set<string|undefined>} */ | ||
const CORE_MODULES = new Set([ | ||
@@ -54,2 +55,3 @@ "assert", | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -95,8 +97,7 @@ meta: { | ||
const packageJson = getPackageJson(filePath) | ||
const deps = new Set( | ||
[].concat( | ||
Object.keys((packageJson && packageJson.dependencies) || {}), | ||
Object.keys((packageJson && packageJson.devDependencies) || {}) | ||
) | ||
) | ||
/** @type {Set<string|undefined>} */ | ||
const deps = new Set([ | ||
...Object.keys(packageJson?.dependencies ?? {}), | ||
...Object.keys(packageJson?.devDependencies ?? {}), | ||
]) | ||
const options = context.options[0] || {} | ||
@@ -110,2 +111,3 @@ const allow = options.allow || [] | ||
) | ||
/** @type {import('../util/import-target.js')[]} */ | ||
const targets = [] | ||
@@ -143,3 +145,5 @@ | ||
node: target.node, | ||
loc: target.node.loc, | ||
loc: /** @type {NonNullable<import('estree').Node["loc"]>} */ ( | ||
target.node.loc | ||
), | ||
messageId: "unexpectedImport", | ||
@@ -155,8 +159,4 @@ data: { | ||
}, | ||
].reduce( | ||
(mergedVisitor, thisVisitor) => | ||
mergeVisitorsInPlace(mergedVisitor, thisVisitor), | ||
{} | ||
) | ||
].reduce(mergeVisitorsInPlace, {}) | ||
}, | ||
} |
@@ -14,2 +14,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -16,0 +17,0 @@ meta: { |
@@ -15,2 +15,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -17,0 +18,0 @@ meta: { |
@@ -68,2 +68,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -130,3 +131,3 @@ meta: { | ||
* Determines the type of a declaration statement. | ||
* @param {ASTNode} initExpression The init node of the VariableDeclarator. | ||
* @param {import('estree').Node | undefined | null} initExpression The init node of the VariableDeclarator. | ||
* @returns {string} The type of declaration represented by the expression. | ||
@@ -167,3 +168,3 @@ */ | ||
* Determines the type of module that is loaded via require. | ||
* @param {ASTNode} initExpression The init node of the VariableDeclarator. | ||
* @param {import('estree').Expression | import('estree').Super} initExpression The init node of the VariableDeclarator. | ||
* @returns {string} The module type. | ||
@@ -176,3 +177,7 @@ */ | ||
} | ||
if (initExpression.arguments.length === 0) { | ||
if ( | ||
/** @type {import('estree').CallExpression} */ (initExpression) | ||
.arguments.length === 0 | ||
) { | ||
// "var x = require();" | ||
@@ -182,3 +187,5 @@ return REQ_COMPUTED | ||
const arg = initExpression.arguments[0] | ||
const arg = /** @type {import('estree').CallExpression} */ ( | ||
initExpression | ||
).arguments[0] | ||
@@ -206,6 +213,7 @@ if (arg.type !== "Literal" || typeof arg.value !== "string") { | ||
* contains both require and other declarations. | ||
* @param {ASTNode} declarations The list of VariableDeclarators. | ||
* @param {import('estree').VariableDeclarator[]} declarations The list of VariableDeclarators. | ||
* @returns {boolean} True if the declarations are mixed, false if not. | ||
*/ | ||
function isMixed(declarations) { | ||
/** @type {Record<string, boolean>} */ | ||
const contains = {} | ||
@@ -228,6 +236,7 @@ | ||
* type. | ||
* @param {ASTNode} declarations The list of VariableDeclarators. | ||
* @param {import('estree').VariableDeclarator[]} declarations The list of VariableDeclarators. | ||
* @returns {boolean} True if the declarations are grouped, false if not. | ||
*/ | ||
function isGrouped(declarations) { | ||
/** @type {Record<string, boolean>} */ | ||
const found = {} | ||
@@ -237,3 +246,9 @@ | ||
if (getDeclarationType(declaration.init) === DECL_REQUIRE) { | ||
found[inferModuleType(declaration.init)] = true | ||
found[ | ||
inferModuleType( | ||
/** @type {import('estree').Expression} */ ( | ||
declaration.init | ||
) | ||
) | ||
] = true | ||
} | ||
@@ -240,0 +255,0 @@ } |
@@ -7,2 +7,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -9,0 +10,0 @@ meta: { |
@@ -16,6 +16,6 @@ /** | ||
* Get the first char of the specified template element. | ||
* @param {TemplateLiteral} node The `TemplateLiteral` node to get. | ||
* @param {import('estree').TemplateLiteral} node The `TemplateLiteral` node to get. | ||
* @param {number} i The number of template elements to get first char. | ||
* @param {Set<Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("escope").Scope} globalScope The global scope object. | ||
* @param {Set<import('estree').Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("eslint").Scope.Scope} globalScope The global scope object. | ||
* @param {string[]} outNextChars The array to collect chars. | ||
@@ -52,5 +52,5 @@ * @returns {void} | ||
* Get the first char of a given node. | ||
* @param {TemplateLiteral} node The `TemplateLiteral` node to get. | ||
* @param {Set<Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("escope").Scope} globalScope The global scope object. | ||
* @param {import('estree').Node} node The `TemplateLiteral` node to get. | ||
* @param {Set<import('estree').Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("eslint").Scope.Scope} globalScope The global scope object. | ||
* @param {string[]} outNextChars The array to collect chars. | ||
@@ -127,2 +127,3 @@ * @returns {void} | ||
} | ||
/** @typedef {import('estree').Identifier & { parent: import('estree').Node }} Identifier */ | ||
@@ -133,4 +134,4 @@ /** | ||
* @param {Identifier} node The `__dirname` or `__filename` node to check. | ||
* @param {Set<Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("escope").Scope} globalScope The global scope object. | ||
* @param {Set<import('estree').Node>} sepNodes The nodes of `path.sep`. | ||
* @param {import("eslint").Scope.Scope} globalScope The global scope object. | ||
* @returns {boolean} `true` if the given Identifier node is followed by string | ||
@@ -141,2 +142,3 @@ * concatenation with a path separator. | ||
const parent = node.parent | ||
/** @type {string[]} */ | ||
const nextChars = [] | ||
@@ -168,2 +170,3 @@ | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -188,7 +191,8 @@ meta: { | ||
return { | ||
"Program:exit"(node) { | ||
"Program:exit"(programNode) { | ||
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 | ||
const globalScope = | ||
sourceCode.getScope?.(node) ?? context.getScope() | ||
sourceCode.getScope?.(programNode) ?? context.getScope() | ||
const tracker = new ReferenceTracker(globalScope) | ||
/** @type {Set<import('estree').Node>} */ | ||
const sepNodes = new Set() | ||
@@ -213,5 +217,13 @@ | ||
})) { | ||
if (isConcat(node, sepNodes, globalScope)) { | ||
if ( | ||
isConcat( | ||
/** @type {Identifier} */ (node), | ||
sepNodes, | ||
globalScope | ||
) | ||
) { | ||
context.report({ | ||
node: node.parent, | ||
node: /** @type {import('estree').Node & { parent: import('estree').Node }}*/ ( | ||
node | ||
).parent, | ||
messageId: "usePathFunctions", | ||
@@ -218,0 +230,0 @@ }) |
@@ -11,2 +11,10 @@ /** | ||
const querySelector = [ | ||
`MemberExpression`, | ||
`[computed!=true]`, | ||
`[object.name="process"]`, | ||
`[property.name="env"]`, | ||
] | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -29,14 +37,5 @@ meta: { | ||
return { | ||
MemberExpression(node) { | ||
const objectName = node.object.name | ||
const propertyName = node.property.name | ||
if ( | ||
objectName === "process" && | ||
!node.computed && | ||
propertyName && | ||
propertyName === "env" | ||
) { | ||
context.report({ node, messageId: "unexpectedProcessEnv" }) | ||
} | ||
/** @param {import('estree').MemberExpression} node */ | ||
[querySelector.join("")](node) { | ||
context.report({ node, messageId: "unexpectedProcessEnv" }) | ||
}, | ||
@@ -43,0 +42,0 @@ } |
@@ -7,2 +7,10 @@ /** | ||
const querySelector = [ | ||
`CallExpression > `, | ||
`MemberExpression.callee`, | ||
`[object.name="process"]`, | ||
`[property.name="exit"]`, | ||
] | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -25,5 +33,4 @@ meta: { | ||
return { | ||
"CallExpression > MemberExpression.callee[object.name = 'process'][property.name = 'exit']"( | ||
node | ||
) { | ||
/** @param {import('estree').MemberExpression & { parent: import('estree').CallExpression}} node */ | ||
[querySelector.join("")](node) { | ||
context.report({ | ||
@@ -30,0 +37,0 @@ node: node.parent, |
@@ -10,2 +10,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -12,0 +13,0 @@ meta: { |
@@ -11,2 +11,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -13,0 +14,0 @@ meta: { |
@@ -14,3 +14,3 @@ /** | ||
":function :not(MemberExpression) > Identifier[name=/Sync$/]", | ||
] | ||
].join(",") | ||
@@ -24,4 +24,5 @@ const disallowedAtRootLevelSelector = [ | ||
":not(MemberExpression) > Identifier[name=/Sync$/]", | ||
] | ||
].join(",") | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -59,2 +60,7 @@ meta: { | ||
return { | ||
/** | ||
* [node description] | ||
* @param {import('estree').Identifier & {parent: import('estree').Node}} node | ||
* @returns {void} | ||
*/ | ||
[selector](node) { | ||
@@ -61,0 +67,0 @@ context.report({ |
@@ -10,24 +10,6 @@ /** | ||
const getNpmignore = require("../util/get-npmignore") | ||
const getPackageJson = require("../util/get-package-json") | ||
const { getPackageJson } = require("../util/get-package-json") | ||
const { isBinFile } = require("../util/is-bin-file") | ||
/** | ||
* 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]) | ||
) | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -67,9 +49,9 @@ meta: { | ||
// Find package.json | ||
const p = getPackageJson(rawFilePath) | ||
if (!p) { | ||
return | ||
const packageJson = getPackageJson(rawFilePath) | ||
if (typeof packageJson?.filePath !== "string") { | ||
return {} | ||
} | ||
// Convert by convertPath option | ||
const basedir = path.dirname(p.filePath) | ||
const basedir = path.dirname(packageJson.filePath) | ||
const relativePath = getConvertPath(context)( | ||
@@ -81,3 +63,3 @@ path.relative(basedir, rawFilePath).replace(/\\/gu, "/") | ||
// Check this file is bin. | ||
if (!isBinFile(filePath, p.bin, basedir)) { | ||
if (!isBinFile(filePath, packageJson.bin, basedir)) { | ||
return | ||
@@ -84,0 +66,0 @@ } |
@@ -13,2 +13,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -15,0 +16,0 @@ meta: { |
@@ -14,2 +14,3 @@ /** | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -16,0 +17,0 @@ meta: { |
@@ -15,3 +15,4 @@ /** | ||
const trackMap = { | ||
/** @type {Record<'globals' | 'modules', import('../../unsupported-features/types.js').SupportVersionTraceMap>} */ | ||
const traceMap = { | ||
globals: { | ||
@@ -620,4 +621,6 @@ // Core js builtins | ||
}, | ||
modules: {}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -642,3 +645,3 @@ meta: { | ||
enum: Array.from( | ||
enumeratePropertyNames(trackMap.globals) | ||
enumeratePropertyNames(traceMap.globals) | ||
), | ||
@@ -657,3 +660,3 @@ }, | ||
"Program:exit"() { | ||
checkUnsupportedBuiltins(context, trackMap) | ||
checkUnsupportedBuiltins(context, traceMap) | ||
}, | ||
@@ -660,0 +663,0 @@ } |
@@ -20,4 +20,4 @@ /** | ||
/** | ||
* @typedef {Object} ESSyntax | ||
* @property {string[]} aliases | ||
* @typedef ESSyntax | ||
* @property {string[]} [aliases] | ||
* @property {string | null} supported | ||
@@ -28,3 +28,3 @@ * @property {string} [strictMode] | ||
/** | ||
* @typedef {Object} RuleMap | ||
* @typedef RuleMap | ||
* @property {string} ruleId | ||
@@ -34,2 +34,3 @@ * @property {string} feature | ||
* @property {import("semver").Range} supported | ||
* @property {import("semver").Range} [strictMode] | ||
* @property {boolean} deprecated | ||
@@ -63,4 +64,8 @@ */ | ||
ignoreNames: ignoreNames, | ||
supported: getSemverRange(meta.supported ?? "<0"), | ||
strictMode: getSemverRange(meta.strictMode), | ||
supported: /** @type {import("semver").Range} */ ( | ||
getSemverRange(meta.supported ?? "<0") | ||
), | ||
strictMode: meta.strictMode | ||
? getSemverRange(meta.strictMode) | ||
: undefined, | ||
deprecated: Boolean(meta.deprecated), | ||
@@ -72,6 +77,7 @@ } | ||
* Parses the options. | ||
* @param {RuleContext} context The rule context. | ||
* @returns {{version:Range,ignores:Set<string>}} Parsed value. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context. | ||
* @returns {{version: import('semver').Range,ignores:Set<string>}} Parsed value. | ||
*/ | ||
function parseOptions(context) { | ||
/** @type {{ ignores?: string[] }} */ | ||
const raw = context.options[0] || {} | ||
@@ -86,5 +92,5 @@ const version = getConfiguredNodeVersion(context) | ||
* 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. | ||
* @param {import('eslint').Scope.Scope} initialScope The initial scope to find. | ||
* @param {import('estree').Node} node The AST node. | ||
* @returns {import('eslint').Scope.Scope} The scope that the node belongs to. | ||
*/ | ||
@@ -94,3 +100,3 @@ function normalizeScope(initialScope, node) { | ||
while (scope && scope.block === node) { | ||
while (scope?.block === node && scope.upper) { | ||
scope = scope.upper | ||
@@ -102,2 +108,7 @@ } | ||
/** | ||
* @param {import('eslint').Rule.RuleContext} context | ||
* @param {import('estree').Node} node | ||
* @returns {boolean} | ||
*/ | ||
function isStrict(context, node) { | ||
@@ -111,4 +122,4 @@ const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 | ||
* Define the visitor object as merging the rules of eslint-plugin-es-x. | ||
* @param {RuleContext} context The rule context. | ||
* @param {{version:Range,ignores:Set<string>}} options The options. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context. | ||
* @param {ReturnType<parseOptions>} options The options. | ||
* @returns {object} The defined visitor. | ||
@@ -129,3 +140,6 @@ */ | ||
.map(rule => { | ||
const esRule = esRules[rule.ruleId] | ||
const esRule = /** @type {import('eslint').Rule.RuleModule} */ ( | ||
esRules[rule.ruleId] | ||
) | ||
/** @type {Partial<import('eslint').Rule.RuleContext>} */ | ||
const esContext = { | ||
@@ -144,3 +158,10 @@ report(descriptor) { | ||
if (rule.strictMode != null) { | ||
if (isStrict(context, descriptor.node) === false) { | ||
if ( | ||
isStrict( | ||
context, | ||
/** @type {{ node: import('estree').Node}} */ ( | ||
descriptor | ||
).node | ||
) === false | ||
) { | ||
descriptor.data.supported = rule.strictMode.raw | ||
@@ -154,3 +175,3 @@ } else if ( | ||
descriptor.messageId = | ||
const messageId = | ||
rule.supported.raw === "<0" | ||
@@ -160,3 +181,3 @@ ? "not-supported-yet" | ||
super.report(descriptor) | ||
super.report({ ...descriptor, messageId }) | ||
}, | ||
@@ -167,3 +188,5 @@ } | ||
return esRule.create(esContext) | ||
return esRule.create( | ||
/** @type {import('eslint').Rule.RuleContext} */ (esContext) | ||
) | ||
}) | ||
@@ -173,2 +196,3 @@ .reduce(mergeVisitorsInPlace, {}) | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -175,0 +199,0 @@ meta: { |
@@ -136,7 +136,7 @@ { | ||
"supported": ">=0.10.0", | ||
"deprecated": true | ||
"supported": ">=0.10.0" | ||
}, | ||
"no-date-prototype-togmtstring": { | ||
"supported": ">=0.10.0", | ||
"deprecated": true | ||
"supported": ">=0.10.0" | ||
}, | ||
@@ -157,3 +157,3 @@ "no-default-parameters": { | ||
"supported": ">=0.10.0", | ||
"deprecated": true | ||
"supported": ">=0.10.0" | ||
}, | ||
@@ -246,3 +246,3 @@ "no-exponential-operators": { | ||
"supported": ">=0.10.0", | ||
"deprecated": true | ||
"supported": ">=0.10.0" | ||
}, | ||
@@ -530,3 +530,3 @@ "no-logical-assignment-operators": { | ||
"supported": ">=0.10.0", | ||
"deprecated": true | ||
"supported": ">=0.10.0" | ||
}, | ||
@@ -533,0 +533,0 @@ "no-string-fromcodepoint": { |
@@ -19,6 +19,11 @@ /** | ||
const trackMap = { | ||
/** | ||
* @typedef TraceMap | ||
* @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} globals | ||
* @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} modules | ||
*/ | ||
const traceMap = { | ||
globals: { | ||
queueMicrotask: { | ||
[READ]: { supported: ["12.0.0"], experimental: "11.0.0" }, | ||
[READ]: { supported: ["12.0.0"], experimental: ["11.0.0"] }, | ||
}, | ||
@@ -33,24 +38,25 @@ require: { | ||
} | ||
Object.assign(trackMap.globals, { | ||
Buffer: trackMap.modules.buffer.Buffer, | ||
Object.assign(traceMap.globals, { | ||
Buffer: traceMap.modules.buffer.Buffer, | ||
TextDecoder: { | ||
...trackMap.modules.util.TextDecoder, | ||
...traceMap.modules.util.TextDecoder, | ||
[READ]: { supported: ["11.0.0"] }, | ||
}, | ||
TextEncoder: { | ||
...trackMap.modules.util.TextEncoder, | ||
...traceMap.modules.util.TextEncoder, | ||
[READ]: { supported: ["11.0.0"] }, | ||
}, | ||
URL: { | ||
...trackMap.modules.url.URL, | ||
...traceMap.modules.url.URL, | ||
[READ]: { supported: ["10.0.0"] }, | ||
}, | ||
URLSearchParams: { | ||
...trackMap.modules.url.URLSearchParams, | ||
...traceMap.modules.url.URLSearchParams, | ||
[READ]: { supported: ["10.0.0"] }, | ||
}, | ||
console: trackMap.modules.console, | ||
process: trackMap.modules.process, | ||
console: traceMap.modules.console, | ||
process: traceMap.modules.process, | ||
}) | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -76,4 +82,4 @@ meta: { | ||
new Set([ | ||
...enumeratePropertyNames(trackMap.globals), | ||
...enumeratePropertyNames(trackMap.modules), | ||
...enumeratePropertyNames(traceMap.globals), | ||
...enumeratePropertyNames(traceMap.modules), | ||
]) | ||
@@ -93,3 +99,3 @@ ), | ||
"Program:exit"() { | ||
checkUnsupportedBuiltins(context, trackMap) | ||
checkUnsupportedBuiltins(context, traceMap) | ||
}, | ||
@@ -96,0 +102,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -16,8 +16,8 @@ Buffer: { [READ]: true }, | ||
modules: { | ||
buffer: { | ||
Buffer: { [READ]: true }, | ||
}, | ||
buffer: { Buffer: { [READ]: true } }, | ||
"node:buffer": { Buffer: { [READ]: true } }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -45,3 +45,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -48,0 +48,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -17,5 +17,7 @@ console: { [READ]: true }, | ||
console: { [READ]: true }, | ||
"node:console": { [READ]: true }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -42,3 +44,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -45,0 +47,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -17,5 +17,7 @@ process: { [READ]: true }, | ||
process: { [READ]: true }, | ||
"node:process": { [READ]: true }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -42,3 +44,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -45,0 +47,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -16,8 +16,8 @@ TextDecoder: { [READ]: true }, | ||
modules: { | ||
util: { | ||
TextDecoder: { [READ]: true }, | ||
}, | ||
util: { TextDecoder: { [READ]: true } }, | ||
"node:util": { TextDecoder: { [READ]: true } }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -45,3 +45,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -48,0 +48,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -16,8 +16,8 @@ TextEncoder: { [READ]: true }, | ||
modules: { | ||
util: { | ||
TextEncoder: { [READ]: true }, | ||
}, | ||
util: { TextEncoder: { [READ]: true } }, | ||
"node:util": { TextEncoder: { [READ]: true } }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -45,3 +45,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -48,0 +48,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -16,8 +16,8 @@ URLSearchParams: { [READ]: true }, | ||
modules: { | ||
url: { | ||
URLSearchParams: { [READ]: true }, | ||
}, | ||
url: { URLSearchParams: { [READ]: true } }, | ||
"node:url": { URLSearchParams: { [READ]: true } }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -45,3 +45,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -48,0 +48,0 @@ } |
@@ -10,3 +10,3 @@ /** | ||
const trackMap = { | ||
const traceMap = { | ||
globals: { | ||
@@ -16,8 +16,8 @@ URL: { [READ]: true }, | ||
modules: { | ||
url: { | ||
URL: { [READ]: true }, | ||
}, | ||
url: { URL: { [READ]: true } }, | ||
"node:url": { URL: { [READ]: true } }, | ||
}, | ||
} | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -44,3 +44,3 @@ meta: { | ||
"Program:exit"() { | ||
checkForPreferGlobal(context, trackMap) | ||
checkForPreferGlobal(context, traceMap) | ||
}, | ||
@@ -47,0 +47,0 @@ } |
@@ -16,2 +16,10 @@ /** | ||
const supportedRangeForEsm = /** @type {import('semver').Range} */ ( | ||
getSemverRange("^12.20.0 || >= 14.13.1") | ||
) | ||
const supportedRangeForCjs = /** @type {import('semver').Range} */ ( | ||
getSemverRange("^14.18.0 || >= 16.0.0") | ||
) | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -41,2 +49,9 @@ meta: { | ||
create(context) { | ||
/** | ||
* @param {import('estree').Node} node | ||
* @param {object} options | ||
* @param {string} options.name | ||
* @param {number} options.argumentsLength | ||
* @returns {node is import('estree').CallExpression} | ||
*/ | ||
function isCallExpression(node, { name, argumentsLength }) { | ||
@@ -65,2 +80,6 @@ if (node?.type !== "CallExpression") { | ||
/** | ||
* @param {import('estree').Node} node | ||
* @returns {node is import('estree').Literal} | ||
*/ | ||
function isStringLiteral(node) { | ||
@@ -70,25 +89,34 @@ return node?.type === "Literal" && typeof node.type === "string" | ||
/** | ||
* @param {import('estree').Node | undefined} node | ||
* @returns {node is import('estree').CallExpression} | ||
*/ | ||
function isStaticRequire(node) { | ||
return ( | ||
node != null && | ||
isCallExpression(node, { | ||
name: "require", | ||
argumentsLength: 1, | ||
}) && isStringLiteral(node.arguments[0]) | ||
}) && | ||
isStringLiteral(node.arguments[0]) | ||
) | ||
} | ||
/** | ||
* @param {import('eslint').Rule.RuleContext} context | ||
* @param {import('../util/import-target.js').ModuleStyle} moduleStyle | ||
* @returns {boolean} | ||
*/ | ||
function isEnablingThisRule(context, moduleStyle) { | ||
const version = getConfiguredNodeVersion(context) | ||
const supportedVersionForEsm = "^12.20.0 || >= 14.13.1" | ||
// Only check Node.js version because this rule is meaningless if configured Node.js version doesn't match semver range. | ||
if (!version.intersects(getSemverRange(supportedVersionForEsm))) { | ||
if (!version.intersects(supportedRangeForEsm)) { | ||
return false | ||
} | ||
const supportedVersionForCjs = "^14.18.0 || >= 16.0.0" | ||
// Only check when using `require` | ||
if ( | ||
moduleStyle === "require" && | ||
!version.intersects(getSemverRange(supportedVersionForCjs)) | ||
!version.intersects(supportedRangeForCjs) | ||
) { | ||
@@ -101,2 +129,3 @@ return false | ||
/** @type {import('../util/import-target.js')[]} */ | ||
const targets = [] | ||
@@ -125,3 +154,3 @@ return [ | ||
const { value } = node | ||
const { value } = /** @type {{ value: string }}*/ (node) | ||
if ( | ||
@@ -143,3 +172,4 @@ typeof value !== "string" || | ||
fix(fixer) { | ||
const firstCharacterIndex = node.range[0] + 1 | ||
const firstCharacterIndex = | ||
(node?.range?.[0] ?? 0) + 1 | ||
return fixer.replaceTextRange( | ||
@@ -146,0 +176,0 @@ [firstCharacterIndex, firstCharacterIndex], |
@@ -13,3 +13,4 @@ /** | ||
const trackMap = { | ||
/** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */ | ||
const traceMap = { | ||
dns: { | ||
@@ -36,4 +37,5 @@ lookup: { [CALL]: true }, | ||
} | ||
trackMap["node:dns"] = trackMap.dns | ||
traceMap["node:dns"] = traceMap.dns | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -62,4 +64,4 @@ meta: { | ||
const references = [ | ||
...tracker.iterateCjsReferences(trackMap), | ||
...tracker.iterateEsmReferences(trackMap), | ||
...tracker.iterateCjsReferences(traceMap), | ||
...tracker.iterateEsmReferences(traceMap), | ||
] | ||
@@ -66,0 +68,0 @@ |
@@ -9,3 +9,4 @@ /** | ||
const trackMap = { | ||
/** @type {import('@eslint-community/eslint-utils').TraceMap<boolean>} */ | ||
const traceMap = { | ||
fs: { | ||
@@ -38,4 +39,5 @@ access: { [CALL]: true }, | ||
} | ||
trackMap["node:fs"] = trackMap.fs | ||
traceMap["node:fs"] = traceMap.fs | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -63,4 +65,4 @@ meta: { | ||
const references = [ | ||
...tracker.iterateCjsReferences(trackMap), | ||
...tracker.iterateEsmReferences(trackMap), | ||
...tracker.iterateCjsReferences(traceMap), | ||
...tracker.iterateEsmReferences(traceMap), | ||
] | ||
@@ -67,0 +69,0 @@ |
@@ -8,2 +8,3 @@ /* eslint-disable eslint-plugin/prefer-message-ids */ | ||
/** @type {typeof import('../types-code-path-analysis/code-path-analyzer.js')} */ | ||
const CodePathAnalyzer = safeRequire( | ||
@@ -13,2 +14,3 @@ "eslint/lib/linter/code-path-analysis/code-path-analyzer", | ||
) | ||
/** @type {typeof import('../types-code-path-analysis/code-path-segment.js')} */ | ||
const CodePathSegment = safeRequire( | ||
@@ -18,2 +20,3 @@ "eslint/lib/linter/code-path-analysis/code-path-segment", | ||
) | ||
/** @type {typeof import('../types-code-path-analysis/code-path.js')} */ | ||
const CodePath = safeRequire( | ||
@@ -24,4 +27,3 @@ "eslint/lib/linter/code-path-analysis/code-path", | ||
const originalLeaveNode = | ||
CodePathAnalyzer && CodePathAnalyzer.prototype.leaveNode | ||
const originalLeaveNode = CodePathAnalyzer?.prototype?.leaveNode | ||
@@ -31,3 +33,3 @@ /** | ||
* @param {...string} moduleNames - module names to import. | ||
* @returns {object|null} The imported object, or null. | ||
* @returns {*} The imported object, or null. | ||
*/ | ||
@@ -38,3 +40,3 @@ function safeRequire(...moduleNames) { | ||
return require(moduleName) | ||
} catch (_err) { | ||
} catch { | ||
// Ignore. | ||
@@ -50,4 +52,4 @@ } | ||
* | ||
* @param {CodePathAnalyzer} analyzer - The instance. | ||
* @param {ASTNode} node - The current AST node. | ||
* @param {import('../types-code-path-analysis/code-path-analyzer.js')} analyzer - The instance. | ||
* @param {import('eslint').Rule.Node} node - The current AST node. | ||
* @returns {void} | ||
@@ -105,3 +107,3 @@ */ | ||
* | ||
* @param {ASTNode} node - A node to check. | ||
* @param {import('eslint').Rule.Node} node - A node to check. | ||
* @returns {boolean} `true` if the node is `process.exit()`. | ||
@@ -125,4 +127,4 @@ */ | ||
* | ||
* @this CodePathAnalyzer | ||
* @param {ASTNode} node - A node to be left. | ||
* @this {import('../types-code-path-analysis/code-path-analyzer.js')} | ||
* @param {import('eslint').Rule.Node} node - A node to be left. | ||
* @returns {void} | ||
@@ -156,2 +158,3 @@ */ | ||
/** @type {import('eslint').Rule.RuleModule} */ | ||
module.exports = { | ||
@@ -158,0 +161,0 @@ meta: { |
@@ -15,5 +15,5 @@ /** | ||
replacedBy: ["n/hashbang"], | ||
docs: { ...hashbang.meta.docs, recommended: false }, | ||
docs: { ...hashbang.meta?.docs, recommended: false }, | ||
}, | ||
create: hashbang.create, | ||
} |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const assert = { | ||
@@ -45,24 +45,24 @@ assert: { [READ]: { supported: ["0.5.9"] } }, | ||
assert.strict = { | ||
...assert, | ||
[READ]: { supported: ["9.9.0", "8.13.0"] }, | ||
...assert, | ||
} | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
assert: { | ||
...assert, | ||
[READ]: { supported: ["0.1.21"] }, | ||
...assert, | ||
}, | ||
"node:assert": { | ||
...assert, | ||
[READ]: { supported: ["14.13.1", "12.20.0"] }, | ||
...assert, | ||
}, | ||
"assert/strict": { | ||
...assert.strict, | ||
[READ]: { supported: ["15.0.0"] }, | ||
...assert.strict, | ||
}, | ||
"node:assert/strict": { | ||
...assert.strict, | ||
[READ]: { supported: ["15.0.0"] }, | ||
...assert.strict, | ||
}, | ||
} |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const async_hooks = { | ||
@@ -29,3 +29,3 @@ createHook: { [READ]: { experimental: ["8.1.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -32,0 +32,0 @@ async_hooks: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const buffer = { | ||
@@ -46,3 +46,3 @@ constants: { [READ]: { supported: ["8.2.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -49,0 +49,0 @@ buffer: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const child_process = { | ||
@@ -18,3 +18,3 @@ exec: { [READ]: { supported: ["0.1.90"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -21,0 +21,0 @@ child_process: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const cluster = { | ||
@@ -22,3 +22,3 @@ isMaster: { [READ]: { supported: ["0.8.1"], deprecated: ["16.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -25,0 +25,0 @@ cluster: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const console = { | ||
@@ -38,3 +38,3 @@ profile: { [READ]: { supported: ["8.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -41,0 +41,0 @@ console: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const WebCrypto = { | ||
@@ -28,3 +28,3 @@ [READ]: { experimental: ["15.0.0"], supported: ["19.0.0"] }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const crypto = { | ||
@@ -121,3 +121,3 @@ constants: { [READ]: { supported: ["6.3.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -124,0 +124,0 @@ crypto: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const dgram = { | ||
@@ -12,3 +12,3 @@ createSocket: { [READ]: { supported: ["0.1.99"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -15,0 +15,0 @@ dgram: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const diagnostics_channel = { | ||
@@ -17,3 +17,3 @@ hasSubscribers: { [READ]: { supported: ["15.1.0", "14.17.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -20,0 +20,0 @@ diagnostics_channel: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const dns = { | ||
@@ -60,3 +60,3 @@ Resolver: { [READ]: { supported: ["8.3.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -63,0 +63,0 @@ dns: { ...dns, [READ]: { supported: ["0.1.16"] } }, |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const domain = { | ||
@@ -12,3 +12,3 @@ create: { [READ]: { supported: ["0.7.8"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -15,0 +15,0 @@ domain: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const EventEmitterStatic = { | ||
@@ -31,3 +31,3 @@ defaultMaxListeners: { [READ]: { supported: ["0.11.2"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const events = { | ||
@@ -59,3 +59,3 @@ Event: { [READ]: { experimental: ["14.5.0"], supported: ["15.4.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -62,0 +62,0 @@ events: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const promises_api = { | ||
@@ -42,3 +42,3 @@ constants: { [READ]: { supported: ["18.4.0", "16.17.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const callback_api = { | ||
@@ -99,3 +99,3 @@ access: { [READ]: { supported: ["0.11.15"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const synchronous_api = { | ||
@@ -150,3 +150,3 @@ accessSync: { [READ]: { supported: ["0.11.15"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const fs = { | ||
@@ -174,3 +174,3 @@ promises: { | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -177,0 +177,0 @@ fs: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const http = { | ||
@@ -26,3 +26,3 @@ METHODS: { [READ]: { supported: ["0.11.8"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -29,0 +29,0 @@ http: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const http2 = { | ||
@@ -28,3 +28,3 @@ constants: { [READ]: { supported: ["8.4.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -31,0 +31,0 @@ http2: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const http = { | ||
@@ -16,3 +16,3 @@ globalAgent: { [READ]: { supported: ["0.5.9"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -19,0 +19,0 @@ http: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const common_objects = { | ||
@@ -15,3 +15,3 @@ console: { [READ]: { supported: ["8.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const promises_api = { | ||
@@ -22,3 +22,3 @@ Session: { [READ]: { supported: ["19.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const callback_api = { | ||
@@ -29,3 +29,3 @@ Session: { [READ]: { supported: ["8.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -32,0 +32,0 @@ inspector: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const Module = { | ||
@@ -25,3 +25,3 @@ builtinModules: { [READ]: { supported: ["9.3.0", "8.10.0", "6.13.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -28,0 +28,0 @@ module: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const net = { | ||
@@ -28,3 +28,3 @@ connect: { [READ]: { supported: ["0.0.1"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -31,0 +31,0 @@ net: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const os = { | ||
@@ -36,3 +36,3 @@ EOL: { [READ]: { supported: ["0.7.8"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -39,0 +39,0 @@ os: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const path = { | ||
@@ -23,3 +23,3 @@ delimiter: { [READ]: { supported: ["0.9.3"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -26,0 +26,0 @@ path: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const perf_hooks = { | ||
@@ -24,3 +24,3 @@ performance: { [READ]: { supported: ["8.5.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -27,0 +27,0 @@ perf_hooks: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const process = { | ||
@@ -121,3 +121,3 @@ allowedNodeEnvironmentFlags: { [READ]: { supported: ["10.10.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -124,0 +124,0 @@ process: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const punycode = { | ||
@@ -16,3 +16,3 @@ ucs2: { [READ]: { supported: ["0.7.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -19,0 +19,0 @@ punycode: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const querystring = { | ||
@@ -16,3 +16,3 @@ decode: { [READ]: { supported: ["0.1.99"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -19,0 +19,0 @@ querystring: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const promises_api = { | ||
@@ -13,3 +13,3 @@ createInterface: { [READ]: { supported: ["17.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const readline = { | ||
@@ -30,3 +30,3 @@ promises: { | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -33,0 +33,0 @@ readline: { |
@@ -7,3 +7,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const Readable = { | ||
@@ -17,3 +17,3 @@ [READ]: { supported: ["0.9.4"] }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const Writable = { | ||
@@ -25,3 +25,3 @@ [READ]: { supported: ["0.9.4"] }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const Duplex = { | ||
@@ -36,3 +36,3 @@ [READ]: { supported: ["0.9.4"] }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const StreamPromise = { | ||
@@ -43,3 +43,3 @@ pipeline: { [READ]: { supported: ["15.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const Stream = { | ||
@@ -66,3 +66,3 @@ promises: { | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const StreamWeb = { | ||
@@ -100,3 +100,3 @@ ReadableStream: { | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -130,10 +130,4 @@ stream: { | ||
"stream/consumers": { | ||
[READ]: { supported: ["16.7.0"] }, | ||
...StreamConsumer, | ||
}, | ||
"node:stream/consumers": { | ||
[READ]: { supported: ["16.7.0"] }, | ||
...StreamConsumer, | ||
}, | ||
"stream/consumers": { ...StreamConsumer }, | ||
"node:stream/consumers": { ...StreamConsumer }, | ||
} |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const string_decoder = { | ||
@@ -11,3 +11,3 @@ StringDecoder: { [READ]: { supported: ["0.1.99"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -14,0 +14,0 @@ string_decoder: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const test = { | ||
@@ -38,3 +38,3 @@ run: { [READ]: { supported: ["18.9.0", "16.19.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -41,0 +41,0 @@ "node:test": { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const promises_api = { | ||
@@ -17,3 +17,3 @@ setTimeout: { [READ]: { supported: ["15.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const timers = { | ||
@@ -36,3 +36,3 @@ Immediate: { [READ]: { supported: ["0.9.1"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -39,0 +39,0 @@ timers: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const tls = { | ||
@@ -28,3 +28,3 @@ rootCertificates: { [READ]: { supported: ["12.3.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -31,0 +31,0 @@ tls: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const trace_events = { | ||
@@ -12,3 +12,3 @@ createTracing: { [READ]: { supported: ["10.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -15,0 +15,0 @@ trace_events: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const tty = { | ||
@@ -13,3 +13,3 @@ isatty: { [READ]: { supported: ["0.5.8"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -16,0 +16,0 @@ tty: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const url = { | ||
@@ -24,3 +24,3 @@ domainToASCII: { [READ]: { supported: ["7.4.0", "6.13.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -27,0 +27,0 @@ url: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const types = { | ||
@@ -56,3 +56,3 @@ [READ]: { supported: ["10.0.0"] }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const deprecated = { | ||
@@ -80,3 +80,3 @@ _extend: { [READ]: { supported: ["0.7.5"], deprecated: ["6.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const util = { | ||
@@ -121,3 +121,3 @@ promisify: { | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -124,0 +124,0 @@ util: util, |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const v8 = { | ||
@@ -48,3 +48,3 @@ serialize: { [READ]: { supported: ["8.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -51,0 +51,0 @@ v8: { ...v8, [READ]: { supported: ["1.0.0"] } }, |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const vm = { | ||
@@ -22,3 +22,3 @@ compileFunction: { [READ]: { supported: ["10.10.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -25,0 +25,0 @@ vm: vm, |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const wasi = { | ||
@@ -11,3 +11,3 @@ WASI: { [READ]: { supported: ["13.3.0", "12.16.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -14,0 +14,0 @@ wasi: wasi, |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const worker_threads = { | ||
@@ -38,3 +38,3 @@ isMainThread: { [READ]: { supported: ["10.5.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -41,0 +41,0 @@ worker_threads: { |
@@ -5,3 +5,3 @@ "use strict" | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
const zlib = { | ||
@@ -47,3 +47,3 @@ constants: { [READ]: { supported: ["7.0.0"] } }, | ||
/** @type {import('../types.js').SupportVersionTree} */ | ||
/** @type {import('../types.js').SupportVersionTraceMap} */ | ||
module.exports = { | ||
@@ -50,0 +50,0 @@ zlib: zlib, |
"use strict" | ||
/** @type {import('./types.js').SupportVersionTree} */ | ||
/** @type {import('./types.js').SupportVersionTraceMap} */ | ||
const NodeBuiltinModules = { | ||
@@ -5,0 +5,0 @@ ...require("./node-builtins-modules/assert.js"), |
"use strict" | ||
/** | ||
* @typedef {Object} SupportInfo | ||
* @property {string[]} experimental The node versions in which experimental support was added | ||
* @property {string[]} supported The node versions in which stable support was added | ||
* @property {string[]} deprecated The node versions in which support was removed | ||
* @typedef {( | ||
* | import("@eslint-community/eslint-utils").READ | ||
* | import("@eslint-community/eslint-utils").CALL | ||
* | import("@eslint-community/eslint-utils").CONSTRUCT | ||
* )} UTIL_SYMBOL | ||
*/ | ||
/** | ||
* @typedef {{ | ||
* [key: readonly unique symbol]: SupportInfo | undefined; | ||
* [key: string]: SupportVersionTree | undefined; | ||
* }} SupportVersionTree | ||
* @typedef SupportInfo | ||
* @property {string[]} [experimental] The node versions in which experimental support was added | ||
* @property {string[]} [supported] The node versions in which stable support was added | ||
* @property {string[]} [deprecated] The node versions in which support was removed | ||
*/ | ||
/** | ||
* @typedef DeprecatedInfo | ||
* @property {string} since | ||
* @property {string|{ name: string, supported: string }[]|null} replacedBy | ||
*/ | ||
/** @typedef {import('@eslint-community/eslint-utils').TraceMap<DeprecatedInfo>} DeprecatedInfoTraceMap */ | ||
/** @typedef {import('@eslint-community/eslint-utils').TraceMap<SupportInfo>} SupportVersionTraceMap */ | ||
/** | ||
* @typedef SupportVersionBuiltins | ||
* @property {SupportVersionTraceMap} globals | ||
* @property {SupportVersionTraceMap} modules | ||
*/ | ||
module.exports = {} |
@@ -11,7 +11,7 @@ /** | ||
const isTypescript = require("./is-typescript") | ||
const mapTypescriptExtension = require("../util/map-typescript-extension") | ||
const { convertJsExtensionToTs } = require("../util/map-typescript-extension") | ||
/** | ||
* Reports a missing file from ImportTarget | ||
* @param {RuleContext} context - A context to report. | ||
* @param {import('eslint').Rule.RuleContext} context - A context to report. | ||
* @param {import('../util/import-target.js')} target - A list of target information to check. | ||
@@ -23,5 +23,7 @@ * @returns {void} | ||
node: target.node, | ||
loc: target.node.loc, | ||
loc: /** @type {import('eslint').AST.SourceLocation} */ ( | ||
target.node.loc | ||
), | ||
messageId: "notFound", | ||
data: target, | ||
data: /** @type {Record<string, *>} */ (target), | ||
}) | ||
@@ -36,3 +38,3 @@ } | ||
* | ||
* @param {RuleContext} context - A context to report. | ||
* @param {import('eslint').Rule.RuleContext} context - A context to report. | ||
* @param {import('../util/import-target.js')[]} targets - A list of target information to check. | ||
@@ -44,3 +46,3 @@ * @returns {void} | ||
for (const target of targets) { | ||
target: for (const target of targets) { | ||
if ( | ||
@@ -55,31 +57,33 @@ target.moduleName != null && | ||
if (target.moduleName != null) { | ||
if ( | ||
target.moduleName != null || | ||
target.filePath == null || | ||
exists(target.filePath) | ||
) { | ||
continue | ||
} | ||
let missingFile = | ||
target.filePath == null ? false : !exists(target.filePath) | ||
if (isTypescript(context) === false) { | ||
markMissing(context, target) | ||
continue | ||
} | ||
if (missingFile && isTypescript(context)) { | ||
const parsed = path.parse(target.filePath) | ||
const pathWithoutExt = path.resolve(parsed.dir, parsed.name) | ||
const parsed = path.parse(target.filePath) | ||
const pathWithoutExt = path.resolve(parsed.dir, parsed.name) | ||
const reversedExts = mapTypescriptExtension( | ||
context, | ||
target.filePath, | ||
parsed.ext, | ||
true | ||
) | ||
const reversedPaths = reversedExts.map( | ||
reversedExt => pathWithoutExt + reversedExt | ||
) | ||
missingFile = reversedPaths.every( | ||
reversedPath => | ||
target.moduleName == null && !exists(reversedPath) | ||
) | ||
const reversedExtensions = convertJsExtensionToTs( | ||
context, | ||
target.filePath, | ||
parsed.ext | ||
) | ||
for (const reversedExtension of reversedExtensions) { | ||
const reversedPath = pathWithoutExt + reversedExtension | ||
if (exists(reversedPath)) { | ||
continue target | ||
} | ||
} | ||
if (missingFile) { | ||
markMissing(context, target) | ||
} | ||
markMissing(context, target) | ||
} | ||
@@ -86,0 +90,0 @@ } |
@@ -8,3 +8,3 @@ /** | ||
const getAllowModules = require("./get-allow-modules") | ||
const getPackageJson = require("./get-package-json") | ||
const { getPackageJson } = require("./get-package-json") | ||
@@ -16,5 +16,5 @@ /** | ||
* | ||
* @param {RuleContext} context - A context to report. | ||
* @param {import('eslint').Rule.RuleContext} context - A context to report. | ||
* @param {string} filePath - The current file path. | ||
* @param {ImportTarget[]} targets - A list of target information to check. | ||
* @param {import('./import-target.js')[]} targets - A list of target information to check. | ||
* @returns {void} | ||
@@ -48,5 +48,7 @@ */ | ||
node: target.node, | ||
loc: target.node.loc, | ||
loc: /** @type {import('eslint').AST.SourceLocation} */ ( | ||
target.node.loc | ||
), | ||
messageId: "extraneous", | ||
data: target, | ||
data: /** @type {Record<string, *>} */ (target), | ||
}) | ||
@@ -53,0 +55,0 @@ } |
@@ -8,5 +8,10 @@ /** | ||
const { ReferenceTracker } = require("@eslint-community/eslint-utils") | ||
const extendTrackmapWithNodePrefix = require("./extend-trackmap-with-node-prefix") | ||
/** | ||
* @typedef TraceMap | ||
* @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} globals | ||
* @property {import('@eslint-community/eslint-utils').TraceMap<boolean>} modules | ||
*/ | ||
/** | ||
* Verifier for `prefer-global/*` rules. | ||
@@ -17,8 +22,8 @@ */ | ||
* Initialize this instance. | ||
* @param {RuleContext} context The rule context to report. | ||
* @param {{modules:object,globals:object}} trackMap The track map. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context to report. | ||
* @param {TraceMap} traceMap The track map. | ||
*/ | ||
constructor(context, trackMap) { | ||
constructor(context, traceMap) { | ||
this.context = context | ||
this.trackMap = trackMap | ||
this.traceMap = traceMap | ||
this.verify = | ||
@@ -35,3 +40,3 @@ context.options[0] === "never" | ||
verifyToPreferGlobals() { | ||
const { context, trackMap } = this | ||
const { context, traceMap } = this | ||
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 | ||
@@ -44,7 +49,5 @@ const scope = | ||
const modules = extendTrackmapWithNodePrefix(trackMap.modules) | ||
for (const { node } of [ | ||
...tracker.iterateCjsReferences(modules), | ||
...tracker.iterateEsmReferences(modules), | ||
...tracker.iterateCjsReferences(traceMap.modules), | ||
...tracker.iterateEsmReferences(traceMap.modules), | ||
]) { | ||
@@ -60,3 +63,3 @@ context.report({ node, messageId: "preferGlobal" }) | ||
verifyToPreferModules() { | ||
const { context, trackMap } = this | ||
const { context, traceMap } = this | ||
const sourceCode = context.sourceCode ?? context.getSourceCode() // TODO: just use context.sourceCode when dropping eslint < v9 | ||
@@ -68,3 +71,3 @@ const scope = | ||
for (const { node } of tracker.iterateGlobalReferences( | ||
trackMap.globals | ||
traceMap.globals | ||
)) { | ||
@@ -76,4 +79,9 @@ context.report({ node, messageId: "preferModule" }) | ||
module.exports = function checkForPreferGlobal(context, trackMap) { | ||
new Verifier(context, trackMap).verify() | ||
/** | ||
* @param {import('eslint').Rule.RuleContext} context | ||
* @param {TraceMap} traceMap | ||
* @returns {void} | ||
*/ | ||
module.exports = function checkForPreferGlobal(context, traceMap) { | ||
new Verifier(context, traceMap).verify() | ||
} |
@@ -11,3 +11,3 @@ /** | ||
const getNpmignore = require("./get-npmignore") | ||
const getPackageJson = require("./get-package-json") | ||
const { getPackageJson } = require("./get-package-json") | ||
@@ -19,10 +19,10 @@ /** | ||
* | ||
* @param {RuleContext} context - A context to report. | ||
* @param {import('eslint').Rule.RuleContext} context - A context to report. | ||
* @param {string} filePath - The current file path. | ||
* @param {ImportTarget[]} targets - A list of target information to check. | ||
* @param {import('./import-target.js')[]} targets - A list of target information to check. | ||
* @returns {void} | ||
*/ | ||
exports.checkPublish = function checkPublish(context, filePath, targets) { | ||
const packageInfo = getPackageJson(filePath) | ||
if (!packageInfo) { | ||
const packageJson = getPackageJson(filePath) | ||
if (typeof packageJson?.filePath !== "string") { | ||
return | ||
@@ -33,3 +33,3 @@ } | ||
// More information: https://docs.npmjs.com/cli/v8/configuring-npm/package-json#private | ||
if (packageInfo.private === true) { | ||
if (packageJson.private === true) { | ||
return | ||
@@ -40,4 +40,5 @@ } | ||
const convertPath = getConvertPath(context) | ||
const basedir = path.dirname(packageInfo.filePath) | ||
const basedir = path.dirname(packageJson.filePath) | ||
/** @type {(fullPath: string) => string} */ | ||
const toRelative = fullPath => { | ||
@@ -49,11 +50,9 @@ const retv = path.relative(basedir, fullPath).replace(/\\/gu, "/") | ||
const devDependencies = new Set( | ||
Object.keys(packageInfo.devDependencies || {}) | ||
Object.keys(packageJson.devDependencies ?? {}) | ||
) | ||
const dependencies = new Set( | ||
[].concat( | ||
Object.keys(packageInfo.dependencies || {}), | ||
Object.keys(packageInfo.peerDependencies || {}), | ||
Object.keys(packageInfo.optionalDependencies || {}) | ||
) | ||
) | ||
const dependencies = new Set([ | ||
...Object.keys(packageJson?.dependencies ?? {}), | ||
...Object.keys(packageJson?.peerDependencies ?? {}), | ||
...Object.keys(packageJson?.optionalDependencies ?? {}), | ||
]) | ||
@@ -82,3 +81,5 @@ if (!npmignore.match(toRelative(filePath))) { | ||
node: target.node, | ||
loc: target.node.loc, | ||
loc: /** @type {import('estree').SourceLocation} */ ( | ||
target.node.loc | ||
), | ||
messageId: "notPublished", | ||
@@ -85,0 +86,0 @@ data: { name: target.moduleName || target.name }, |
@@ -12,3 +12,3 @@ /** | ||
/** | ||
* @typedef {Object} DefinitionData | ||
* @typedef DefinitionData | ||
* @property {string | string[]} name The name to disallow. | ||
@@ -20,5 +20,5 @@ * @property {string} [message] The custom message to show. | ||
* Check if matched or not. | ||
* @param {InstanceType<Minimatch>} matcher The matcher. | ||
* @param {Minimatch} matcher The matcher. | ||
* @param {boolean} absolute The flag that the matcher is for absolute paths. | ||
* @param {ImportTarget} importee The importee information. | ||
* @param {import('./import-target.js')} importee The importee information. | ||
*/ | ||
@@ -59,3 +59,3 @@ function match(matcher, absolute, { filePath, name }) { | ||
* Check if a given importee is disallowed. | ||
* @param {ImportTarget} importee The importee to check. | ||
* @param {import('./import-target.js')} importee The importee to check. | ||
* @returns {boolean} `true` if the importee is disallowed. | ||
@@ -88,4 +88,4 @@ */ | ||
* Create restrictions. | ||
* @param {(string | DefinitionData | GlobDefinition)[]} defs Definitions. | ||
* @returns {(Restriction | GlobRestriction)[]} Created restrictions. | ||
* @param {(string | DefinitionData)[]} defs Definitions. | ||
* @returns {(Restriction)[]} Created restrictions. | ||
*/ | ||
@@ -98,4 +98,4 @@ function createRestrictions(defs) { | ||
* Checks if given importees are disallowed or not. | ||
* @param {RuleContext} context - A context to report. | ||
* @param {ImportTarget[]} targets - A list of target information to check. | ||
* @param {import('eslint').Rule.RuleContext} context - A context to report. | ||
* @param {import('./import-target.js')[]} targets - A list of target information to check. | ||
* @returns {void} | ||
@@ -102,0 +102,0 @@ */ |
@@ -14,11 +14,8 @@ /** | ||
/** | ||
* @typedef {Object} SupportInfo | ||
* @property {string[]} supported The stably supported version. If `null` is present, it hasn't been supported yet. | ||
* @property {string} [experimental] The added version as experimental. | ||
*/ | ||
/** | ||
* Parses the options. | ||
* @param {RuleContext} context The rule context. | ||
* @returns {{version:Range,ignores:Set<string>}} Parsed value. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context. | ||
* @returns {Readonly<{ | ||
* version: import('semver').Range, | ||
* ignores: Set<string> | ||
* }>} Parsed value. | ||
*/ | ||
@@ -35,4 +32,4 @@ function parseOptions(context) { | ||
* Check if it has been supported. | ||
* @param {SupportInfo} info The support info. | ||
* @param {Range} configured The configured version range. | ||
* @param {import('../unsupported-features/types.js').SupportInfo} info The support info. | ||
* @param {import('semver').Range} configured The configured version range. | ||
*/ | ||
@@ -54,2 +51,6 @@ function isSupported({ supported }, configured) { | ||
if (range == null) { | ||
return false | ||
} | ||
return configured.intersects(range) | ||
@@ -60,8 +61,8 @@ } | ||
* Get the formatted text of a given supported version. | ||
* @param {SupportInfo} info The support info. | ||
* @returns {null|string} | ||
* @param {import('../unsupported-features/types.js').SupportInfo} info The support info. | ||
* @returns {string | undefined} | ||
*/ | ||
function supportedVersionToString({ supported }) { | ||
if (supported == null || supported.length === 0) { | ||
return null | ||
return | ||
} | ||
@@ -82,4 +83,4 @@ | ||
* Verify the code to report unsupported APIs. | ||
* @param {RuleContext} context The rule context. | ||
* @param {{modules:object,globals:object}} trackMap The map for APIs to report. | ||
* @param {import('eslint').Rule.RuleContext} context The rule context. | ||
* @param {import('../unsupported-features/types.js').SupportVersionBuiltins} traceMap The map for APIs to report. | ||
* @returns {void} | ||
@@ -89,3 +90,3 @@ */ | ||
context, | ||
trackMap | ||
traceMap | ||
) { | ||
@@ -97,5 +98,5 @@ const options = parseOptions(context) | ||
const references = [ | ||
...tracker.iterateCjsReferences(trackMap.modules || {}), | ||
...tracker.iterateEsmReferences(trackMap.modules || {}), | ||
...tracker.iterateGlobalReferences(trackMap.globals || {}), | ||
...tracker.iterateCjsReferences(traceMap.modules ?? {}), | ||
...tracker.iterateEsmReferences(traceMap.modules ?? {}), | ||
...tracker.iterateGlobalReferences(traceMap.globals ?? {}), | ||
] | ||
@@ -111,22 +112,13 @@ | ||
const supportedVersion = supportedVersionToString(info) | ||
if (supportedVersion == null) { | ||
context.report({ | ||
node, | ||
messageId: "not-supported-yet", | ||
data: { | ||
name: path.join("."), | ||
version: options.version.raw, | ||
}, | ||
}) | ||
} else { | ||
context.report({ | ||
node, | ||
messageId: "not-supported-till", | ||
data: { | ||
name: path.join("."), | ||
supported: supportedVersionToString(info), | ||
version: options.version.raw, | ||
}, | ||
}) | ||
} | ||
context.report({ | ||
node, | ||
messageId: supportedVersion | ||
? "not-supported-till" | ||
: "not-supported-yet", | ||
data: { | ||
name: path.join("."), | ||
supported: /** @type string */ (supportedVersion), | ||
version: options.version.raw, | ||
}, | ||
}) | ||
} | ||
@@ -133,0 +125,0 @@ } |
@@ -10,23 +10,31 @@ /** | ||
/** @typedef {import('../unsupported-features/types.js').DeprecatedInfoTraceMap} DeprecatedInfoTraceMap */ | ||
/** @typedef {import('../unsupported-features/types.js').SupportVersionTraceMap} SupportVersionTraceMap */ | ||
/** | ||
* @template {SupportVersionTraceMap | DeprecatedInfoTraceMap} TraceMap | ||
* Enumerate property names of a given object recursively. | ||
* @param {import('../unsupported-features/types.js').SupportVersionTree} trackMap The map for APIs to enumerate. | ||
* @param {TraceMap} traceMap The map for APIs to enumerate. | ||
* @param {string[]} [path] The path to the current map. | ||
* @param {WeakSet} [recursionSet] A WeakSet used to block recursion (eg Module, Module.Module, Module.Module.Module) | ||
* @param {WeakSet<TraceMap>} [recursionSet] A WeakSet used to block recursion (eg Module, Module.Module, Module.Module.Module) | ||
* @returns {IterableIterator<string>} The property names of the map. | ||
*/ | ||
function* enumeratePropertyNames( | ||
trackMap, | ||
traceMap, | ||
path = [], | ||
recursionSet = new WeakSet() | ||
) { | ||
if (recursionSet.has(trackMap)) { | ||
if (recursionSet.has(traceMap)) { | ||
return | ||
} | ||
for (const key of Object.getOwnPropertyNames(trackMap)) { | ||
const childValue = trackMap[key] | ||
for (const key of Object.getOwnPropertyNames(traceMap)) { | ||
const childValue = traceMap[key] | ||
const childPath = [...path, key] | ||
const childName = unprefixNodeColon(childPath.join(".")) | ||
if (childValue == null) { | ||
continue | ||
} | ||
if (childValue[CALL]) { | ||
@@ -47,3 +55,3 @@ yield `${childName}()` | ||
childPath, | ||
recursionSet.add(trackMap) | ||
recursionSet.add(traceMap) | ||
) | ||
@@ -50,0 +58,0 @@ } |
@@ -53,3 +53,6 @@ /** | ||
} catch (error) { | ||
if (error.code !== "ENOENT") { | ||
if ( | ||
error instanceof Error && | ||
("code" in error === false || error.code !== "ENOENT") | ||
) { | ||
throw error | ||
@@ -56,0 +59,0 @@ } |
"use strict" | ||
const isCoreModule = require("is-core-module") | ||
const isBuiltinModule = require("is-builtin-module") | ||
/** | ||
* Extend trackMap.modules with `node:` prefixed modules | ||
* @param {Object} modules Like `{assert: foo}` | ||
* @returns {Object} Like `{assert: foo}, "node:assert": foo}` | ||
* Extend traceMap.modules with `node:` prefixed modules | ||
* @template {import('@eslint-community/eslint-utils').TraceMap<*>} TraceMap | ||
* @param {TraceMap} modules Like `{assert: foo}` | ||
* @returns {TraceMap} Like `{assert: foo}, "node:assert": foo}` | ||
*/ | ||
module.exports = function extendTrackMapWithNodePrefix(modules) { | ||
module.exports = function extendTraceMapWithNodePrefix(modules) { | ||
const ret = { | ||
@@ -16,4 +17,5 @@ ...modules, | ||
.map(([name, value]) => [`node:${name}`, value]) | ||
// Note: "999" arbitrary to check current/future Node.js version | ||
.filter(([name]) => isCoreModule(name, "999")) | ||
.filter(([name]) => | ||
isBuiltinModule(/** @type {string} */ (name)) | ||
) | ||
), | ||
@@ -20,0 +22,0 @@ } |
@@ -7,3 +7,6 @@ /** | ||
const DEFAULT_VALUE = Object.freeze([]) | ||
/** | ||
* @type {string[]} | ||
*/ | ||
const DEFAULT_VALUE = [] | ||
@@ -13,7 +16,7 @@ /** | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @param {{allowModules:? string[]}|undefined} option - An option object to get. | ||
* @returns {string[]|null} The `allowModules` value, or `null`. | ||
*/ | ||
function get(option) { | ||
if (option && option.allowModules && Array.isArray(option.allowModules)) { | ||
if (Array.isArray(option?.allowModules)) { | ||
return option.allowModules.map(String) | ||
@@ -31,3 +34,3 @@ } | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @returns {string[]} A list of extensions. | ||
@@ -37,6 +40,5 @@ */ | ||
return ( | ||
get(context.options && context.options[0]) || | ||
get( | ||
context.settings && (context.settings.n || context.settings.node) | ||
) || | ||
get(context.options[0]) ?? | ||
get(context.settings?.n) ?? | ||
get(context.settings?.node) ?? | ||
DEFAULT_VALUE | ||
@@ -43,0 +45,0 @@ ) |
@@ -7,4 +7,3 @@ /** | ||
const { Range } = require("semver") // eslint-disable-line no-unused-vars | ||
const getPackageJson = require("./get-package-json") | ||
const { getPackageJson } = require("./get-package-json") | ||
const getSemverRange = require("./get-semver-range") | ||
@@ -15,20 +14,25 @@ | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @returns {string[]|null} The `allowModules` value, or `null`. | ||
* @param {Record<string, string>|undefined} option - An option object to get. | ||
* @returns {import("semver").Range|undefined} The `allowModules` value, or `null`. | ||
*/ | ||
function get(option) { | ||
if (option && option.version) { | ||
return option.version | ||
function getVersionRange(option) { | ||
if (option?.version) { | ||
return getSemverRange(option.version) | ||
} | ||
return null | ||
} | ||
/** | ||
* @typedef {{ [EngineName in 'npm' | 'node' | string]?: string }} Engines | ||
*/ | ||
/** | ||
* Get the `engines.node` field of package.json. | ||
* @param {string} filename The path to the current linting file. | ||
* @returns {Range|null} The range object of the `engines.node` field. | ||
* @param {import('eslint').Rule.RuleContext} context The path to the current linting file. | ||
* @returns {import("semver").Range|undefined} The range object of the `engines.node` field. | ||
*/ | ||
function getEnginesNode(filename) { | ||
function getEnginesNode(context) { | ||
const filename = context.filename ?? context.getFilename() | ||
const info = getPackageJson(filename) | ||
return getSemverRange(info && info.engines && info.engines.node) | ||
const engines = /** @type {Engines | undefined} */ (info?.engines) | ||
if (typeof engines?.node === "string") { | ||
return getSemverRange(engines?.node) | ||
} | ||
} | ||
@@ -43,17 +47,13 @@ | ||
* | ||
* @param {string|undefined} version The version range text. | ||
* @param {string} filename The path to the current linting file. | ||
* @param {import('eslint').Rule.RuleContext} context The version range text. | ||
* This will be used to look package.json up if `version` is not a valid version range. | ||
* @returns {Range} The configured version range. | ||
* @returns {import("semver").Range} The configured version range. | ||
*/ | ||
module.exports = function getConfiguredNodeVersion(context) { | ||
const version = | ||
get(context.options && context.options[0]) || | ||
get(context.settings && (context.settings.n || context.settings.node)) | ||
const filePath = context.filename ?? context.getFilename() | ||
return ( | ||
getSemverRange(version) || | ||
getEnginesNode(filePath) || | ||
getSemverRange(">=16.0.0") | ||
getVersionRange(context.options?.[0]) ?? | ||
getVersionRange(context.settings?.n) ?? | ||
getVersionRange(context.settings?.node) ?? | ||
getEnginesNode(context) ?? | ||
/** @type {import("semver").Range} */ (getSemverRange(">=16.0.0")) | ||
) | ||
@@ -60,0 +60,0 @@ } |
@@ -10,5 +10,11 @@ /** | ||
/** | ||
* @param {any} x - An any value. | ||
* @returns {any} Always `x`. | ||
* @typedef PathConvertion | ||
* @property {string[]} include | ||
* @property {string[]} exclude | ||
* @property {[string, string]} replace | ||
*/ | ||
/** @typedef {PathConvertion[] | Record<string, [ string, string ]>} ConvertPath */ | ||
/** @typedef {{match: (filePath: string) => boolean, convert: (filePath: string) => string}} Converter */ | ||
/** @type {Converter['convert']} */ | ||
function identity(x) { | ||
@@ -19,30 +25,34 @@ return x | ||
/** | ||
* Converts old-style value to new-style value. | ||
* Ensures the given value is a string array. | ||
* | ||
* @param {any} x - The value to convert. | ||
* @returns {({include: string[], exclude: string[], replace: string[]})[]} Normalized value. | ||
* @param {unknown[]} x - The value to ensure. | ||
* @returns {string[]} The string array. | ||
*/ | ||
function normalizeValue(x) { | ||
function toStringArray(x) { | ||
if (Array.isArray(x)) { | ||
return x | ||
return x.map(String) | ||
} | ||
return Object.keys(x).map(pattern => ({ | ||
include: [pattern], | ||
exclude: [], | ||
replace: x[pattern], | ||
})) | ||
return [] | ||
} | ||
/** | ||
* Ensures the given value is a string array. | ||
* Converts old-style value to new-style value. | ||
* | ||
* @param {any} x - The value to ensure. | ||
* @returns {string[]} The string array. | ||
* @param {ConvertPath} x - The value to convert. | ||
* @returns {PathConvertion[]} Normalized value. | ||
*/ | ||
function toStringArray(x) { | ||
function normalizeValue(x) { | ||
if (Array.isArray(x)) { | ||
return x.map(String) | ||
return x.map(({ include, exclude, replace }) => ({ | ||
include: toStringArray(include), | ||
exclude: toStringArray(exclude), | ||
replace: replace, | ||
})) | ||
} | ||
return [] | ||
return Object.entries(x).map(([pattern, replace]) => ({ | ||
include: [pattern], | ||
exclude: [], | ||
replace: replace, | ||
})) | ||
} | ||
@@ -66,3 +76,3 @@ | ||
* @param {string[]} excludePatterns - The glob patterns to exclude files. | ||
* @returns {function} Created predicate function. | ||
* @returns {Converter['match']} Created predicate function. | ||
*/ | ||
@@ -83,3 +93,3 @@ function createMatch(includePatterns, excludePatterns) { | ||
* @param {string} toStr - A new string to replace. | ||
* @returns {function} A function which replaces a given path. | ||
* @returns {Converter['convert']} A function which replaces a given path. | ||
*/ | ||
@@ -94,4 +104,4 @@ function defineConvert(fromRegexp, toStr) { | ||
* | ||
* @param {{match: function, convert: function}} converters - A list of converters to combine. | ||
* @returns {function} A function which replaces a given path. | ||
* @param {Converter[]} converters - A list of converters to combine. | ||
* @returns {Converter['convert']} A function which replaces a given path. | ||
*/ | ||
@@ -112,11 +122,7 @@ function combine(converters) { | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @returns {function|null} A function which converts a path., or `null`. | ||
* @param {{convertPath?: ConvertPath}|undefined} option - An option object to get. | ||
* @returns {Converter['convert']|null} A function which converts a path., or `null`. | ||
*/ | ||
function parse(option) { | ||
if ( | ||
!option || | ||
!option.convertPath || | ||
typeof option.convertPath !== "object" | ||
) { | ||
if (option?.convertPath == null) { | ||
return null | ||
@@ -127,4 +133,2 @@ } | ||
for (const pattern of normalizeValue(option.convertPath)) { | ||
const include = toStringArray(pattern.include) | ||
const exclude = toStringArray(pattern.exclude) | ||
const fromRegexp = new RegExp(String(pattern.replace[0])) | ||
@@ -134,3 +138,3 @@ const toStr = String(pattern.replace[1]) | ||
converters.push({ | ||
match: createMatch(include, exclude), | ||
match: createMatch(pattern.include, pattern.exclude), | ||
convert: defineConvert(fromRegexp, toStr), | ||
@@ -150,11 +154,10 @@ }) | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @returns {function} A function which converts a path. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @returns {Converter['convert']} A function which converts a path. | ||
*/ | ||
module.exports = function getConvertPath(context) { | ||
return ( | ||
parse(context.options && context.options[0]) || | ||
parse( | ||
context.settings && (context.settings.n || context.settings.node) | ||
) || | ||
parse(context.options?.[0]) ?? | ||
parse(context.settings?.n) ?? | ||
parse(context.settings?.node) ?? | ||
identity | ||
@@ -161,0 +164,0 @@ ) |
@@ -9,6 +9,6 @@ /** | ||
const path = require("path") | ||
const ignore = require("ignore") | ||
const ignore = require("ignore").default | ||
const Cache = require("./cache") | ||
const exists = require("./exists") | ||
const getPackageJson = require("./get-package-json") | ||
const { getPackageJson } = require("./get-package-json") | ||
@@ -33,5 +33,5 @@ const cache = new Cache() | ||
/** | ||
* @param {function} f - A function. | ||
* @param {function} g - A function. | ||
* @returns {function} A logical-and function of `f` and `g`. | ||
* @param {(filePath: string) => boolean} f - A function. | ||
* @param {(filePath: string) => boolean} g - A function. | ||
* @returns {(filePath: string) => boolean} A logical-and function of `f` and `g`. | ||
*/ | ||
@@ -43,14 +43,18 @@ function and(f, g) { | ||
/** | ||
* @param {function} f - A function. | ||
* @param {function} g - A function. | ||
* @param {function|null} h - A function. | ||
* @returns {function} A logical-or function of `f`, `g`, and `h`. | ||
* @param {(filePath: string) => boolean} f - A function. | ||
* @param {(filePath: string) => boolean} g - A function. | ||
* @param {(filePath: string) => boolean} [h] - A function. | ||
* @returns {(filePath: string) => boolean} A logical-or function of `f`, `g`, and `h`. | ||
*/ | ||
function or(f, g, h) { | ||
return filePath => f(filePath) || g(filePath) || (h && h(filePath)) | ||
if (h == null) { | ||
return filePath => f(filePath) || g(filePath) | ||
} | ||
return filePath => f(filePath) || g(filePath) || h(filePath) | ||
} | ||
/** | ||
* @param {function} f - A function. | ||
* @returns {function} A logical-not function of `f`. | ||
* @param {(filePath: string) => boolean} f - A function. | ||
* @returns {(filePath: string) => boolean} A logical-not function of `f`. | ||
*/ | ||
@@ -64,9 +68,15 @@ function not(f) { | ||
* | ||
* @param {object} p - An object of package.json. | ||
* @returns {function} A function which checks whether or not a given file is ignoreable. | ||
* @param {import('type-fest').JsonObject} packageJson - An object of package.json. | ||
* @returns {(filePath: string) => boolean} A function which checks whether or not a given file is ignoreable. | ||
*/ | ||
function filterNeverIgnoredFiles(p) { | ||
const basedir = path.dirname(p.filePath) | ||
function filterNeverIgnoredFiles(packageJson) { | ||
if (typeof packageJson?.filePath !== "string") { | ||
return () => false | ||
} | ||
const basedir = path.dirname(packageJson.filePath) | ||
const mainFilePath = | ||
typeof p.main === "string" ? path.join(basedir, p.main) : null | ||
typeof packageJson.main === "string" | ||
? path.join(basedir, packageJson.main) | ||
: null | ||
@@ -82,7 +92,7 @@ return filePath => | ||
* | ||
* @param {string[]|null} files - File names of whitelist. | ||
* @returns {function|null} A function which checks whether or not a given file should be ignored. | ||
* @param {unknown} files - File names of whitelist. | ||
* @returns {((filePath: string) => boolean) | null} A function which checks whether or not a given file should be ignored. | ||
*/ | ||
function parseWhiteList(files) { | ||
if (!files || !Array.isArray(files)) { | ||
if (Array.isArray(files) === false) { | ||
return null | ||
@@ -122,3 +132,3 @@ } | ||
* @param {boolean} filesFieldExists - `true` if `files` field of `package.json` exists. | ||
* @returns {function|null} A function which checks whether or not a given file should be ignored. | ||
* @returns {((filePath: string) => boolean)|null} A function which checks whether or not a given file should be ignored. | ||
*/ | ||
@@ -151,3 +161,3 @@ function parseNpmignore(basedir, filesFieldExists) { | ||
* @param {string} startPath - A file path to lookup. | ||
* @returns {object} | ||
* @returns {{ match: (filePath: string) => boolean }} | ||
* An object to check whther or not a given path should be ignored. | ||
@@ -160,36 +170,39 @@ * The object has a method `match`. | ||
const p = getPackageJson(startPath) | ||
if (p) { | ||
const data = cache.get(p.filePath) | ||
if (data) { | ||
return data | ||
} | ||
const packageJson = getPackageJson(startPath) | ||
if (typeof packageJson?.filePath !== "string") { | ||
return retv | ||
} | ||
const filesIgnore = parseWhiteList(p.files) | ||
const npmignoreIgnore = parseNpmignore( | ||
path.dirname(p.filePath), | ||
Boolean(filesIgnore) | ||
) | ||
const data = cache.get(packageJson.filePath) | ||
if (data) { | ||
return data | ||
} | ||
if (filesIgnore && npmignoreIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(p), | ||
or(isAncestorFiles, filesIgnore, npmignoreIgnore) | ||
) | ||
} else if (filesIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(p), | ||
or(isAncestorFiles, filesIgnore) | ||
) | ||
} else if (npmignoreIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(p), | ||
or(isAncestorFiles, npmignoreIgnore) | ||
) | ||
} | ||
const filesIgnore = parseWhiteList(packageJson.files) | ||
cache.set(p.filePath, retv) | ||
const npmignoreIgnore = parseNpmignore( | ||
path.dirname(packageJson.filePath), | ||
Boolean(filesIgnore) | ||
) | ||
if (filesIgnore && npmignoreIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(packageJson), | ||
or(isAncestorFiles, filesIgnore, npmignoreIgnore) | ||
) | ||
} else if (filesIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(packageJson), | ||
or(isAncestorFiles, filesIgnore) | ||
) | ||
} else if (npmignoreIgnore) { | ||
retv.match = and( | ||
filterNeverIgnoredFiles(packageJson), | ||
or(isAncestorFiles, npmignoreIgnore) | ||
) | ||
} | ||
cache.set(packageJson.filePath, retv) | ||
return retv | ||
} |
@@ -19,3 +19,3 @@ /** | ||
* @param {string} dir - The path to a directory to read. | ||
* @returns {object|null} The read `package.json` data, or null. | ||
* @returns {import('type-fest').JsonObject|null} The read `package.json` data, or null. | ||
*/ | ||
@@ -28,7 +28,11 @@ function readPackageJson(dir) { | ||
if (typeof data === "object" && data !== null) { | ||
if ( | ||
data != null && | ||
typeof data === "object" && | ||
Array.isArray(data) === false | ||
) { | ||
data.filePath = filePath | ||
return data | ||
} | ||
} catch (_err) { | ||
} catch { | ||
// do nothing. | ||
@@ -45,6 +49,6 @@ } | ||
* @param {string} [startPath] - A file path to lookup. | ||
* @returns {object|null} A found `package.json` data or `null`. | ||
* @returns {import('type-fest').JsonObject|null} A found `package.json` data or `null`. | ||
* This object have additional property `filePath`. | ||
*/ | ||
module.exports = function getPackageJson(startPath = "a.js") { | ||
function getPackageJson(startPath = "a.js") { | ||
const startDir = path.dirname(path.resolve(startPath)) | ||
@@ -79,1 +83,3 @@ let dir = startDir | ||
} | ||
module.exports = { getPackageJson } |
@@ -7,3 +7,4 @@ /** | ||
const DEFAULT_VALUE = Object.freeze([]) | ||
/** @type {string[]} */ | ||
const DEFAULT_VALUE = [] | ||
@@ -13,10 +14,9 @@ /** | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @returns {string[]|null} The `allowModules` value, or `null`. | ||
* @param {{ resolvePaths: unknown[] } | undefined} option - An option object to get. | ||
* @returns {string[] | undefined} The `allowModules` value, or `null`. | ||
*/ | ||
function get(option) { | ||
if (option && option.resolvePaths && Array.isArray(option.resolvePaths)) { | ||
if (Array.isArray(option?.resolvePaths)) { | ||
return option.resolvePaths.map(String) | ||
} | ||
return null | ||
} | ||
@@ -31,3 +31,3 @@ | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @returns {string[]} A list of extensions. | ||
@@ -37,6 +37,5 @@ */ | ||
return ( | ||
get(context.options && context.options[optionIndex]) || | ||
get( | ||
context.settings && (context.settings.n || context.settings.node) | ||
) || | ||
get(context.options?.[optionIndex]) ?? | ||
get(context.settings?.n) ?? | ||
get(context.settings?.node) ?? | ||
DEFAULT_VALUE | ||
@@ -43,0 +42,0 @@ ) |
@@ -13,19 +13,20 @@ /** | ||
* @param {string} x The text expression for a semver range. | ||
* @returns {Range|null} The range object of a given range text. | ||
* @returns {Range|undefined} The range object of a given range text. | ||
* It's null if the `x` is not a valid range text. | ||
*/ | ||
module.exports = function getSemverRange(x) { | ||
const s = String(x) | ||
let ret = cache.get(s) || null | ||
const stringVersion = String(x) | ||
const cached = cache.get(stringVersion) | ||
if (cached != null) { | ||
return cached | ||
} | ||
if (!ret) { | ||
try { | ||
ret = new Range(s) | ||
} catch (_error) { | ||
// Ignore parsing error. | ||
} | ||
cache.set(s, ret) | ||
try { | ||
const output = new Range(stringVersion) | ||
cache.set(stringVersion, output) | ||
return output | ||
} catch { | ||
// Ignore parsing error. | ||
cache.set(stringVersion, null) | ||
} | ||
return ret | ||
} |
@@ -10,11 +10,7 @@ /** | ||
const DEFAULT_JS_VALUE = Object.freeze([ | ||
/** @type {string[]} */ | ||
const DEFAULT_JS_VALUE = [".js", ".json", ".node", ".mjs", ".cjs"] | ||
/** @type {string[]} */ | ||
const DEFAULT_TS_VALUE = [ | ||
".js", | ||
".json", | ||
".node", | ||
".mjs", | ||
".cjs", | ||
]) | ||
const DEFAULT_TS_VALUE = Object.freeze([ | ||
".js", | ||
".ts", | ||
@@ -27,3 +23,3 @@ ".mjs", | ||
".node", | ||
]) | ||
] | ||
@@ -33,4 +29,4 @@ /** | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @returns {string[]|null} The `tryExtensions` value, or `null`. | ||
* @param {{ tryExtensions: unknown[] } | undefined} option - An option object to get. | ||
* @returns {string[] | undefined} The `tryExtensions` value, or `null`. | ||
*/ | ||
@@ -41,3 +37,2 @@ function get(option) { | ||
} | ||
return null | ||
} | ||
@@ -52,3 +47,3 @@ | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @returns {string[]} A list of extensions. | ||
@@ -66,9 +61,14 @@ */ | ||
if (isTypescript(context)) { | ||
const tsconfig = getTSConfigForContext(context) | ||
if (tsconfig?.config?.compilerOptions?.allowImportingTsExtensions) { | ||
return DEFAULT_TS_VALUE | ||
} | ||
if (isTypescript(context) === false) { | ||
return DEFAULT_JS_VALUE | ||
} | ||
const allowImportingTsExtensions = | ||
getTSConfigForContext(context)?.config?.compilerOptions | ||
?.allowImportingTsExtensions | ||
if (allowImportingTsExtensions === true) { | ||
return DEFAULT_TS_VALUE | ||
} | ||
return DEFAULT_JS_VALUE | ||
@@ -75,0 +75,0 @@ } |
@@ -30,3 +30,3 @@ "use strict" | ||
/** | ||
* @typedef {Object} ExtensionMap | ||
* @typedef ExtensionMap | ||
* @property {Record<string, string>} forward Convert from typescript to javascript | ||
@@ -37,7 +37,10 @@ * @property {Record<string, string[]>} backward Convert from javascript to typescript | ||
/** | ||
* @param {Record<string, string>} typescriptExtensionMap A forward extension mapping | ||
* @param {[string, string][]} typescriptExtensionMap A forward extension mapping | ||
* @returns {ExtensionMap} | ||
*/ | ||
function normalise(typescriptExtensionMap) { | ||
/** @type {Record<string, string>} */ | ||
const forward = {} | ||
/** @type {Record<string, string[]>} */ | ||
const backward = {} | ||
@@ -61,3 +64,3 @@ | ||
* @param {import("get-tsconfig").TsConfigJsonResolved} [tsconfig] - The resolved tsconfig | ||
* @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`. | ||
* @returns {ExtensionMap | null} The `typescriptExtensionMap` value, or `null`. | ||
*/ | ||
@@ -67,3 +70,3 @@ function getMappingFromTSConfig(tsconfig) { | ||
if ({}.hasOwnProperty.call(tsConfigMapping, jsx)) { | ||
if (jsx != null && {}.hasOwnProperty.call(tsConfigMapping, jsx)) { | ||
return tsConfigMapping[jsx] | ||
@@ -76,9 +79,16 @@ } | ||
/** | ||
* @typedef Options | ||
* @property {[string, string][] | keyof tsConfigMapping} typescriptExtensionMap | ||
* @property {string | null} tsconfigPath | ||
*/ | ||
/** | ||
* Gets `typescriptExtensionMap` property from a given option object. | ||
* | ||
* @param {object|undefined} option - An option object to get. | ||
* @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`. | ||
* @param {Options} [option] - An option object to get. | ||
* @returns {ExtensionMap | null} The `typescriptExtensionMap` value, or `null`. | ||
*/ | ||
function get(option) { | ||
if ( | ||
typeof option?.typescriptExtensionMap === "string" && | ||
{}.hasOwnProperty.call(tsConfigMapping, option?.typescriptExtensionMap) | ||
@@ -104,3 +114,3 @@ ) { | ||
* @param {import('eslint').Rule.RuleContext} context - The current file context | ||
* @returns {ExtensionMap} The `typescriptExtensionMap` value, or `null`. | ||
* @returns {ExtensionMap | null} The `typescriptExtensionMap` value, or `null`. | ||
*/ | ||
@@ -107,0 +117,0 @@ function getFromTSConfigFromFile(context) { |
@@ -8,3 +8,3 @@ /** | ||
const { resolve } = require("path") | ||
const isBuiltin = require("is-builtin-module") | ||
const isBuiltinModule = require("is-builtin-module") | ||
const resolver = require("enhanced-resolve") | ||
@@ -16,8 +16,22 @@ | ||
/** | ||
* @overload | ||
* @param {string[]} input | ||
* @returns {string[]} | ||
*/ | ||
/** | ||
* @overload | ||
* @param {string} input | ||
* @returns {string} | ||
*/ | ||
/** | ||
* @param {string | string[]} input | ||
* @returns {string | string[]} | ||
*/ | ||
function removeTrailWildcard(input) { | ||
if (Array.isArray(input)) { | ||
return [...input].map(removeTrailWildcard) | ||
if (typeof input === "string") { | ||
return input.replace(/[/\\*]+$/, "") | ||
} | ||
return input.replace(/[/\\*]+$/, "") | ||
return input.map(removeTrailWildcard) | ||
} | ||
@@ -46,3 +60,3 @@ | ||
/** | ||
* @typedef {Object} Options | ||
* @typedef Options | ||
* @property {string[]} [extensions] | ||
@@ -52,7 +66,11 @@ * @property {string[]} [paths] | ||
*/ | ||
/** @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType */ | ||
/** @typedef { 'import' | 'require' | 'type' } ModuleStyle */ | ||
/** | ||
* @typedef { 'unknown' | 'relative' | 'absolute' | 'node' | 'npm' | 'http' } ModuleType | ||
* @typedef { 'import' | 'require' | 'type' } ModuleStyle | ||
* @param {string} string The string to manipulate | ||
* @param {string} matcher The character to use as a segmenter | ||
* @param {Number} [count=1] How many segments to keep | ||
* @returns {string} | ||
*/ | ||
function trimAfter(string, matcher, count = 1) { | ||
@@ -62,2 +80,4 @@ return string.split(matcher).slice(0, count).join(matcher) | ||
/** @typedef {import('estree').Node & { parent?: Node }} Node */ | ||
/** | ||
@@ -70,3 +90,3 @@ * Information of an import target. | ||
* @param {import('eslint').Rule.RuleContext} context - The context for the import origin. | ||
* @param {import('eslint').Rule.Node} node - The node of a `require()` or a module declaraiton. | ||
* @param {Node} node - The node of a `require()` or a module declaraiton. | ||
* @param {string} name - The name of an import target. | ||
@@ -79,3 +99,3 @@ * @param {Options} options - The options of `enhanced-resolve` module. | ||
* The context for the import origin | ||
* @type {import('eslint').Rule.Node} | ||
* @type {import('eslint').Rule.RuleContext} | ||
*/ | ||
@@ -86,3 +106,3 @@ this.context = context | ||
* The node of a `require()` or a module declaraiton. | ||
* @type {import('eslint').Rule.Node} | ||
* @type {Node} | ||
*/ | ||
@@ -118,3 +138,3 @@ this.node = node | ||
* If the target is a relative path then this is `null`. | ||
* @type {string | null} | ||
* @type {string | undefined} | ||
*/ | ||
@@ -144,3 +164,3 @@ this.moduleName = this.getModuleName() | ||
if (isBuiltin(this.name)) { | ||
if (isBuiltinModule(this.name)) { | ||
return "node" | ||
@@ -166,12 +186,14 @@ } | ||
getModuleStyle(fallback) { | ||
/** @type {import('eslint').Rule.Node} */ | ||
let node = { parent: this.node } | ||
let node = this.node | ||
do { | ||
node = node.parent | ||
if (node.parent == null) { | ||
break | ||
} | ||
// `const {} = require('')` | ||
if ( | ||
node.type === "CallExpression" && | ||
node.callee.name === "require" | ||
node.parent.type === "CallExpression" && | ||
node.parent.callee.type === "Identifier" && | ||
node.parent.callee.name === "require" | ||
) { | ||
@@ -181,17 +203,12 @@ return "require" | ||
// `import type {} from '';` | ||
if ( | ||
node.type === "ImportDeclaration" && | ||
node.importKind === "type" | ||
) { | ||
return "type" | ||
// `import {} from '';` | ||
if (node.parent.type === "ImportDeclaration") { | ||
// `import type {} from '';` | ||
return "importKind" in node.parent && | ||
node.parent.importKind === "type" | ||
? "type" | ||
: "import" | ||
} | ||
// `import {} from '';` | ||
if ( | ||
node.type === "ImportDeclaration" && | ||
node.importKind === "value" | ||
) { | ||
return "import" | ||
} | ||
node = node.parent | ||
} while (node.parent) | ||
@@ -204,3 +221,3 @@ | ||
* Get the node or npm module name | ||
* @returns {string} | ||
* @returns {string | undefined} | ||
*/ | ||
@@ -227,2 +244,5 @@ getModuleName() { | ||
/** | ||
* @returns {string[]} | ||
*/ | ||
getPaths() { | ||
@@ -285,3 +305,4 @@ if (Array.isArray(this.options.paths)) { | ||
const baseDir = resolve(cwd, directory) | ||
return requireResolve(baseDir, this.name) | ||
const resolved = requireResolve(baseDir, this.name) | ||
if (typeof resolved === "string") return resolved | ||
} catch { | ||
@@ -288,0 +309,0 @@ continue |
@@ -10,3 +10,3 @@ "use strict" | ||
* | ||
* @param {RuleContext} context - A context | ||
* @param {import('eslint').Rule.RuleContext} context - A context | ||
* @returns {boolean} | ||
@@ -13,0 +13,0 @@ */ |
@@ -17,22 +17,10 @@ "use strict" | ||
* @param {string} fallbackExtension The non-typescript fallback | ||
* @param {boolean} reverse Execute a reverse path mapping | ||
* @returns {string} The file extension to append to the import statement. | ||
*/ | ||
module.exports = function mapTypescriptExtension( | ||
context, | ||
filePath, | ||
fallbackExtension, | ||
reverse = false | ||
) { | ||
const { forward, backward } = getTypescriptExtensionMap(context) | ||
function convertTsExtensionToJs(context, filePath, fallbackExtension) { | ||
const { forward } = getTypescriptExtensionMap(context) | ||
const ext = path.extname(filePath) | ||
if (reverse) { | ||
if (isTypescript(context) && ext in backward) { | ||
return backward[ext] | ||
} | ||
return [fallbackExtension] | ||
} else { | ||
if (isTypescript(context) && ext in forward) { | ||
return forward[ext] | ||
} | ||
if (isTypescript(context) && ext in forward) { | ||
return forward[ext] | ||
} | ||
@@ -42,1 +30,29 @@ | ||
} | ||
/** | ||
* Maps the typescript file extension that should be added in an import statement, | ||
* based on the given file extension of the referenced file OR fallsback to the original given extension. | ||
* | ||
* For example, in typescript, when referencing another typescript from a typescript file, | ||
* a .js extension should be used instead of the original .ts extension of the referenced file. | ||
* | ||
* @param {import('eslint').Rule.RuleContext} context | ||
* @param {string} filePath The filePath of the import | ||
* @param {string} fallbackExtension The non-typescript fallback | ||
* @returns {string[]} The file extension to append to the import statement. | ||
*/ | ||
function convertJsExtensionToTs(context, filePath, fallbackExtension) { | ||
const { backward } = getTypescriptExtensionMap(context) | ||
const ext = path.extname(filePath) | ||
if (isTypescript(context) && Object.hasOwn(backward, ext)) { | ||
return backward[ext] | ||
} | ||
return [fallbackExtension] | ||
} | ||
module.exports = { | ||
convertTsExtensionToJs, | ||
convertJsExtensionToTs, | ||
} |
@@ -10,5 +10,5 @@ /** | ||
* This function modifies `visitor1` directly to merge. | ||
* @param {Visitor} visitor1 The visitor which is assigned. | ||
* @param {Visitor} visitor2 The visitor which is assigning. | ||
* @returns {Visitor} `visitor1`. | ||
* @param {import('eslint').Rule.RuleListener} visitor1 The visitor which is assigned. | ||
* @param {import('eslint').Rule.RuleListener} visitor2 The visitor which is assigning. | ||
* @returns {import('eslint').Rule.RuleListener} `visitor1`. | ||
*/ | ||
@@ -20,14 +20,22 @@ module.exports = function mergeVisitorsInPlace(visitor1, visitor2) { | ||
if (typeof handler1 === "function") { | ||
if (handler1._handlers) { | ||
handler1._handlers.push(handler2) | ||
} else { | ||
const handlers = [handler1, handler2] | ||
visitor1[key] = Object.assign(dispatch.bind(null, handlers), { | ||
_handlers: handlers, | ||
}) | ||
} | ||
} else { | ||
if (typeof handler1 !== "function") { | ||
visitor1[key] = handler2 | ||
continue | ||
} | ||
if (typeof handler2 !== "function") { | ||
continue | ||
} | ||
if ("_handlers" in handler1 && Array.isArray(handler1._handlers)) { | ||
handler1._handlers.push(handler2) | ||
continue | ||
} | ||
const handlers = [handler1, handler2] | ||
// @ts-expect-error - This is because its expecting a function that can match all {Rule.RuleListener[string]} functions! | ||
visitor1[key] = Object.assign(dispatch.bind(null, handlers), { | ||
_handlers: handlers, | ||
}) | ||
} | ||
@@ -34,0 +42,0 @@ |
@@ -7,5 +7,15 @@ /** | ||
/** | ||
* @param {unknown} path | ||
* @returns {string} | ||
*/ | ||
module.exports = function stripImportPathParams(path) { | ||
const i = path.toString().indexOf("!") | ||
return i === -1 ? path : path.slice(0, i) | ||
const pathString = String(path) | ||
const index = pathString.indexOf("!") | ||
if (index === -1) { | ||
return pathString | ||
} | ||
return pathString.slice(0, index) | ||
} |
@@ -8,3 +8,3 @@ /** | ||
const path = require("path") | ||
const isCoreModule = require("is-core-module") | ||
const isBuiltinModule = require("is-builtin-module") | ||
const getResolvePaths = require("./get-resolve-paths") | ||
@@ -15,3 +15,12 @@ const getTryExtensions = require("./get-try-extensions") | ||
/** @typedef {import('@typescript-eslint/typescript-estree').TSESTree.ImportDeclaration} ImportDeclaration */ | ||
/** | ||
* @typedef VisitImportOptions | ||
* @property {boolean} [includeCore=false] The flag to include core modules. | ||
* @property {number} [optionIndex=0] The index of rule options. | ||
* @property {boolean} [ignoreTypeImport=false] The flag to ignore typescript type imports. | ||
*/ | ||
/** | ||
* Gets a list of `import`/`export` declaration targets. | ||
@@ -21,15 +30,13 @@ * | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @param {Object} [options] - The flag to include core modules. | ||
* @param {boolean} [options.includeCore] - The flag to include core modules. | ||
* @param {number} [options.optionIndex] - The index of rule options. | ||
* @param {boolean} [options.ignoreTypeImport] - The flag to ignore typescript type imports. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @param {VisitImportOptions} options - The flag to include core modules. | ||
* @param {function(ImportTarget[]):void} callback The callback function to get result. | ||
* @returns {ImportTarget[]} A list of found target's information. | ||
* @returns {import('eslint').Rule.RuleListener} A list of found target's information. | ||
*/ | ||
module.exports = function visitImport( | ||
context, | ||
{ includeCore = false, optionIndex = 0, ignoreTypeImport = false } = {}, | ||
{ includeCore = false, optionIndex = 0, ignoreTypeImport = false }, | ||
callback | ||
) { | ||
/** @type {import('./import-target.js')[]} */ | ||
const targets = [] | ||
@@ -43,16 +50,37 @@ const basedir = path.dirname( | ||
/** | ||
* @param {( | ||
* | import('estree').ExportAllDeclaration | ||
* | import('estree').ExportNamedDeclaration | ||
* | import('estree').ImportDeclaration | ||
* | import('estree').ImportExpression | ||
* )} node | ||
*/ | ||
function addTarget(node) { | ||
if (node.source == null || node.source.type !== "Literal") { | ||
return | ||
} | ||
const name = stripImportPathParams(node.source?.value) | ||
if (includeCore === true || isBuiltinModule(name) === false) { | ||
targets.push( | ||
new ImportTarget(context, node.source, name, options, "import") | ||
) | ||
} | ||
} | ||
return { | ||
[[ | ||
"ExportAllDeclaration", | ||
"ExportNamedDeclaration", | ||
"ImportDeclaration", | ||
"ImportExpression", | ||
]](node) { | ||
const sourceNode = node.source | ||
// skip `import(foo)` | ||
ExportAllDeclaration(node) { | ||
addTarget(node) | ||
}, | ||
ExportNamedDeclaration(node) { | ||
addTarget(node) | ||
}, | ||
ImportDeclaration(node) { | ||
if (node.source?.value == null) { | ||
return | ||
} | ||
if ( | ||
node.type === "ImportExpression" && | ||
sourceNode && | ||
sourceNode.type !== "Literal" | ||
ignoreTypeImport === true && | ||
/** @type {ImportDeclaration} */ (node).importKind === "type" | ||
) { | ||
@@ -62,24 +90,10 @@ return | ||
// skip `import type { foo } from 'bar'` (for eslint-typescript) | ||
if ( | ||
ignoreTypeImport && | ||
node.type === "ImportDeclaration" && | ||
node.importKind === "type" | ||
) { | ||
addTarget(node) | ||
}, | ||
ImportExpression(node) { | ||
if (node.source?.type !== "Literal") { | ||
return | ||
} | ||
const name = sourceNode && stripImportPathParams(sourceNode.value) | ||
// Note: "999" arbitrary to check current/future Node.js version | ||
if (name && (includeCore || !isCoreModule(name, "999"))) { | ||
targets.push( | ||
new ImportTarget( | ||
context, | ||
sourceNode, | ||
name, | ||
options, | ||
"import" | ||
) | ||
) | ||
} | ||
addTarget(node) | ||
}, | ||
@@ -86,0 +100,0 @@ |
@@ -13,3 +13,3 @@ /** | ||
} = require("@eslint-community/eslint-utils") | ||
const isCoreModule = require("is-core-module") | ||
const isBuiltinModule = require("is-builtin-module") | ||
const getResolvePaths = require("./get-resolve-paths") | ||
@@ -21,2 +21,7 @@ const getTryExtensions = require("./get-try-extensions") | ||
/** | ||
* @typedef VisitRequireOptions | ||
* @property {boolean} [includeCore=false] The flag to include core modules. | ||
*/ | ||
/** | ||
* Gets a list of `require()` targets. | ||
@@ -26,13 +31,13 @@ * | ||
* | ||
* @param {RuleContext} context - The rule context. | ||
* @param {Object} [options] - The flag to include core modules. | ||
* @param {boolean} [options.includeCore] - The flag to include core modules. | ||
* @param {function(ImportTarget[]):void} callback The callback function to get result. | ||
* @returns {Object} The visitor. | ||
* @param {import('eslint').Rule.RuleContext} context - The rule context. | ||
* @param {VisitRequireOptions} options - The flag to include core modules. | ||
* @param {function(ImportTarget[]): void} callback The callback function to get result. | ||
* @returns {import('eslint').Rule.RuleListener} The visitor. | ||
*/ | ||
module.exports = function visitRequire( | ||
context, | ||
{ includeCore = false } = {}, | ||
{ includeCore = false }, | ||
callback | ||
) { | ||
/** @type {import('./import-target.js')[]} */ | ||
const targets = [] | ||
@@ -60,7 +65,14 @@ const basedir = path.dirname( | ||
for (const { node } of references) { | ||
if (node.type !== "CallExpression") { | ||
continue | ||
} | ||
const targetNode = node.arguments[0] | ||
const rawName = getStringIfConstant(targetNode) | ||
const name = rawName && stripImportPathParams(rawName) | ||
// Note: "999" arbitrary to check current/future Node.js version | ||
if (name && (includeCore || !isCoreModule(name, "999"))) { | ||
if (typeof rawName !== "string") { | ||
continue | ||
} | ||
const name = stripImportPathParams(rawName) | ||
if (includeCore || !isBuiltinModule(name)) { | ||
targets.push( | ||
@@ -67,0 +79,0 @@ new ImportTarget( |
{ | ||
"name": "eslint-plugin-n", | ||
"version": "17.0.0-6", | ||
"version": "17.0.0-7", | ||
"description": "Additional ESLint's rules for Node.js", | ||
@@ -9,11 +9,14 @@ "engines": { | ||
"main": "lib/index.js", | ||
"types": "types/index.d.ts", | ||
"files": [ | ||
"lib/", | ||
"configs/" | ||
"configs/", | ||
"types/index.d.ts" | ||
], | ||
"peerDependencies": { | ||
"eslint": "^8.23.0 || >=9.0.0-0" | ||
"eslint": ">=8.23.0" | ||
}, | ||
"dependencies": { | ||
"@eslint-community/eslint-utils": "^4.4.0", | ||
"@types/eslint": "^8.56.2", | ||
"enhanced-resolve": "^5.15.0", | ||
@@ -25,3 +28,2 @@ "eslint-plugin-es-x": "^7.5.0", | ||
"is-builtin-module": "^3.2.1", | ||
"is-core-module": "^2.12.1", | ||
"minimatch": "^9.0.0", | ||
@@ -31,6 +33,9 @@ "semver": "^7.5.3" | ||
"devDependencies": { | ||
"@eslint/js": "^8.43.0", | ||
"@eslint/js": "^9.0.0", | ||
"@types/eslint": "^8.56.2", | ||
"@types/estree": "^1.0.5", | ||
"@types/node": "^20.11.0", | ||
"@typescript-eslint/parser": "^7.0.0", | ||
"eslint": "^8", | ||
"@typescript-eslint/typescript-estree": "^6.18.1", | ||
"eslint": "^9.0.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
@@ -52,2 +57,3 @@ "eslint-doc-generator": "^1.6.1", | ||
"rimraf": "^5.0.1", | ||
"type-fest": "^4.9.0", | ||
"typescript": "^5.1.3" | ||
@@ -66,9 +72,9 @@ }, | ||
"postversion": "git push && git push --tags", | ||
"prepack": "tsc --emitDeclarationOnly", | ||
"prepare": "husky", | ||
"pretest": "npm run -s lint", | ||
"preversion": "npm test", | ||
"test": "nyc npm run -s test:_mocha", | ||
"test:_mocha": "_mocha --reporter progress --timeout 4000 \"tests/lib/**/*.js\"", | ||
"test": "run-p lint:* test:types test:tests", | ||
"test:mocha": "_mocha --reporter progress --timeout 4000", | ||
"test:ci": "nyc npm run -s test:_mocha", | ||
"test:tests": "npm run test:mocha \"tests/lib/**/*.js\"", | ||
"test:types": "tsc --noEmit --emitDeclarationOnly false", | ||
"update:eslint-docs": "eslint-doc-generator", | ||
@@ -75,0 +81,0 @@ "version": "npm run -s build && eslint lib/rules --fix && git add .", |
427456
132
11336
25
+ Added@types/eslint@^8.56.2
+ Added@types/eslint@8.56.10(transitive)
+ Added@types/estree@1.0.5(transitive)
+ Added@types/json-schema@7.0.15(transitive)
- Removedis-core-module@^2.12.1
- Removedfunction-bind@1.1.2(transitive)
- Removedhasown@2.0.2(transitive)
- Removedis-core-module@2.14.0(transitive)