Add components
This commit is contained in:
717
slider/node_modules/eslint/lib/rules/one-var.js
generated
vendored
Normal file
717
slider/node_modules/eslint/lib/rules/one-var.js
generated
vendored
Normal file
@@ -0,0 +1,717 @@
|
||||
/**
|
||||
* @fileoverview A rule to control the use of single variable declarations.
|
||||
* @author Ian Christian Myers
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determines whether the given node is in a statement list.
|
||||
* @param {ASTNode} node node to check
|
||||
* @returns {boolean} `true` if the given node is in a statement list
|
||||
*/
|
||||
function isInStatementList(node) {
|
||||
return astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Enforce variables to be declared either together or separately in functions",
|
||||
recommended: false,
|
||||
frozen: true,
|
||||
url: "https://eslint.org/docs/latest/rules/one-var",
|
||||
},
|
||||
|
||||
fixable: "code",
|
||||
|
||||
schema: [
|
||||
{
|
||||
oneOf: [
|
||||
{
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
separateRequires: {
|
||||
type: "boolean",
|
||||
},
|
||||
var: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
let: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
const: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
using: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
awaitUsing: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
initialized: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
uninitialized: {
|
||||
enum: ["always", "never", "consecutive"],
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
combineUninitialized:
|
||||
"Combine this with the previous '{{type}}' statement with uninitialized variables.",
|
||||
combineInitialized:
|
||||
"Combine this with the previous '{{type}}' statement with initialized variables.",
|
||||
splitUninitialized:
|
||||
"Split uninitialized '{{type}}' declarations into multiple statements.",
|
||||
splitInitialized:
|
||||
"Split initialized '{{type}}' declarations into multiple statements.",
|
||||
splitRequires:
|
||||
"Split requires to be separated into a single block.",
|
||||
combine: "Combine this with the previous '{{type}}' statement.",
|
||||
split: "Split '{{type}}' declarations into multiple statements.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const MODE_ALWAYS = "always";
|
||||
const MODE_NEVER = "never";
|
||||
const MODE_CONSECUTIVE = "consecutive";
|
||||
const mode = context.options[0] || MODE_ALWAYS;
|
||||
|
||||
const options = {};
|
||||
|
||||
if (typeof mode === "string") {
|
||||
// simple options configuration with just a string
|
||||
options.var = { uninitialized: mode, initialized: mode };
|
||||
options.let = { uninitialized: mode, initialized: mode };
|
||||
options.const = { uninitialized: mode, initialized: mode };
|
||||
options.using = { uninitialized: mode, initialized: mode };
|
||||
options.awaitUsing = { uninitialized: mode, initialized: mode };
|
||||
} else if (typeof mode === "object") {
|
||||
// options configuration is an object
|
||||
options.separateRequires = !!mode.separateRequires;
|
||||
options.var = { uninitialized: mode.var, initialized: mode.var };
|
||||
options.let = { uninitialized: mode.let, initialized: mode.let };
|
||||
options.const = {
|
||||
uninitialized: mode.const,
|
||||
initialized: mode.const,
|
||||
};
|
||||
options.using = {
|
||||
uninitialized: mode.using,
|
||||
initialized: mode.using,
|
||||
};
|
||||
options.awaitUsing = {
|
||||
uninitialized: mode.awaitUsing,
|
||||
initialized: mode.awaitUsing,
|
||||
};
|
||||
if (Object.hasOwn(mode, "uninitialized")) {
|
||||
options.var.uninitialized = mode.uninitialized;
|
||||
options.let.uninitialized = mode.uninitialized;
|
||||
options.const.uninitialized = mode.uninitialized;
|
||||
options.using.uninitialized = mode.uninitialized;
|
||||
options.awaitUsing.uninitialized = mode.uninitialized;
|
||||
}
|
||||
if (Object.hasOwn(mode, "initialized")) {
|
||||
options.var.initialized = mode.initialized;
|
||||
options.let.initialized = mode.initialized;
|
||||
options.const.initialized = mode.initialized;
|
||||
options.using.initialized = mode.initialized;
|
||||
options.awaitUsing.initialized = mode.initialized;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
const functionStack = [];
|
||||
const blockStack = [];
|
||||
|
||||
/**
|
||||
* Increments the blockStack counter.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function startBlock() {
|
||||
blockStack.push({
|
||||
let: { initialized: false, uninitialized: false },
|
||||
const: { initialized: false, uninitialized: false },
|
||||
using: { initialized: false, uninitialized: false },
|
||||
awaitUsing: { initialized: false, uninitialized: false },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the functionStack counter.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function startFunction() {
|
||||
functionStack.push({ initialized: false, uninitialized: false });
|
||||
startBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the blockStack counter.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function endBlock() {
|
||||
blockStack.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the functionStack counter.
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function endFunction() {
|
||||
functionStack.pop();
|
||||
endBlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a variable declaration is a require.
|
||||
* @param {ASTNode} decl variable declaration Node
|
||||
* @returns {bool} if decl is a require, return true; else return false.
|
||||
* @private
|
||||
*/
|
||||
function isRequire(decl) {
|
||||
return (
|
||||
decl.init &&
|
||||
decl.init.type === "CallExpression" &&
|
||||
decl.init.callee.name === "require"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records whether initialized/uninitialized/required variables are defined in current scope.
|
||||
* @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
|
||||
* @param {ASTNode[]} declarations List of declarations
|
||||
* @param {Object} currentScope The scope being investigated
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function recordTypes(statementType, declarations, currentScope) {
|
||||
for (let i = 0; i < declarations.length; i++) {
|
||||
if (declarations[i].init === null) {
|
||||
if (
|
||||
options[statementType] &&
|
||||
options[statementType].uninitialized === MODE_ALWAYS
|
||||
) {
|
||||
currentScope.uninitialized = true;
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
options[statementType] &&
|
||||
options[statementType].initialized === MODE_ALWAYS
|
||||
) {
|
||||
if (
|
||||
options.separateRequires &&
|
||||
isRequire(declarations[i])
|
||||
) {
|
||||
currentScope.required = true;
|
||||
} else {
|
||||
currentScope.initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the current scope (function or block)
|
||||
* @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
|
||||
* @returns {Object} The scope associated with statementType
|
||||
*/
|
||||
function getCurrentScope(statementType) {
|
||||
let currentScope;
|
||||
|
||||
if (statementType === "var") {
|
||||
currentScope = functionStack.at(-1);
|
||||
} else if (statementType === "let") {
|
||||
currentScope = blockStack.at(-1).let;
|
||||
} else if (statementType === "const") {
|
||||
currentScope = blockStack.at(-1).const;
|
||||
} else if (statementType === "using") {
|
||||
currentScope = blockStack.at(-1).using;
|
||||
} else if (statementType === "awaitUsing") {
|
||||
currentScope = blockStack.at(-1).awaitUsing;
|
||||
}
|
||||
return currentScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of initialized and uninitialized declarations in a list of declarations
|
||||
* @param {ASTNode[]} declarations List of declarations
|
||||
* @returns {Object} Counts of 'uninitialized' and 'initialized' declarations
|
||||
* @private
|
||||
*/
|
||||
function countDeclarations(declarations) {
|
||||
const counts = { uninitialized: 0, initialized: 0 };
|
||||
|
||||
for (let i = 0; i < declarations.length; i++) {
|
||||
if (declarations[i].init === null) {
|
||||
counts.uninitialized++;
|
||||
} else {
|
||||
counts.initialized++;
|
||||
}
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if there is more than one var statement in the current scope.
|
||||
* @param {string} statementType one of: "var", "let", "const", "using", or "awaitUsing"
|
||||
* @param {ASTNode[]} declarations List of declarations
|
||||
* @returns {boolean} Returns true if it is the first var declaration, false if not.
|
||||
* @private
|
||||
*/
|
||||
function hasOnlyOneStatement(statementType, declarations) {
|
||||
const declarationCounts = countDeclarations(declarations);
|
||||
const currentOptions = options[statementType] || {};
|
||||
const currentScope = getCurrentScope(statementType);
|
||||
const hasRequires = declarations.some(isRequire);
|
||||
|
||||
if (
|
||||
currentOptions.uninitialized === MODE_ALWAYS &&
|
||||
currentOptions.initialized === MODE_ALWAYS
|
||||
) {
|
||||
if (currentScope.uninitialized || currentScope.initialized) {
|
||||
if (!hasRequires) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declarationCounts.uninitialized > 0) {
|
||||
if (
|
||||
currentOptions.uninitialized === MODE_ALWAYS &&
|
||||
currentScope.uninitialized
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (declarationCounts.initialized > 0) {
|
||||
if (
|
||||
currentOptions.initialized === MODE_ALWAYS &&
|
||||
currentScope.initialized
|
||||
) {
|
||||
if (!hasRequires) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentScope.required && hasRequires) {
|
||||
return false;
|
||||
}
|
||||
recordTypes(statementType, declarations, currentScope);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixer to join VariableDeclaration's into a single declaration
|
||||
* @param {VariableDeclarator[]} declarations The `VariableDeclaration` to join
|
||||
* @returns {Function} The fixer function
|
||||
*/
|
||||
function joinDeclarations(declarations) {
|
||||
const declaration = declarations[0];
|
||||
const body = Array.isArray(declaration.parent.parent.body)
|
||||
? declaration.parent.parent.body
|
||||
: [];
|
||||
const currentIndex = body.findIndex(
|
||||
node => node.range[0] === declaration.parent.range[0],
|
||||
);
|
||||
const previousNode = body[currentIndex - 1];
|
||||
|
||||
return function* joinDeclarationsFixer(fixer) {
|
||||
const type = sourceCode.getFirstToken(declaration.parent);
|
||||
const beforeType = sourceCode.getTokenBefore(type);
|
||||
|
||||
if (
|
||||
previousNode &&
|
||||
previousNode.kind === declaration.parent.kind
|
||||
) {
|
||||
if (beforeType.value === ";") {
|
||||
yield fixer.replaceText(beforeType, ",");
|
||||
} else {
|
||||
yield fixer.insertTextAfter(beforeType, ",");
|
||||
}
|
||||
|
||||
if (declaration.parent.kind === "await using") {
|
||||
const usingToken = sourceCode.getTokenAfter(type);
|
||||
yield fixer.remove(usingToken);
|
||||
}
|
||||
|
||||
yield fixer.replaceText(type, "");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixer to split a VariableDeclaration into individual declarations
|
||||
* @param {VariableDeclaration} declaration The `VariableDeclaration` to split
|
||||
* @returns {Function|null} The fixer function
|
||||
*/
|
||||
function splitDeclarations(declaration) {
|
||||
const { parent } = declaration;
|
||||
|
||||
// don't autofix code such as: if (foo) var x, y;
|
||||
if (
|
||||
!isInStatementList(
|
||||
parent.type === "ExportNamedDeclaration"
|
||||
? parent
|
||||
: declaration,
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fixer =>
|
||||
declaration.declarations
|
||||
.map(declarator => {
|
||||
const tokenAfterDeclarator =
|
||||
sourceCode.getTokenAfter(declarator);
|
||||
|
||||
if (tokenAfterDeclarator === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const afterComma = sourceCode.getTokenAfter(
|
||||
tokenAfterDeclarator,
|
||||
{ includeComments: true },
|
||||
);
|
||||
|
||||
if (tokenAfterDeclarator.value !== ",") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const exportPlacement =
|
||||
declaration.parent.type === "ExportNamedDeclaration"
|
||||
? "export "
|
||||
: "";
|
||||
|
||||
/*
|
||||
* `var x,y`
|
||||
* tokenAfterDeclarator ^^ afterComma
|
||||
*/
|
||||
if (
|
||||
afterComma.range[0] ===
|
||||
tokenAfterDeclarator.range[1]
|
||||
) {
|
||||
return fixer.replaceText(
|
||||
tokenAfterDeclarator,
|
||||
`; ${exportPlacement}${declaration.kind} `,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* `var x,
|
||||
* tokenAfterDeclarator ^
|
||||
* y`
|
||||
* ^ afterComma
|
||||
*/
|
||||
if (
|
||||
afterComma.loc.start.line >
|
||||
tokenAfterDeclarator.loc.end.line ||
|
||||
afterComma.type === "Line" ||
|
||||
afterComma.type === "Block"
|
||||
) {
|
||||
let lastComment = afterComma;
|
||||
|
||||
while (
|
||||
lastComment.type === "Line" ||
|
||||
lastComment.type === "Block"
|
||||
) {
|
||||
lastComment = sourceCode.getTokenAfter(
|
||||
lastComment,
|
||||
{ includeComments: true },
|
||||
);
|
||||
}
|
||||
|
||||
return fixer.replaceTextRange(
|
||||
[
|
||||
tokenAfterDeclarator.range[0],
|
||||
lastComment.range[0],
|
||||
],
|
||||
`;${sourceCode.text.slice(
|
||||
tokenAfterDeclarator.range[1],
|
||||
lastComment.range[0],
|
||||
)}${exportPlacement}${declaration.kind} `,
|
||||
);
|
||||
}
|
||||
|
||||
return fixer.replaceText(
|
||||
tokenAfterDeclarator,
|
||||
`; ${exportPlacement}${declaration.kind}`,
|
||||
);
|
||||
})
|
||||
.filter(x => x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a given VariableDeclaration node for errors.
|
||||
* @param {ASTNode} node The VariableDeclaration node to check
|
||||
* @returns {void}
|
||||
* @private
|
||||
*/
|
||||
function checkVariableDeclaration(node) {
|
||||
const parent = node.parent;
|
||||
const type = node.kind;
|
||||
const key = type === "await using" ? "awaitUsing" : type;
|
||||
|
||||
if (!options[key]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const declarations = node.declarations;
|
||||
const declarationCounts = countDeclarations(declarations);
|
||||
const mixedRequires =
|
||||
declarations.some(isRequire) && !declarations.every(isRequire);
|
||||
|
||||
if (options[key].initialized === MODE_ALWAYS) {
|
||||
if (options.separateRequires && mixedRequires) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "splitRequires",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// consecutive
|
||||
const nodeIndex =
|
||||
(parent.body &&
|
||||
parent.body.length > 0 &&
|
||||
parent.body.indexOf(node)) ||
|
||||
0;
|
||||
|
||||
if (nodeIndex > 0) {
|
||||
const previousNode = parent.body[nodeIndex - 1];
|
||||
const isPreviousNodeDeclaration =
|
||||
previousNode.type === "VariableDeclaration";
|
||||
const declarationsWithPrevious = declarations.concat(
|
||||
previousNode.declarations || [],
|
||||
);
|
||||
|
||||
if (
|
||||
isPreviousNodeDeclaration &&
|
||||
previousNode.kind === type &&
|
||||
!(
|
||||
declarationsWithPrevious.some(isRequire) &&
|
||||
!declarationsWithPrevious.every(isRequire)
|
||||
)
|
||||
) {
|
||||
const previousDeclCounts = countDeclarations(
|
||||
previousNode.declarations,
|
||||
);
|
||||
|
||||
if (
|
||||
options[key].initialized === MODE_CONSECUTIVE &&
|
||||
options[key].uninitialized === MODE_CONSECUTIVE
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combine",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
} else if (
|
||||
options[key].initialized === MODE_CONSECUTIVE &&
|
||||
declarationCounts.initialized > 0 &&
|
||||
previousDeclCounts.initialized > 0
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combineInitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
} else if (
|
||||
options[key].uninitialized === MODE_CONSECUTIVE &&
|
||||
declarationCounts.uninitialized > 0 &&
|
||||
previousDeclCounts.uninitialized > 0
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combineUninitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// always
|
||||
if (!hasOnlyOneStatement(key, declarations)) {
|
||||
if (
|
||||
options[key].initialized === MODE_ALWAYS &&
|
||||
options[key].uninitialized === MODE_ALWAYS
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combine",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
} else {
|
||||
if (
|
||||
options[key].initialized === MODE_ALWAYS &&
|
||||
declarationCounts.initialized > 0
|
||||
) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combineInitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
}
|
||||
if (
|
||||
options[key].uninitialized === MODE_ALWAYS &&
|
||||
declarationCounts.uninitialized > 0
|
||||
) {
|
||||
if (
|
||||
node.parent.left === node &&
|
||||
(node.parent.type === "ForInStatement" ||
|
||||
node.parent.type === "ForOfStatement")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
messageId: "combineUninitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: joinDeclarations(declarations),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// never
|
||||
if (parent.type !== "ForStatement" || parent.init !== node) {
|
||||
const totalDeclarations =
|
||||
declarationCounts.uninitialized +
|
||||
declarationCounts.initialized;
|
||||
|
||||
if (totalDeclarations > 1) {
|
||||
if (
|
||||
options[key].initialized === MODE_NEVER &&
|
||||
options[key].uninitialized === MODE_NEVER
|
||||
) {
|
||||
// both initialized and uninitialized
|
||||
context.report({
|
||||
node,
|
||||
messageId: "split",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: splitDeclarations(node),
|
||||
});
|
||||
} else if (
|
||||
options[key].initialized === MODE_NEVER &&
|
||||
declarationCounts.initialized > 0
|
||||
) {
|
||||
// initialized
|
||||
context.report({
|
||||
node,
|
||||
messageId: "splitInitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: splitDeclarations(node),
|
||||
});
|
||||
} else if (
|
||||
options[key].uninitialized === MODE_NEVER &&
|
||||
declarationCounts.uninitialized > 0
|
||||
) {
|
||||
// uninitialized
|
||||
context.report({
|
||||
node,
|
||||
messageId: "splitUninitialized",
|
||||
data: {
|
||||
type,
|
||||
},
|
||||
fix: splitDeclarations(node),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public API
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
return {
|
||||
Program: startFunction,
|
||||
FunctionDeclaration: startFunction,
|
||||
FunctionExpression: startFunction,
|
||||
ArrowFunctionExpression: startFunction,
|
||||
StaticBlock: startFunction, // StaticBlock creates a new scope for `var` variables
|
||||
|
||||
BlockStatement: startBlock,
|
||||
ForStatement: startBlock,
|
||||
ForInStatement: startBlock,
|
||||
ForOfStatement: startBlock,
|
||||
SwitchStatement: startBlock,
|
||||
VariableDeclaration: checkVariableDeclaration,
|
||||
"ForStatement:exit": endBlock,
|
||||
"ForOfStatement:exit": endBlock,
|
||||
"ForInStatement:exit": endBlock,
|
||||
"SwitchStatement:exit": endBlock,
|
||||
"BlockStatement:exit": endBlock,
|
||||
|
||||
"Program:exit": endFunction,
|
||||
"FunctionDeclaration:exit": endFunction,
|
||||
"FunctionExpression:exit": endFunction,
|
||||
"ArrowFunctionExpression:exit": endFunction,
|
||||
"StaticBlock:exit": endFunction,
|
||||
};
|
||||
},
|
||||
};
|
Reference in New Issue
Block a user