UNPKG

7.02 kBJavaScriptView Raw
1const {analyze} = require('eslint-scope');
2const astMatcher = require('ast-matcher');
3const {ensureParsed, compilePattern, extract, traverse, STOP} = astMatcher;
4require('./ensure-parser-set')();
5
6// https://github.com/jrburke/amdefine
7const amdefinePattern = compilePattern('var define = require("amdefine")(__anl)').declarations[0];
8
9function globalIndentifiers (code) {
10 let ast = ensureParsed(code);
11 let scopeManager = analyze(ast, {ecmaVersion: 6});
12 let globalScope = scopeManager.acquire(ast);
13
14 // This is very interesting.
15 // If you do `let globals = {};`, globals actually has some properties inherited
16 // like __defineSetter__, which makes globals['__defineSetter__'] not empty!
17 // Check last test in spec/parser.uses-common-js.spec.js
18 let globals = Object.create(null);
19
20 globalScope.through.forEach(function (ref) {
21 let name = ref.identifier.name;
22 // user defined the variable in global scope
23 let variable = globalScope.set.get(name);
24 // amdefine will be ignored in browser,
25 // don't remove 'define' from the list.
26 if (variable && !(name === 'define' && extract(amdefinePattern, variable.defs[0].node))) {
27 return;
28 }
29
30 if (globals[name]) {
31 globals[name].push(ref.identifier);
32 } else {
33 globals[name] = [ref.identifier];
34 }
35 });
36
37 return globals;
38}
39
40function some(list, interested) {
41 if (interested) {
42 let len = interested.length
43 for (let i = 0; i < len; i += 1) {
44 let item = interested[i];
45
46 if (list.indexOf(item) !== -1) {
47 return true;
48 }
49 }
50 }
51}
52
53const cjsRequireFinder = astMatcher('require(__str)');
54
55const cjsDeps = function (code, globals) {
56 let deps = [];
57
58 let matches = cjsRequireFinder(code);
59 if (matches) {
60 matches.forEach(function (m) {
61 if (globals['require'].indexOf(m.node.callee) !== -1) {
62 deps.push(m.node.arguments[0].value);
63 }
64 });
65 }
66
67 if (deps.length) return deps;
68};
69
70function usesCommonJs (code, globals) {
71 let ast = ensureParsed(code);
72 if (!globals) {
73 globals = globalIndentifiers(ast);
74 }
75 let usage = {};
76
77 if (globals['require']) {
78 const deps = cjsDeps(ast, globals);
79 if (deps) usage.require = deps;
80 }
81
82 if (globals['exports']) usage['exports'] = true;
83 if (globals['module']) usage['moduleExports'] = true;
84 if (globals['__dirname']) usage['dirname'] = true;
85 if (globals['__filename']) usage['filename'] = true;
86 // special nodejs globals
87 if (globals['global']) usage['global'] = true;
88 if (globals['process']) usage['process'] = true;
89 if (globals['Buffer']) usage['Buffer'] = true;
90
91 if (Object.keys(usage).length) {
92 return usage;
93 }
94}
95
96const amdRequireFinders = [
97 astMatcher('require([__anl])'),
98 astMatcher('require([__anl], __any)')
99];
100
101const amdRequireFinders2 = [
102 // first param is a requirejs config
103 astMatcher('require(__any, [__anl])'),
104 astMatcher('require(__any, [__anl], __any)'),
105];
106
107const amdRequireDeps = function (code, globals) {
108 let deps = [];
109
110 amdRequireFinders.forEach(function (f) {
111 let matches = f(code);
112 if (matches) {
113 matches.forEach(function (m) {
114 if (globals['require'].indexOf(m.node.callee) !== -1) {
115 m.node.arguments[0].elements.forEach(arg => {
116 deps.push(arg.value);
117 })
118 }
119 });
120 }
121 });
122
123 amdRequireFinders2.forEach(function (f) {
124 let matches = f(code);
125 if (matches) {
126 matches.forEach(function (m) {
127 if (globals['require'].indexOf(m.node.callee) !== -1) {
128 m.node.arguments[1].elements.forEach(arg => {
129 deps.push(arg.value);
130 })
131 }
132 });
133 }
134 });
135
136 if (deps.length) return deps;
137};
138
139const amdRequireJSFinders = [
140 astMatcher('requirejs([__anl])'),
141 astMatcher('requirejs([__anl], __any)')
142];
143
144const amdRequireJSFinders2 = [
145 // first param is a requirejs config
146 astMatcher('requirejs(__any, [__anl])'),
147 astMatcher('requirejs(__any, [__anl], __any)'),
148];
149
150const amdRequireJSDeps = function (code, globals) {
151 let deps = [];
152
153 amdRequireJSFinders.forEach(function (f) {
154 let matches = f(code);
155 if (matches) {
156 matches.forEach(function (m) {
157 if (globals['requirejs'].indexOf(m.node.callee) !== -1) {
158 m.node.arguments[0].elements.forEach(arg => {
159 if (arg.type === 'Literal') deps.push(arg.value);
160 })
161 }
162 });
163 }
164 });
165
166 amdRequireJSFinders2.forEach(function (f) {
167 let matches = f(code);
168 if (matches) {
169 matches.forEach(function (m) {
170 if (globals['requirejs'].indexOf(m.node.callee) !== -1) {
171 m.node.arguments[1].elements.forEach(arg => {
172 if (arg.type === 'Literal') deps.push(arg.value);
173 })
174 }
175 });
176 }
177 });
178
179 if (deps.length) return deps;
180};
181
182const amdRequireConfigFinder = astMatcher('require.config(__anl)');
183const amdRequireJSConfigFinder = astMatcher('requirejs.config(__anl)');
184
185const findAmdRequireConfigIdentifiers = function (code) {
186 let requireIdendifers = [];
187
188 let matches = amdRequireConfigFinder(code);
189 if (matches) {
190 matches.forEach(function (m) {
191 requireIdendifers.push(m.node.callee.object);
192 });
193 }
194
195 if (requireIdendifers.length) return requireIdendifers;
196};
197
198const findAmdRequireJSConfigIdentifiers = function (code) {
199 let requireIdendifers = [];
200
201 let matches = amdRequireJSConfigFinder(code);
202 if (matches) {
203 matches.forEach(function (m) {
204 requireIdendifers.push(m.node.callee.object);
205 });
206 }
207
208 if (requireIdendifers.length) return requireIdendifers;
209};
210
211function usesAmdOrRequireJs (code, globals) {
212 let ast = ensureParsed(code);
213 if (!globals) {
214 globals = globalIndentifiers(ast);
215 }
216 let usage = {};
217
218
219 if (globals['requirejs']) {
220 const deps = amdRequireJSDeps(ast, globals);
221 if (deps) usage['requirejs'] = deps;
222
223 if (some(globals['requirejs'], findAmdRequireJSConfigIdentifiers(ast))) {
224 usage['requireConfig'] = true;
225 }
226 }
227
228 if (globals['require']) {
229 const deps = amdRequireDeps(ast, globals);
230 if (deps) usage['require'] = deps;
231
232 if (some(globals['require'], findAmdRequireConfigIdentifiers(ast))) {
233 usage['requireConfig'] = true;
234 }
235 }
236
237 if (globals['define']) {
238 usage['define'] = true;
239 // We didn't implement declaresDefine and defineAmd here.
240 // If we want, use eslint-scope getDeclaredVariables(node) api to get function definition in inner scope.
241 }
242
243 if (Object.keys(usage).length) {
244 return usage;
245 }
246}
247
248function usesEsm(code) {
249 let ast = ensureParsed(code);
250 let isEsm = false;
251 traverse(ast, node => {
252 if (node.type === 'ImportDeclaration' ||
253 node.type === 'ExportAllDeclaration' ||
254 node.type === 'ExportDefaultDeclaration' ||
255 node.type === 'ExportNamedDeclaration') {
256 isEsm = true;
257 return STOP;
258 }
259 });
260 return isEsm;
261}
262
263exports.globalIndentifiers = globalIndentifiers;
264exports.usesCommonJs = usesCommonJs;
265exports.usesAmdOrRequireJs = usesAmdOrRequireJs;
266exports.usesEsm = usesEsm;