UNPKG

10.5 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 jsdocLib = require("../javascript/jsdoc");
17const model_1 = require("../model/model");
18const immutable_1 = require("./immutable");
19/**
20 * Represents a JS class as encountered in source code.
21 *
22 * We only emit a ScannedClass when there's not a more specific kind of feature.
23 * Like, we don't emit a ScannedClass when we encounter an element or a mixin
24 * (though in the future those features will likely extend from
25 * ScannedClass/Class).
26 *
27 * TODO(rictic): currently there's a ton of duplicated code across the Class,
28 * Element, Mixin, PolymerElement, and PolymerMixin classes. We should
29 * really unify this stuff to a single representation and set of algorithms.
30 */
31class ScannedClass {
32 constructor(className, localClassName, astNode, statementAst, jsdoc, description, sourceRange, properties, methods, constructorMethod, staticMethods, superClass, mixins, privacy, warnings, abstract, demos) {
33 this.name = className;
34 this.localName = localClassName;
35 this.astNode = astNode;
36 this.statementAst = statementAst;
37 this.jsdoc = jsdoc;
38 this.description = description;
39 this.sourceRange = sourceRange;
40 this.properties = properties;
41 this.methods = methods;
42 this.constructorMethod = constructorMethod;
43 this.staticMethods = staticMethods;
44 this.superClass = superClass;
45 this.mixins = mixins;
46 this.privacy = privacy;
47 this.warnings = warnings;
48 this.abstract = abstract;
49 const summaryTag = jsdocLib.getTag(jsdoc, 'summary');
50 this.summary = (summaryTag && summaryTag.description) || '';
51 this.demos = demos;
52 }
53 resolve(document) {
54 return new Class(this, document);
55 }
56 /**
57 * Allows additional properties and methods
58 * to be added to the class after initialization.
59 * For example, members found attached to the
60 * prototype at a later place in the document
61 */
62 finishInitialization(methods, properties) {
63 const mutableMethods = immutable_1.unsafeAsMutable(this.methods);
64 for (const [name, method] of methods) {
65 mutableMethods.set(name, method);
66 }
67 for (const [name, prop] of properties) {
68 this.properties.set(name, prop);
69 }
70 }
71}
72exports.ScannedClass = ScannedClass;
73class Class {
74 constructor(init, document) {
75 this.kinds = new Set(['class']);
76 this.identifiers = new Set();
77 this.properties = new Map();
78 this.methods = new Map();
79 this.staticMethods = new Map();
80 /**
81 * Mixins that this class declares with `@mixes`.
82 *
83 * Mixins are applied linearly after the superclass, in order from first
84 * to last. Mixins that compose other mixins will be flattened into a
85 * single list. A mixin can be applied more than once, each time its
86 * members override those before it in the prototype chain.
87 */
88 this.mixins = [];
89 ({
90 jsdoc: this.jsdoc,
91 description: this.description,
92 summary: this.summary,
93 abstract: this.abstract,
94 privacy: this.privacy,
95 astNode: this.astNode,
96 statementAst: this.statementAst,
97 sourceRange: this.sourceRange
98 } = init);
99 this._parsedDocument = document.parsedDocument;
100 this.warnings =
101 init.warnings === undefined ? [] : Array.from(init.warnings);
102 this.demos = [...init.demos || [], ...jsdocLib.extractDemos(init.jsdoc)];
103 this.name = init.name || init.className;
104 if (this.name) {
105 this.identifiers.add(this.name);
106 }
107 if (init.superClass) {
108 this.superClass = init.superClass;
109 }
110 this.mixins = (init.mixins || []);
111 this.constructorMethod = init.constructorMethod;
112 const superClassLikes = this._getSuperclassAndMixins(document, init);
113 for (const superClassLike of superClassLikes) {
114 this.inheritFrom(superClassLike);
115 }
116 if (init.properties !== undefined) {
117 this._overwriteInherited(this.properties, init.properties, undefined, true);
118 }
119 if (init.methods !== undefined) {
120 this._overwriteInherited(this.methods, init.methods, undefined, true);
121 }
122 if (init.constructorMethod !== undefined) {
123 this.constructorMethod = this._overwriteSingleInherited(this.constructorMethod, init.constructorMethod, undefined);
124 }
125 if (init.staticMethods !== undefined) {
126 this._overwriteInherited(this.staticMethods, init.staticMethods, undefined, true);
127 }
128 }
129 /**
130 * @deprecated use the `name` field instead.
131 */
132 get className() {
133 return this.name;
134 }
135 inheritFrom(superClass) {
136 this._overwriteInherited(this.staticMethods, superClass.staticMethods, superClass.name);
137 this._overwriteInherited(this.properties, superClass.properties, superClass.name);
138 this._overwriteInherited(this.methods, superClass.methods, superClass.name);
139 this.constructorMethod = this._overwriteSingleInherited(this.constructorMethod, superClass.constructorMethod, superClass.name);
140 }
141 /**
142 * This method is applied to an array of members to overwrite members lower in
143 * the prototype graph (closer to Object) with members higher up (closer to
144 * the final class we're constructing).
145 *
146 * @param . existing The array of members so far. N.B. *This param is
147 * mutated.*
148 * @param . overriding The array of members from this new, higher prototype in
149 * the graph
150 * @param . overridingClassName The name of the prototype whose members are
151 * being applied over the existing ones. Should be `undefined` when
152 * applyingSelf is true
153 * @param . applyingSelf True on the last call to this method, when we're
154 * applying the class's own local members.
155 */
156 _overwriteInherited(existing, overriding, overridingClassName, applyingSelf = false) {
157 for (const [key, overridingVal] of overriding) {
158 const newVal = Object.assign({}, overridingVal, {
159 inheritedFrom: overridingVal['inheritedFrom'] || overridingClassName
160 });
161 if (existing.has(key)) {
162 /**
163 * TODO(rictic): if existingVal.privacy is protected, newVal should be
164 * protected unless an explicit privacy was specified.
165 * https://github.com/Polymer/polymer-analyzer/issues/631
166 */
167 const existingValue = existing.get(key);
168 if (existingValue.privacy === 'private') {
169 let warningSourceRange = this.sourceRange;
170 if (applyingSelf) {
171 warningSourceRange = newVal.sourceRange || this.sourceRange;
172 }
173 this.warnings.push(new model_1.Warning({
174 code: 'overriding-private',
175 message: `Overriding private member '${overridingVal.name}' ` +
176 `inherited from ${existingValue.inheritedFrom || 'parent'}`,
177 sourceRange: warningSourceRange,
178 severity: model_1.Severity.WARNING,
179 parsedDocument: this._parsedDocument,
180 }));
181 }
182 }
183 existing.set(key, newVal);
184 }
185 }
186 /**
187 * This method is applied to a single member to overwrite members lower in
188 * the prototype graph (closer to Object) with members higher up (closer to
189 * the final class we're constructing).
190 *
191 * @param . existing The existin property on the class
192 * @param . overriding The array of members from this new, higher prototype in
193 * the graph
194 * @param . overridingClassName The name of the prototype whose members are
195 * being applied over the existing ones. Should be `undefined` when
196 * applyingSelf is true
197 * @param . applyingSelf True on the last call to this method, when we're
198 * applying the class's own local members.
199 */
200 _overwriteSingleInherited(existing, overridingVal, overridingClassName) {
201 if (!overridingVal) {
202 return existing;
203 }
204 return Object.assign({}, overridingVal, {
205 inheritedFrom: overridingVal['inheritedFrom'] || overridingClassName
206 });
207 }
208 /**
209 * Returns the elementLikes that make up this class's prototype chain.
210 *
211 * Should return them in the order that they're constructed in JS
212 * engine (i.e. closest to HTMLElement first, closest to `this` last).
213 */
214 _getSuperclassAndMixins(document, _init) {
215 const mixins = this.mixins.map((m) => this._resolveReferenceToSuperClass(m, document));
216 const superClass = this._resolveReferenceToSuperClass(this.superClass, document);
217 const prototypeChain = [];
218 if (superClass) {
219 prototypeChain.push(superClass);
220 }
221 for (const mixin of mixins) {
222 if (mixin) {
223 prototypeChain.push(mixin);
224 }
225 }
226 return prototypeChain;
227 }
228 _resolveReferenceToSuperClass(scannedReference, document) {
229 if (!scannedReference || scannedReference.identifier === 'HTMLElement') {
230 return undefined;
231 }
232 const reference = scannedReference.resolve(document);
233 if (reference.warnings.length > 0) {
234 this.warnings.push(...reference.warnings);
235 }
236 return reference.feature;
237 }
238 emitMetadata() {
239 return {};
240 }
241 emitPropertyMetadata(_property) {
242 return {};
243 }
244 emitMethodMetadata(_method) {
245 return {};
246 }
247}
248exports.Class = Class;
249//# sourceMappingURL=class.js.map
\No newline at end of file