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 dom5 = require("dom5/lib/index-next");
|
17 | const parse5 = require("parse5");
|
18 | const esutil_1 = require("../javascript/esutil");
|
19 | const jsdoc = require("../javascript/jsdoc");
|
20 | const model_1 = require("../model/model");
|
21 | function mergePropertyDeclarations(propA, propB) {
|
22 | if (propA.name !== propB.name) {
|
23 | throw new Error(`Tried to merge properties with different names: ` +
|
24 | `'${propA.name}' and ' ${propB.name}'`);
|
25 | }
|
26 | const name = propA.name;
|
27 | const description = jsdoc.pickBestDescription(propA.description, propB.description);
|
28 | const jsdocAnn = { description: description || '', tags: [] };
|
29 | if (propA.jsdoc) {
|
30 | jsdocAnn.tags.push(...propA.jsdoc.tags);
|
31 | }
|
32 | if (propB.jsdoc) {
|
33 | jsdocAnn.tags.push(...propB.jsdoc.tags);
|
34 | }
|
35 | const privacy = esutil_1.getOrInferPrivacy(propA.name, jsdocAnn);
|
36 | const warnings = [...propA.warnings, ...propB.warnings];
|
37 |
|
38 | const readOnly = propA.readOnly || propB.readOnly;
|
39 |
|
40 | const scannedRegularProperty = {
|
41 |
|
42 | name,
|
43 | privacy,
|
44 | description,
|
45 | warnings,
|
46 | readOnly,
|
47 | jsdoc: jsdocAnn,
|
48 |
|
49 | sourceRange: propA.sourceRange || propB.sourceRange,
|
50 | astNode: propA.astNode || propB.astNode,
|
51 | changeEvent: propA.changeEvent || propB.changeEvent,
|
52 | default: propA.default || propB.default,
|
53 | type: propA.type || propB.type,
|
54 | };
|
55 | const scannedPolymerProperty = scannedRegularProperty;
|
56 |
|
57 | const keys = [
|
58 | 'published',
|
59 | 'notify',
|
60 | 'observer',
|
61 | 'observerNode',
|
62 | 'observerExpression',
|
63 | 'reflectToAttribute',
|
64 | 'computedExpression'
|
65 | ];
|
66 | for (const key of keys) {
|
67 | if (propA[key] || propB[key]) {
|
68 | scannedPolymerProperty[key] = propA[key] || propB[key];
|
69 | }
|
70 | }
|
71 | if (propA.published || propB.published) {
|
72 | scannedPolymerProperty.published = propA.published || propB.published;
|
73 | }
|
74 | return scannedPolymerProperty;
|
75 | }
|
76 | exports.mergePropertyDeclarations = mergePropertyDeclarations;
|
77 | class LocalId {
|
78 | constructor(name, range, nodeName) {
|
79 | this.name = name;
|
80 | this.range = range;
|
81 | this.nodeName = nodeName;
|
82 | }
|
83 | }
|
84 | exports.LocalId = LocalId;
|
85 | function addProperty(target, prop) {
|
86 | const existingProp = target.properties.get(prop.name);
|
87 | if (existingProp) {
|
88 | prop = mergePropertyDeclarations(existingProp, prop);
|
89 | }
|
90 | target.properties.set(prop.name, prop);
|
91 | const attributeName = propertyToAttributeName(prop.name);
|
92 |
|
93 |
|
94 |
|
95 | if ((prop.privacy && prop.privacy !== 'public') || !attributeName ||
|
96 | !prop.published) {
|
97 | return;
|
98 | }
|
99 | target.attributes.set(attributeName, {
|
100 | name: attributeName,
|
101 | sourceRange: prop.sourceRange,
|
102 | description: prop.description,
|
103 | type: prop.type,
|
104 | changeEvent: prop.notify ? `${attributeName}-changed` : undefined
|
105 | });
|
106 | if (prop.notify) {
|
107 | const name = `${attributeName}-changed`;
|
108 | target.events.set(name, {
|
109 | name,
|
110 | description: `Fired when the \`${prop.name}\` property changes.`,
|
111 | sourceRange: prop.sourceRange,
|
112 | astNode: prop.astNode,
|
113 | warnings: [],
|
114 | params: []
|
115 | });
|
116 | }
|
117 | }
|
118 | exports.addProperty = addProperty;
|
119 | function addMethod(target, method) {
|
120 | target.methods.set(method.name, method);
|
121 | }
|
122 | exports.addMethod = addMethod;
|
123 | function addStaticMethod(target, method) {
|
124 | target.staticMethods.set(method.name, method);
|
125 | }
|
126 | exports.addStaticMethod = addStaticMethod;
|
127 |
|
128 |
|
129 |
|
130 | class ScannedPolymerElement extends model_1.ScannedElement {
|
131 | constructor(options) {
|
132 | super();
|
133 | this.properties = new Map();
|
134 | this.methods = new Map();
|
135 | this.staticMethods = new Map();
|
136 | this.observers = [];
|
137 | this.listeners = [];
|
138 | this.behaviorAssignments = [];
|
139 |
|
140 | this.pseudo = false;
|
141 | this.abstract = false;
|
142 | this.tagName = options.tagName;
|
143 | this.className = options.className;
|
144 | this.superClass = options.superClass;
|
145 | this.mixins = options.mixins;
|
146 | this.extends = options.extends;
|
147 | this.jsdoc = options.jsdoc;
|
148 | this.description = options.description || '';
|
149 | this.attributes = options.attributes;
|
150 | this.observers = options.observers;
|
151 | this.listeners = options.listeners;
|
152 | this.behaviorAssignments = options.behaviors;
|
153 | this.events = options.events;
|
154 | this.abstract = options.abstract;
|
155 | this.privacy = options.privacy;
|
156 | this.astNode = options.astNode;
|
157 | this.statementAst = options.statementAst;
|
158 | this.sourceRange = options.sourceRange;
|
159 | this.isLegacyFactoryCall = options.isLegacyFactoryCall || false;
|
160 | if (options.properties) {
|
161 | options.properties.forEach((p) => this.addProperty(p));
|
162 | }
|
163 | if (options.methods) {
|
164 | options.methods.forEach((m) => this.addMethod(m));
|
165 | }
|
166 | if (options.staticMethods) {
|
167 | options.staticMethods.forEach((m) => this.addStaticMethod(m));
|
168 | }
|
169 | const summaryTag = jsdoc.getTag(this.jsdoc, 'summary');
|
170 | this.summary =
|
171 | (summaryTag !== undefined && summaryTag.description != null) ?
|
172 | summaryTag.description :
|
173 | '';
|
174 | }
|
175 | addProperty(prop) {
|
176 | addProperty(this, prop);
|
177 | }
|
178 | addMethod(method) {
|
179 | addMethod(this, method);
|
180 | }
|
181 | addStaticMethod(method) {
|
182 | addStaticMethod(this, method);
|
183 | }
|
184 | resolve(document) {
|
185 | return new PolymerElement(this, document);
|
186 | }
|
187 | }
|
188 | exports.ScannedPolymerElement = ScannedPolymerElement;
|
189 | class PolymerElement extends model_1.Element {
|
190 | constructor(scannedElement, document) {
|
191 | super(scannedElement, document);
|
192 | this.observers = [];
|
193 | this.listeners = [];
|
194 | this.behaviorAssignments = [];
|
195 | this.localIds = [];
|
196 | this.kinds.add('polymer-element');
|
197 | this.observers = Array.from(scannedElement.observers);
|
198 | this.listeners = Array.from(scannedElement.listeners);
|
199 | this.behaviorAssignments = Array.from(scannedElement.behaviorAssignments);
|
200 | const domModules = scannedElement.tagName == null ?
|
201 | new Set() :
|
202 | document.getFeatures({
|
203 | kind: 'dom-module',
|
204 | id: scannedElement.tagName,
|
205 | imported: true,
|
206 | externalPackages: true
|
207 | });
|
208 | let domModule = undefined;
|
209 | if (domModules.size === 1) {
|
210 |
|
211 | domModule = domModules.values().next().value;
|
212 | }
|
213 | if (domModule) {
|
214 | this.domModule = domModule.node;
|
215 | this.slots = this.slots.concat(domModule.slots);
|
216 | this.localIds = domModule.localIds.slice();
|
217 |
|
218 |
|
219 | if (domModule.comment) {
|
220 | const domModuleJsdoc = jsdoc.parseJsdoc(domModule.comment);
|
221 | this.demos = [...jsdoc.extractDemos(domModuleJsdoc), ...this.demos];
|
222 | if (domModuleJsdoc.description) {
|
223 | this.description =
|
224 | (domModuleJsdoc.description + '\n\n' + this.description).trim();
|
225 | }
|
226 | }
|
227 | const template = dom5.query(domModule.node, dom5.predicates.hasTagName('template'));
|
228 | if (template) {
|
229 | this.template = {
|
230 | kind: 'polymer-databinding',
|
231 | contents: parse5.treeAdapters.default.getTemplateContent(template)
|
232 | };
|
233 | }
|
234 | }
|
235 | if (scannedElement.pseudo) {
|
236 | this.kinds.add('pseudo-element');
|
237 | }
|
238 | this.isLegacyFactoryCall = scannedElement.isLegacyFactoryCall;
|
239 | }
|
240 | emitPropertyMetadata(property) {
|
241 | return {
|
242 | polymer: {
|
243 | notify: property.notify,
|
244 | observer: property.observer,
|
245 | readOnly: property.readOnly,
|
246 | attributeType: property.attributeType,
|
247 | }
|
248 | };
|
249 | }
|
250 | _getSuperclassAndMixins(document, init) {
|
251 | const prototypeChain = super._getSuperclassAndMixins(document, init);
|
252 | const { warnings, behaviors } = getBehaviors(init.behaviorAssignments, document);
|
253 | this.warnings.push(...warnings);
|
254 | prototypeChain.push(...behaviors);
|
255 | return prototypeChain;
|
256 | }
|
257 | }
|
258 | exports.PolymerElement = PolymerElement;
|
259 |
|
260 |
|
261 |
|
262 |
|
263 |
|
264 | function propertyToAttributeName(propertyName) {
|
265 |
|
266 |
|
267 | if (propertyName[0].toUpperCase() === propertyName[0]) {
|
268 | return null;
|
269 | }
|
270 | return propertyName.replace(/([A-Z])/g, (_, c1) => `-${c1.toLowerCase()}`);
|
271 | }
|
272 | function getBehaviors(behaviorReferences, document) {
|
273 | const warnings = [];
|
274 | const behaviors = [];
|
275 | for (const scannedReference of behaviorReferences) {
|
276 | const resolvedReference = scannedReference.resolve(document);
|
277 | if (resolvedReference.warnings.length > 0) {
|
278 | warnings.push(...resolvedReference.warnings);
|
279 | }
|
280 | if (resolvedReference.feature) {
|
281 | behaviors.push(resolvedReference.feature);
|
282 | }
|
283 | }
|
284 | return { warnings, behaviors };
|
285 | }
|
286 | exports.getBehaviors = getBehaviors;
|
287 |
|
\ | No newline at end of file |