UNPKG

6.64 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt
7 * The complete set of authors may be found at
8 * http://polymer.github.io/AUTHORS.txt
9 * The complete set of contributors may be found at
10 * http://polymer.github.io/CONTRIBUTORS.txt
11 * Code distributed by Google as part of the polymer project is also
12 * subject to an additional IP rights grant found at
13 * http://polymer.github.io/PATENTS.txt
14 */
15Object.defineProperty(exports, "__esModule", { value: true });
16const babel = require("@babel/types");
17const ast_value_1 = require("../javascript/ast-value");
18const class_scanner_1 = require("../javascript/class-scanner");
19const esutil = require("../javascript/esutil");
20const esutil_1 = require("../javascript/esutil");
21const jsdoc = require("../javascript/jsdoc");
22const polymer_element_mixin_1 = require("./polymer-element-mixin");
23const polymer2_config_1 = require("./polymer2-config");
24class MixinVisitor {
25 constructor(document, prototypeMemberFinder) {
26 this.mixins = [];
27 this._currentMixin = null;
28 this._currentMixinNode = null;
29 this._currentMixinFunction = null;
30 this.warnings = [];
31 this._document = document;
32 this._prototypeMemberFinder = prototypeMemberFinder;
33 }
34 enterAssignmentExpression(node, _parent, path) {
35 this.tryInitializeMixin(path, node.left);
36 }
37 enterFunctionDeclaration(node, _parent, path) {
38 this.tryInitializeMixin(path, node.id);
39 }
40 leaveFunctionDeclaration(node, _parent) {
41 this.clearOnLeave(node);
42 }
43 enterVariableDeclaration(node, _parent, path) {
44 this.tryInitializeMixin(path, node.declarations[0].id);
45 }
46 leaveVariableDeclaration(node, _parent) {
47 this.clearOnLeave(node);
48 }
49 tryInitializeMixin(nodePath, nameNode) {
50 const comment = esutil.getBestComment(nodePath);
51 if (comment === undefined) {
52 return;
53 }
54 const docs = jsdoc.parseJsdoc(comment);
55 if (!hasMixinFunctionDocTag(docs)) {
56 return;
57 }
58 const node = nodePath.node;
59 const name = ast_value_1.getIdentifierName(nameNode);
60 const namespacedName = name ? ast_value_1.getNamespacedIdentifier(name, docs) : undefined;
61 if (namespacedName === undefined) {
62 // TODO(#903): Warn about a mixin whose name we can't infer?
63 return;
64 }
65 const sourceRange = this._document.sourceRangeForNode(node);
66 const summaryTag = jsdoc.getTag(docs, 'summary');
67 const mixin = new polymer_element_mixin_1.ScannedPolymerElementMixin({
68 name: namespacedName,
69 sourceRange,
70 astNode: { language: 'js', node, containingDocument: this._document },
71 statementAst: esutil.getCanonicalStatement(nodePath),
72 description: docs.description,
73 summary: (summaryTag && summaryTag.description) || '',
74 privacy: esutil_1.getOrInferPrivacy(namespacedName, docs),
75 jsdoc: docs,
76 mixins: jsdoc.getMixinApplications(this._document, node, docs, this.warnings, nodePath)
77 });
78 this._currentMixin = mixin;
79 this._currentMixinNode = node;
80 this.mixins.push(this._currentMixin);
81 }
82 clearOnLeave(node) {
83 if (this._currentMixinNode === node) {
84 this._currentMixin = null;
85 this._currentMixinNode = null;
86 this._currentMixinFunction = null;
87 }
88 }
89 enterFunctionExpression(node, _parent) {
90 if (this._currentMixin != null && this._currentMixinFunction == null) {
91 this._currentMixinFunction = node;
92 }
93 }
94 enterArrowFunctionExpression(node, _parent) {
95 if (this._currentMixin != null && this._currentMixinFunction == null) {
96 this._currentMixinFunction = node;
97 }
98 }
99 enterClassExpression(node, parent) {
100 if (!babel.isReturnStatement(parent) &&
101 !babel.isArrowFunctionExpression(parent)) {
102 return;
103 }
104 this._handleClass(node);
105 }
106 enterClassDeclaration(node, _parent) {
107 const comment = esutil.getAttachedComment(node) || '';
108 const docs = jsdoc.parseJsdoc(comment);
109 const isMixinClass = hasMixinClassDocTag(docs);
110 if (isMixinClass) {
111 this._handleClass(node);
112 }
113 }
114 _handleClass(node) {
115 const mixin = this._currentMixin;
116 if (mixin == null) {
117 return;
118 }
119 mixin.classAstNode = node;
120 const classProperties = class_scanner_1.extractPropertiesFromClass(node, this._document);
121 for (const prop of classProperties.values()) {
122 mixin.addProperty(prop);
123 }
124 polymer2_config_1.getPolymerProperties(node, this._document)
125 .forEach((p) => mixin.addProperty(p));
126 esutil_1.getMethods(node, this._document).forEach((m) => mixin.addMethod(m));
127 esutil_1.getStaticMethods(node, this._document)
128 .forEach((m) => mixin.staticMethods.set(m.name, m));
129 mixin.events = esutil.getEventComments(node);
130 // mixin.sourceRange = this._document.sourceRangeForNode(node);
131 // Also add members that were described like:
132 // /** @type {string} */
133 // MixinClass.prototype.foo;
134 const name = ast_value_1.getIdentifierName(node.id);
135 if (name !== undefined) {
136 const prototypeMembers = this._prototypeMemberFinder.members.get(name);
137 if (prototypeMembers !== undefined) {
138 for (const [, property] of prototypeMembers.properties) {
139 mixin.addProperty(property);
140 }
141 for (const [, method] of prototypeMembers.methods) {
142 mixin.addMethod(method);
143 }
144 }
145 }
146 }
147}
148exports.MixinVisitor = MixinVisitor;
149function hasMixinFunctionDocTag(docs) {
150 // TODO(justinfagnani): remove polymerMixin support
151 return (jsdoc.hasTag(docs, 'polymer') &&
152 jsdoc.hasTag(docs, 'mixinFunction')) ||
153 jsdoc.hasTag(docs, 'polymerMixin');
154}
155exports.hasMixinFunctionDocTag = hasMixinFunctionDocTag;
156function hasMixinClassDocTag(docs) {
157 // TODO(justinfagnani): remove polymerMixinClass support
158 return (jsdoc.hasTag(docs, 'polymer') && jsdoc.hasTag(docs, 'mixinClass')) ||
159 jsdoc.hasTag(docs, 'polymerMixinClass');
160}
161exports.hasMixinClassDocTag = hasMixinClassDocTag;
162//# sourceMappingURL=polymer2-mixin-scanner.js.map
\No newline at end of file