mirror of
https://github.com/Sevichecc/Urara-Blog.git
synced 2025-05-03 04:59:31 +08:00
328 lines
No EOL
12 KiB
Text
328 lines
No EOL
12 KiB
Text
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const utils_1 = require("@typescript-eslint/utils");
|
|
const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
|
const util = __importStar(require("../util"));
|
|
const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/;
|
|
/**
|
|
* Parses a given value as options.
|
|
*/
|
|
function parseOptions(options) {
|
|
let functions = true;
|
|
let classes = true;
|
|
let enums = true;
|
|
let variables = true;
|
|
let typedefs = true;
|
|
let ignoreTypeReferences = true;
|
|
let allowNamedExports = false;
|
|
if (typeof options === 'string') {
|
|
functions = options !== 'nofunc';
|
|
}
|
|
else if (typeof options === 'object' && options !== null) {
|
|
functions = options.functions !== false;
|
|
classes = options.classes !== false;
|
|
enums = options.enums !== false;
|
|
variables = options.variables !== false;
|
|
typedefs = options.typedefs !== false;
|
|
ignoreTypeReferences = options.ignoreTypeReferences !== false;
|
|
allowNamedExports = options.allowNamedExports !== false;
|
|
}
|
|
return {
|
|
functions,
|
|
classes,
|
|
enums,
|
|
variables,
|
|
typedefs,
|
|
ignoreTypeReferences,
|
|
allowNamedExports,
|
|
};
|
|
}
|
|
/**
|
|
* Checks whether or not a given variable is a function declaration.
|
|
*/
|
|
function isFunction(variable) {
|
|
return variable.defs[0].type === scope_manager_1.DefinitionType.FunctionName;
|
|
}
|
|
/**
|
|
* Checks whether or not a given variable is a type declaration.
|
|
*/
|
|
function isTypedef(variable) {
|
|
return variable.defs[0].type === scope_manager_1.DefinitionType.Type;
|
|
}
|
|
/**
|
|
* Checks whether or not a given variable is a enum declaration.
|
|
*/
|
|
function isOuterEnum(variable, reference) {
|
|
return (variable.defs[0].type == scope_manager_1.DefinitionType.TSEnumName &&
|
|
variable.scope.variableScope !== reference.from.variableScope);
|
|
}
|
|
/**
|
|
* Checks whether or not a given variable is a class declaration in an upper function scope.
|
|
*/
|
|
function isOuterClass(variable, reference) {
|
|
return (variable.defs[0].type === scope_manager_1.DefinitionType.ClassName &&
|
|
variable.scope.variableScope !== reference.from.variableScope);
|
|
}
|
|
/**
|
|
* Checks whether or not a given variable is a variable declaration in an upper function scope.
|
|
*/
|
|
function isOuterVariable(variable, reference) {
|
|
return (variable.defs[0].type === scope_manager_1.DefinitionType.Variable &&
|
|
variable.scope.variableScope !== reference.from.variableScope);
|
|
}
|
|
/**
|
|
* Checks whether or not a given reference is a export reference.
|
|
*/
|
|
function isNamedExports(reference) {
|
|
var _a;
|
|
const { identifier } = reference;
|
|
return (((_a = identifier.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.ExportSpecifier &&
|
|
identifier.parent.local === identifier);
|
|
}
|
|
/**
|
|
* Recursively checks whether or not a given reference has a type query declaration among it's parents
|
|
*/
|
|
function referenceContainsTypeQuery(node) {
|
|
switch (node.type) {
|
|
case utils_1.AST_NODE_TYPES.TSTypeQuery:
|
|
return true;
|
|
case utils_1.AST_NODE_TYPES.TSQualifiedName:
|
|
case utils_1.AST_NODE_TYPES.Identifier:
|
|
if (!node.parent) {
|
|
return false;
|
|
}
|
|
return referenceContainsTypeQuery(node.parent);
|
|
default:
|
|
// if we find a different node, there's no chance that we're in a TSTypeQuery
|
|
return false;
|
|
}
|
|
}
|
|
/**
|
|
* Checks whether or not a given reference is a type reference.
|
|
*/
|
|
function isTypeReference(reference) {
|
|
return (reference.isTypeReference ||
|
|
referenceContainsTypeQuery(reference.identifier));
|
|
}
|
|
/**
|
|
* Checks whether or not a given location is inside of the range of a given node.
|
|
*/
|
|
function isInRange(node, location) {
|
|
return !!node && node.range[0] <= location && location <= node.range[1];
|
|
}
|
|
/**
|
|
* Decorators are transpiled such that the decorator is placed after the class declaration
|
|
* So it is considered safe
|
|
*/
|
|
function isClassRefInClassDecorator(variable, reference) {
|
|
if (variable.defs[0].type !== scope_manager_1.DefinitionType.ClassName) {
|
|
return false;
|
|
}
|
|
if (!variable.defs[0].node.decorators ||
|
|
variable.defs[0].node.decorators.length === 0) {
|
|
return false;
|
|
}
|
|
for (const deco of variable.defs[0].node.decorators) {
|
|
if (reference.identifier.range[0] >= deco.range[0] &&
|
|
reference.identifier.range[1] <= deco.range[1]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Checks whether or not a given reference is inside of the initializers of a given variable.
|
|
*
|
|
* @returns `true` in the following cases:
|
|
* - var a = a
|
|
* - var [a = a] = list
|
|
* - var {a = a} = obj
|
|
* - for (var a in a) {}
|
|
* - for (var a of a) {}
|
|
*/
|
|
function isInInitializer(variable, reference) {
|
|
var _a;
|
|
if (variable.scope !== reference.from) {
|
|
return false;
|
|
}
|
|
let node = variable.identifiers[0].parent;
|
|
const location = reference.identifier.range[1];
|
|
while (node) {
|
|
if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
if (isInRange(node.init, location)) {
|
|
return true;
|
|
}
|
|
if (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) &&
|
|
(node.parent.parent.type === utils_1.AST_NODE_TYPES.ForInStatement ||
|
|
node.parent.parent.type === utils_1.AST_NODE_TYPES.ForOfStatement) &&
|
|
isInRange(node.parent.parent.right, location)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
else if (node.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
if (isInRange(node.right, location)) {
|
|
return true;
|
|
}
|
|
}
|
|
else if (SENTINEL_TYPE.test(node.type)) {
|
|
break;
|
|
}
|
|
node = node.parent;
|
|
}
|
|
return false;
|
|
}
|
|
exports.default = util.createRule({
|
|
name: 'no-use-before-define',
|
|
meta: {
|
|
type: 'problem',
|
|
docs: {
|
|
description: 'Disallow the use of variables before they are defined',
|
|
recommended: false,
|
|
extendsBaseRule: true,
|
|
},
|
|
messages: {
|
|
noUseBeforeDefine: "'{{name}}' was used before it was defined.",
|
|
},
|
|
schema: [
|
|
{
|
|
oneOf: [
|
|
{
|
|
enum: ['nofunc'],
|
|
},
|
|
{
|
|
type: 'object',
|
|
properties: {
|
|
functions: { type: 'boolean' },
|
|
classes: { type: 'boolean' },
|
|
enums: { type: 'boolean' },
|
|
variables: { type: 'boolean' },
|
|
typedefs: { type: 'boolean' },
|
|
ignoreTypeReferences: { type: 'boolean' },
|
|
allowNamedExports: { type: 'boolean' },
|
|
},
|
|
additionalProperties: false,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
defaultOptions: [
|
|
{
|
|
functions: true,
|
|
classes: true,
|
|
enums: true,
|
|
variables: true,
|
|
typedefs: true,
|
|
ignoreTypeReferences: true,
|
|
allowNamedExports: false,
|
|
},
|
|
],
|
|
create(context, optionsWithDefault) {
|
|
const options = parseOptions(optionsWithDefault[0]);
|
|
/**
|
|
* Determines whether a given use-before-define case should be reported according to the options.
|
|
* @param variable The variable that gets used before being defined
|
|
* @param reference The reference to the variable
|
|
*/
|
|
function isForbidden(variable, reference) {
|
|
if (options.ignoreTypeReferences && isTypeReference(reference)) {
|
|
return false;
|
|
}
|
|
if (isFunction(variable)) {
|
|
return options.functions;
|
|
}
|
|
if (isOuterClass(variable, reference)) {
|
|
return options.classes;
|
|
}
|
|
if (isOuterVariable(variable, reference)) {
|
|
return options.variables;
|
|
}
|
|
if (isOuterEnum(variable, reference)) {
|
|
return options.enums;
|
|
}
|
|
if (isTypedef(variable)) {
|
|
return options.typedefs;
|
|
}
|
|
return true;
|
|
}
|
|
function isDefinedBeforeUse(variable, reference) {
|
|
return (variable.identifiers[0].range[1] <= reference.identifier.range[1] &&
|
|
!isInInitializer(variable, reference));
|
|
}
|
|
/**
|
|
* Finds and validates all variables in a given scope.
|
|
*/
|
|
function findVariablesInScope(scope) {
|
|
scope.references.forEach(reference => {
|
|
const variable = reference.resolved;
|
|
function report() {
|
|
context.report({
|
|
node: reference.identifier,
|
|
messageId: 'noUseBeforeDefine',
|
|
data: {
|
|
name: reference.identifier.name,
|
|
},
|
|
});
|
|
}
|
|
// Skips when the reference is:
|
|
// - initializations.
|
|
// - referring to an undefined variable.
|
|
// - referring to a global environment variable (there're no identifiers).
|
|
// - located preceded by the variable (except in initializers).
|
|
// - allowed by options.
|
|
if (reference.init) {
|
|
return;
|
|
}
|
|
if (!options.allowNamedExports && isNamedExports(reference)) {
|
|
if (!variable || !isDefinedBeforeUse(variable, reference)) {
|
|
report();
|
|
}
|
|
return;
|
|
}
|
|
if (!variable) {
|
|
return;
|
|
}
|
|
if (variable.identifiers.length === 0 ||
|
|
isDefinedBeforeUse(variable, reference) ||
|
|
!isForbidden(variable, reference) ||
|
|
isClassRefInClassDecorator(variable, reference) ||
|
|
reference.from.type === utils_1.TSESLint.Scope.ScopeType.functionType) {
|
|
return;
|
|
}
|
|
// Reports.
|
|
report();
|
|
});
|
|
scope.childScopes.forEach(findVariablesInScope);
|
|
}
|
|
return {
|
|
Program() {
|
|
findVariablesInScope(context.getScope());
|
|
},
|
|
};
|
|
},
|
|
});
|
|
//# sourceMappingURL=no-use-before-define.js.map |