Path: blob/main/src/rules/no-deprecated.js
829 views
import declaredScope from 'eslint-module-utils/declaredScope';1import Exports from '../ExportMap';2import docsUrl from '../docsUrl';34function message(deprecation) {5return 'Deprecated' + (deprecation.description ? ': ' + deprecation.description : '.');6}78function getDeprecation(metadata) {9if (!metadata || !metadata.doc) return;1011return metadata.doc.tags.find(t => t.title === 'deprecated');12}1314module.exports = {15meta: {16type: 'suggestion',17docs: {18url: docsUrl('no-deprecated'),19},20schema: [],21},2223create(context) {24const deprecated = new Map();25const namespaces = new Map();2627function checkSpecifiers(node) {28if (node.type !== 'ImportDeclaration') return;29if (node.source == null) return; // local export, ignore3031const imports = Exports.get(node.source.value, context);32if (imports == null) return;3334const moduleDeprecation = imports.doc && imports.doc.tags.find(t => t.title === 'deprecated');35if (moduleDeprecation) {36context.report({ node, message: message(moduleDeprecation) });37}3839if (imports.errors.length) {40imports.reportErrors(context, node);41return;42}4344node.specifiers.forEach(function (im) {45let imported; let local;46switch (im.type) {474849case 'ImportNamespaceSpecifier':{50if (!imports.size) return;51namespaces.set(im.local.name, imports);52return;53}5455case 'ImportDefaultSpecifier':56imported = 'default';57local = im.local.name;58break;5960case 'ImportSpecifier':61imported = im.imported.name;62local = im.local.name;63break;6465default: return; // can't handle this one66}6768// unknown thing can't be deprecated69const exported = imports.get(imported);70if (exported == null) return;7172// capture import of deep namespace73if (exported.namespace) namespaces.set(local, exported.namespace);7475const deprecation = getDeprecation(imports.get(imported));76if (!deprecation) return;7778context.report({ node: im, message: message(deprecation) });7980deprecated.set(local, deprecation);8182});83}8485return {86'Program': ({ body }) => body.forEach(checkSpecifiers),8788'Identifier': function (node) {89if (node.parent.type === 'MemberExpression' && node.parent.property === node) {90return; // handled by MemberExpression91}9293// ignore specifier identifiers94if (node.parent.type.slice(0, 6) === 'Import') return;9596if (!deprecated.has(node.name)) return;9798if (declaredScope(context, node.name) !== 'module') return;99context.report({100node,101message: message(deprecated.get(node.name)),102});103},104105'MemberExpression': function (dereference) {106if (dereference.object.type !== 'Identifier') return;107if (!namespaces.has(dereference.object.name)) return;108109if (declaredScope(context, dereference.object.name) !== 'module') return;110111// go deep112let namespace = namespaces.get(dereference.object.name);113const namepath = [dereference.object.name];114// while property is namespace and parent is member expression, keep validating115while (namespace instanceof Exports &&116dereference.type === 'MemberExpression') {117118// ignore computed parts for now119if (dereference.computed) return;120121const metadata = namespace.get(dereference.property.name);122123if (!metadata) break;124const deprecation = getDeprecation(metadata);125126if (deprecation) {127context.report({ node: dereference.property, message: message(deprecation) });128}129130// stash and pop131namepath.push(dereference.property.name);132namespace = metadata.namespace;133dereference = dereference.parent;134}135},136};137},138};139140141