1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | Object.defineProperty(exports, "__esModule", { value: true });
|
16 | const generator_1 = require("@babel/generator");
|
17 | const traverse_1 = require("@babel/traverse");
|
18 | const babel = require("@babel/types");
|
19 | const assert = require("assert");
|
20 | const doctrine = require("doctrine");
|
21 | const util = require("util");
|
22 | const model_1 = require("../model/model");
|
23 | const docs = require("../polymer/docs");
|
24 | const docs_1 = require("../polymer/docs");
|
25 | const astValue = require("./ast-value");
|
26 | const jsdoc = require("./jsdoc");
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | function matchesCallExpression(expression, path) {
|
39 | if (!expression.property || !expression.object) {
|
40 | return false;
|
41 | }
|
42 | assert(path.length >= 2);
|
43 | if (!babel.isIdentifier(expression.property)) {
|
44 | return false;
|
45 | }
|
46 |
|
47 | if (expression.property.name !== path[path.length - 1]) {
|
48 | return false;
|
49 | }
|
50 |
|
51 | if (path.length === 2 && babel.isIdentifier(expression.object)) {
|
52 | return expression.object.name === path[0];
|
53 | }
|
54 |
|
55 | if (path.length > 2 && babel.isMemberExpression(expression.object)) {
|
56 | return matchesCallExpression(expression.object, path.slice(0, path.length - 1));
|
57 | }
|
58 | return false;
|
59 | }
|
60 | exports.matchesCallExpression = matchesCallExpression;
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | function getPropertyName(prop) {
|
66 | const key = prop.key;
|
67 |
|
68 | if (!prop.computed && babel.isIdentifier(key)) {
|
69 | return key.name;
|
70 | }
|
71 |
|
72 | const keyValue = astValue.expressionToValue(key);
|
73 | if (keyValue !== undefined) {
|
74 | return '' + keyValue;
|
75 | }
|
76 | return undefined;
|
77 | }
|
78 | exports.getPropertyName = getPropertyName;
|
79 |
|
80 |
|
81 |
|
82 |
|
83 | function* getSimpleObjectProperties(node) {
|
84 | for (const property of node.properties) {
|
85 | if (babel.isObjectProperty(property) || babel.isObjectMethod(property)) {
|
86 | yield property;
|
87 | }
|
88 | }
|
89 | }
|
90 | exports.getSimpleObjectProperties = getSimpleObjectProperties;
|
91 |
|
92 | function* getSimpleObjectPropPaths(nodePath) {
|
93 |
|
94 | const props = nodePath.get('properties');
|
95 | for (const propPath of props) {
|
96 | if (propPath.isObjectProperty() || propPath.isObjectMethod()) {
|
97 | yield propPath;
|
98 | }
|
99 | }
|
100 | }
|
101 | exports.getSimpleObjectPropPaths = getSimpleObjectPropPaths;
|
102 | exports.CLOSURE_CONSTRUCTOR_MAP = new Map([['Boolean', 'boolean'], ['Number', 'number'], ['String', 'string']]);
|
103 | const VALID_EXPRESSION_TYPES = new Map([
|
104 | ['ArrayExpression', 'Array'],
|
105 | ['BlockStatement', 'Function'],
|
106 | ['BooleanLiteral', 'boolean'],
|
107 | ['FunctionExpression', 'Function'],
|
108 | ['NullLiteral', 'null'],
|
109 | ['NumericLiteral', 'number'],
|
110 | ['ObjectExpression', 'Object'],
|
111 | ['RegExpLiteral', 'RegExp'],
|
112 | ['StringLiteral', 'string'],
|
113 | ['TemplateLiteral', 'string'],
|
114 | ]);
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function getClosureType(node, parsedJsdoc, sourceRange, document) {
|
124 | if (parsedJsdoc) {
|
125 | const typeTag = jsdoc.getTag(parsedJsdoc, 'type');
|
126 | if (typeTag) {
|
127 | return { successful: true, value: doctrine.type.stringify(typeTag.type) };
|
128 | }
|
129 | }
|
130 | const type = VALID_EXPRESSION_TYPES.get(node.type);
|
131 | if (type) {
|
132 | return { successful: true, value: type };
|
133 | }
|
134 | if (babel.isIdentifier(node)) {
|
135 | return {
|
136 | successful: true,
|
137 | value: exports.CLOSURE_CONSTRUCTOR_MAP.get(node.name) || node.name
|
138 | };
|
139 | }
|
140 | const warning = new model_1.Warning({
|
141 | code: 'no-closure-type',
|
142 | message: `Unable to determine closure type for expression of type ` +
|
143 | `${node.type}`,
|
144 | severity: model_1.Severity.WARNING,
|
145 | sourceRange,
|
146 | parsedDocument: document,
|
147 | });
|
148 | return { successful: false, error: warning };
|
149 | }
|
150 | exports.getClosureType = getClosureType;
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 | function getBestComment(nodePath) {
|
158 | const maybeComment = getAttachedComment(nodePath.node);
|
159 | if (maybeComment !== undefined) {
|
160 | return maybeComment;
|
161 | }
|
162 | const parent = nodePath.parentPath;
|
163 | if (parent === undefined) {
|
164 | return undefined;
|
165 | }
|
166 | if (!isStatementWithUniqueStatementChild(parent.node) &&
|
167 | babel.isStatement(nodePath.node)) {
|
168 |
|
169 | return undefined;
|
170 | }
|
171 | if (babel.isVariableDeclaration(parent.node) &&
|
172 | parent.node.declarations.length !== 1) {
|
173 |
|
174 |
|
175 | return undefined;
|
176 | }
|
177 | if (parent.isClassBody() || nodePath.isObjectMember()) {
|
178 |
|
179 | return undefined;
|
180 | }
|
181 | return getBestComment(parent);
|
182 | }
|
183 | exports.getBestComment = getBestComment;
|
184 | function getAttachedComment(node) {
|
185 | const comments = getLeadingComments(node) || [];
|
186 | return comments && comments[comments.length - 1];
|
187 | }
|
188 | exports.getAttachedComment = getAttachedComment;
|
189 |
|
190 |
|
191 |
|
192 | function getEventComments(node) {
|
193 | const eventComments = new Set();
|
194 | traverse_1.default(node, {
|
195 | enter(path) {
|
196 | const node = path.node;
|
197 | [...(node.leadingComments || []), ...(node.trailingComments || [])]
|
198 | .map((commentAST) => commentAST.value)
|
199 | .filter((comment) => comment.indexOf('@event') !== -1)
|
200 | .forEach((comment) => eventComments.add(comment));
|
201 | },
|
202 | noScope: true,
|
203 | });
|
204 | const events = [...eventComments]
|
205 | .map((comment) => docs_1.annotateEvent(jsdoc.parseJsdoc(jsdoc.removeLeadingAsterisks(comment).trim())))
|
206 | .filter((ev) => !!ev)
|
207 | .sort((ev1, ev2) => ev1.name.localeCompare(ev2.name));
|
208 | return new Map(events.map((e) => [e.name, e]));
|
209 | }
|
210 | exports.getEventComments = getEventComments;
|
211 | function getLeadingComments(node) {
|
212 | if (!node) {
|
213 | return;
|
214 | }
|
215 | const comments = [];
|
216 | for (const comment of node.leadingComments || []) {
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | if (!node.loc || !comment.loc ||
|
222 | node.loc.start.line - comment.loc.end.line < 2) {
|
223 | comments.push(comment.value);
|
224 | }
|
225 | }
|
226 | return comments.length ? comments : undefined;
|
227 | }
|
228 | function getPropertyValue(node, name) {
|
229 | for (const property of getSimpleObjectProperties(node)) {
|
230 | if (getPropertyName(property) === name) {
|
231 | return property.value;
|
232 | }
|
233 | }
|
234 | }
|
235 | exports.getPropertyValue = getPropertyValue;
|
236 |
|
237 |
|
238 |
|
239 | function toScannedMethod(node, sourceRange, document) {
|
240 | const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(node) || '');
|
241 | const description = parsedJsdoc.description.trim();
|
242 | const maybeName = getPropertyName(node);
|
243 | const warnings = [];
|
244 | if (!maybeName) {
|
245 | warnings.push(new model_1.Warning({
|
246 | code: 'unknown-method-name',
|
247 | message: `Could not determine name of method from expression of type: ` +
|
248 | `${node.key.type}`,
|
249 | sourceRange: sourceRange,
|
250 | severity: model_1.Severity.INFO,
|
251 | parsedDocument: document
|
252 | }));
|
253 | }
|
254 | const value = babel.isObjectProperty(node) ? node.value : node;
|
255 | const result = getClosureType(value, parsedJsdoc, sourceRange, document);
|
256 | const type = result.successful === true ? result.value : 'Function';
|
257 | const name = maybeName || '';
|
258 | const scannedMethod = {
|
259 | name,
|
260 | type,
|
261 | description,
|
262 | sourceRange,
|
263 | warnings,
|
264 | astNode: { language: 'js', node, containingDocument: document },
|
265 | jsdoc: parsedJsdoc,
|
266 | privacy: getOrInferPrivacy(name, parsedJsdoc)
|
267 | };
|
268 | if (value && babel.isFunction(value)) {
|
269 | if (scannedMethod.jsdoc !== undefined) {
|
270 | scannedMethod.return = getReturnFromAnnotation(scannedMethod.jsdoc);
|
271 | }
|
272 | if (scannedMethod.return === undefined) {
|
273 | scannedMethod.return = inferReturnFromBody(value);
|
274 | }
|
275 | scannedMethod.params =
|
276 | (value.params ||
|
277 | []).map((nodeParam) => toMethodParam(nodeParam, scannedMethod.jsdoc));
|
278 | }
|
279 | return scannedMethod;
|
280 | }
|
281 | exports.toScannedMethod = toScannedMethod;
|
282 | function getReturnFromAnnotation(jsdocAnn) {
|
283 | const tag = jsdoc.getTag(jsdocAnn, 'return') || jsdoc.getTag(jsdocAnn, 'returns');
|
284 | if (!tag || (!tag.type && !tag.description)) {
|
285 | return undefined;
|
286 | }
|
287 | const type = {};
|
288 | if (tag && (tag.type || tag.description)) {
|
289 | if (tag.type) {
|
290 | type.type = doctrine.type.stringify(tag.type);
|
291 | }
|
292 | if (tag.description) {
|
293 | type.desc = tag.description;
|
294 | }
|
295 | }
|
296 | return type;
|
297 | }
|
298 | exports.getReturnFromAnnotation = getReturnFromAnnotation;
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
|
304 | function inferReturnFromBody(node) {
|
305 | if (node.async === true || node.generator === true) {
|
306 |
|
307 |
|
308 | return undefined;
|
309 | }
|
310 | if (babel.isArrowFunctionExpression(node) &&
|
311 | !babel.isBlockStatement(node.body)) {
|
312 |
|
313 | return undefined;
|
314 | }
|
315 | let returnsVoid = true;
|
316 | traverse_1.default(node, {
|
317 | ReturnStatement(path) {
|
318 | const statement = path.node;
|
319 |
|
320 |
|
321 | if (statement.argument !== null) {
|
322 | returnsVoid = false;
|
323 | path.stop();
|
324 | }
|
325 | },
|
326 |
|
327 |
|
328 | FunctionDeclaration(path) {
|
329 | path.skip();
|
330 | },
|
331 | FunctionExpression(path) {
|
332 | path.skip();
|
333 | },
|
334 | ClassMethod(path) {
|
335 | path.skip();
|
336 | },
|
337 | ArrowFunctionExpression(path) {
|
338 | path.skip();
|
339 | },
|
340 | ObjectMethod(path) {
|
341 | path.skip();
|
342 | },
|
343 | noScope: true
|
344 | });
|
345 | if (returnsVoid) {
|
346 | return { type: 'void' };
|
347 | }
|
348 | return undefined;
|
349 | }
|
350 | exports.inferReturnFromBody = inferReturnFromBody;
|
351 | function toMethodParam(nodeParam, jsdocAnn) {
|
352 | const paramTags = new Map();
|
353 | let name;
|
354 | let defaultValue;
|
355 | let rest;
|
356 | if (jsdocAnn) {
|
357 | for (const tag of (jsdocAnn.tags || [])) {
|
358 | if (tag.title === 'param' && tag.name) {
|
359 | paramTags.set(tag.name, tag);
|
360 | }
|
361 | }
|
362 | }
|
363 | if (babel.isIdentifier(nodeParam)) {
|
364 |
|
365 | name = nodeParam.name;
|
366 | }
|
367 | else if (babel.isRestElement(nodeParam) &&
|
368 | babel.isIdentifier(nodeParam.argument)) {
|
369 |
|
370 | name = nodeParam.argument.name;
|
371 | rest = true;
|
372 | }
|
373 | else if (babel.isAssignmentPattern(nodeParam) &&
|
374 | babel.isIdentifier(nodeParam.left)) {
|
375 |
|
376 | name = nodeParam.left.name;
|
377 | defaultValue = generator_1.default(nodeParam.right).code;
|
378 | }
|
379 | else {
|
380 |
|
381 |
|
382 | name = generator_1.default(nodeParam).code;
|
383 | }
|
384 | let type;
|
385 | let description;
|
386 | const tag = paramTags.get(name);
|
387 | if (tag) {
|
388 | if (tag.type) {
|
389 | type = doctrine.type.stringify(tag.type);
|
390 | }
|
391 | if (tag.description) {
|
392 | description = tag.description;
|
393 | }
|
394 | }
|
395 | const param = { name, type, defaultValue, rest, description };
|
396 | return param;
|
397 | }
|
398 | exports.toMethodParam = toMethodParam;
|
399 | function getOrInferPrivacy(name, annotation, defaultPrivacy = 'public') {
|
400 | const explicitPrivacy = jsdoc.getPrivacy(annotation);
|
401 | const specificName = name.slice(name.lastIndexOf('.') + 1);
|
402 | if (explicitPrivacy) {
|
403 | return explicitPrivacy;
|
404 | }
|
405 | if (specificName.startsWith('__')) {
|
406 | return 'private';
|
407 | }
|
408 | else if (specificName.startsWith('_')) {
|
409 | return 'protected';
|
410 | }
|
411 | else if (specificName.endsWith('_')) {
|
412 | return 'private';
|
413 | }
|
414 | else if (exports.configurationProperties.has(specificName)) {
|
415 | return 'protected';
|
416 | }
|
417 | return defaultPrivacy;
|
418 | }
|
419 | exports.getOrInferPrivacy = getOrInferPrivacy;
|
420 |
|
421 |
|
422 |
|
423 |
|
424 |
|
425 |
|
426 |
|
427 | exports.configurationProperties = new Set([
|
428 | 'attached',
|
429 | 'attributeChanged',
|
430 | 'beforeRegister',
|
431 | 'configure',
|
432 | 'constructor',
|
433 | 'created',
|
434 | 'detached',
|
435 | 'enableCustomStyleProperties',
|
436 | 'extends',
|
437 | 'hostAttributes',
|
438 | 'is',
|
439 | 'listeners',
|
440 | 'mixins',
|
441 | 'observers',
|
442 | 'properties',
|
443 | 'ready',
|
444 | 'registered',
|
445 | ]);
|
446 |
|
447 |
|
448 |
|
449 | function getMethods(node, document) {
|
450 | const methods = new Map();
|
451 | for (const statement of _getMethods(node)) {
|
452 | if (statement.static === false) {
|
453 | const method = toScannedMethod(statement, document.sourceRangeForNode(statement), document);
|
454 | docs.annotate(method);
|
455 | methods.set(method.name, method);
|
456 | }
|
457 | }
|
458 | return methods;
|
459 | }
|
460 | exports.getMethods = getMethods;
|
461 | function getConstructorMethod(astNode, document) {
|
462 | if (!babel.isClass(astNode)) {
|
463 | return;
|
464 | }
|
465 | const statement = getConstructorClassMethod(astNode);
|
466 | if (statement) {
|
467 | const method = toScannedMethod(statement, document.sourceRangeForNode(statement), document);
|
468 | const typeTag = getReturnFromAnnotation(jsdoc.parseJsdoc(getAttachedComment(statement) || ''));
|
469 | if (typeTag) {
|
470 | method.return = Object.assign({}, method.return, typeTag);
|
471 | }
|
472 | else {
|
473 | method.return = undefined;
|
474 | }
|
475 | return method;
|
476 | }
|
477 | }
|
478 | exports.getConstructorMethod = getConstructorMethod;
|
479 | function getConstructorClassMethod(astNode) {
|
480 | for (const member of astNode.body.body) {
|
481 | if (babel.isClassMethod(member) && member.kind === 'constructor') {
|
482 | return member;
|
483 | }
|
484 | }
|
485 | }
|
486 | exports.getConstructorClassMethod = getConstructorClassMethod;
|
487 |
|
488 |
|
489 |
|
490 |
|
491 | function getStaticMethods(node, document) {
|
492 | const methods = new Map();
|
493 | for (const method of _getMethods(node)) {
|
494 | if (method.static === true) {
|
495 | const scannedMethod = toScannedMethod(method, document.sourceRangeForNode(method), document);
|
496 | docs.annotate(scannedMethod);
|
497 | methods.set(scannedMethod.name, scannedMethod);
|
498 | }
|
499 | }
|
500 | return methods;
|
501 | }
|
502 | exports.getStaticMethods = getStaticMethods;
|
503 | function* _getMethods(node) {
|
504 | if (!babel.isClassDeclaration(node) && !babel.isClassExpression(node)) {
|
505 | return;
|
506 | }
|
507 | for (const statement of node.body.body) {
|
508 | if (babel.isClassMethod(statement) && statement.kind === 'method') {
|
509 | yield statement;
|
510 | }
|
511 | }
|
512 | }
|
513 |
|
514 |
|
515 |
|
516 |
|
517 | function extractPropertyFromGetterOrSetter(method, jsdocAnn, document) {
|
518 |
|
519 | if (babel.isClassMethod(method) && method.static) {
|
520 | return null;
|
521 | }
|
522 | if (method.kind !== 'get' && method.kind !== 'set') {
|
523 | return null;
|
524 | }
|
525 |
|
526 |
|
527 | const name = getPropertyName(method);
|
528 | if (name === undefined) {
|
529 | return null;
|
530 | }
|
531 | let type;
|
532 | let description;
|
533 | let privacy = 'public';
|
534 | let readOnly = false;
|
535 | if (jsdocAnn) {
|
536 | const ret = getReturnFromAnnotation(jsdocAnn);
|
537 | type = ret ? ret.type : undefined;
|
538 | description = jsdoc.getDescription(jsdocAnn);
|
539 | privacy = getOrInferPrivacy(name, jsdocAnn);
|
540 | readOnly = jsdoc.hasTag(jsdocAnn, 'readonly');
|
541 | }
|
542 | return {
|
543 | name,
|
544 | astNode: { language: 'js', node: method, containingDocument: document },
|
545 | type,
|
546 | jsdoc: jsdocAnn,
|
547 | sourceRange: document.sourceRangeForNode(method),
|
548 | description,
|
549 | privacy,
|
550 | warnings: [],
|
551 | readOnly,
|
552 | };
|
553 | }
|
554 | exports.extractPropertyFromGetterOrSetter = extractPropertyFromGetterOrSetter;
|
555 |
|
556 |
|
557 |
|
558 |
|
559 | function extractPropertiesFromClassOrObjectBody(node, document) {
|
560 | const properties = new Map();
|
561 | const accessors = new Map();
|
562 | let body;
|
563 | if (babel.isClass(node)) {
|
564 | body = node.body.body;
|
565 | }
|
566 | else {
|
567 | body = node.properties;
|
568 | }
|
569 | for (const member of body) {
|
570 | if (!babel.isMethod(member) && !babel.isObjectProperty(member)) {
|
571 | continue;
|
572 | }
|
573 | const name = getPropertyName(member);
|
574 | if (name === undefined) {
|
575 | continue;
|
576 | }
|
577 | if (babel.isMethod(member) || babel.isFunction(member.value)) {
|
578 | if (babel.isMethod(member) &&
|
579 | (member.kind === 'get' || member.kind === 'set')) {
|
580 | let accessor = accessors.get(name);
|
581 | if (!accessor) {
|
582 | accessor = {};
|
583 | accessors.set(name, accessor);
|
584 | }
|
585 | if (member.kind === 'get') {
|
586 | accessor.getter = member;
|
587 | }
|
588 | else {
|
589 | accessor.setter = member;
|
590 | }
|
591 | }
|
592 | continue;
|
593 | }
|
594 | const astNode = member.key;
|
595 | const sourceRange = document.sourceRangeForNode(member);
|
596 | const jsdocAnn = jsdoc.parseJsdoc(getAttachedComment(member) || '');
|
597 | const detectedType = getClosureType(member.value, jsdocAnn, sourceRange, document);
|
598 | let type = undefined;
|
599 | if (detectedType.successful) {
|
600 | type = detectedType.value;
|
601 | }
|
602 | properties.set(name, {
|
603 | name,
|
604 | astNode: { language: 'js', node: astNode, containingDocument: document },
|
605 | type,
|
606 | jsdoc: jsdocAnn,
|
607 | sourceRange,
|
608 | description: jsdocAnn ? jsdoc.getDescription(jsdocAnn) : undefined,
|
609 | privacy: getOrInferPrivacy(name, jsdocAnn),
|
610 | warnings: [],
|
611 | readOnly: jsdoc.hasTag(jsdocAnn, 'readonly'),
|
612 | });
|
613 | }
|
614 | for (const val of accessors.values()) {
|
615 | let getter = null;
|
616 | let setter = null;
|
617 | if (val.getter) {
|
618 | const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(val.getter) || '');
|
619 | getter =
|
620 | extractPropertyFromGetterOrSetter(val.getter, parsedJsdoc, document);
|
621 | }
|
622 | if (val.setter) {
|
623 | const parsedJsdoc = jsdoc.parseJsdoc(getAttachedComment(val.setter) || '');
|
624 | setter =
|
625 | extractPropertyFromGetterOrSetter(val.setter, parsedJsdoc, document);
|
626 | }
|
627 | const prop = getter || setter;
|
628 | if (!prop) {
|
629 | continue;
|
630 | }
|
631 | if (!prop.readOnly) {
|
632 | prop.readOnly = (val.setter === undefined);
|
633 | }
|
634 | properties.set(prop.name, prop);
|
635 | }
|
636 | return properties;
|
637 | }
|
638 | exports.extractPropertiesFromClassOrObjectBody = extractPropertiesFromClassOrObjectBody;
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 |
|
645 |
|
646 |
|
647 |
|
648 |
|
649 |
|
650 |
|
651 |
|
652 |
|
653 |
|
654 |
|
655 |
|
656 | function getCanonicalStatement(nodePath) {
|
657 | const node = nodePath.node;
|
658 | const parent = nodePath.parentPath;
|
659 | if ((parent && !isStatementWithUniqueStatementChild(parent.node)) &&
|
660 | babel.isStatement(node)) {
|
661 | return node;
|
662 | }
|
663 | if (parent != null) {
|
664 | return getCanonicalStatement(parent);
|
665 | }
|
666 | return undefined;
|
667 | }
|
668 | exports.getCanonicalStatement = getCanonicalStatement;
|
669 |
|
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 |
|
678 | function isStatementWithUniqueStatementChild(node) {
|
679 | return babel.isExportNamedDeclaration(node) ||
|
680 | babel.isExportDefaultDeclaration(node);
|
681 | }
|
682 |
|
683 | function* getBindingNamesFromDeclaration(declaration) {
|
684 | if (declaration == null) {
|
685 | return;
|
686 | }
|
687 | switch (declaration.type) {
|
688 | case 'ClassDeclaration':
|
689 | case 'DeclareClass':
|
690 | yield declaration.id.name;
|
691 | break;
|
692 | case 'VariableDeclaration':
|
693 | for (const varDecl of declaration.declarations) {
|
694 | yield* getNamesFromLValue(varDecl.id);
|
695 | }
|
696 | break;
|
697 | case 'FunctionDeclaration':
|
698 | case 'DeclareFunction':
|
699 | case 'DeclareInterface':
|
700 | case 'DeclareTypeAlias':
|
701 | case 'InterfaceDeclaration':
|
702 | case 'DeclareVariable':
|
703 | case 'TypeAlias':
|
704 | yield declaration.id.name;
|
705 | break;
|
706 | case 'ExportAllDeclaration':
|
707 |
|
708 | break;
|
709 | case 'ExportDefaultDeclaration':
|
710 | yield 'default';
|
711 | break;
|
712 | case 'ExportNamedDeclaration':
|
713 | for (const specifier of declaration.specifiers) {
|
714 | if (specifier.exported.type === 'Identifier') {
|
715 | yield specifier.exported.name;
|
716 | }
|
717 | }
|
718 | yield* getBindingNamesFromDeclaration(declaration.declaration);
|
719 | break;
|
720 | case 'DeclareModule':
|
721 | if (declaration.id.type === 'StringLiteral') {
|
722 | yield declaration.id.value;
|
723 | }
|
724 | else {
|
725 | yield declaration.id.name;
|
726 | }
|
727 | break;
|
728 | case 'ImportDeclaration':
|
729 | for (const specifier of declaration.specifiers) {
|
730 | yield specifier.local.name;
|
731 | }
|
732 | break;
|
733 | default:
|
734 | assertNever(declaration);
|
735 | }
|
736 | }
|
737 | exports.getBindingNamesFromDeclaration = getBindingNamesFromDeclaration;
|
738 |
|
739 |
|
740 |
|
741 |
|
742 |
|
743 | function* getNamesFromLValue(lhs) {
|
744 | switch (lhs.type) {
|
745 | case 'Identifier':
|
746 |
|
747 | yield lhs.name;
|
748 | break;
|
749 | case 'ArrayPattern':
|
750 |
|
751 | for (const element of lhs.elements) {
|
752 | if (babel.isLVal(element)) {
|
753 | yield* getNamesFromLValue(element);
|
754 | }
|
755 | }
|
756 | break;
|
757 | case 'RestElement':
|
758 |
|
759 |
|
760 |
|
761 | yield* getNamesFromLValue(lhs.argument);
|
762 | break;
|
763 | case 'MemberExpression':
|
764 |
|
765 | const name = astValue.getIdentifierName(lhs);
|
766 | if (name !== undefined) {
|
767 | yield name;
|
768 | }
|
769 | break;
|
770 | case 'ObjectPattern':
|
771 |
|
772 | for (const prop of lhs.properties) {
|
773 | switch (prop.type) {
|
774 | case 'ObjectProperty':
|
775 |
|
776 | yield* getNamesFromLValue(prop.value);
|
777 | break;
|
778 | case 'RestProperty':
|
779 | yield* getNamesFromLValue(prop.argument);
|
780 | break;
|
781 | default:
|
782 | assertNever(prop);
|
783 | }
|
784 | }
|
785 | break;
|
786 | case 'AssignmentPattern':
|
787 |
|
788 | yield* getNamesFromLValue(lhs.left);
|
789 | break;
|
790 | default:
|
791 | assertNever(lhs);
|
792 | }
|
793 | }
|
794 | function assertNever(never) {
|
795 | throw new Error(`Unexpected ast node: ${util.inspect(never)}`);
|
796 | }
|
797 |
|
\ | No newline at end of file |