polymer-analyzer
Version:
Static analysis for Web Components
195 lines (193 loc) • 8.43 kB
JavaScript
"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 ast_value_1 = require("../javascript/ast-value");
const esutil = require("../javascript/esutil");
const esutil_1 = require("../javascript/esutil");
const jsdoc = require("../javascript/jsdoc");
const polymer_element_mixin_1 = require("./polymer-element-mixin");
const polymer2_config_1 = require("./polymer2-config");
class MixinVisitor {
constructor(document) {
this.mixins = [];
this._currentMixin = null;
this._currentMixinNode = null;
this._currentMixinFunction = null;
this.warnings = [];
this._document = document;
}
enterAssignmentExpression(node, parent) {
if (parent.type !== 'ExpressionStatement') {
return;
}
const parentComments = esutil.getAttachedComment(parent) || '';
const parentJsDocs = jsdoc.parseJsdoc(parentComments);
if (hasMixinFunctionDocTag(parentJsDocs)) {
const name = ast_value_1.getIdentifierName(node.left);
const namespacedName = name ? ast_value_1.getNamespacedIdentifier(name, parentJsDocs) : undefined;
const sourceRange = this._document.sourceRangeForNode(node);
const summaryTag = jsdoc.getTag(parentJsDocs, 'summary');
if (namespacedName) {
this._currentMixin = new polymer_element_mixin_1.ScannedPolymerElementMixin({
name: namespacedName,
sourceRange,
astNode: node,
description: parentJsDocs.description,
summary: (summaryTag && summaryTag.description) || '',
privacy: esutil_1.getOrInferPrivacy(namespacedName, parentJsDocs),
jsdoc: parentJsDocs,
mixins: jsdoc.getMixinApplications(this._document, node, parentJsDocs, this.warnings),
});
this._currentMixinNode = node;
this.mixins.push(this._currentMixin);
}
else {
// TODO(rictic): warn for a mixin whose name we can't determine.
}
}
}
enterFunctionDeclaration(node, _parent) {
const nodeComments = esutil.getAttachedComment(node) || '';
const nodeJsDocs = jsdoc.parseJsdoc(nodeComments);
if (hasMixinFunctionDocTag(nodeJsDocs)) {
const name = node.id.name;
const namespacedName = name ? ast_value_1.getNamespacedIdentifier(name, nodeJsDocs) : undefined;
const sourceRange = this._document.sourceRangeForNode(node);
this._currentMixinFunction = node;
const summaryTag = jsdoc.getTag(nodeJsDocs, 'summary');
if (namespacedName) {
this._currentMixin = new polymer_element_mixin_1.ScannedPolymerElementMixin({
name: namespacedName,
sourceRange,
astNode: node,
description: nodeJsDocs.description,
summary: (summaryTag && summaryTag.description) || '',
privacy: esutil_1.getOrInferPrivacy(namespacedName, nodeJsDocs),
jsdoc: nodeJsDocs,
mixins: jsdoc.getMixinApplications(this._document, node, nodeJsDocs, this.warnings)
});
this._currentMixinNode = node;
this.mixins.push(this._currentMixin);
}
else {
// Warn about a mixin whose name we can't infer.
}
}
}
leaveFunctionDeclaration(node, _parent) {
if (this._currentMixinNode === node) {
this._currentMixin = null;
this._currentMixinNode = null;
this._currentMixinFunction = null;
}
}
enterVariableDeclaration(node, _parent) {
const comment = esutil.getAttachedComment(node) || '';
const docs = jsdoc.parseJsdoc(comment);
const isMixin = hasMixinFunctionDocTag(docs);
const sourceRange = this._document.sourceRangeForNode(node);
const summaryTag = jsdoc.getTag(docs, 'summary');
if (isMixin) {
let mixin = undefined;
if (node.declarations.length === 1) {
const declaration = node.declarations[0];
const name = ast_value_1.getIdentifierName(declaration.id);
if (name) {
const namespacedName = ast_value_1.getNamespacedIdentifier(name, docs);
mixin = new polymer_element_mixin_1.ScannedPolymerElementMixin({
name: namespacedName,
sourceRange,
astNode: node,
description: docs.description,
summary: (summaryTag && summaryTag.description) || '',
privacy: esutil_1.getOrInferPrivacy(namespacedName, docs),
jsdoc: docs,
mixins: jsdoc.getMixinApplications(this._document, declaration, docs, this.warnings)
});
}
}
if (mixin) {
this._currentMixin = mixin;
this._currentMixinNode = node;
this.mixins.push(this._currentMixin);
}
else {
// TODO(rictic); warn about being unable to determine mixin name.
}
}
}
leaveVariableDeclaration(node, _parent) {
if (this._currentMixinNode === node) {
this._currentMixin = null;
this._currentMixinNode = null;
this._currentMixinFunction = null;
}
}
enterFunctionExpression(node, _parent) {
if (this._currentMixin != null && this._currentMixinFunction == null) {
this._currentMixinFunction = node;
}
}
enterArrowFunctionExpression(node, _parent) {
if (this._currentMixin != null && this._currentMixinFunction == null) {
this._currentMixinFunction = node;
}
}
enterClassExpression(node, parent) {
if (parent.type !== 'ReturnStatement' &&
parent.type !== 'ArrowFunctionExpression') {
return;
}
this._handleClass(node);
}
enterClassDeclaration(node, _parent) {
const comment = esutil.getAttachedComment(node) || '';
const docs = jsdoc.parseJsdoc(comment);
const isMixinClass = hasMixinClassDocTag(docs);
if (isMixinClass) {
this._handleClass(node);
}
}
_handleClass(node) {
const mixin = this._currentMixin;
if (mixin == null) {
return;
}
mixin.classAstNode = node;
polymer2_config_1.getPolymerProperties(node, this._document)
.forEach((p) => mixin.addProperty(p));
esutil_1.getMethods(node, this._document).forEach((m) => mixin.addMethod(m));
esutil_1.getStaticMethods(node, this._document)
.forEach((m) => mixin.staticMethods.set(m.name, m));
mixin.events = esutil.getEventComments(node);
// mixin.sourceRange = this._document.sourceRangeForNode(node);
return mixin;
}
}
exports.MixinVisitor = MixinVisitor;
function hasMixinFunctionDocTag(docs) {
// TODO(justinfagnani): remove polymerMixin support
return (jsdoc.hasTag(docs, 'polymer') &&
jsdoc.hasTag(docs, 'mixinFunction')) ||
jsdoc.hasTag(docs, 'polymerMixin');
}
exports.hasMixinFunctionDocTag = hasMixinFunctionDocTag;
function hasMixinClassDocTag(docs) {
// TODO(justinfagnani): remove polymerMixinClass support
return (jsdoc.hasTag(docs, 'polymer') && jsdoc.hasTag(docs, 'mixinClass')) ||
jsdoc.hasTag(docs, 'polymerMixinClass');
}
exports.hasMixinClassDocTag = hasMixinClassDocTag;
//# sourceMappingURL=polymer2-mixin-scanner.js.map