UNPKG

9.67 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22const utils_1 = require("@typescript-eslint/utils");
23const util = __importStar(require("../util"));
24exports.default = util.createRule({
25 name: 'no-redeclare',
26 meta: {
27 type: 'suggestion',
28 docs: {
29 description: 'Disallow variable redeclaration',
30 recommended: false,
31 extendsBaseRule: true,
32 },
33 schema: [
34 {
35 type: 'object',
36 properties: {
37 builtinGlobals: {
38 type: 'boolean',
39 },
40 ignoreDeclarationMerge: {
41 type: 'boolean',
42 },
43 },
44 additionalProperties: false,
45 },
46 ],
47 messages: {
48 redeclared: "'{{id}}' is already defined.",
49 redeclaredAsBuiltin: "'{{id}}' is already defined as a built-in global variable.",
50 redeclaredBySyntax: "'{{id}}' is already defined by a variable declaration.",
51 },
52 },
53 defaultOptions: [
54 {
55 builtinGlobals: true,
56 ignoreDeclarationMerge: true,
57 },
58 ],
59 create(context, [options]) {
60 const sourceCode = context.getSourceCode();
61 const CLASS_DECLARATION_MERGE_NODES = new Set([
62 utils_1.AST_NODE_TYPES.TSInterfaceDeclaration,
63 utils_1.AST_NODE_TYPES.TSModuleDeclaration,
64 utils_1.AST_NODE_TYPES.ClassDeclaration,
65 ]);
66 const FUNCTION_DECLARATION_MERGE_NODES = new Set([
67 utils_1.AST_NODE_TYPES.TSModuleDeclaration,
68 utils_1.AST_NODE_TYPES.FunctionDeclaration,
69 ]);
70 const ENUM_DECLARATION_MERGE_NODES = new Set([
71 utils_1.AST_NODE_TYPES.TSEnumDeclaration,
72 utils_1.AST_NODE_TYPES.TSModuleDeclaration,
73 ]);
74 function* iterateDeclarations(variable) {
75 if ((options === null || options === void 0 ? void 0 : options.builtinGlobals) &&
76 'eslintImplicitGlobalSetting' in variable &&
77 (variable.eslintImplicitGlobalSetting === 'readonly' ||
78 variable.eslintImplicitGlobalSetting === 'writable')) {
79 yield { type: 'builtin' };
80 }
81 if ('eslintExplicitGlobalComments' in variable &&
82 variable.eslintExplicitGlobalComments) {
83 for (const comment of variable.eslintExplicitGlobalComments) {
84 yield {
85 type: 'comment',
86 node: comment,
87 loc: util.getNameLocationInGlobalDirectiveComment(sourceCode, comment, variable.name),
88 };
89 }
90 }
91 const identifiers = variable.identifiers
92 .map(id => ({
93 identifier: id,
94 parent: id.parent,
95 }))
96 // ignore function declarations because TS will treat them as an overload
97 .filter(({ parent }) => parent.type !== utils_1.AST_NODE_TYPES.TSDeclareFunction);
98 if (options.ignoreDeclarationMerge && identifiers.length > 1) {
99 if (
100 // interfaces merging
101 identifiers.every(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration)) {
102 return;
103 }
104 if (
105 // namespace/module merging
106 identifiers.every(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration)) {
107 return;
108 }
109 if (
110 // class + interface/namespace merging
111 identifiers.every(({ parent }) => CLASS_DECLARATION_MERGE_NODES.has(parent.type))) {
112 const classDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.ClassDeclaration);
113 if (classDecls.length === 1) {
114 // safe declaration merging
115 return;
116 }
117 // there's more than one class declaration, which needs to be reported
118 for (const { identifier } of classDecls) {
119 yield { type: 'syntax', node: identifier, loc: identifier.loc };
120 }
121 return;
122 }
123 if (
124 // class + interface/namespace merging
125 identifiers.every(({ parent }) => FUNCTION_DECLARATION_MERGE_NODES.has(parent.type))) {
126 const functionDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.FunctionDeclaration);
127 if (functionDecls.length === 1) {
128 // safe declaration merging
129 return;
130 }
131 // there's more than one function declaration, which needs to be reported
132 for (const { identifier } of functionDecls) {
133 yield { type: 'syntax', node: identifier, loc: identifier.loc };
134 }
135 return;
136 }
137 if (
138 // enum + namespace merging
139 identifiers.every(({ parent }) => ENUM_DECLARATION_MERGE_NODES.has(parent.type))) {
140 const enumDecls = identifiers.filter(({ parent }) => parent.type === utils_1.AST_NODE_TYPES.TSEnumDeclaration);
141 if (enumDecls.length === 1) {
142 // safe declaration merging
143 return;
144 }
145 // there's more than one enum declaration, which needs to be reported
146 for (const { identifier } of enumDecls) {
147 yield { type: 'syntax', node: identifier, loc: identifier.loc };
148 }
149 return;
150 }
151 }
152 for (const { identifier } of identifiers) {
153 yield { type: 'syntax', node: identifier, loc: identifier.loc };
154 }
155 }
156 function findVariablesInScope(scope) {
157 for (const variable of scope.variables) {
158 const [declaration, ...extraDeclarations] = iterateDeclarations(variable);
159 if (extraDeclarations.length === 0) {
160 continue;
161 }
162 /*
163 * If the type of a declaration is different from the type of
164 * the first declaration, it shows the location of the first
165 * declaration.
166 */
167 const detailMessageId = declaration.type === 'builtin'
168 ? 'redeclaredAsBuiltin'
169 : 'redeclaredBySyntax';
170 const data = { id: variable.name };
171 // Report extra declarations.
172 for (const { type, node, loc } of extraDeclarations) {
173 const messageId = type === declaration.type ? 'redeclared' : detailMessageId;
174 if (node) {
175 context.report({ node, loc, messageId, data });
176 }
177 else if (loc) {
178 context.report({ loc, messageId, data });
179 }
180 }
181 }
182 }
183 /**
184 * Find variables in the current scope.
185 */
186 function checkForBlock(node) {
187 const scope = context.getScope();
188 /*
189 * In ES5, some node type such as `BlockStatement` doesn't have that scope.
190 * `scope.block` is a different node in such a case.
191 */
192 if (scope.block === node) {
193 findVariablesInScope(scope);
194 }
195 }
196 return {
197 Program() {
198 const scope = context.getScope();
199 findVariablesInScope(scope);
200 // Node.js or ES modules has a special scope.
201 if (scope.type === 'global' &&
202 scope.childScopes[0] &&
203 // The special scope's block is the Program node.
204 scope.block === scope.childScopes[0].block) {
205 findVariablesInScope(scope.childScopes[0]);
206 }
207 },
208 FunctionDeclaration: checkForBlock,
209 FunctionExpression: checkForBlock,
210 ArrowFunctionExpression: checkForBlock,
211 BlockStatement: checkForBlock,
212 ForStatement: checkForBlock,
213 ForInStatement: checkForBlock,
214 ForOfStatement: checkForBlock,
215 SwitchStatement: checkForBlock,
216 };
217 },
218});
219//# sourceMappingURL=no-redeclare.js.map
\No newline at end of file