UNPKG

polymer-analyzer

Version:
221 lines (219 loc) 8.28 kB
"use strict"; /** * @license * Copyright (c) 2015 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at * http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at * http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ Object.defineProperty(exports, "__esModule", { value: true }); const jsdoc = require("../javascript/jsdoc"); const model_1 = require("../model/model"); class LocalId { constructor(name, range) { this.name = name; this.range = range; } } exports.LocalId = LocalId; function addProperty(target, prop) { target.properties.set(prop.name, prop); const attributeName = propertyToAttributeName(prop.name); // Don't produce attributes or events for nonpublic properties, properties // that aren't in Polymer's `properties` block (i.e. not published), // or properties whose names can't be converted into attribute names. if ((prop.privacy && prop.privacy !== 'public') || !attributeName || !prop.published) { return; } target.attributes.set(attributeName, { name: attributeName, sourceRange: prop.sourceRange, description: prop.description, type: prop.type, changeEvent: prop.notify ? `${attributeName}-changed` : undefined }); if (prop.notify) { const name = `${attributeName}-changed`; target.events.set(name, { name, description: `Fired when the \`${prop.name}\` property changes.`, sourceRange: prop.sourceRange, astNode: prop.astNode, warnings: [], params: [] }); } } exports.addProperty = addProperty; function addMethod(target, method) { target.methods.set(method.name, method); } exports.addMethod = addMethod; /** * The metadata for a single polymer element */ class ScannedPolymerElement extends model_1.ScannedElement { constructor(options) { super(); this.properties = new Map(); this.methods = new Map(); this.observers = []; this.listeners = []; this.behaviorAssignments = []; // Indicates if an element is a pseudo element this.pseudo = false; this.abstract = false; this.tagName = options.tagName; this.className = options.className; this.superClass = options.superClass; this.mixins = options.mixins; this.extends = options.extends; this.jsdoc = options.jsdoc; this.description = options.description || ''; this.attributes = options.attributes; this.observers = options.observers; this.listeners = options.listeners; this.behaviorAssignments = options.behaviors; this.events = options.events; this.abstract = options.abstract; this.privacy = options.privacy; this.astNode = options.astNode; this.sourceRange = options.sourceRange; if (options.properties) { options.properties.forEach((p) => this.addProperty(p)); } if (options.methods) { options.methods.forEach((m) => this.addMethod(m)); } const summaryTag = jsdoc.getTag(this.jsdoc, 'summary'); this.summary = (summaryTag !== undefined && summaryTag.description != null) ? summaryTag.description : ''; } addProperty(prop) { addProperty(this, prop); } addMethod(method) { addMethod(this, method); } resolve(document) { return new PolymerElement(this, document); } } exports.ScannedPolymerElement = ScannedPolymerElement; class PolymerElement extends model_1.Element { constructor(scannedElement, document) { super(scannedElement, document); this.observers = []; this.listeners = []; this.behaviorAssignments = []; this.localIds = []; this.kinds.add('polymer-element'); this.observers = Array.from(scannedElement.observers); this.listeners = Array.from(scannedElement.listeners); this.behaviorAssignments = Array.from(scannedElement.behaviorAssignments); this.scriptElement = scannedElement.scriptElement; const domModules = scannedElement.tagName == null ? new Set() : document.getFeatures({ kind: 'dom-module', id: scannedElement.tagName, imported: true, externalPackages: true }); let domModule = undefined; if (domModules.size === 1) { // TODO(rictic): warn if this isn't true. domModule = domModules.values().next().value; } if (domModule) { this.description = this.description || domModule.comment || ''; this.domModule = domModule.node; this.slots = this.slots.concat(domModule.slots); this.localIds = domModule.localIds.slice(); } if (scannedElement.pseudo) { this.kinds.add('pseudo-element'); } } emitPropertyMetadata(property) { const polymerMetadata = {}; const polymerMetadataFields = ['notify', 'observer', 'readOnly']; for (const field of polymerMetadataFields) { if (field in property) { polymerMetadata[field] = property[field]; } } return { polymer: polymerMetadata }; } _getSuperclassAndMixins(document, init) { const prototypeChain = super._getSuperclassAndMixins(document, init); const { warnings, behaviors } = getBehaviors(init.behaviorAssignments, document); this.warnings.push(...warnings); prototypeChain.push(...behaviors); return prototypeChain; } } exports.PolymerElement = PolymerElement; /** * Implements Polymer core's translation of property names to attribute names. * * Returns null if the property name cannot be so converted. */ function propertyToAttributeName(propertyName) { // Polymer core will not map a property name that starts with an uppercase // character onto an attribute. if (propertyName[0].toUpperCase() === propertyName[0]) { return null; } return propertyName.replace(/([A-Z])/g, (_, c1) => `-${c1.toLowerCase()}`); } function getBehaviors(behaviorAssignments, document) { const warnings = []; const behaviors = []; for (const behavior of behaviorAssignments) { const foundBehaviors = document.getFeatures({ kind: 'behavior', id: behavior.name, imported: true, externalPackages: true }); if (foundBehaviors.size === 0) { warnings.push(new model_1.Warning({ message: `Unable to resolve behavior ` + `\`${behavior.name}\`. Did you import it? Is it annotated with ` + `@polymerBehavior?`, severity: model_1.Severity.WARNING, code: 'unknown-polymer-behavior', sourceRange: behavior.sourceRange, parsedDocument: document.parsedDocument })); // Skip processing this behavior. continue; } if (foundBehaviors.size > 1) { warnings.push(new model_1.Warning({ message: `Found more than one behavior named ${behavior.name}.`, severity: model_1.Severity.WARNING, code: 'multiple-polymer-behaviors', sourceRange: behavior.sourceRange, parsedDocument: document.parsedDocument })); // Don't skip processing this behavior, just take the most recently // declared instance. } const foundBehavior = Array.from(foundBehaviors)[foundBehaviors.size - 1]; behaviors.push(foundBehavior); } return { warnings, behaviors }; } exports.getBehaviors = getBehaviors; //# sourceMappingURL=polymer-element.js.map