Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/eslint-plugin-import
Path: blob/main/src/rules/no-deprecated.js
829 views
1
import declaredScope from 'eslint-module-utils/declaredScope';
2
import Exports from '../ExportMap';
3
import docsUrl from '../docsUrl';
4
5
function message(deprecation) {
6
return 'Deprecated' + (deprecation.description ? ': ' + deprecation.description : '.');
7
}
8
9
function getDeprecation(metadata) {
10
if (!metadata || !metadata.doc) return;
11
12
return metadata.doc.tags.find(t => t.title === 'deprecated');
13
}
14
15
module.exports = {
16
meta: {
17
type: 'suggestion',
18
docs: {
19
url: docsUrl('no-deprecated'),
20
},
21
schema: [],
22
},
23
24
create(context) {
25
const deprecated = new Map();
26
const namespaces = new Map();
27
28
function checkSpecifiers(node) {
29
if (node.type !== 'ImportDeclaration') return;
30
if (node.source == null) return; // local export, ignore
31
32
const imports = Exports.get(node.source.value, context);
33
if (imports == null) return;
34
35
const moduleDeprecation = imports.doc && imports.doc.tags.find(t => t.title === 'deprecated');
36
if (moduleDeprecation) {
37
context.report({ node, message: message(moduleDeprecation) });
38
}
39
40
if (imports.errors.length) {
41
imports.reportErrors(context, node);
42
return;
43
}
44
45
node.specifiers.forEach(function (im) {
46
let imported; let local;
47
switch (im.type) {
48
49
50
case 'ImportNamespaceSpecifier':{
51
if (!imports.size) return;
52
namespaces.set(im.local.name, imports);
53
return;
54
}
55
56
case 'ImportDefaultSpecifier':
57
imported = 'default';
58
local = im.local.name;
59
break;
60
61
case 'ImportSpecifier':
62
imported = im.imported.name;
63
local = im.local.name;
64
break;
65
66
default: return; // can't handle this one
67
}
68
69
// unknown thing can't be deprecated
70
const exported = imports.get(imported);
71
if (exported == null) return;
72
73
// capture import of deep namespace
74
if (exported.namespace) namespaces.set(local, exported.namespace);
75
76
const deprecation = getDeprecation(imports.get(imported));
77
if (!deprecation) return;
78
79
context.report({ node: im, message: message(deprecation) });
80
81
deprecated.set(local, deprecation);
82
83
});
84
}
85
86
return {
87
'Program': ({ body }) => body.forEach(checkSpecifiers),
88
89
'Identifier': function (node) {
90
if (node.parent.type === 'MemberExpression' && node.parent.property === node) {
91
return; // handled by MemberExpression
92
}
93
94
// ignore specifier identifiers
95
if (node.parent.type.slice(0, 6) === 'Import') return;
96
97
if (!deprecated.has(node.name)) return;
98
99
if (declaredScope(context, node.name) !== 'module') return;
100
context.report({
101
node,
102
message: message(deprecated.get(node.name)),
103
});
104
},
105
106
'MemberExpression': function (dereference) {
107
if (dereference.object.type !== 'Identifier') return;
108
if (!namespaces.has(dereference.object.name)) return;
109
110
if (declaredScope(context, dereference.object.name) !== 'module') return;
111
112
// go deep
113
let namespace = namespaces.get(dereference.object.name);
114
const namepath = [dereference.object.name];
115
// while property is namespace and parent is member expression, keep validating
116
while (namespace instanceof Exports &&
117
dereference.type === 'MemberExpression') {
118
119
// ignore computed parts for now
120
if (dereference.computed) return;
121
122
const metadata = namespace.get(dereference.property.name);
123
124
if (!metadata) break;
125
const deprecation = getDeprecation(metadata);
126
127
if (deprecation) {
128
context.report({ node: dereference.property, message: message(deprecation) });
129
}
130
131
// stash and pop
132
namepath.push(dereference.property.name);
133
namespace = metadata.namespace;
134
dereference = dereference.parent;
135
}
136
},
137
};
138
},
139
};
140
141