| "use strict"; |
| /* eslint-disable no-fallthrough */ |
| var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { |
| if (k2 === undefined) k2 = k; |
| Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); |
| }) : (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 }); |
| exports.DEFAULT_IGNORED_REGEX_STRING = void 0; |
| const ts = __importStar(require("typescript")); |
| const util = __importStar(require("../util")); |
| exports.DEFAULT_IGNORED_REGEX_STRING = '^_'; |
| exports.default = util.createRule({ |
| name: 'no-unused-vars-experimental', |
| meta: { |
| type: 'problem', |
| docs: { |
| description: 'Disallow unused variables and arguments', |
| category: 'Best Practices', |
| recommended: false, |
| }, |
| deprecated: true, |
| replacedBy: ['no-unused-vars'], |
| schema: [ |
| { |
| type: 'object', |
| properties: { |
| ignoredNamesRegex: { |
| oneOf: [ |
| { |
| type: 'string', |
| }, |
| { |
| type: 'boolean', |
| enum: [false], |
| }, |
| ], |
| }, |
| ignoreArgsIfArgsAfterAreUsed: { |
| type: 'boolean', |
| }, |
| }, |
| additionalProperties: false, |
| }, |
| ], |
| messages: { |
| unused: "{{type}} '{{name}}' is declared but its value is never read.", |
| unusedWithIgnorePattern: "{{type}} '{{name}}' is declared but its value is never read. Allowed unused names must match {{pattern}}.", |
| unusedImport: 'All imports in import declaration are unused.', |
| unusedTypeParameters: 'All type parameters are unused.', |
| }, |
| }, |
| defaultOptions: [ |
| { |
| ignoredNamesRegex: exports.DEFAULT_IGNORED_REGEX_STRING, |
| ignoreArgsIfArgsAfterAreUsed: false, |
| }, |
| ], |
| create(context, [userOptions]) { |
| var _a; |
| const parserServices = util.getParserServices(context, true); |
| const tsProgram = parserServices.program; |
| const afterAllDiagnosticsCallbacks = []; |
| const options = { |
| ignoredNames: userOptions && typeof userOptions.ignoredNamesRegex === 'string' |
| ? new RegExp(userOptions.ignoredNamesRegex) |
| : null, |
| ignoreArgsIfArgsAfterAreUsed: (_a = userOptions.ignoreArgsIfArgsAfterAreUsed) !== null && _a !== void 0 ? _a : false, |
| }; |
| function handleIdentifier(identifier) { |
| function report(type) { |
| const node = parserServices.tsNodeToESTreeNodeMap.get(identifier); |
| const regex = options.ignoredNames; |
| const name = identifier.getText(); |
| if (regex) { |
| if (!regex.test(name)) { |
| context.report({ |
| node, |
| messageId: 'unusedWithIgnorePattern', |
| data: { |
| name, |
| type, |
| pattern: regex.toString(), |
| }, |
| }); |
| } |
| } |
| else { |
| context.report({ |
| node, |
| messageId: 'unused', |
| data: { |
| name, |
| type, |
| }, |
| }); |
| } |
| } |
| const parent = identifier.parent; |
| // is a single variable diagnostic |
| switch (parent.kind) { |
| case ts.SyntaxKind.BindingElement: |
| case ts.SyntaxKind.ObjectBindingPattern: |
| report('Destructured Variable'); |
| break; |
| case ts.SyntaxKind.ClassDeclaration: |
| report('Class'); |
| break; |
| case ts.SyntaxKind.EnumDeclaration: |
| report('Enum'); |
| break; |
| case ts.SyntaxKind.FunctionDeclaration: |
| report('Function'); |
| break; |
| // this won't happen because there are specific nodes that wrap up named/default import identifiers |
| // case ts.SyntaxKind.ImportDeclaration: |
| // import equals is always treated as a variable |
| case ts.SyntaxKind.ImportEqualsDeclaration: |
| // the default import is NOT used, but a named import is used |
| case ts.SyntaxKind.ImportClause: |
| // a named import is NOT used, but either another named import, or the default import is used |
| case ts.SyntaxKind.ImportSpecifier: |
| // a namespace import is NOT used, but the default import is used |
| case ts.SyntaxKind.NamespaceImport: |
| report('Import'); |
| break; |
| case ts.SyntaxKind.InterfaceDeclaration: |
| report('Interface'); |
| break; |
| case ts.SyntaxKind.MethodDeclaration: |
| report('Method'); |
| break; |
| case ts.SyntaxKind.Parameter: |
| handleParameterDeclaration(identifier, parent); |
| break; |
| case ts.SyntaxKind.PropertyDeclaration: |
| // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum |
| report('Property'); |
| break; |
| case ts.SyntaxKind.TypeAliasDeclaration: |
| report('Type'); |
| break; |
| case ts.SyntaxKind.TypeParameter: |
| handleTypeParam(identifier); |
| break; |
| case ts.SyntaxKind.VariableDeclaration: |
| report('Variable'); |
| break; |
| default: |
| throw new Error(`Unknown node with kind ${parent.kind}.`); |
| // TODO - should we just handle this gracefully? |
| // report('Unknown Node'); |
| // break; |
| } |
| } |
| const unusedParameters = new Set(); |
| function handleParameterDeclaration(identifier, parent) { |
| const name = identifier.getText(); |
| // regardless of if the parameter is ignored, track that it had a diagnostic fired on it |
| unusedParameters.add(identifier); |
| /* |
| NOTE - Typescript will automatically ignore parameters that have a |
| leading underscore in their name. We cannot do anything about this. |
| */ |
| function report() { |
| const node = parserServices.tsNodeToESTreeNodeMap.get(identifier); |
| context.report({ |
| node, |
| messageId: 'unused', |
| data: { |
| name, |
| type: 'Parameter', |
| }, |
| }); |
| } |
| const isLastParameter = parent.parent.parameters.indexOf(parent) === |
| parent.parent.parameters.length - 1; |
| if (!isLastParameter && options.ignoreArgsIfArgsAfterAreUsed) { |
| // once all diagnostics are processed, we can check if the following args are unused |
| afterAllDiagnosticsCallbacks.push(() => { |
| for (const param of parent.parent.parameters) { |
| if (!unusedParameters.has(param.name)) { |
| return; |
| } |
| } |
| // none of the following params were unused, so report |
| report(); |
| }); |
| } |
| else { |
| report(); |
| } |
| } |
| function handleImportDeclaration(parent) { |
| // the entire import statement is unused |
| /* |
| NOTE - Typescript will automatically ignore imports that have a |
| leading underscore in their name. We cannot do anything about this. |
| */ |
| context.report({ |
| messageId: 'unusedImport', |
| node: parserServices.tsNodeToESTreeNodeMap.get(parent), |
| }); |
| } |
| function handleDestructure(parent) { |
| // the entire destructure is unused |
| // note that this case only ever triggers for simple, single-level destructured objects |
| // i.e. these will not trigger it: |
| // - const {a:_a, b, c: {d}} = z; |
| // - const [a, b] = c; |
| parent.elements.forEach(element => { |
| if (element.kind === ts.SyntaxKind.BindingElement) { |
| const name = element.name; |
| if (name.kind === ts.SyntaxKind.Identifier) { |
| handleIdentifier(name); |
| } |
| } |
| }); |
| } |
| function handleTypeParamList(node) { |
| // the entire generic decl list is unused |
| /* |
| NOTE - Typescript will automatically ignore generics that have a |
| leading underscore in their name. We cannot do anything about this. |
| */ |
| const parent = parserServices.tsNodeToESTreeNodeMap.get(node); |
| context.report({ |
| messageId: 'unusedTypeParameters', |
| node: parent.typeParameters, |
| }); |
| } |
| function handleTypeParam(identifier) { |
| context.report({ |
| node: parserServices.tsNodeToESTreeNodeMap.get(identifier), |
| messageId: 'unused', |
| data: { |
| name: identifier.getText(), |
| type: 'Type Parameter', |
| }, |
| }); |
| } |
| return { |
| 'Program:exit'(program) { |
| const tsNode = parserServices.esTreeNodeToTSNodeMap.get(program); |
| const sourceFile = util.getSourceFileOfNode(tsNode); |
| const diagnostics = tsProgram.getSemanticDiagnostics(sourceFile); |
| diagnostics.forEach(diag => { |
| if (isUnusedDiagnostic(diag.code)) { |
| if (diag.start !== undefined) { |
| const node = util.getTokenAtPosition(sourceFile, diag.start); |
| const parent = node.parent; |
| if (isIdentifier(node)) { |
| handleIdentifier(node); |
| } |
| else if (isImport(parent)) { |
| handleImportDeclaration(parent); |
| } |
| else if (isDestructure(parent)) { |
| handleDestructure(parent); |
| } |
| else if (isGeneric(node, parent)) { |
| handleTypeParamList(parent); |
| } |
| } |
| } |
| }); |
| // trigger all the checks to be done after all the diagnostics have been evaluated |
| afterAllDiagnosticsCallbacks.forEach(cb => cb()); |
| }, |
| }; |
| }, |
| }); |
| /** |
| * Checks if the diagnostic code is one of the expected "unused var" codes |
| */ |
| function isUnusedDiagnostic(code) { |
| return [ |
| 6133, |
| 6138, |
| 6192, |
| 6196, |
| 6198, |
| 6199, |
| 6205, |
| ].includes(code); |
| } |
| /** |
| * Checks if the given node is a destructuring pattern |
| */ |
| function isDestructure(node) { |
| return (node.kind === ts.SyntaxKind.ObjectBindingPattern || |
| node.kind === ts.SyntaxKind.ArrayBindingPattern); |
| } |
| function isImport(node) { |
| return node.kind === ts.SyntaxKind.ImportDeclaration; |
| } |
| function isIdentifier(node) { |
| return node.kind === ts.SyntaxKind.Identifier; |
| } |
| function isGeneric(node, parent) { |
| return (node.kind === ts.SyntaxKind.LessThanToken && |
| parent.typeParameters !== undefined); |
| } |
| //# sourceMappingURL=no-unused-vars-experimental.js.map |