1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
16 | return new (P || (P = Promise))(function (resolve, reject) {
|
17 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
18 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
19 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
20 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
21 | });
|
22 | };
|
23 | Object.defineProperty(exports, "__esModule", { value: true });
|
24 | const babel = require("@babel/types");
|
25 | const assert = require("assert");
|
26 | const ast_value_1 = require("../javascript/ast-value");
|
27 | const esutil = require("../javascript/esutil");
|
28 | const jsdoc = require("../javascript/jsdoc");
|
29 | const model_1 = require("../model/model");
|
30 | const behavior_1 = require("./behavior");
|
31 | const declaration_property_handlers_1 = require("./declaration-property-handlers");
|
32 | const docs = require("./docs");
|
33 | const templatizer = 'Polymer.Templatizer';
|
34 | class BehaviorScanner {
|
35 | scan(document, visit) {
|
36 | return __awaiter(this, void 0, void 0, function* () {
|
37 | const visitor = new BehaviorVisitor(document);
|
38 | yield visit(visitor);
|
39 | return {
|
40 | features: Array.from(visitor.behaviors),
|
41 | warnings: visitor.warnings
|
42 | };
|
43 | });
|
44 | }
|
45 | }
|
46 | exports.BehaviorScanner = BehaviorScanner;
|
47 | class BehaviorVisitor {
|
48 | constructor(document) {
|
49 |
|
50 | this.behaviors = new Set();
|
51 | this.warnings = [];
|
52 | this.currentBehavior = null;
|
53 | this.propertyHandlers = null;
|
54 | this.document = document;
|
55 | }
|
56 | |
57 |
|
58 |
|
59 | enterVariableDeclarator(node, _parent, path) {
|
60 | this._initBehavior(node, ast_value_1.getIdentifierName(node.id), path);
|
61 | }
|
62 | enterExportDefaultDeclaration(node, _parent, path) {
|
63 | this._initBehavior(node, 'default', path);
|
64 | }
|
65 | |
66 |
|
67 |
|
68 | enterAssignmentExpression(node, parent, path) {
|
69 | this._initBehavior(parent, ast_value_1.getIdentifierName(node.left), path);
|
70 | }
|
71 | |
72 |
|
73 |
|
74 |
|
75 | enterObjectExpression(node, _parent) {
|
76 | if (!this.currentBehavior || !this.propertyHandlers) {
|
77 | return;
|
78 | }
|
79 | for (const prop of esutil.getSimpleObjectProperties(node)) {
|
80 | const name = esutil.getPropertyName(prop);
|
81 | if (!name) {
|
82 | this.currentBehavior.warnings.push(new model_1.Warning({
|
83 | code: 'cant-determine-name',
|
84 | message: `Unable to determine property name from expression of type ` +
|
85 | `${node.type}`,
|
86 | severity: model_1.Severity.WARNING,
|
87 | sourceRange: this.document.sourceRangeForNode(node),
|
88 | parsedDocument: this.document
|
89 | }));
|
90 | continue;
|
91 | }
|
92 | if (name in this.propertyHandlers) {
|
93 | this.propertyHandlers[name](prop.value);
|
94 | }
|
95 | else if ((babel.isMethod(prop) && prop.kind === 'method') ||
|
96 | babel.isFunction(prop.value)) {
|
97 | const method = esutil.toScannedMethod(prop, this.document.sourceRangeForNode(prop), this.document);
|
98 | this.currentBehavior.addMethod(method);
|
99 | }
|
100 | }
|
101 | for (const prop of esutil
|
102 | .extractPropertiesFromClassOrObjectBody(node, this.document)
|
103 | .values()) {
|
104 | if (prop.name in this.propertyHandlers) {
|
105 | continue;
|
106 | }
|
107 | this.currentBehavior.addProperty(Object.assign({}, prop, { isConfiguration: esutil.configurationProperties.has(prop.name) }));
|
108 | }
|
109 | this._finishBehavior();
|
110 | }
|
111 | _startBehavior(behavior) {
|
112 | assert(this.currentBehavior == null);
|
113 | this.currentBehavior = behavior;
|
114 | }
|
115 | _finishBehavior() {
|
116 | assert(this.currentBehavior != null);
|
117 | this.behaviors.add(this.currentBehavior);
|
118 | this.currentBehavior = null;
|
119 | }
|
120 | _initBehavior(node, name, path) {
|
121 | if (name === undefined) {
|
122 | return;
|
123 | }
|
124 | const comment = esutil.getBestComment(path);
|
125 |
|
126 | if (!comment || comment.indexOf('@polymerBehavior') === -1) {
|
127 | if (name !== templatizer) {
|
128 | return;
|
129 | }
|
130 | }
|
131 | const parsedJsdocs = jsdoc.parseJsdoc(comment || '');
|
132 | if (!jsdoc.hasTag(parsedJsdocs, 'polymerBehavior')) {
|
133 | if (name !== templatizer) {
|
134 | return;
|
135 | }
|
136 | }
|
137 | this._startBehavior(new behavior_1.ScannedBehavior({
|
138 | astNode: { language: 'js', node, containingDocument: this.document },
|
139 | statementAst: esutil.getCanonicalStatement(path),
|
140 | description: parsedJsdocs.description,
|
141 | events: esutil.getEventComments(node),
|
142 | sourceRange: this.document.sourceRangeForNode(node),
|
143 | privacy: esutil.getOrInferPrivacy(name, parsedJsdocs),
|
144 | abstract: jsdoc.hasTag(parsedJsdocs, 'abstract'),
|
145 | attributes: new Map(),
|
146 | properties: [],
|
147 | behaviors: [],
|
148 | className: undefined,
|
149 | extends: undefined,
|
150 | jsdoc: parsedJsdocs,
|
151 | listeners: [],
|
152 | methods: new Map(),
|
153 | staticMethods: new Map(),
|
154 | mixins: [],
|
155 | observers: [],
|
156 | superClass: undefined,
|
157 | tagName: undefined,
|
158 | isLegacyFactoryCall: false,
|
159 | }));
|
160 | const behavior = this.currentBehavior;
|
161 | this.propertyHandlers =
|
162 | declaration_property_handlers_1.declarationPropertyHandlers(behavior, this.document, path);
|
163 | docs.annotateElementHeader(behavior);
|
164 | const behaviorTag = jsdoc.getTag(behavior.jsdoc, 'polymerBehavior');
|
165 | behavior.className = behaviorTag && behaviorTag.name ||
|
166 | ast_value_1.getNamespacedIdentifier(name, behavior.jsdoc);
|
167 | if (!behavior.className) {
|
168 | throw new Error(`Unable to determine name for @polymerBehavior: ${comment}`);
|
169 | }
|
170 | behavior.privacy =
|
171 | esutil.getOrInferPrivacy(behavior.className, behavior.jsdoc);
|
172 | this._parseChainedBehaviors(node, path);
|
173 | this.currentBehavior = this.mergeBehavior(behavior);
|
174 | this.propertyHandlers =
|
175 | declaration_property_handlers_1.declarationPropertyHandlers(this.currentBehavior, this.document, path);
|
176 |
|
177 |
|
178 | if (isSimpleBehaviorArray(behaviorExpression(node))) {
|
179 | this._finishBehavior();
|
180 | }
|
181 | }
|
182 | |
183 |
|
184 |
|
185 |
|
186 |
|
187 | mergeBehavior(newBehavior) {
|
188 | const isBehaviorImpl = (b) => {
|
189 |
|
190 | return newBehavior.className === undefined ||
|
191 | b.identifier.indexOf(newBehavior.className) === -1;
|
192 | };
|
193 | for (const behavior of this.behaviors) {
|
194 | if (newBehavior.className !== behavior.className) {
|
195 | continue;
|
196 | }
|
197 |
|
198 |
|
199 | if (newBehavior.description) {
|
200 | if (behavior.description) {
|
201 | if (newBehavior.description.length > behavior.description.length) {
|
202 | behavior.description = newBehavior.description;
|
203 | }
|
204 | }
|
205 | else {
|
206 | behavior.description = newBehavior.description;
|
207 | }
|
208 | }
|
209 |
|
210 | behavior.demos = behavior.demos.concat(newBehavior.demos);
|
211 | for (const [key, val] of newBehavior.events) {
|
212 | behavior.events.set(key, val);
|
213 | }
|
214 | for (const property of newBehavior.properties.values()) {
|
215 | behavior.addProperty(property);
|
216 | }
|
217 | behavior.observers = behavior.observers.concat(newBehavior.observers);
|
218 | behavior.behaviorAssignments =
|
219 | (behavior.behaviorAssignments)
|
220 | .concat(newBehavior.behaviorAssignments)
|
221 | .filter(isBehaviorImpl);
|
222 | return behavior;
|
223 | }
|
224 | return newBehavior;
|
225 | }
|
226 | _parseChainedBehaviors(node, path) {
|
227 | if (this.currentBehavior == null) {
|
228 | throw new Error(`_parsedChainedBehaviors was called without a current behavior.`);
|
229 | }
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 | const expression = behaviorExpression(node);
|
237 | const chained = [];
|
238 | if (expression && babel.isArrayExpression(expression)) {
|
239 | for (const arrElement of expression.elements) {
|
240 | const behaviorName = ast_value_1.getIdentifierName(arrElement);
|
241 | if (behaviorName) {
|
242 | chained.push(new model_1.ScannedReference('behavior', behaviorName, this.document.sourceRangeForNode(arrElement), {
|
243 | language: 'js',
|
244 | node: arrElement,
|
245 | containingDocument: this.document
|
246 | }, path));
|
247 | }
|
248 | }
|
249 | if (chained.length > 0) {
|
250 | this.currentBehavior.behaviorAssignments = chained;
|
251 | }
|
252 | }
|
253 | }
|
254 | }
|
255 |
|
256 |
|
257 |
|
258 | function behaviorExpression(node) {
|
259 | if (babel.isVariableDeclarator(node)) {
|
260 | return node.init;
|
261 | }
|
262 | if (babel.isAssignmentExpression(node)) {
|
263 | return node.right;
|
264 | }
|
265 | if (babel.isExportDefaultDeclaration(node) ||
|
266 | babel.isExportNamedDeclaration(node)) {
|
267 | return behaviorExpression(node.declaration);
|
268 | }
|
269 | if (babel.isExpressionStatement(node)) {
|
270 | return behaviorExpression(node.expression);
|
271 | }
|
272 | if (babel.isVariableDeclaration(node)) {
|
273 | return behaviorExpression(node.declarations[0]);
|
274 | }
|
275 | if (babel.isObjectExpression(node) || babel.isArrayExpression(node)) {
|
276 | return node;
|
277 | }
|
278 | }
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | function isSimpleBehaviorArray(expression) {
|
284 | if (!expression || !babel.isArrayExpression(expression)) {
|
285 | return false;
|
286 | }
|
287 | for (const element of expression.elements) {
|
288 | if (!babel.isMemberExpression(element) && !babel.isIdentifier(element)) {
|
289 | return false;
|
290 | }
|
291 | }
|
292 | return true;
|
293 | }
|
294 |
|
\ | No newline at end of file |