UNPKG

6.83 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 ts = __importStar(require("typescript"));
24const tsutils = __importStar(require("tsutils"));
25const util = __importStar(require("../util"));
26exports.default = util.createRule({
27 name: 'no-unnecessary-qualifier',
28 meta: {
29 docs: {
30 description: 'Warns when a namespace qualifier is unnecessary',
31 recommended: false,
32 requiresTypeChecking: true,
33 },
34 fixable: 'code',
35 messages: {
36 unnecessaryQualifier: "Qualifier is unnecessary since '{{ name }}' is in scope.",
37 },
38 schema: [],
39 type: 'suggestion',
40 },
41 defaultOptions: [],
42 create(context) {
43 const namespacesInScope = [];
44 let currentFailedNamespaceExpression = null;
45 const parserServices = util.getParserServices(context);
46 const esTreeNodeToTSNodeMap = parserServices.esTreeNodeToTSNodeMap;
47 const program = parserServices.program;
48 const checker = program.getTypeChecker();
49 const sourceCode = context.getSourceCode();
50 function tryGetAliasedSymbol(symbol, checker) {
51 return tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)
52 ? checker.getAliasedSymbol(symbol)
53 : null;
54 }
55 function symbolIsNamespaceInScope(symbol) {
56 var _a;
57 const symbolDeclarations = (_a = symbol.getDeclarations()) !== null && _a !== void 0 ? _a : [];
58 if (symbolDeclarations.some(decl => namespacesInScope.some(ns => ns === decl))) {
59 return true;
60 }
61 const alias = tryGetAliasedSymbol(symbol, checker);
62 return alias !== null && symbolIsNamespaceInScope(alias);
63 }
64 function getSymbolInScope(node, flags, name) {
65 // TODO:PERF `getSymbolsInScope` gets a long list. Is there a better way?
66 const scope = checker.getSymbolsInScope(node, flags);
67 return scope.find(scopeSymbol => scopeSymbol.name === name);
68 }
69 function symbolsAreEqual(accessed, inScope) {
70 return accessed === checker.getExportSymbolOfSymbol(inScope);
71 }
72 function qualifierIsUnnecessary(qualifier, name) {
73 const tsQualifier = esTreeNodeToTSNodeMap.get(qualifier);
74 const tsName = esTreeNodeToTSNodeMap.get(name);
75 const namespaceSymbol = checker.getSymbolAtLocation(tsQualifier);
76 if (typeof namespaceSymbol === 'undefined' ||
77 !symbolIsNamespaceInScope(namespaceSymbol)) {
78 return false;
79 }
80 const accessedSymbol = checker.getSymbolAtLocation(tsName);
81 if (typeof accessedSymbol === 'undefined') {
82 return false;
83 }
84 // If the symbol in scope is different, the qualifier is necessary.
85 const fromScope = getSymbolInScope(tsQualifier, accessedSymbol.flags, sourceCode.getText(name));
86 return (typeof fromScope === 'undefined' ||
87 symbolsAreEqual(accessedSymbol, fromScope));
88 }
89 function visitNamespaceAccess(node, qualifier, name) {
90 // Only look for nested qualifier errors if we didn't already fail on the outer qualifier.
91 if (!currentFailedNamespaceExpression &&
92 qualifierIsUnnecessary(qualifier, name)) {
93 currentFailedNamespaceExpression = node;
94 context.report({
95 node: qualifier,
96 messageId: 'unnecessaryQualifier',
97 data: {
98 name: sourceCode.getText(name),
99 },
100 fix(fixer) {
101 return fixer.removeRange([qualifier.range[0], name.range[0]]);
102 },
103 });
104 }
105 }
106 function enterDeclaration(node) {
107 namespacesInScope.push(esTreeNodeToTSNodeMap.get(node));
108 }
109 function exitDeclaration() {
110 namespacesInScope.pop();
111 }
112 function resetCurrentNamespaceExpression(node) {
113 if (node === currentFailedNamespaceExpression) {
114 currentFailedNamespaceExpression = null;
115 }
116 }
117 function isPropertyAccessExpression(node) {
118 return node.type === utils_1.AST_NODE_TYPES.MemberExpression && !node.computed;
119 }
120 function isEntityNameExpression(node) {
121 return (node.type === utils_1.AST_NODE_TYPES.Identifier ||
122 (isPropertyAccessExpression(node) &&
123 isEntityNameExpression(node.object)));
124 }
125 return {
126 TSModuleDeclaration: enterDeclaration,
127 TSEnumDeclaration: enterDeclaration,
128 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]': enterDeclaration,
129 'ExportNamedDeclaration[declaration.type="TSEnumDeclaration"]': enterDeclaration,
130 'TSModuleDeclaration:exit': exitDeclaration,
131 'TSEnumDeclaration:exit': exitDeclaration,
132 'ExportNamedDeclaration[declaration.type="TSModuleDeclaration"]:exit': exitDeclaration,
133 'ExportNamedDeclaration[declaration.type="TSEnumDeclaration"]:exit': exitDeclaration,
134 TSQualifiedName(node) {
135 visitNamespaceAccess(node, node.left, node.right);
136 },
137 'MemberExpression[computed=false]': function (node) {
138 const property = node.property;
139 if (isEntityNameExpression(node.object)) {
140 visitNamespaceAccess(node, node.object, property);
141 }
142 },
143 'TSQualifiedName:exit': resetCurrentNamespaceExpression,
144 'MemberExpression:exit': resetCurrentNamespaceExpression,
145 };
146 },
147});
148//# sourceMappingURL=no-unnecessary-qualifier.js.map
\No newline at end of file