Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hydrolysis

Package Overview
Dependencies
Maintainers
4
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hydrolysis - npm Package Compare versions

Comparing version 1.22.0 to 1.23.3

hydrolysis-analyzer.html

30

index.js

@@ -10,5 +10,5 @@ /**

*/
/*jslint node: true */
'use strict';
require("babel-polyfill");
/**

@@ -18,14 +18,14 @@ * Static analysis for Polymer.

*/
module.exports = {
Analyzer: require('./lib/analyzer'),
docs: require('./lib/ast-utils/docs'),
FSResolver: require('./lib/loader/fs-resolver'),
jsdoc: require('./lib/ast-utils/jsdoc'),
Loader: require('./lib/loader/file-loader'),
NoopResolver: require('./lib/loader/noop-resolver'),
RedirectResolver: require('./lib/loader/redirect-resolver'),
XHRResolver: require('./lib/loader/xhr-resolver'),
StringResolver: require('./lib/loader/string-resolver'),
_jsParse: require('./lib/ast-utils/js-parse'),
_importParse: require('./lib/ast-utils/import-parse'),
};
exports.Analyzer = require('./lib/analyzer').Analyzer;
exports.FSResolver = require('./lib/loader/fs-resolver').FSResolver;
exports.Loader = require('./lib/loader/file-loader').FileLoader;
exports.NoopResolver = require('./lib/loader/noop-resolver').NoopResolver;
exports.RedirectResolver =
require('./lib/loader/redirect-resolver').RedirectResolver;
exports.XHRResolver = require('./lib/loader/xhr-resolver').XHRResolver;
exports.StringResolver = require('./lib/loader/string-resolver').StringResolver;
exports._jsParse = require('./lib/ast-utils/js-parse').jsParse;
exports._importParse = require('./lib/ast-utils/import-parse').importParse;
exports.docs = require('./lib/ast-utils/docs');
exports.jsdoc = require('./lib/ast-utils/jsdoc');

@@ -10,765 +10,878 @@ /**

*/
// jshint node: true
'use strict';
// jshint -W079
var Promise = global.Promise || require('es6-promise').Promise;
// jshint +W079
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var __awaiter = undefined && undefined.__awaiter || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : new P(function (resolve) {
resolve(result.value);
}).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments)).next());
});
};
var dom5 = require('dom5');
var url = require('url');
var docs = require('./ast-utils/docs');
var FileLoader = require('./loader/file-loader');
var importParse = require('./ast-utils/import-parse');
var jsParse = require('./ast-utils/js-parse');
var NoopResolver = require('./loader/noop-resolver');
var StringResolver = require('./loader/string-resolver');
var file_loader_1 = require('./loader/file-loader');
var import_parse_1 = require('./ast-utils/import-parse');
var js_parse_1 = require('./ast-utils/js-parse');
var noop_resolver_1 = require('./loader/noop-resolver');
var string_resolver_1 = require('./loader/string-resolver');
var fs_resolver_1 = require('./loader/fs-resolver');
var xhr_resolver_1 = require('./loader/xhr-resolver');
var error_swallowing_fs_resolver_1 = require('./loader/error-swallowing-fs-resolver');
function reduceMetadata(m1, m2) {
return {
elements: m1.elements.concat(m2.elements),
features: m1.features.concat(m2.features),
behaviors: m1.behaviors.concat(m2.behaviors),
};
return {
elements: m1.elements.concat(m2.elements),
features: m1.features.concat(m2.features),
behaviors: m1.behaviors.concat(m2.behaviors)
};
}
var EMPTY_METADATA = {elements: [], features: [], behaviors: []};
var EMPTY_METADATA = { elements: [], features: [], behaviors: [] };
/**
* Parse5's representation of a parsed html document
* @typedef {Object} DocumentAST
* @memberof hydrolysis
* A database of Polymer metadata defined in HTML
*/
/**
* espree's representation of a parsed html document
* @typedef {Object} JSAST
* @memberof hydrolysis
*/
var Analyzer = function () {
/**
* @param {boolean} attachAST If true, attach a parse5 compliant AST
* @param {FileLoader=} loader An optional `FileLoader` used to load external
* resources
*/
/**
* Package of a parsed JS script
* @typedef {Object} ParsedJS
* @property {JSAST} ast The script's AST
* @property {DocumentAST} scriptElement If inline, the script's containing tag.
* @memberof hydrolysis
*/
function Analyzer(attachAST, loader) {
_classCallCheck(this, Analyzer);
/**
* The metadata for a single polymer element
* @typedef {Object} ElementDescriptor
* @memberof hydrolysis
*/
/**
* A list of all elements the `Analyzer` has metadata for.
*/
this.elements = [];
/**
* A view into `elements`, keyed by tag name.
*/
this.elementsByTagName = {};
/**
* A list of API features added to `Polymer.Base` encountered by the
* analyzer.
*/
this.features = [];
/**
* The behaviors collected by the analysis pass.
*/
this.behaviors = [];
/**
* The behaviors collected by the analysis pass by name.
*/
this.behaviorsByName = {};
/**
* A map, keyed by absolute path, of Document metadata.
*/
this.html = {};
/**
* A map, keyed by path, of HTML document ASTs.
*/
this.parsedDocuments = {};
/**
* A map, keyed by path, of JS script ASTs.
*
* If the path is an HTML file with multiple scripts,
* the entry will be an array of scripts.
*/
this.parsedScripts = {};
/**
* A map, keyed by path, of document content.
*/
this._content = {};
this.loader = loader;
}
/**
* The metadata for a Polymer feature.
* @typedef {Object} FeatureDescriptor
* @memberof hydrolysis
*/
_createClass(Analyzer, [{
key: 'load',
value: function load(href) {
var _this = this;
/**
* The metadata for a Polymer behavior mixin.
* @typedef {Object} BehaviorDescriptor
* @memberof hydrolysis
*/
return this.loader.request(href).then(function (content) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
_this._content[href] = content;
resolve(_this._parseHTML(content, href));
}, 0);
}).catch(function (err) {
console.error("Error processing document at " + href);
throw err;
});
});
}
}, {
key: '_parseHTML',
/**
* The metadata for all features and elements defined in one document
* @typedef {Object} DocumentDescriptor
* @memberof hydrolysis
* @property {Array<ElementDescriptor>} elements The elements from the document
* @property {Array<FeatureDescriptor>} features The features from the document
* @property {Array<FeatureDescriptor>} behaviors The behaviors from the document
*/
/**
* Returns an `AnalyzedDocument` representing the provided document
* @private
* @param {string} htmlImport Raw text of an HTML document
* @param {string} href The document's URL.
* @return {AnalyzedDocument} An `AnalyzedDocument`
*/
value: function _parseHTML(htmlImport, href) {
var _this2 = this;
/**
* The metadata of an entire HTML document, in promises.
* @typedef {Object} AnalyzedDocument
* @memberof hydrolysis
* @property {string} href The url of the document.
* @property {Promise<ParsedImport>} htmlLoaded The parsed representation of
* the doc. Use the `ast` property to get the full `parse5` ast
*
* @property {Promise<Array<string>>} depsLoaded Resolves to the list of this
* Document's transitive import dependencies
*
* @property {Array<string>} depHrefs The direct dependencies of the document.
*
* @property {Promise<DocumentDescriptor>} metadataLoaded Resolves to the list of
* this Document's import dependencies
*/
if (href in this.html) {
return this.html[href];
}
var depsLoaded = [];
var depHrefs = [];
var metadataLoaded = Promise.resolve(EMPTY_METADATA);
var parsed;
try {
parsed = import_parse_1.importParse(htmlImport, href);
} catch (err) {
console.error('Error parsing!');
throw err;
}
var htmlLoaded = Promise.resolve(parsed);
if (parsed.script) {
metadataLoaded = this._processScripts(parsed.script, href);
}
var commentText = parsed.comment.map(function (comment) {
return dom5.getTextContent(comment);
});
var pseudoElements = docs.parsePseudoElements(commentText);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
/**
* A database of Polymer metadata defined in HTML
*
* @constructor
* @memberOf hydrolysis
* @param {boolean} attachAST If true, attach a parse5 compliant AST
* @param {FileLoader=} loader An optional `FileLoader` used to load external
* resources
*/
var Analyzer = function Analyzer(attachAST,
loader) {
this.loader = loader;
try {
for (var _iterator = pseudoElements[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var element = _step.value;
/**
* A list of all elements the `Analyzer` has metadata for.
* @member {Array.<ElementDescriptor>}
*/
this.elements = [];
element.contentHref = href;
this.elements.push(element);
this.elementsByTagName[element.is] = element;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
/**
* A view into `elements`, keyed by tag name.
* @member {Object.<string,ElementDescriptor>}
*/
this.elementsByTagName = {};
metadataLoaded = metadataLoaded.then(function (metadata) {
var metadataEntry = {
elements: pseudoElements,
features: [],
behaviors: []
};
return [metadata, metadataEntry].reduce(reduceMetadata);
});
depsLoaded.push(metadataLoaded);
if (this.loader) {
var baseUri = href;
if (parsed.base.length > 1) {
console.error("Only one base tag per document!");
throw "Multiple base tags in " + href;
} else if (parsed.base.length == 1) {
var baseHref = dom5.getAttribute(parsed.base[0], "href");
if (baseHref) {
baseHref = baseHref + "/";
baseUri = url.resolve(baseUri, baseHref);
}
}
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
/**
* A list of API features added to `Polymer.Base` encountered by the
* analyzer.
* @member {Array<FeatureDescriptor>}
*/
this.features = [];
try {
for (var _iterator2 = parsed.import[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var link = _step2.value;
/**
* The behaviors collected by the analysis pass.
*
* @member {Array<BehaviorDescriptor>}
*/
this.behaviors = [];
var linkurl = dom5.getAttribute(link, 'href');
if (linkurl) {
var resolvedUrl = url.resolve(baseUri, linkurl);
depHrefs.push(resolvedUrl);
depsLoaded.push(this._dependenciesLoadedFor(resolvedUrl, href));
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
/**
* The behaviors collected by the analysis pass by name.
*
* @member {Object<string,BehaviorDescriptor>}
*/
this.behaviorsByName = {};
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
/**
* A map, keyed by absolute path, of Document metadata.
* @member {Object<string,AnalyzedDocument>}
*/
this.html = {};
try {
for (var _iterator3 = parsed.style[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var styleElement = _step3.value;
/**
* A map, keyed by path, of HTML document ASTs.
* @type {Object}
*/
this.parsedDocuments = {};
if (polymerExternalStyle(styleElement)) {
var styleHref = dom5.getAttribute(styleElement, 'href');
if (href) {
styleHref = url.resolve(baseUri, styleHref);
depsLoaded.push(this.loader.request(styleHref).then(function (content) {
_this2._content[styleHref] = content;
return {};
}));
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
var depsStrLoaded = Promise.all(depsLoaded).then(function () {
return depHrefs;
}).catch(function (err) {
throw err;
});
this.parsedDocuments[href] = parsed.ast;
this.html[href] = {
href: href,
htmlLoaded: htmlLoaded,
metadataLoaded: metadataLoaded,
depHrefs: depHrefs,
depsLoaded: depsStrLoaded
};
return this.html[href];
}
}, {
key: '_processScripts',
value: function _processScripts(scripts, href) {
var _this3 = this;
/**
* A map, keyed by path, of JS script ASTs.
*
* If the path is an HTML file with multiple scripts, the entry will be an array of scripts.
*
* @type {Object<string,Array<ParsedJS>>}
*/
this.parsedScripts = {};
var scriptPromises = [];
scripts.forEach(function (script) {
scriptPromises.push(_this3._processScript(script, href));
});
return Promise.all(scriptPromises).then(function (metadataList) {
// TODO(ajo) remove this cast.
var list = metadataList;
return list.reduce(reduceMetadata, EMPTY_METADATA);
});
}
}, {
key: '_processScript',
value: function _processScript(script, href) {
var _this4 = this;
var src = dom5.getAttribute(script, 'src');
var parsedJs;
if (!src) {
try {
parsedJs = js_parse_1.jsParse(script.childNodes.length ? script.childNodes[0].value : '');
} catch (err) {
// Figure out the correct line number for the error.
var line = 0;
var col = 0;
if (script.__ownerDocument && script.__ownerDocument == href) {
line = script.__locationDetail.line - 1;
col = script.__locationDetail.column - 1;
}
line += err.lineNumber;
col += err.column;
var message = "Error parsing script in " + href + " at " + line + ":" + col;
message += "\n" + err.stack;
var fixedErr = new Error(message);
fixedErr.location = { line: line, column: col };
fixedErr.ownerDocument = script.__ownerDocument;
return Promise.reject(fixedErr);
}
if (parsedJs.elements) {
parsedJs.elements.forEach(function (element) {
element.scriptElement = script;
element.contentHref = href;
_this4.elements.push(element);
if (element.is in _this4.elementsByTagName) {
console.warn('Ignoring duplicate element definition: ' + element.is);
} else {
_this4.elementsByTagName[element.is] = element;
}
});
}
if (parsedJs.features) {
parsedJs.features.forEach(function (feature) {
feature.contentHref = href;
feature.scriptElement = script;
});
this.features = this.features.concat(parsedJs.features);
}
if (parsedJs.behaviors) {
parsedJs.behaviors.forEach(function (behavior) {
behavior.contentHref = href;
_this4.behaviorsByName[behavior.is] = behavior;
_this4.behaviorsByName[behavior.symbol] = behavior;
});
this.behaviors = this.behaviors.concat(parsedJs.behaviors);
}
if (!Object.hasOwnProperty.call(this.parsedScripts, href)) {
this.parsedScripts[href] = [];
}
var scriptElement;
if (script.__ownerDocument && script.__ownerDocument == href) {
scriptElement = script;
}
this.parsedScripts[href].push({
ast: parsedJs.parsedScript,
scriptElement: scriptElement
});
return Promise.resolve(parsedJs);
}
if (this.loader) {
var resolvedSrc = url.resolve(href, src);
return this.loader.request(resolvedSrc).then(function (content) {
_this4._content[resolvedSrc] = content;
var scriptText = dom5.constructors.text(content);
dom5.append(script, scriptText);
dom5.removeAttribute(script, 'src');
script.__hydrolysisInlined = src;
return _this4._processScript(script, resolvedSrc);
}).catch(function (err) {
throw err;
});
} else {
return Promise.resolve(EMPTY_METADATA);
}
}
}, {
key: '_dependenciesLoadedFor',
value: function _dependenciesLoadedFor(href, root) {
var _this5 = this;
var found = {};
if (root !== undefined) {
found[root] = true;
}
return this._getDependencies(href, found).then(function (deps) {
var depPromises = deps.map(function (depHref) {
return _this5.load(depHref).then(function (htmlMonomer) {
return htmlMonomer.metadataLoaded;
});
});
return Promise.all(depPromises);
});
}
}, {
key: '_getDependencies',
/**
* A map, keyed by path, of document content.
* @type {Object}
*/
this._content = {};
};
/**
* List all the html dependencies for the document at `href`.
* @param {string} href The href to get dependencies for.
* @param {Object.<string,boolean>=} found An object keyed by URL of the
* already resolved dependencies.
* @param {boolean=} transitive Whether to load transitive
* dependencies. Defaults to true.
* @return {Array.<string>} A list of all the html dependencies.
*/
value: function _getDependencies(href, found, transitive) {
var _this6 = this;
/**
* Options for `Analyzer.analzye`
* @typedef {Object} LoadOptions
* @memberof hydrolysis
* @property {boolean} noAnnotations Whether `annotate()` should be skipped.
* @property {String=} content Content to resolve `href` to instead of loading
* from the file system.
* @property {boolean} clean Whether the generated descriptors should be cleaned
* of redundant data.
* @property {string=} resolver.
* `xhr` to use XMLHttpRequest
* `fs` to use the local filesystem.
* `permissive` to use the local filesystem and return empty files when a
* path can't be found.
* Default is `fs` in node and `xhr` in the browser.
* @property {function(string): boolean} filter A predicate function that
* indicates which files should be ignored by the loader. By default all
* files not located under the dirname of `href` will be ignored.
*/
if (found === undefined) {
found = {};
found[href] = true;
}
if (transitive === undefined) {
transitive = true;
}
var deps = [];
return this.load(href).then(function (htmlMonomer) {
var transitiveDeps = [];
htmlMonomer.depHrefs.forEach(function (depHref) {
if (found[depHref]) {
return;
}
deps.push(depHref);
found[depHref] = true;
if (transitive) {
transitiveDeps.push(_this6._getDependencies(depHref, found));
}
});
return Promise.all(transitiveDeps);
}).then(function (transitiveDeps) {
var alldeps = transitiveDeps.reduce(function (a, b) {
return a.concat(b);
}, []).concat(deps);
return alldeps;
});
}
}, {
key: 'elementsForFolder',
/**
* Shorthand for transitively loading and processing all imports beginning at
* `href`.
*
* In order to properly filter paths, `href` _must_ be an absolute URI.
*
* @param {string} href The root import to begin loading from.
* @param {LoadOptions=} options Any additional options for the load.
* @return {Promise<Analyzer>} A promise that will resolve once `href` and its
* dependencies have been loaded and analyzed.
*/
Analyzer.analyze = function analyze(href, options) {
options = options || {};
options.filter = options.filter || _defaultFilter(href);
/**
* Returns the elements defined in the folder containing `href`.
* @param {string} href path to search.
*/
value: function elementsForFolder(href) {
return this.elements.filter(function (element) {
return matchesDocumentFolder(element, href);
});
}
}, {
key: 'behaviorsForFolder',
var loader = new FileLoader();
/**
* Returns the behaviors defined in the folder containing `href`.
* @param {string} href path to search.
* @return {Array.<BehaviorDescriptor>}
*/
value: function behaviorsForFolder(href) {
return this.behaviors.filter(function (behavior) {
return matchesDocumentFolder(behavior, href);
});
}
}, {
key: 'metadataTree',
var resolver = options.resolver;
if (resolver === undefined) {
if (typeof window === 'undefined') {
resolver = 'fs';
} else {
resolver = 'xhr';
}
}
var PrimaryResolver;
if (resolver === 'fs') {
PrimaryResolver = require('./loader/fs-resolver');
} else if (resolver === 'xhr') {
PrimaryResolver = require('./loader/xhr-resolver');
} else if (resolver === 'permissive') {
PrimaryResolver = require('./loader/error-swallowing-fs-resolver');
} else {
throw new Error("Resolver must be one of 'fs' or 'xhr'");
}
/**
* Returns a promise that resolves to a POJO representation of the import
* tree, in a format that maintains the ordering of the HTML imports spec.
* @param {string} href the import to get metadata for.
* @return {Promise}
*/
value: function metadataTree(href) {
var _this7 = this;
loader.addResolver(new PrimaryResolver(options));
if (options.content) {
loader.addResolver(new StringResolver({url: href, content: options.content}));
}
loader.addResolver(new NoopResolver({test: options.filter}));
return this.load(href).then(function (monomer) {
var loadedHrefs = {};
loadedHrefs[href] = true;
return _this7._metadataTree(monomer, loadedHrefs);
});
}
}, {
key: '_metadataTree',
value: function _metadataTree(htmlMonomer, loadedHrefs) {
return __awaiter(this, void 0, void 0, regeneratorRuntime.mark(function _callee() {
var metadata, hrefs, depMetadata, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, href, metadataPromise;
var analyzer = new this(null, loader);
return analyzer.metadataTree(href).then(function(root) {
if (!options.noAnnotations) {
analyzer.annotate();
}
if (options.clean) {
analyzer.clean();
}
return Promise.resolve(analyzer);
});
};
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
if (loadedHrefs === undefined) {
loadedHrefs = {};
}
_context.next = 3;
return htmlMonomer.metadataLoaded;
/**
* @private
* @param {string} href
* @return {function(string): boolean}
*/
function _defaultFilter(href) {
// Everything up to the last `/` or `\`.
var base = href.match(/^(.*?)[^\/\\]*$/)[1];
return function(uri) {
return uri.indexOf(base) !== 0;
};
}
case 3:
metadata = _context.sent;
Analyzer.prototype.load = function load(href) {
return this.loader.request(href).then(function(content) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
this._content[href] = content;
resolve(this._parseHTML(content, href));
}.bind(this), 0);
}.bind(this)).catch(function(err){
console.error("Error processing document at " + href);
throw err;
});
}.bind(this));
};
metadata = {
elements: metadata.elements,
features: metadata.features,
behaviors: [],
href: htmlMonomer.href
};
_context.next = 7;
return htmlMonomer.depsLoaded;
/**
* Returns an `AnalyzedDocument` representing the provided document
* @private
* @param {string} htmlImport Raw text of an HTML document
* @param {string} href The document's URL.
* @return {AnalyzedDocument} An `AnalyzedDocument`
*/
Analyzer.prototype._parseHTML = function _parseHTML(htmlImport,
href) {
if (href in this.html) {
return this.html[href];
}
var depsLoaded = [];
var depHrefs = [];
var metadataLoaded = Promise.resolve(EMPTY_METADATA);
var parsed;
try {
parsed = importParse(htmlImport, href);
} catch (err) {
console.error('Error parsing!');
throw err;
}
var htmlLoaded = Promise.resolve(parsed);
if (parsed.script) {
metadataLoaded = this._processScripts(parsed.script, href);
}
var commentText = parsed.comment.map(function(comment){
return dom5.getTextContent(comment);
});
var pseudoElements = docs.parsePseudoElements(commentText);
pseudoElements.forEach(function(element){
element.contentHref = href;
this.elements.push(element);
this.elementsByTagName[element.is] = element;
}.bind(this));
metadataLoaded = metadataLoaded.then(function(metadata){
var metadataEntry = {
elements: pseudoElements,
features: [],
behaviors: []
};
return [metadata, metadataEntry].reduce(reduceMetadata);
});
depsLoaded.push(metadataLoaded);
case 7:
hrefs = _context.sent;
depMetadata = [];
_iteratorNormalCompletion4 = true;
_didIteratorError4 = false;
_iteratorError4 = undefined;
_context.prev = 12;
_iterator4 = hrefs[Symbol.iterator]();
case 14:
if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
_context.next = 29;
break;
}
if (this.loader) {
var baseUri = href;
if (parsed.base.length > 1) {
console.error("Only one base tag per document!");
throw "Multiple base tags in " + href;
} else if (parsed.base.length == 1) {
var baseHref = dom5.getAttribute(parsed.base[0], "href");
if (baseHref) {
baseHref = baseHref + "/";
baseUri = url.resolve(baseUri, baseHref);
}
}
parsed.import.forEach(function(link) {
var linkurl = dom5.getAttribute(link, 'href');
if (linkurl) {
var resolvedUrl = url.resolve(baseUri, linkurl);
depHrefs.push(resolvedUrl);
depsLoaded.push(this._dependenciesLoadedFor(resolvedUrl, href));
}
}.bind(this));
parsed.style.forEach(function(styleElement) {
if (polymerExternalStyle(styleElement)) {
var styleHref = dom5.getAttribute(styleElement, 'href');
if (href) {
styleHref = url.resolve(baseUri, styleHref);
depsLoaded.push(this.loader.request(styleHref).then(function(content){
this._content[styleHref] = content;
}.bind(this)));
}
}
}.bind(this));
}
depsLoaded = Promise.all(depsLoaded)
.then(function() {return depHrefs;})
.catch(function(err) {throw err;});
this.parsedDocuments[href] = parsed.ast;
this.html[href] = {
href: href,
htmlLoaded: htmlLoaded,
metadataLoaded: metadataLoaded,
depHrefs: depHrefs,
depsLoaded: depsLoaded
};
return this.html[href];
};
href = _step4.value;
metadataPromise = undefined;
Analyzer.prototype._processScripts = function _processScripts(scripts, href) {
var scriptPromises = [];
scripts.forEach(function(script) {
scriptPromises.push(this._processScript(script, href));
}.bind(this));
return Promise.all(scriptPromises).then(function(metadataList) {
return metadataList.reduce(reduceMetadata, EMPTY_METADATA);
});
};
if (loadedHrefs[href]) {
_context.next = 24;
break;
}
Analyzer.prototype._processScript = function _processScript(script, href) {
var src = dom5.getAttribute(script, 'src');
var parsedJs;
if (!src) {
try {
parsedJs = jsParse((script.childNodes.length) ? script.childNodes[0].value : '');
} catch (err) {
// Figure out the correct line number for the error.
var line = 0;
var col = 0;
if (script.__ownerDocument && script.__ownerDocument == href) {
line = script.__locationDetail.line - 1;
col = script.__locationDetail.column - 1;
}
line += err.lineNumber;
col += err.column;
var message = "Error parsing script in " + href + " at " + line + ":" + col;
message += "\n" + err.stack;
var fixedErr = new Error(message);
fixedErr.location = {line: line, column: col};
fixedErr.ownerDocument = script.__ownerDocument;
return Promise.reject(fixedErr);
}
if (parsedJs.elements) {
parsedJs.elements.forEach(function(element) {
element.scriptElement = script;
element.contentHref = href;
this.elements.push(element);
if (element.is in this.elementsByTagName) {
console.warn('Ignoring duplicate element definition: ' + element.is);
} else {
this.elementsByTagName[element.is] = element;
}
}.bind(this));
}
if (parsedJs.features) {
parsedJs.features.forEach(function(feature){
feature.contentHref = href;
feature.scriptElement = script;
});
this.features = this.features.concat(parsedJs.features);
}
if (parsedJs.behaviors) {
parsedJs.behaviors.forEach(function(behavior){
behavior.contentHref = href;
this.behaviorsByName[behavior.is] = behavior;
this.behaviorsByName[behavior.symbol] = behavior;
}.bind(this));
this.behaviors = this.behaviors.concat(parsedJs.behaviors);
}
if (!Object.hasOwnProperty.call(this.parsedScripts, href)) {
this.parsedScripts[href] = [];
}
var scriptElement;
if (script.__ownerDocument && script.__ownerDocument == href) {
scriptElement = script;
}
this.parsedScripts[href].push({
ast: parsedJs.parsedScript,
scriptElement: scriptElement
});
return parsedJs;
}
if (this.loader) {
var resolvedSrc = url.resolve(href, src);
return this.loader.request(resolvedSrc).then(function(content) {
this._content[resolvedSrc] = content;
var resolvedScript = Object.create(script);
resolvedScript.childNodes = [{value: content}];
resolvedScript.attrs = resolvedScript.attrs.slice();
dom5.removeAttribute(resolvedScript, 'src');
return this._processScript(resolvedScript, resolvedSrc);
}.bind(this)).catch(function(err) {throw err;});
} else {
return Promise.resolve(EMPTY_METADATA);
}
};
loadedHrefs[href] = true;
metadataPromise = this._metadataTree(this.html[href], loadedHrefs);
_context.next = 22;
return metadataPromise;
Analyzer.prototype._dependenciesLoadedFor = function _dependenciesLoadedFor(href, root) {
var found = {};
if (root !== undefined) {
found[root] = true;
}
return this._getDependencies(href, found).then(function(deps) {
var depMetadataLoaded = [];
var depPromises = deps.map(function(depHref){
return this.load(depHref).then(function(htmlMonomer) {
return htmlMonomer.metadataLoaded;
});
}.bind(this));
return Promise.all(depPromises);
}.bind(this));
};
case 22:
_context.next = 25;
break;
/**
* List all the html dependencies for the document at `href`.
* @param {string} href The href to get dependencies for.
* @param {Object.<string,boolean>=} found An object keyed by URL of the
* already resolved dependencies.
* @param {boolean=} transitive Whether to load transitive
* dependencies. Defaults to true.
* @return {Array.<string>} A list of all the html dependencies.
*/
Analyzer.prototype._getDependencies = function _getDependencies(href, found, transitive) {
if (found === undefined) {
found = {};
found[href] = true;
}
if (transitive === undefined) {
transitive = true;
}
var deps = [];
return this.load(href).then(function(htmlMonomer) {
var transitiveDeps = [];
htmlMonomer.depHrefs.forEach(function(depHref){
if (found[depHref]) {
return;
}
deps.push(depHref);
found[depHref] = true;
if (transitive) {
transitiveDeps.push(this._getDependencies(depHref, found));
}
}.bind(this));
return Promise.all(transitiveDeps);
}.bind(this)).then(function(transitiveDeps) {
var alldeps = transitiveDeps.reduce(function(a, b) {
return a.concat(b);
}, []).concat(deps);
return alldeps;
});
};
case 24:
metadataPromise = Promise.resolve({});
function matchesDocumentFolder(descriptor, href) {
if (!descriptor.contentHref) {
return false;
}
var descriptorDoc = url.parse(descriptor.contentHref);
if (!descriptorDoc || !descriptorDoc.pathname) {
return false;
}
var searchDoc = url.parse(href);
if (!searchDoc || !searchDoc.pathname) {
return false;
}
var searchPath = searchDoc.pathname;
var lastSlash = searchPath.lastIndexOf("/");
if (lastSlash > 0) {
searchPath = searchPath.slice(0, lastSlash);
}
return descriptorDoc.pathname.indexOf(searchPath) === 0;
}
case 25:
depMetadata.push(metadataPromise);
/**
* Returns the elements defined in the folder containing `href`.
* @param {string} href path to search.
* @return {Array.<ElementDescriptor>}
*/
Analyzer.prototype.elementsForFolder = function elementsForFolder(href) {
return this.elements.filter(function(element){
return matchesDocumentFolder(element, href);
});
};
case 26:
_iteratorNormalCompletion4 = true;
_context.next = 14;
break;
/**
* Returns the behaviors defined in the folder containing `href`.
* @param {string} href path to search.
* @return {Array.<BehaviorDescriptor>}
*/
Analyzer.prototype.behaviorsForFolder = function behaviorsForFolder(href) {
return this.behaviors.filter(function(behavior){
return matchesDocumentFolder(behavior, href);
});
};
case 29:
_context.next = 35;
break;
/**
* Returns a promise that resolves to a POJO representation of the import
* tree, in a format that maintains the ordering of the HTML imports spec.
* @param {string} href the import to get metadata for.
* @return {Promise}
*/
Analyzer.prototype.metadataTree = function metadataTree(href) {
return this.load(href).then(function(monomer){
var loadedHrefs = {};
loadedHrefs[href] = true;
return this._metadataTree(monomer, loadedHrefs);
}.bind(this));
};
case 31:
_context.prev = 31;
_context.t0 = _context['catch'](12);
_didIteratorError4 = true;
_iteratorError4 = _context.t0;
Analyzer.prototype._metadataTree = function _metadataTree(htmlMonomer,
loadedHrefs) {
if (loadedHrefs === undefined) {
loadedHrefs = {};
}
return htmlMonomer.metadataLoaded.then(function(metadata) {
metadata = {
elements: metadata.elements,
features: metadata.features,
href: htmlMonomer.href
};
return htmlMonomer.depsLoaded.then(function(hrefs) {
var depMetadata = [];
hrefs.forEach(function(href) {
var metadataPromise = Promise.resolve(true);
if (depMetadata.length > 0) {
metadataPromise = depMetadata[depMetadata.length - 1];
case 35:
_context.prev = 35;
_context.prev = 36;
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
case 38:
_context.prev = 38;
if (!_didIteratorError4) {
_context.next = 41;
break;
}
throw _iteratorError4;
case 41:
return _context.finish(38);
case 42:
return _context.finish(35);
case 43:
return _context.abrupt('return', Promise.all(depMetadata).then(function (importMetadata) {
// TODO(ajo): remove this when tsc stops having issues.
metadata.imports = importMetadata;
return htmlMonomer.htmlLoaded.then(function (parsedHtml) {
metadata.html = parsedHtml;
if (metadata.elements) {
metadata.elements.forEach(function (element) {
attachDomModule(parsedHtml, element);
});
}
return metadata;
});
}));
case 44:
case 'end':
return _context.stop();
}
}
}, _callee, this, [[12, 31, 35, 43], [36,, 38, 42]]);
}));
}
metadataPromise = metadataPromise.then(function() {
if (!loadedHrefs[href]) {
loadedHrefs[href] = true;
return this._metadataTree(this.html[href], loadedHrefs);
} else {
return Promise.resolve({});
}
}.bind(this));
depMetadata.push(metadataPromise);
}.bind(this));
return Promise.all(depMetadata).then(function(importMetadata) {
metadata.imports = importMetadata;
return htmlMonomer.htmlLoaded.then(function(parsedHtml) {
metadata.html = parsedHtml;
if (metadata.elements) {
metadata.elements.forEach(function(element) {
attachDomModule(parsedHtml, element);
}, {
key: '_inlineStyles',
value: function _inlineStyles(ast, href) {
var _this8 = this;
var cssLinks = dom5.queryAll(ast, polymerExternalStyle);
cssLinks.forEach(function (link) {
var linkHref = dom5.getAttribute(link, 'href');
var uri = url.resolve(href, linkHref);
var content = _this8._content[uri];
var style = dom5.constructors.element('style');
dom5.setTextContent(style, '\n' + content + '\n');
dom5.replace(link, style);
});
}
return metadata;
});
});
}.bind(this));
}.bind(this));
};
return cssLinks.length > 0;
}
}, {
key: '_inlineScripts',
value: function _inlineScripts(ast, href) {
var _this9 = this;
function matchingImport(importElement) {
var matchesTag = dom5.predicates.hasTagName(importElement.tagName);
var matchesHref = dom5.predicates.hasAttrValue('href', dom5.getAttribute(importElement, 'href'));
var matchesRel = dom5.predicates.hasAttrValue('rel', dom5.getAttribute(importElement, 'rel'));
return dom5.predicates.AND(matchesTag, matchesHref, matchesRel);
}
var scripts = dom5.queryAll(ast, externalScript);
scripts.forEach(function (script) {
var scriptHref = dom5.getAttribute(script, 'src');
var uri = url.resolve(href, scriptHref);
var content = _this9._content[uri];
var inlined = dom5.constructors.element('script');
dom5.setTextContent(inlined, '\n' + content + '\n');
dom5.replace(script, inlined);
});
return scripts.length > 0;
}
}, {
key: '_inlineImports',
value: function _inlineImports(ast, href, loaded) {
var _this10 = this;
// TODO(ajo): Refactor out of vulcanize into dom5.
var polymerExternalStyle = dom5.predicates.AND(
dom5.predicates.hasTagName('link'),
dom5.predicates.hasAttrValue('rel', 'import'),
dom5.predicates.hasAttrValue('type', 'css')
);
var imports = dom5.queryAll(ast, isHtmlImportNode);
imports.forEach(function (htmlImport) {
var importHref = dom5.getAttribute(htmlImport, 'href');
var uri = url.resolve(href, importHref);
if (loaded[uri]) {
dom5.remove(htmlImport);
return;
}
var content = _this10.getLoadedAst(uri, loaded);
dom5.replace(htmlImport, content);
});
return imports.length > 0;
}
}, {
key: 'getLoadedAst',
var externalScript = dom5.predicates.AND(
dom5.predicates.hasTagName('script'),
dom5.predicates.hasAttr('src')
);
/**
* Returns a promise resolving to a form of the AST with all links replaced
* with the document they link to. .css and .script files become &lt;style&gt; and
* &lt;script&gt;, respectively.
*
* The elements in the loaded document are unmodified from their original
* documents.
*
* @param {string} href The document to load.
* @param {Object.<string,boolean>=} loaded An object keyed by already loaded documents.
* @return {Promise.<DocumentAST>}
*/
value: function getLoadedAst(href, loaded) {
if (!loaded) {
loaded = {};
}
loaded[href] = true;
var parsedDocument = this.parsedDocuments[href];
var analyzedDocument = this.html[href];
var astCopy = dom5.parse(dom5.serialize(parsedDocument));
// Whenever we inline something, reset inlined to true to know that anoather
// inlining pass is needed;
this._inlineStyles(astCopy, href);
this._inlineScripts(astCopy, href);
this._inlineImports(astCopy, href, loaded);
return astCopy;
}
}, {
key: 'nodeWalkDocuments',
var isHtmlImportNode = dom5.predicates.AND(
dom5.predicates.hasTagName('link'),
dom5.predicates.hasAttrValue('rel', 'import'),
dom5.predicates.NOT(
dom5.predicates.hasAttrValue('type', 'css')
)
);
/**
* Calls `dom5.nodeWalkAll` on each document that `Anayzler` has laoded.
*/
value: function nodeWalkDocuments(predicate) {
var results = [];
for (var href in this.parsedDocuments) {
var newNodes = dom5.nodeWalkAll(this.parsedDocuments[href], predicate);
results = results.concat(newNodes);
}
return results;
}
}, {
key: 'nodeWalkAllDocuments',
Analyzer.prototype._inlineStyles = function _inlineStyles(ast, href) {
var cssLinks = dom5.queryAll(ast, polymerExternalStyle);
cssLinks.forEach(function(link) {
var linkHref = dom5.getAttribute(link, 'href');
var uri = url.resolve(href, linkHref);
var content = this._content[uri];
var style = dom5.constructors.element('style');
dom5.setTextContent(style, '\n' + content + '\n');
dom5.replace(link, style);
}.bind(this));
return cssLinks.length > 0;
};
/**
* Calls `dom5.nodeWalkAll` on each document that `Anayzler` has laoded.
*
* TODO: make nodeWalkAll & nodeWalkAllDocuments distict, or delete one.
*/
value: function nodeWalkAllDocuments(predicate) {
var results = [];
for (var href in this.parsedDocuments) {
var newNodes = dom5.nodeWalkAll(this.parsedDocuments[href], predicate);
results = results.concat(newNodes);
}
return results;
}
}, {
key: 'annotate',
Analyzer.prototype._inlineScripts = function _inlineScripts(ast, href) {
var scripts = dom5.queryAll(ast, externalScript);
scripts.forEach(function(script) {
var scriptHref = dom5.getAttribute(script, 'src');
var uri = url.resolve(href, scriptHref);
var content = this._content[uri];
var inlined = dom5.constructors.element('script');
dom5.setTextContent(inlined, '\n' + content + '\n');
dom5.replace(script, inlined);
}.bind(this));
return scripts.length > 0;
};
/** Annotates all loaded metadata with its documentation. */
value: function annotate() {
var _this11 = this;
Analyzer.prototype._inlineImports = function _inlineImports(ast, href, loaded) {
var imports = dom5.queryAll(ast, isHtmlImportNode);
imports.forEach(function(htmlImport) {
var importHref = dom5.getAttribute(htmlImport, 'href');
var uri = url.resolve(href, importHref);
if (loaded[uri]) {
dom5.remove(htmlImport);
return;
}
var content = this.getLoadedAst(uri, loaded);
dom5.replace(htmlImport, content);
}.bind(this));
return imports.length > 0;
};
if (this.features.length > 0) {
var featureEl = docs.featureElement(this.features);
this.elements.unshift(featureEl);
this.elementsByTagName[featureEl.is] = featureEl;
}
var behaviorsByName = this.behaviorsByName;
var elementHelper = function elementHelper(descriptor) {
docs.annotateElement(descriptor, behaviorsByName);
};
this.elements.forEach(elementHelper);
this.behaviors.forEach(elementHelper); // Same shape.
this.behaviors.forEach(function (behavior) {
if (behavior.is !== behavior.symbol && behavior.symbol) {
_this11.behaviorsByName[behavior.symbol] = undefined;
}
});
}
}, {
key: 'clean',
/** Removes redundant properties from the collected descriptors. */
value: function clean() {
this.elements.forEach(docs.cleanElement);
}
}]);
return Analyzer;
}();
/**
* Returns a promise resolving to a form of the AST with all links replaced
* with the document they link to. .css and .script files become &lt;style&gt; and
* &lt;script&gt;, respectively.
* Shorthand for transitively loading and processing all imports beginning at
* `href`.
*
* The elements in the loaded document are unmodified from their original
* documents.
* In order to properly filter paths, `href` _must_ be an absolute URI.
*
* @param {string} href The document to load.
* @param {Object.<string,boolean>=} loaded An object keyed by already loaded documents.
* @return {Promise.<DocumentAST>}
* @param {string} href The root import to begin loading from.
* @param {LoadOptions=} options Any additional options for the load.
* @return {Promise<Analyzer>} A promise that will resolve once `href` and its
* dependencies have been loaded and analyzed.
*/
Analyzer.prototype.getLoadedAst = function getLoadedAst(href, loaded) {
if (!loaded) {
loaded = {};
}
loaded[href] = true;
var parsedDocument = this.parsedDocuments[href];
var analyzedDocument = this.html[href];
var astCopy = dom5.parse(dom5.serialize(parsedDocument));
// Whenever we inline something, reset inlined to true to know that anoather
// inlining pass is needed;
this._inlineStyles(astCopy, href);
this._inlineScripts(astCopy, href);
this._inlineImports(astCopy, href, loaded);
return astCopy;
};
/**
* Calls `dom5.nodeWalkAll` on each document that `Anayzler` has laoded.
* @param {Object} predicate A dom5 predicate.
* @return {Object}
*/
Analyzer.prototype.nodeWalkDocuments = function nodeWalkDocuments(predicate) {
for (var href in this.parsedDocuments) {
var match = dom5.nodeWalk(this.parsedDocuments[href], predicate);
if (match) {
return match;
Analyzer.analyze = function analyze(href, options) {
options = options || {};
options.filter = options.filter || _defaultFilter(href);
var loader = new file_loader_1.FileLoader();
var resolver = options.resolver;
if (resolver === undefined) {
if (typeof window === 'undefined') {
resolver = 'fs';
} else {
resolver = 'xhr';
}
}
}
return null;
var primaryResolver = undefined;
if (resolver === 'fs') {
primaryResolver = new fs_resolver_1.FSResolver(options);
} else if (resolver === 'xhr') {
primaryResolver = new xhr_resolver_1.XHRResolver(options);
} else if (resolver === 'permissive') {
primaryResolver = new error_swallowing_fs_resolver_1.ErrorSwallowingFSResolver(options);
} else {
throw new Error("Resolver must be one of 'fs', 'xhr', or 'permissive'");
}
loader.addResolver(primaryResolver);
if (options.content) {
loader.addResolver(new string_resolver_1.StringResolver({ url: href, content: options.content }));
}
loader.addResolver(new noop_resolver_1.NoopResolver({ test: options.filter }));
var analyzer = new Analyzer(false, loader);
return analyzer.metadataTree(href).then(function (root) {
if (!options.noAnnotations) {
analyzer.annotate();
}
if (options.clean) {
analyzer.clean();
}
return Promise.resolve(analyzer);
});
};
exports.Analyzer = Analyzer;
;
/**
* Calls `dom5.nodeWalkAll` on each document that `Anayzler` has laoded.
* @param {Object} predicate A dom5 predicate.
* @return {Object}
* @private
* @param {string} href
* @return {function(string): boolean}
*/
Analyzer.prototype.nodeWalkAllDocuments = function nodeWalkDocuments(predicate) {
var results = [];
for (var href in this.parsedDocuments) {
var newNodes = dom5.nodeWalkAll(this.parsedDocuments[href], predicate);
results = results.concat(newNodes);
}
return results;
};
/** Annotates all loaded metadata with its documentation. */
Analyzer.prototype.annotate = function annotate() {
if (this.features.length > 0) {
var featureEl = docs.featureElement(this.features);
this.elements.unshift(featureEl);
this.elementsByTagName[featureEl.is] = featureEl;
}
var behaviorsByName = this.behaviorsByName;
var elementHelper = function(descriptor){
docs.annotateElement(descriptor, behaviorsByName);
};
this.elements.forEach(elementHelper);
this.behaviors.forEach(elementHelper); // Same shape.
this.behaviors.forEach(function(behavior){
if (behavior.is !== behavior.symbol && behavior.symbol) {
this.behaviorsByName[behavior.symbol] = undefined;
function _defaultFilter(href) {
// Everything up to the last `/` or `\`.
var base = href.match(/^(.*?)[^\/\\]*$/)[1];
return function (uri) {
return uri.indexOf(base) !== 0;
};
}
function matchesDocumentFolder(descriptor, href) {
if (!descriptor.contentHref) {
return false;
}
}.bind(this));
};
function attachDomModule(parsedImport, element) {
var domModules = parsedImport['dom-module'];
for (var i = 0, domModule; i < domModules.length; i++) {
domModule = domModules[i];
if (dom5.getAttribute(domModule, 'id') === element.is) {
element.domModule = domModule;
return;
var descriptorDoc = url.parse(descriptor.contentHref);
if (!descriptorDoc || !descriptorDoc.pathname) {
return false;
}
}
var searchDoc = url.parse(href);
if (!searchDoc || !searchDoc.pathname) {
return false;
}
var searchPath = searchDoc.pathname;
var lastSlash = searchPath.lastIndexOf("/");
if (lastSlash > 0) {
searchPath = searchPath.slice(0, lastSlash);
}
return descriptorDoc.pathname.indexOf(searchPath) === 0;
}
// TODO(ajo): Refactor out of vulcanize into dom5.
var polymerExternalStyle = dom5.predicates.AND(dom5.predicates.hasTagName('link'), dom5.predicates.hasAttrValue('rel', 'import'), dom5.predicates.hasAttrValue('type', 'css'));
var externalScript = dom5.predicates.AND(dom5.predicates.hasTagName('script'), dom5.predicates.hasAttr('src'));
var isHtmlImportNode = dom5.predicates.AND(dom5.predicates.hasTagName('link'), dom5.predicates.hasAttrValue('rel', 'import'), dom5.predicates.NOT(dom5.predicates.hasAttrValue('type', 'css')));
function attachDomModule(parsedImport, element) {
var domModules = parsedImport['dom-module'];
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
/** Removes redundant properties from the collected descriptors. */
Analyzer.prototype.clean = function clean() {
this.elements.forEach(docs.cleanElement);
};
try {
for (var _iterator5 = domModules[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var domModule = _step5.value;
module.exports = Analyzer;
if (dom5.getAttribute(domModule, 'id') === element.is) {
element.domModule = domModule;
return;
}
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}

@@ -10,99 +10,92 @@ /**

*/
// jshint node: true
'use strict';
var esutil = require('./esutil');
var esutil = require('./esutil');
var astValue = require('./ast-value');
var analyzeProperties = function(node) {
var analyzedProps = [];
if (node.type != 'ObjectExpression') {
return analyzedProps;
}
for (var i = 0; i < node.properties.length; i++) {
var property = node.properties[i];
var prop = esutil.toPropertyDescriptor(property);
prop.published = true;
if (property.value.type == 'ObjectExpression') {
/**
* Parse the expression inside a property object block.
* property: {
* key: {
* type: String,
* notify: true,
* value: -1,
* readOnly: true,
* reflectToAttribute: true
* }
* }
*/
for (var j = 0; j < property.value.properties.length; j++) {
var propertyArg = property.value.properties[j];
var propertyKey = esutil.objectKeyToString(propertyArg.key);
switch(propertyKey) {
case 'type': {
prop.type = esutil.objectKeyToString(propertyArg.value);
if (prop.type === undefined) {
throw {
message: 'Invalid type in property object.',
location: propertyArg.loc.start
};
function analyzeProperties(node) {
var analyzedProps = [];
if (node.type != 'ObjectExpression') {
return analyzedProps;
}
var obEx = node;
for (var i = 0; i < obEx.properties.length; i++) {
var property = obEx.properties[i];
var prop = esutil.toPropertyDescriptor(property);
prop.published = true;
if (property.value.type == 'ObjectExpression') {
/**
* Parse the expression inside a property object block. e.g.
* property: {
* key: {
* type: String,
* notify: true,
* value: -1,
* readOnly: true,
* reflectToAttribute: true
* }
* }
*/
var propDescExpr = property.value;
for (var j = 0; j < propDescExpr.properties.length; j++) {
var propertyArg = propDescExpr.properties[j];
var propertyKey = esutil.objectKeyToString(propertyArg.key);
switch (propertyKey) {
case 'type':
{
prop.type = esutil.objectKeyToString(propertyArg.value);
if (prop.type === undefined) {
throw {
message: 'Invalid type in property object.',
location: propertyArg.loc.start
};
}
}
break;
case 'notify':
{
prop.notify = astValue.expressionToValue(propertyArg.value);
if (prop.notify === undefined) prop.notify = astValue.CANT_CONVERT;
}
break;
case 'observer':
{
prop.observer = astValue.expressionToValue(propertyArg.value);
prop.observerNode = propertyArg.value;
if (prop.observer === undefined) prop.observer = astValue.CANT_CONVERT;
}
break;
case 'readOnly':
{
prop.readOnly = astValue.expressionToValue(propertyArg.value);
if (prop.readOnly === undefined) prop.readOnly = astValue.CANT_CONVERT;
}
break;
case 'reflectToAttribute':
{
prop.reflectToAttribute = astValue.expressionToValue(propertyArg);
if (prop.reflectToAttribute === undefined) prop.reflectToAttribute = astValue.CANT_CONVERT;
}
break;
case 'value':
{
prop.default = astValue.expressionToValue(propertyArg.value);
if (prop.default === undefined) prop.default = astValue.CANT_CONVERT;
}
break;
default:
break;
}
}
}
break;
case 'notify': {
prop.notify = astValue.expressionToValue(propertyArg.value);
if (prop.notify === undefined)
prop.notify = astValue.CANT_CONVERT;
}
break;
case 'observer': {
prop.observer = astValue.expressionToValue(propertyArg.value);
prop.observerNode = propertyArg.value;
if (prop.observer === undefined)
prop.observer = astValue.CANT_CONVERT;
}
break;
case 'readOnly': {
prop.readOnly = astValue.expressionToValue(propertyArg.value);
if (prop.readOnly === undefined)
prop.readOnly = astValue.CANT_CONVERT;
}
break;
case 'reflectToAttribute': {
prop.reflectToAttribute = astValue.expressionToValue(propertyArg);
if (prop.reflectToAttribute === undefined)
prop.reflectToAttribute = astValue.CANT_CONVERT;
}
break;
case 'value': {
prop.default = astValue.expressionToValue(propertyArg.value);
if (prop.default === undefined)
prop.default = astValue.CANT_CONVERT;
}
break;
default:
break;
}
}
if (!prop.type) {
throw {
message: 'Unable to determine name for property key.',
location: node.loc.start
};
}
analyzedProps.push(prop);
}
if (!prop.type) {
throw {
message: 'Unable to determine name for property key.',
location: node.loc.start
};
}
analyzedProps.push(prop);
}
return analyzedProps;
};
module.exports = analyzeProperties;
return analyzedProps;
}
exports.analyzeProperties = analyzeProperties;
;

@@ -10,7 +10,4 @@ /**

*/
// jshint node: true
'use strict';
// useful tool to visualize AST: http://esprima.org/demo/parse.html
/**

@@ -20,17 +17,14 @@ * converts literal: {"type": "Literal", "value": 5, "raw": "5" }

*/
function literalToValue(literal) {
return literal.value;
return literal.value;
}
/**
* converts unary to string
* unary: { type: 'UnaryExpression', operator: '-', argument: { ... } }
*/
function unaryToValue(unary) {
var argValue = expressionToValue(unary.argument);
if (argValue === undefined)
return;
return unary.operator + argValue;
var argValue = expressionToValue(unary.argument);
if (argValue === undefined) return;
return unary.operator + argValue;
}
/**

@@ -41,5 +35,4 @@ * converts identifier to its value

function identifierToValue(identifier) {
return identifier.name;
return identifier.name;
}
/**

@@ -49,9 +42,6 @@ * Function is a block statement.

function functionDeclarationToValue(fn) {
if (fn.body.type == "BlockStatement")
return blockStatementToValue(fn.body);
if (fn.body.type == "BlockStatement") return blockStatementToValue(fn.body);
}
function functionExpressionToValue(fn) {
if (fn.body.type == "BlockStatement")
return blockStatementToValue(fn.body);
if (fn.body.type == "BlockStatement") return blockStatementToValue(fn.body);
}

@@ -62,8 +52,6 @@ /**

function blockStatementToValue(block) {
for (var i=block.body.length - 1; i>= 0; i--) {
if (block.body[i].type === "ReturnStatement")
return returnStatementToValue(block.body[i]);
}
for (var i = block.body.length - 1; i >= 0; i--) {
if (block.body[i].type === "ReturnStatement") return returnStatementToValue(block.body[i]);
}
}
/**

@@ -73,5 +61,4 @@ * Evaluates return's argument

function returnStatementToValue(ret) {
return expressionToValue(ret.argument);
return expressionToValue(ret.argument);
}
/**

@@ -81,15 +68,12 @@ * Enclose containing values in []

function arrayExpressionToValue(arry) {
var value = '[';
for (var i=0; i<arry.elements.length; i++) {
var v = expressionToValue(arry.elements[i]);
if (v === undefined)
continue;
if (i !== 0)
value += ', ';
value += v;
}
value += ']';
return value;
var value = '[';
for (var i = 0; i < arry.elements.length; i++) {
var v = expressionToValue(arry.elements[i]);
if (v === undefined) continue;
if (i !== 0) value += ', ';
value += v;
}
value += ']';
return value;
}
/**

@@ -99,16 +83,13 @@ * Make it look like an object

function objectExpressionToValue(obj) {
var value = '{';
for (var i=0; i<obj.properties.length; i++) {
var k = expressionToValue(obj.properties[i].key);
var v = expressionToValue(obj.properties[i].value);
if (v === undefined)
continue;
if (i !== 0)
value += ', ';
value += '"' + k + '": ' + v;
}
value += '}';
return value;
var value = '{';
for (var i = 0; i < obj.properties.length; i++) {
var k = expressionToValue(obj.properties[i].key);
var v = expressionToValue(obj.properties[i].value);
if (v === undefined) continue;
if (i !== 0) value += ', ';
value += '"' + k + '": ' + v;
}
value += '}';
return value;
}
/**

@@ -118,8 +99,10 @@ * BinaryExpressions are of the form "literal" + "literal"

function binaryExpressionToValue(member) {
if (member.operator == "+") {
return expressionToValue(member.left) + expressionToValue(member.right);
}
return
if (member.operator == "+") {
// We need to cast to `any` here because, while it's usually not the right
// thing to do to use '+' on two values of a mix of types because it's
// unpredictable, that is what the original code we're evaluating does.
return expressionToValue(member.left) + expressionToValue(member.right);
}
return;
}
/**

@@ -129,42 +112,34 @@ * MemberExpression references a variable with name

function memberExpressionToValue(member) {
return expressionToValue(member.object) + "." + expressionToValue(member.property);
return expressionToValue(member.object) + "." + expressionToValue(member.property);
}
/**
* Tries to get a value from expression. Handles Literal, UnaryExpression
* returns undefined on failure
* valueExpression example:
* { type: "Literal",
* Tries to get the value of an expression. Returns undefined on failure.
*/
function expressionToValue(valueExpression) {
switch(valueExpression.type) {
case 'Literal':
return literalToValue(valueExpression);
case 'UnaryExpression':
return unaryToValue(valueExpression);
case 'Identifier':
return identifierToValue(valueExpression);
case 'FunctionDeclaration':
return functionDeclarationToValue(valueExpression);
case 'FunctionExpression':
return functionExpressionToValue(valueExpression);
case 'ArrayExpression':
return arrayExpressionToValue(valueExpression);
case 'ObjectExpression':
return objectExpressionToValue(valueExpression);
case 'Identifier':
return identifierToValue(valueExpression);
case 'MemberExpression':
return memberExpressionToValue(valueExpression);
case 'BinaryExpression':
return binaryExpressionToValue(valueExpression);
default:
return;
}
switch (valueExpression.type) {
case 'Literal':
return literalToValue(valueExpression);
case 'UnaryExpression':
return unaryToValue(valueExpression);
case 'Identifier':
return identifierToValue(valueExpression);
case 'FunctionDeclaration':
return functionDeclarationToValue(valueExpression);
case 'FunctionExpression':
return functionExpressionToValue(valueExpression);
case 'ArrayExpression':
return arrayExpressionToValue(valueExpression);
case 'ObjectExpression':
return objectExpressionToValue(valueExpression);
case 'Identifier':
return identifierToValue(valueExpression);
case 'MemberExpression':
return memberExpressionToValue(valueExpression);
case 'BinaryExpression':
return binaryExpressionToValue(valueExpression);
default:
return;
}
}
var CANT_CONVERT = 'UNKNOWN';
module.exports = {
CANT_CONVERT: CANT_CONVERT,
expressionToValue: expressionToValue
};
exports.expressionToValue = expressionToValue;
exports.CANT_CONVERT = 'UNKNOWN';

@@ -10,217 +10,208 @@ /**

*/
// jshint node: true
'use strict';
var estraverse = require('estraverse');
var docs = require('./docs');
var docs = require('./docs');
var esutil = require('./esutil');
var jsdoc = require('./jsdoc');
var analyzeProperties = require('./analyze-properties');
var astValue = require('./ast-value.js');
var declarationPropertyHandlers = require('./declaration-property-handlers');
module.exports = function behaviorFinder() {
/** @type {!Array<BehaviorDescriptor>} The behaviors we've found. */
var behaviors = [];
var currentBehavior = null;
var propertyHandlers = null;
/**
* merges behavior with preexisting behavior with the same name.
* here to support multiple @polymerBehavior tags referring
* to same behavior. See iron-multi-selectable for example.
*/
function mergeBehavior(newBehavior) {
var isBehaviorImpl = function(b) { // filter out BehaviorImpl
return b.indexOf(newBehavior.is) === -1;
};
for (var i=0; i<behaviors.length; i++) {
if (newBehavior.is !== behaviors[i].is)
continue;
// merge desc, longest desc wins
if (newBehavior.desc) {
if (behaviors[i].desc) {
if (newBehavior.desc.length > behaviors[i].desc.length)
behaviors[i].desc = newBehavior.desc;
var jsdoc = require('./jsdoc');
var astValue = require('./ast-value');
var declaration_property_handlers_1 = require('./declaration-property-handlers');
function dedupe(array, keyFunc) {
var bucket = {};
array.forEach(function (el) {
var key = keyFunc(el);
if (key in bucket) {
return;
}
else {
behaviors[i].desc = newBehavior.desc;
bucket[key] = el;
});
var returned = [];
Object.keys(bucket).forEach(function (k) {
returned.push(bucket[k]);
});
return returned;
}
// TODO(rictic): turn this into a class.
function behaviorFinder() {
/** The behaviors we've found. */
var behaviors = [];
var currentBehavior = null;
var propertyHandlers = null;
/**
* merges behavior with preexisting behavior with the same name.
* here to support multiple @polymerBehavior tags referring
* to same behavior. See iron-multi-selectable for example.
*/
function mergeBehavior(newBehavior) {
var isBehaviorImpl = function isBehaviorImpl(b) {
// filter out BehaviorImpl
return b.indexOf(newBehavior.is) === -1;
};
for (var i = 0; i < behaviors.length; i++) {
if (newBehavior.is !== behaviors[i].is) continue;
// merge desc, longest desc wins
if (newBehavior.desc) {
if (behaviors[i].desc) {
if (newBehavior.desc.length > behaviors[i].desc.length) behaviors[i].desc = newBehavior.desc;
} else {
behaviors[i].desc = newBehavior.desc;
}
}
// merge demos
behaviors[i].demos = (behaviors[i].demos || []).concat(newBehavior.demos || []);
// merge events,
behaviors[i].events = (behaviors[i].events || []).concat(newBehavior.events || []);
behaviors[i].events = dedupe(behaviors[i].events, function (e) {
return e.name;
});
// merge properties
behaviors[i].properties = (behaviors[i].properties || []).concat(newBehavior.properties || []);
// merge observers
behaviors[i].observers = (behaviors[i].observers || []).concat(newBehavior.observers || []);
// merge behaviors
behaviors[i].behaviors = (behaviors[i].behaviors || []).concat(newBehavior.behaviors || []).filter(isBehaviorImpl);
return behaviors[i];
}
}
// merge demos
behaviors[i].demos = (behaviors[i].demos || []).concat(newBehavior.demos || []);
// merge events,
behaviors[i].events = (behaviors[i].events || []).concat(newBehavior.events || []);
// merge properties
behaviors[i].properties = (behaviors[i].properties || []).concat(newBehavior.properties || []);
// merge observers
behaviors[i].observers = (behaviors[i].observers || []).concat(newBehavior.observers || []);
// merge behaviors
behaviors[i].behaviors =
(behaviors[i].behaviors || []).concat(newBehavior.behaviors || [])
.filter(isBehaviorImpl);
return behaviors[i];
return newBehavior;
}
return newBehavior;
}
/**
* gets the expression representing a behavior from a node.
*/
function behaviorExpression(node) {
switch(node.type) {
case 'ExpressionStatement':
return node.expression.right;
case 'VariableDeclaration':
return node.declarations.length > 0 ? node.declarations[0].init : null;
}
}
/**
* checks whether an expression is a simple array containing only member
* expressions or identifiers.
*/
function isSimpleBehaviorArray(expression) {
if (!expression || expression.type !== 'ArrayExpression') return false;
for (var i=0; i < expression.elements.length; i++) {
if (expression.elements[i].type !== 'MemberExpression' &&
expression.elements[i].type !== 'Identifier') {
return false;
}
}
return true;
}
var templatizer = "Polymer.Templatizer";
var visitors = {
/**
* Look for object declarations with @behavior in the docs.
* gets the expression representing a behavior from a node.
*/
enterVariableDeclaration: function(node, parent) {
if (node.declarations.length !== 1) return; // Ambiguous.
this._initBehavior(node, function () {
return esutil.objectKeyToString(node.declarations[0].id);
});
},
function behaviorExpression(node) {
switch (node.type) {
case 'ExpressionStatement':
// need to cast to `any` here because ExpressionStatement is super
// super general. this code is suspicious.
return node.expression.right;
case 'VariableDeclaration':
var n = node;
return n.declarations.length > 0 ? n.declarations[0].init : null;
}
}
/**
* Look for object assignments with @polymerBehavior in the docs.
* checks whether an expression is a simple array containing only member
* expressions or identifiers.
*/
enterAssignmentExpression: function(node, parent) {
this._initBehavior(parent, function () {
return esutil.objectKeyToString(node.left);
});
},
_parseChainedBehaviors: function(node) {
// if current behavior is part of an array, it gets extended by other behaviors
// inside the array. Ex:
// Polymer.IronMultiSelectableBehavior = [ {....}, Polymer.IronSelectableBehavior]
// We add these to behaviors array
var expression = behaviorExpression(node);
var chained = [];
if (expression && expression.type === 'ArrayExpression') {
for (var i=0; i < expression.elements.length; i++) {
if (expression.elements[i].type === 'MemberExpression' ||
expression.elements[i].type === 'Identifier') {
chained.push(astValue.expressionToValue(expression.elements[i]));
}
function isSimpleBehaviorArray(expression) {
if (!expression || expression.type !== 'ArrayExpression') return false;
var arrayExpr = expression;
for (var i = 0; i < arrayExpr.elements.length; i++) {
if (arrayExpr.elements[i].type !== 'MemberExpression' && arrayExpr.elements[i].type !== 'Identifier') {
return false;
}
}
if (chained.length > 0)
currentBehavior.behaviors = chained;
}
},
_initBehavior: function(node, getName) {
var comment = esutil.getAttachedComment(node);
var symbol = getName();
// Quickly filter down to potential candidates.
if (!comment || comment.indexOf('@polymerBehavior') === -1) {
if (symbol !== templatizer) {
return;
return true;
}
var templatizer = "Polymer.Templatizer";
function _parseChainedBehaviors(node) {
// if current behavior is part of an array, it gets extended by other behaviors
// inside the array. Ex:
// Polymer.IronMultiSelectableBehavior = [ {....}, Polymer.IronSelectableBehavior]
// We add these to behaviors array
var expression = behaviorExpression(node);
var chained = [];
if (expression && expression.type === 'ArrayExpression') {
var arrExpr = expression;
for (var i = 0; i < arrExpr.elements.length; i++) {
if (arrExpr.elements[i].type === 'MemberExpression' || arrExpr.elements[i].type === 'Identifier') {
chained.push(astValue.expressionToValue(arrExpr.elements[i]));
}
}
if (chained.length > 0) currentBehavior.behaviors = chained;
}
}
currentBehavior = {
type: 'behavior',
desc: comment,
events: esutil.getEventComments(node).map( function(event) {
return { desc: event};
})
};
propertyHandlers = declarationPropertyHandlers(currentBehavior);
docs.annotateBehavior(currentBehavior);
// Make sure that we actually parsed a behavior tag!
if (!jsdoc.hasTag(currentBehavior.jsdoc, 'polymerBehavior') &&
symbol !== templatizer) {
currentBehavior = null;
propertyHandlers = null;
return;
}
var name = jsdoc.getTag(currentBehavior.jsdoc, 'polymerBehavior', 'name');
currentBehavior.symbol = symbol;
if (!name) {
name = currentBehavior.symbol;
}
if (!name) {
console.warn('Unable to determine name for @polymerBehavior:', comment);
}
currentBehavior.is = name;
this._parseChainedBehaviors(node);
currentBehavior = mergeBehavior(currentBehavior);
propertyHandlers = declarationPropertyHandlers(currentBehavior);
// Some behaviors are just lists of other behaviors. If this is one then
// add it to behaviors right away.
if (isSimpleBehaviorArray(behaviorExpression(node))) {
// TODO(ajo): Add a test to confirm the presence of `properties`
if (!currentBehavior.observers) currentBehavior.observers = [];
if (!currentBehavior.properties) currentBehavior.properties = [];
if (behaviors.indexOf(currentBehavior) === -1)
behaviors.push(currentBehavior);
currentBehavior = null;
propertyHandlers = null;
}
},
/**
* We assume that the object expression after such an assignment is the
* behavior's declaration. Seems to be a decent assumption for now.
*/
enterObjectExpression: function(node, parent) {
if (!currentBehavior || currentBehavior.properties) return;
currentBehavior.properties = currentBehavior.properties || [];
currentBehavior.observers = currentBehavior.observers || [];
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
var name = esutil.objectKeyToString(prop.key);
}
function _initBehavior(node, getName) {
var comment = esutil.getAttachedComment(node);
var symbol = getName();
// Quickly filter down to potential candidates.
if (!comment || comment.indexOf('@polymerBehavior') === -1) {
if (symbol !== templatizer) {
return;
}
}
currentBehavior = {
type: 'behavior',
desc: comment,
events: esutil.getEventComments(node).map(function (event) {
return { desc: event };
})
};
propertyHandlers = declaration_property_handlers_1.declarationPropertyHandlers(currentBehavior);
docs.annotateBehavior(currentBehavior);
// Make sure that we actually parsed a behavior tag!
if (!jsdoc.hasTag(currentBehavior.jsdoc, 'polymerBehavior') && symbol !== templatizer) {
currentBehavior = null;
propertyHandlers = null;
return;
}
var name = jsdoc.getTag(currentBehavior.jsdoc, 'polymerBehavior', 'name');
currentBehavior.symbol = symbol;
if (!name) {
throw {
message: 'Cant determine name for property key.',
location: node.loc.start
};
name = currentBehavior.symbol;
}
if (name in propertyHandlers) {
propertyHandlers[name](prop.value);
if (!name) {
console.warn('Unable to determine name for @polymerBehavior:', comment);
}
else {
currentBehavior.properties.push(esutil.toPropertyDescriptor(prop));
currentBehavior.is = name;
_parseChainedBehaviors(node);
currentBehavior = mergeBehavior(currentBehavior);
propertyHandlers = declaration_property_handlers_1.declarationPropertyHandlers(currentBehavior);
// Some behaviors are just lists of other behaviors. If this is one then
// add it to behaviors right away.
if (isSimpleBehaviorArray(behaviorExpression(node))) {
// TODO(ajo): Add a test to confirm the presence of `properties`
if (!currentBehavior.observers) currentBehavior.observers = [];
if (!currentBehavior.properties) currentBehavior.properties = [];
if (behaviors.indexOf(currentBehavior) === -1) behaviors.push(currentBehavior);
currentBehavior = null;
propertyHandlers = null;
}
}
behaviors.push(currentBehavior);
currentBehavior = null;
},
};
return {visitors: visitors, behaviors: behaviors};
};
}
var visitors = {
/**
* Look for object declarations with @behavior in the docs.
*/
enterVariableDeclaration: function enterVariableDeclaration(node, parent) {
if (node.declarations.length !== 1) return; // Ambiguous.
_initBehavior(node, function () {
return esutil.objectKeyToString(node.declarations[0].id);
});
},
/**
* Look for object assignments with @polymerBehavior in the docs.
*/
enterAssignmentExpression: function enterAssignmentExpression(node, parent) {
_initBehavior(parent, function () {
return esutil.objectKeyToString(node.left);
});
},
/**
* We assume that the object expression after such an assignment is the
* behavior's declaration. Seems to be a decent assumption for now.
*/
enterObjectExpression: function enterObjectExpression(node, parent) {
if (!currentBehavior || currentBehavior.properties) return;
currentBehavior.properties = currentBehavior.properties || [];
currentBehavior.observers = currentBehavior.observers || [];
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
var name = esutil.objectKeyToString(prop.key);
if (!name) {
throw {
message: 'Cant determine name for property key.',
location: node.loc.start
};
}
if (name in propertyHandlers) {
propertyHandlers[name](prop.value);
} else {
currentBehavior.properties.push(esutil.toPropertyDescriptor(prop));
}
}
behaviors.push(currentBehavior);
currentBehavior = null;
}
};
return { visitors: visitors, behaviors: behaviors };
}
exports.behaviorFinder = behaviorFinder;
;

@@ -10,8 +10,6 @@ /**

*/
// jshint node: true
'use strict';
var astValue = require('./ast-value');
var analyzeProperties = require('./analyze-properties');
var analyze_properties_1 = require('./analyze-properties');
/**

@@ -26,45 +24,85 @@ * Returns an object containing functions that will annotate `declaration` with

function declarationPropertyHandlers(declaration) {
return {
is: function(node) {
if (node.type == 'Literal') {
declaration.is = node.value;
}
},
properties: function(node) {
return {
is: function is(node) {
if (node.type == 'Literal') {
declaration.is = node.value.toString();
}
},
properties: function properties(node) {
var props = analyze_properties_1.analyzeProperties(node);
for (var i = 0; i < props.length; i++) {
declaration.properties.push(props[i]);
}
},
behaviors: function behaviors(node) {
if (node.type != 'ArrayExpression') {
return;
}
var arrNode = node;
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var props = analyzeProperties(node);
try {
for (var _iterator = arrNode.elements[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var element = _step.value;
for (var i=0; i<props.length; i++) {
declaration.properties.push(props[i]);
}
},
behaviors: function(node) {
if (node.type != 'ArrayExpression') {
return;
}
var v = astValue.expressionToValue(element);
if (v === undefined) {
v = astValue.CANT_CONVERT;
}
declaration.behaviors.push(v);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
},
observers: function observers(node) {
if (node.type != 'ArrayExpression') {
return;
}
var arrNode = node;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
for (var i=0; i<node.elements.length; i++) {
var v = astValue.expressionToValue(node.elements[i]);
if (v === undefined)
v = astValue.CANT_CONVERT;
declaration.behaviors.push(v);
}
},
observers: function(node) {
if (node.type != 'ArrayExpression') {
return;
}
for (var i=0; i<node.elements.length; i++) {
var v = astValue.expressionToValue(node.elements[i]);
if (v === undefined)
v = astValue.CANT_CONVERT;
declaration.observers.push({
javascriptNode: node.elements[i],
expression: v
});
}
}
};
try {
for (var _iterator2 = arrNode.elements[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var element = _step2.value;
var v = astValue.expressionToValue(element);
if (v === undefined) v = astValue.CANT_CONVERT;
declaration.observers.push({
javascriptNode: element,
expression: v
});
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
};
}
module.exports = declarationPropertyHandlers;
exports.declarationPropertyHandlers = declarationPropertyHandlers;

@@ -12,34 +12,8 @@ /**

// jshint node:true
var jsdoc = require('./jsdoc');
var dom5 = require('dom5');
/** Properties on element prototypes that are purely configuration. */
var ELEMENT_CONFIGURATION = [
'attached',
'attributeChanged',
'configure',
'constructor',
'created',
'detached',
'enableCustomStyleProperties',
'extends',
'hostAttributes',
'is',
'listeners',
'mixins',
'properties',
'ready',
'registered'
];
var ELEMENT_CONFIGURATION = ['attached', 'attributeChanged', 'beforeRegister', 'configure', 'constructor', 'created', 'detached', 'enableCustomStyleProperties', 'extends', 'hostAttributes', 'is', 'listeners', 'mixins', 'properties', 'ready', 'registered'];
/** Tags understood by the annotation process, to be removed during `clean`. */
var HANDLED_TAGS = [
'param',
'return',
'type',
];
var HANDLED_TAGS = ['param', 'return', 'type'];
/**

@@ -58,14 +32,12 @@ * Annotates Hydrolysis descriptors, processing any `desc` properties as JSDoc.

function annotate(descriptor) {
if (!descriptor || descriptor.jsdoc) return descriptor;
if (typeof descriptor.desc === 'string') {
descriptor.jsdoc = jsdoc.parseJsdoc(descriptor.desc);
// We want to present the normalized form of a descriptor.
descriptor.jsdoc.orig = descriptor.desc;
descriptor.desc = descriptor.jsdoc.description;
}
return descriptor;
if (!descriptor || descriptor.jsdoc) return descriptor;
if (typeof descriptor.desc === 'string') {
descriptor.jsdoc = jsdoc.parseJsdoc(descriptor.desc);
// We want to present the normalized form of a descriptor.
descriptor.jsdoc.orig = descriptor.desc;
descriptor.desc = descriptor.jsdoc.description;
}
return descriptor;
}
exports.annotate = annotate;
/**

@@ -75,94 +47,91 @@ * Annotates @event, @hero, & @demo tags

function annotateElementHeader(descriptor) {
if (descriptor.events) {
descriptor.events.forEach(function(event) {
_annotateEvent(event);
});
descriptor.events.sort( function(a,b) {
return a.name.localeCompare(b.name);
});
}
descriptor.demos = [];
if (descriptor.jsdoc && descriptor.jsdoc.tags) {
descriptor.jsdoc.tags.forEach( function(tag) {
switch(tag.tag) {
case 'hero':
descriptor.hero = tag.name || 'hero.png';
break;
case 'demo':
descriptor.demos.push({
desc: tag.description || 'demo',
path: tag.name || 'demo/index.html'
});
}
});
}
if (descriptor.events) {
descriptor.events.forEach(function (event) {
_annotateEvent(event);
});
descriptor.events.sort(function (a, b) {
return a.name.localeCompare(b.name);
});
}
descriptor.demos = [];
if (descriptor.jsdoc && descriptor.jsdoc.tags) {
descriptor.jsdoc.tags.forEach(function (tag) {
switch (tag.tag) {
case 'hero':
descriptor.hero = tag.name || 'hero.png';
break;
case 'demo':
descriptor.demos.push({
desc: tag.description || 'demo',
path: tag.name || 'demo/index.html'
});
break;
}
});
}
}
function matchByName(propa, propb) {
return propa.name == propb.name;
}
exports.annotateElementHeader = annotateElementHeader;
function copyProperties(from, to, behaviorsByName) {
if (from.properties) {
from.properties.forEach(function(fromProp){
for (var toProp, i = 0; i < to.properties.length; i++) {
toProp = to.properties[i];
if (fromProp.name === toProp.name) {
return;
}
}
var newProp = {__fromBehavior: from.is};
if (fromProp.__fromBehavior) {
if (from.properties) {
from.properties.forEach(function (fromProp) {
for (var toProp, i = 0; i < to.properties.length; i++) {
toProp = to.properties[i];
if (fromProp.name === toProp.name) {
return;
}
}
var newProp = { __fromBehavior: from.is };
if (fromProp.__fromBehavior) {
return;
}
Object.keys(fromProp).forEach(function (propertyField) {
newProp[propertyField] = fromProp[propertyField];
});
to.properties.push(newProp);
});
from.events.forEach(function (fromEvent) {
for (var toEvent, i = 0; i < to.events.length; i++) {
toEvent = to.events[i];
if (fromEvent.name === toEvent.name) {
return;
}
}
if (fromEvent.__fromBehavior) {
return;
}
var newEvent = { __fromBehavior: from.is };
Object.keys(fromEvent).forEach(function (eventField) {
newEvent[eventField] = fromEvent[eventField];
});
to.events.push(newEvent);
});
}
if (!from.behaviors) {
return;
}
Object.keys(fromProp).forEach(function(propertyField){
newProp[propertyField] = fromProp[propertyField];
});
to.properties.push(newProp);
});
from.events.forEach(function(fromEvent){
for (var toEvent, i = 0; i < to.events.length; i++) {
toEvent = to.events[i];
if (fromEvent.name === toEvent.name) {
return;
}
for (var i = from.behaviors.length - 1; i >= 0; i--) {
// TODO: what's up with behaviors sometimes being a literal, and sometimes
// being a descriptor object?
var localBehavior = from.behaviors[i];
var definedBehavior = behaviorsByName[localBehavior] || behaviorsByName[localBehavior.symbol];
if (!definedBehavior) {
console.warn("Behavior " + localBehavior + " not found when mixing " + "properties into " + to.is + "!");
return;
}
}
if (fromEvent.__fromBehavior) {
return;
}
var newEvent = Object.create(fromEvent);
newEvent.__fromBehavior = from.is;
to.events.push(newEvent);
});
}
if (!from.behaviors) {
return;
}
for (var i = from.behaviors.length - 1; i >= 0; i--) {
var behavior = from.behaviors[i];
var definedBehavior = behaviorsByName[behavior] || behaviorsByName[behavior.symbol];
if (!definedBehavior) {
console.warn("Behavior " + behavior + " not found when mixing " +
"properties into " + to.is + "!");
return;
copyProperties(definedBehavior, to, behaviorsByName);
}
copyProperties(definedBehavior, to, behaviorsByName);
}
}
function mixinBehaviors(descriptor, behaviorsByName) {
if (descriptor.behaviors) {
for (var i = descriptor.behaviors.length - 1; i >= 0; i--) {
var behavior = descriptor.behaviors[i];
if (!behaviorsByName[behavior]) {
console.warn("Behavior " + behavior + " not found when mixing " +
"properties into " + descriptor.is + "!");
break;
}
var definedBehavior = behaviorsByName[behavior];
copyProperties(definedBehavior, descriptor, behaviorsByName);
if (descriptor.behaviors) {
for (var i = descriptor.behaviors.length - 1; i >= 0; i--) {
var behavior = descriptor.behaviors[i];
if (!behaviorsByName[behavior]) {
console.warn("Behavior " + behavior + " not found when mixing " + "properties into " + descriptor.is + "!");
break;
}
var definedBehavior = behaviorsByName[behavior];
copyProperties(definedBehavior, descriptor, behaviorsByName);
}
}
}
}
/**

@@ -179,44 +148,35 @@ * Annotates documentation found within a Hydrolysis element descriptor. Also

function annotateElement(descriptor, behaviorsByName) {
if (!descriptor.desc && descriptor.type === 'element') {
descriptor.desc = _findElementDocs(descriptor.is,
descriptor.domModule,
descriptor.scriptElement);
}
annotate(descriptor);
// The `<dom-module>` is too low level for most needs, and it is _not_
// serializable. So we drop it now that we've extracted all the useful bits
// from it.
// TODO: Don't worry about serializability here, provide an API to get JSON.
delete descriptor.domModule;
mixinBehaviors(descriptor, behaviorsByName);
// Descriptors that should have their `desc` properties parsed as JSDoc.
descriptor.properties.forEach(function(property) {
// Feature properties are special, configuration is really just a matter of
// inheritance...
annotateProperty(property, descriptor.abstract);
});
// It may seem like overkill to always sort, but we have an assumption that
// these properties are typically being consumed by user-visible tooling.
// As such, it's good to have consistent output/ordering to aid the user.
descriptor.properties.sort(function(a, b) {
// Private properties are always last.
if (a.private && !b.private) {
return 1;
} else if (!a.private && b.private) {
return -1;
// Otherwise, we're just sorting alphabetically.
} else {
return a.name.localeCompare(b.name);
if (!descriptor.desc && descriptor.type === 'element') {
descriptor.desc = _findElementDocs(descriptor.is, descriptor.domModule, descriptor.scriptElement);
}
});
annotateElementHeader(descriptor);
return descriptor;
annotate(descriptor);
// The `<dom-module>` is too low level for most needs, and it is _not_
// serializable. So we drop it now that we've extracted all the useful bits
// from it.
// TODO: Don't worry about serializability here, provide an API to get JSON.
delete descriptor.domModule;
mixinBehaviors(descriptor, behaviorsByName);
// Descriptors that should have their `desc` properties parsed as JSDoc.
descriptor.properties.forEach(function (property) {
// Feature properties are special, configuration is really just a matter of
// inheritance...
annotateProperty(property, descriptor.abstract);
});
// It may seem like overkill to always sort, but we have an assumption that
// these properties are typically being consumed by user-visible tooling.
// As such, it's good to have consistent output/ordering to aid the user.
descriptor.properties.sort(function (a, b) {
// Private properties are always last.
if (a.private && !b.private) {
return 1;
} else if (!a.private && b.private) {
return -1;
} else {
return a.name.localeCompare(b.name);
}
});
annotateElementHeader(descriptor);
return descriptor;
}
exports.annotateElement = annotateElement;
/**

@@ -227,9 +187,8 @@ * Annotates behavior descriptor.

*/
function annotateBehavior(descriptor, behaviorsByName) {
annotate(descriptor);
annotateElementHeader(descriptor);
return descriptor;
function annotateBehavior(descriptor) {
annotate(descriptor);
annotateElementHeader(descriptor);
return descriptor;
}
exports.annotateBehavior = annotateBehavior;
/**

@@ -239,23 +198,19 @@ * Annotates event documentation

function _annotateEvent(descriptor) {
annotate(descriptor);
// process @event
var eventTag = jsdoc.getTag(descriptor.jsdoc, 'event');
descriptor.name = eventTag ? eventTag.description : "N/A";
// process @params
descriptor.params = (descriptor.jsdoc.tags || [])
.filter( function(tag) {
return tag.tag === 'param';
})
.map( function(tag) {
return {
type: tag.type || "N/A",
desc: tag.description,
name: tag.name || "N/A"
};
annotate(descriptor);
// process @event
var eventTag = jsdoc.getTag(descriptor.jsdoc, 'event');
descriptor.name = eventTag ? eventTag.description : "N/A";
// process @params
descriptor.params = (descriptor.jsdoc.tags || []).filter(function (tag) {
return tag.tag === 'param';
}).map(function (tag) {
return {
type: tag.type || "N/A",
desc: tag.description,
name: tag.name || "N/A"
};
});
// process @params
return descriptor;
// process @params
return descriptor;
}
/**

@@ -269,59 +224,48 @@ * Annotates documentation found about a Hydrolysis property descriptor.

function annotateProperty(descriptor, ignoreConfiguration) {
annotate(descriptor);
if (descriptor.name[0] === '_' || jsdoc.hasTag(descriptor.jsdoc, 'private')) {
descriptor.private = true;
}
if (!ignoreConfiguration && ELEMENT_CONFIGURATION.indexOf(descriptor.name) !== -1) {
descriptor.private = true;
descriptor.configuration = true;
}
// @type JSDoc wins
descriptor.type = jsdoc.getTag(descriptor.jsdoc, 'type', 'type') || descriptor.type;
if (descriptor.type.match(/^function/i)) {
_annotateFunctionProperty(descriptor);
}
// @default JSDoc wins
var defaultTag = jsdoc.getTag(descriptor.jsdoc, 'default');
if (defaultTag !== null) {
var newDefault = (defaultTag.name || '') + (defaultTag.description || '');
if (newDefault !== '') {
descriptor.default = newDefault;
annotate(descriptor);
if (descriptor.name[0] === '_' || jsdoc.hasTag(descriptor.jsdoc, 'private')) {
descriptor.private = true;
}
}
return descriptor;
if (!ignoreConfiguration && ELEMENT_CONFIGURATION.indexOf(descriptor.name) !== -1) {
descriptor.private = true;
descriptor.configuration = true;
}
// @type JSDoc wins
descriptor.type = jsdoc.getTag(descriptor.jsdoc, 'type', 'type') || descriptor.type;
if (descriptor.type.match(/^function/i)) {
_annotateFunctionProperty(descriptor);
}
// @default JSDoc wins
var defaultTag = jsdoc.getTag(descriptor.jsdoc, 'default');
if (defaultTag !== null) {
var newDefault = (defaultTag.name || '') + (defaultTag.description || '');
if (newDefault !== '') {
descriptor.default = newDefault;
}
}
return descriptor;
}
/** @param {Object} descriptor */
function _annotateFunctionProperty(descriptor) {
descriptor.function = true;
var returnTag = jsdoc.getTag(descriptor.jsdoc, 'return');
if (returnTag) {
descriptor.return = {
type: returnTag.type,
desc: returnTag.description,
};
}
var paramsByName = {};
(descriptor.params || []).forEach(function(param) {
paramsByName[param.name] = param;
});
(descriptor.jsdoc && descriptor.jsdoc.tags || []).forEach(function(tag) {
if (tag.tag !== 'param') return;
var param = paramsByName[tag.name];
if (!param) {
return;
descriptor.function = true;
var returnTag = jsdoc.getTag(descriptor.jsdoc, 'return');
if (returnTag) {
descriptor.return = {
type: returnTag.type,
desc: returnTag.description
};
}
param.type = tag.type || param.type;
param.desc = tag.description;
});
var paramsByName = {};
(descriptor.params || []).forEach(function (param) {
paramsByName[param.name] = param;
});
(descriptor.jsdoc && descriptor.jsdoc.tags || []).forEach(function (tag) {
if (tag.tag !== 'param') return;
var param = paramsByName[tag.name];
if (!param) {
return;
}
param.type = tag.type || param.type;
param.desc = tag.description;
});
}
/**

@@ -337,21 +281,14 @@ * Converts raw features into an abstract `Polymer.Base` element.

function featureElement(features) {
var properties = features.reduce(function(result, feature) {
return result.concat(feature.properties);
}, []);
return {
type: 'element',
is: 'Polymer.Base',
abstract: true,
properties: properties,
desc: '`Polymer.Base` acts as a base prototype for all Polymer ' +
'elements. It is composed via various calls to ' +
'`Polymer.Base._addFeature()`.\n' +
'\n' +
'The properties reflected here are the combined view of all ' +
'features found in this library. There may be more properties ' +
'added via other libraries, as well.',
};
var properties = features.reduce(function (result, feature) {
return result.concat(feature.properties);
}, []);
return {
type: 'element',
is: 'Polymer.Base',
abstract: true,
properties: properties,
desc: '`Polymer.Base` acts as a base prototype for all Polymer ' + 'elements. It is composed via various calls to ' + '`Polymer.Base._addFeature()`.\n' + '\n' + 'The properties reflected here are the combined view of all ' + 'features found in this library. There may be more properties ' + 'added via other libraries, as well.'
};
}
exports.featureElement = featureElement;
/**

@@ -364,22 +301,20 @@ * Cleans redundant properties from a descriptor, assuming that you have already

function clean(descriptor) {
if (!descriptor.jsdoc) return;
// The doctext was written to `descriptor.desc`
delete descriptor.jsdoc.description;
delete descriptor.jsdoc.orig;
var cleanTags = [];
(descriptor.jsdoc.tags || []).forEach(function(tag) {
// Drop any tags we've consumed.
if (HANDLED_TAGS.indexOf(tag.tag) !== -1) return;
cleanTags.push(tag);
});
if (cleanTags.length === 0) {
// No tags? no docs left!
delete descriptor.jsdoc;
} else {
descriptor.jsdoc.tags = cleanTags;
}
if (!descriptor.jsdoc) return;
// The doctext was written to `descriptor.desc`
delete descriptor.jsdoc.description;
delete descriptor.jsdoc.orig;
var cleanTags = [];
(descriptor.jsdoc.tags || []).forEach(function (tag) {
// Drop any tags we've consumed.
if (HANDLED_TAGS.indexOf(tag.tag) !== -1) return;
cleanTags.push(tag);
});
if (cleanTags.length === 0) {
// No tags? no docs left!
delete descriptor.jsdoc;
} else {
descriptor.jsdoc.tags = cleanTags;
}
}
exports.clean = clean;
/**

@@ -392,6 +327,6 @@ * Cleans redundant properties from an element, assuming that you have already

function cleanElement(element) {
clean(element);
element.properties.forEach(cleanProperty);
clean(element);
element.properties.forEach(cleanProperty);
}
exports.cleanElement = cleanElement;
/**

@@ -404,5 +339,4 @@ * Cleans redundant properties from a property, assuming that you have already

function cleanProperty(property) {
clean(property);
clean(property);
}
/**

@@ -414,84 +348,70 @@ * Parse elements defined only in comments.

function parsePseudoElements(comments) {
var elements = [];
comments.forEach(function(comment) {
var parsed = jsdoc.parseJsdoc(comment);
var pseudoTag = jsdoc.getTag(parsed, 'pseudoElement', 'name');
if (pseudoTag) {
parsed.is = pseudoTag;
parsed.jsdoc = {description: parsed.description, tags: parsed.tags};
parsed.properties = [];
parsed.desc = parsed.description;
parsed.description = undefined;
parsed.tags = undefined;
annotateElementHeader(parsed);
elements.push(parsed);
}
});
return elements;
var elements = [];
comments.forEach(function (comment) {
var parsedJsdoc = jsdoc.parseJsdoc(comment);
var pseudoTag = jsdoc.getTag(parsedJsdoc, 'pseudoElement', 'name');
if (pseudoTag) {
var element = {
is: pseudoTag,
type: 'element',
jsdoc: { description: parsedJsdoc.description, tags: parsedJsdoc.tags },
properties: [],
desc: parsedJsdoc.description
};
annotateElementHeader(element);
elements.push(element);
}
});
return elements;
}
exports.parsePseudoElements = parsePseudoElements;
/**
* @param {string} elementId
* @param {DocumentAST} domModule
* @param {DocumentAST} scriptElement The script that the element was defined in.
* @param {DocumentAST} scriptElement The script that the element was
* defined in.
*/
function _findElementDocs(elementId, domModule, scriptElement) {
// Note that we concatenate docs from all sources if we find them.
// element can be defined in:
// html comment right before dom-module
// html commnet right before script defining the module, if dom-module is empty
var found = [];
// Do we have a HTML comment on the `<dom-module>` or `<script>`?
//
// Confusingly, with our current style, the comment will be attached to
// `<head>`, rather than being a sibling to the `<dom-module>`
var searchRoot = domModule || scriptElement;
var parents = dom5.nodeWalkAllPrior(searchRoot, dom5.isCommentNode);
var comment = parents.length > 0 ? parents[0] : null;
if (comment && comment.data) {
found.push(comment.data);
}
if (found.length === 0) return null;
return found
.filter(function(comment) {
// skip @license comments
if (comment && comment.indexOf('@license' === -1)) {
return true;
}
else {
return false;
}
})
.map(jsdoc.unindent).join('\n');
// Note that we concatenate docs from all sources if we find them.
// element can be defined in:
// html comment right before dom-module
// html commnet right before script defining the module,
// if dom-module is empty
var found = [];
// Do we have a HTML comment on the `<dom-module>` or `<script>`?
//
// Confusingly, with our current style, the comment will be attached to
// `<head>`, rather than being a sibling to the `<dom-module>`
var searchRoot = domModule || scriptElement;
var parents = dom5.nodeWalkAllPrior(searchRoot, dom5.isCommentNode);
var comment = parents.length > 0 ? parents[0] : null;
if (comment && comment.data) {
found.push(comment.data);
}
if (found.length === 0) return null;
return found.filter(function (comment) {
// skip @license comments
if (comment && comment.indexOf('@license') === -1) {
return true;
} else {
return false;
}
}).map(jsdoc.unindent).join('\n');
}
function _findLastChildNamed(name, parent) {
var children = parent.childNodes;
for (var i = children.length - 1, child; i >= 0; i--) {
child = children[i];
if (child.nodeName === name) return child;
}
return null;
var children = parent.childNodes;
for (var i = children.length - 1; i >= 0; i--) {
var child = children[i];
if (child.nodeName === name) return child;
}
return null;
}
// TODO(nevir): parse5-utils!
function _getNodeAttribute(node, name) {
for (var i = 0, attr; i < node.attrs.length; i++) {
attr = node.attrs[i];
if (attr.name === name) {
return attr.value;
for (var i = 0; i < node.attrs.length; i++) {
var attr = node.attrs[i];
if (attr.name === name) {
return attr.value;
}
}
}
}
module.exports = {
annotate: annotate,
annotateElement: annotateElement,
annotateBehavior: annotateBehavior,
clean: clean,
cleanElement: cleanElement,
featureElement: featureElement,
parsePseudoElements: parsePseudoElements
};
}

@@ -10,107 +10,182 @@ /**

*/
// jshint node: true
'use strict';
var estraverse = require('estraverse');
var esutil = require('./esutil');
var findAlias = require('./find-alias');
var analyzeProperties = require('./analyze-properties');
var astValue = require('./ast-value');
var declarationPropertyHandlers = require('./declaration-property-handlers');
var elementFinder = function elementFinder() {
/**
* The list of elements exported by each traversed script.
*/
var elements = [];
/**
* The element being built during a traversal;
*/
var element = null;
var propertyHandlers = null;
var visitors = {
enterCallExpression: function enterCallExpression(node, parent) {
var callee = node.callee;
if (callee.type == 'Identifier') {
if (callee.name == 'Polymer') {
element = {
type: 'element',
desc: esutil.getAttachedComment(parent),
events: esutil.getEventComments(parent).map( function(event) {
return {desc: event};
})
};
propertyHandlers = declarationPropertyHandlers(element);
}
}
},
leaveCallExpression: function leaveCallExpression(node, parent) {
var callee = node.callee;
if (callee.type == 'Identifier') {
if (callee.name == 'Polymer') {
if (element) {
elements.push(element);
element = null;
propertyHandlers = null;
}
}
}
},
enterObjectExpression: function enterObjectExpression(node, parent) {
if (element && !element.properties) {
element.properties = [];
element.behaviors = [];
element.observers = [];
var getters = {};
var setters = {};
var definedProperties = {};
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
var name = esutil.objectKeyToString(prop.key);
if (!name) {
throw {
message: 'Cant determine name for property key.',
location: node.loc.start
var esutil = require('./esutil');
var declaration_property_handlers_1 = require('./declaration-property-handlers');
var docs = require('./docs');
function elementFinder() {
/**
* The list of elements exported by each traversed script.
*/
var elements = [];
/**
* The element being built during a traversal;
*/
var element = null;
var propertyHandlers = null;
var visitors = {
classDetected: false,
enterClassDeclaration: function enterClassDeclaration(node, parent) {
this.classDetected = true;
element = {
type: 'element',
desc: esutil.getAttachedComment(node),
events: esutil.getEventComments(node).map(function (event) {
return { desc: event };
}),
properties: [],
behaviors: [],
observers: []
};
}
if (name in propertyHandlers) {
propertyHandlers[name](prop.value);
continue;
}
var descriptor = esutil.toPropertyDescriptor(prop);
if (descriptor.getter) {
getters[descriptor.name] = descriptor;
} else if (descriptor.setter) {
setters[descriptor.name] = descriptor;
} else {
element.properties.push(esutil.toPropertyDescriptor(prop));
}
propertyHandlers = declaration_property_handlers_1.declarationPropertyHandlers(element);
},
leaveClassDeclaration: function leaveClassDeclaration(node, parent) {
element.properties.map(function (property) {
return docs.annotate(property);
});
if (element) {
elements.push(element);
element = null;
propertyHandlers = null;
}
this.classDetected = false;
},
enterAssignmentExpression: function enterAssignmentExpression(node, parent) {
if (!element) {
return;
}
var left = node.left;
if (left && left.object && left.object.type !== 'ThisExpression') {
return;
}
var prop = left.property;
if (prop && prop.name) {
var name = prop.name;
if (name in propertyHandlers) {
propertyHandlers[name](node.right);
}
}
},
enterMethodDefinition: function enterMethodDefinition(node, parent) {
if (!element) {
return;
}
var prop = {
key: node.key,
value: node.value,
kind: node.kind,
method: true,
leadingComments: node.leadingComments,
shorthand: false,
computed: false,
type: 'Property'
};
var propDesc = docs.annotate(esutil.toPropertyDescriptor(prop));
if (prop && prop.kind === 'get' && (propDesc.name === 'behaviors' || propDesc.name === 'observers')) {
var returnStatement = node.value.body.body[0];
var argument = returnStatement.argument;
if (propDesc.name === 'behaviors') {
argument.elements.forEach(function (elementObject) {
element.behaviors.push(elementObject.name);
});
} else {
argument.elements.forEach(function (elementObject) {
element.observers.push({ javascriptNode: elementObject, expression: elementObject.raw });
});
}
} else {
element.properties.push(propDesc);
}
},
enterCallExpression: function enterCallExpression(node, parent) {
// When dealing with a class, enterCallExpression is called after the parsing actually starts
if (this.classDetected) {
return estraverse.VisitorOption.Skip;
}
var callee = node.callee;
if (callee.type == 'Identifier') {
var ident = callee;
if (ident.name == 'Polymer') {
element = {
type: 'element',
desc: esutil.getAttachedComment(parent),
events: esutil.getEventComments(parent).map(function (event) {
return { desc: event };
})
};
propertyHandlers = declaration_property_handlers_1.declarationPropertyHandlers(element);
}
}
},
leaveCallExpression: function leaveCallExpression(node, parent) {
var callee = node.callee;
if (callee.type == 'Identifier') {
var ident = callee;
if (ident.name == 'Polymer') {
if (element) {
elements.push(element);
element = null;
propertyHandlers = null;
}
}
}
},
enterObjectExpression: function enterObjectExpression(node, parent) {
// When dealing with a class, there is no single object that we can parse to retrieve all properties
if (this.classDetected) {
return estraverse.VisitorOption.Skip;
}
if (element && !element.properties) {
element.properties = [];
element.behaviors = [];
element.observers = [];
var getters = {};
var setters = {};
var definedProperties = {};
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
var name = esutil.objectKeyToString(prop.key);
if (!name) {
throw {
message: 'Cant determine name for property key.',
location: node.loc.start
};
}
if (name in propertyHandlers) {
propertyHandlers[name](prop.value);
continue;
}
var descriptor = esutil.toPropertyDescriptor(prop);
if (descriptor.getter) {
getters[descriptor.name] = descriptor;
} else if (descriptor.setter) {
setters[descriptor.name] = descriptor;
} else {
element.properties.push(esutil.toPropertyDescriptor(prop));
}
}
Object.keys(getters).forEach(function (getter) {
var get = getters[getter];
definedProperties[get.name] = get;
});
Object.keys(setters).forEach(function (setter) {
var set = setters[setter];
if (!(set.name in definedProperties)) {
definedProperties[set.name] = set;
} else {
definedProperties[set.name].setter = true;
}
});
Object.keys(definedProperties).forEach(function (p) {
var prop = definedProperties[p];
element.properties.push(prop);
});
return estraverse.VisitorOption.Skip;
}
}
Object.keys(getters).forEach(function(getter) {
var get = getters[getter];
definedProperties[get.name] = get;
});
Object.keys(setters).forEach(function(setter) {
var set = setters[setter];
if (!(set.name in definedProperties)) {
definedProperties[set.name] = set;
} else {
definedProperties[set.name].setter = true;
}
});
Object.keys(definedProperties).forEach(function(p){
var prop = definedProperties[p];
element.properties.push(p);
});
return estraverse.VisitorOption.Skip;
}
}
};
return {visitors: visitors, elements: elements};
};
module.exports = elementFinder;
};
return { visitors: visitors, elements: elements };
}
exports.elementFinder = elementFinder;
;

@@ -10,6 +10,8 @@ /**

*/
// jshint node: true
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var estraverse = require("estraverse");
var escodegen = require('escodegen');
/**

@@ -23,23 +25,25 @@ * Returns whether an Espree node matches a particular object path.

*
* @param {Node} expression The Espree node to match against.
* @param {ESTree.Node} expression The Espree node to match against.
* @param {Array<string>} path The path to look for.
*/
function matchesCallExpression(expression, path) {
if (!expression.property || !expression.object) return;
console.assert(path.length >= 2);
// Unravel backwards, make sure properties match each step of the way.
if (expression.property.name !== path[path.length - 1]) return false;
// We've got ourselves a final member expression.
if (path.length == 2 && expression.object.type === 'Identifier') {
return expression.object.name === path[0];
}
// Nested expressions.
if (path.length > 2 && expression.object.type == 'MemberExpression') {
return matchesCallExpression(expression.object, path.slice(0, path.length - 1));
}
return false;
if (!expression.property || !expression.object) return;
console.assert(path.length >= 2);
if (expression.property.type !== 'Identifier') {
return;
}
var property = expression.property;
// Unravel backwards, make sure properties match each step of the way.
if (property.name !== path[path.length - 1]) return false;
// We've got ourselves a final member expression.
if (path.length == 2 && expression.object.type === 'Identifier') {
return expression.object.name === path[0];
}
// Nested expressions.
if (path.length > 2 && expression.object.type == 'MemberExpression') {
return matchesCallExpression(expression.object, path.slice(0, path.length - 1));
}
return false;
}
exports.matchesCallExpression = matchesCallExpression;
/**

@@ -50,19 +54,19 @@ * @param {Node} key The node representing an object key or expression.

function objectKeyToString(key) {
if (key.type == 'Identifier') {
return key.name;
}
if (key.type == 'Literal') {
return key.value;
}
if (key.type == 'MemberExpression') {
return objectKeyToString(key.object) + '.' + objectKeyToString(key.property);
}
if (key.type == 'Identifier') {
return key.name;
}
if (key.type == 'Literal') {
return key.value.toString();
}
if (key.type == 'MemberExpression') {
var mEx = key;
return objectKeyToString(mEx.object) + '.' + objectKeyToString(mEx.property);
}
}
exports.objectKeyToString = objectKeyToString;
var CLOSURE_CONSTRUCTOR_MAP = {
'Boolean': 'boolean',
'Number': 'number',
'String': 'string',
'Boolean': 'boolean',
'Number': 'number',
'String': 'string'
};
/**

@@ -77,106 +81,86 @@ * AST expression -> Closure type.

function closureType(node) {
if (node.type.match(/Expression$/)) {
return node.type.substr(0, node.type.length - 10);
} else if (node.type === 'Literal') {
return typeof node.value;
} else if (node.type === 'Identifier') {
return CLOSURE_CONSTRUCTOR_MAP[node.name] || node.name;
} else {
throw {
message: 'Unknown Closure type for node: ' + node.type,
location: node.loc.start,
};
}
if (node.type.match(/Expression$/)) {
return node.type.substr(0, node.type.length - 10);
} else if (node.type === 'Literal') {
return _typeof(node.value);
} else if (node.type === 'Identifier') {
var ident = node;
return CLOSURE_CONSTRUCTOR_MAP[ident.name] || ident.name;
} else {
throw {
message: 'Unknown Closure type for node: ' + node.type,
location: node.loc.start
};
}
}
/**
* @param {Node} node
* @return {?string}
*/
exports.closureType = closureType;
function getAttachedComment(node) {
var comments = getLeadingComments(node) || getLeadingComments(node.key);
if (!comments) {
return;
}
return comments[comments.length - 1];
var comments = getLeadingComments(node) || getLeadingComments(node['key']);
if (!comments) {
return;
}
return comments[comments.length - 1];
}
exports.getAttachedComment = getAttachedComment;
/**
* Returns all comments from a tree defined with @event.
* @param {Node} node [description]
* @return {[type]} [description]
*/
function getEventComments(node) {
var eventComments = [];
estraverse.traverse(node, {
enter: function (node) {
var comments = (node.leadingComments || []).concat(node.trailingComments || [])
.map( function(commentAST) {
return commentAST.value;
})
.filter( function(comment) {
return comment.indexOf("@event") != -1;
});
eventComments = eventComments.concat(comments);
}
});
// dedup
return eventComments.filter( function(el, index, array) {
return array.indexOf(el) === index;
});
var eventComments = [];
estraverse.traverse(node, {
enter: function enter(node) {
var comments = (node.leadingComments || []).concat(node.trailingComments || []).map(function (commentAST) {
return commentAST.value;
}).filter(function (comment) {
return comment.indexOf("@event") != -1;
});
eventComments = eventComments.concat(comments);
},
keys: {
Super: []
}
});
// dedup
return eventComments.filter(function (el, index, array) {
return array.indexOf(el) === index;
});
}
/**
* @param {Node} node
* @param
* @return {Array.<string>}
*/
exports.getEventComments = getEventComments;
function getLeadingComments(node) {
if (!node) {
return;
}
var comments = node.leadingComments;
if (!comments || comments.length === 0) return;
return comments.map(function(comment) {
return comment.value;
});
if (!node) {
return;
}
var comments = node.leadingComments;
if (!comments || comments.length === 0) return;
return comments.map(function (comment) {
return comment.value;
});
}
/**
* Converts a parse5 Property AST node into its Hydrolysis representation.
*
* @param {Node} node
* @return {PropertyDescriptor}
* Converts a estree Property AST node into its Hydrolysis representation.
*/
function toPropertyDescriptor(node) {
var type = closureType(node.value);
if (type == "Function") {
if (node.kind === "get" || node.kind === "set") {
type = '';
node[node.kind+"ter"] = true;
var type = closureType(node.value);
if (type == "Function") {
if (node.kind === "get" || node.kind === "set") {
type = '';
node[node.kind + "ter"] = true;
}
}
}
var result = {
name: objectKeyToString(node.key),
type: type,
desc: getAttachedComment(node),
javascriptNode: node
};
if (type === 'Function') {
result.params = (node.value.params || []).map(function(param) {
return {name: param.name};
});
}
return result;
var result = {
name: objectKeyToString(node.key),
type: type,
desc: getAttachedComment(node),
javascriptNode: node
};
if (type === 'Function') {
var value = node.value;
result.params = (value.params || []).map(function (param) {
// With ES6 we can have a variety of param patterns. Best to leave the
// formatting to escodegen.
return { name: escodegen.generate(param) };
});
}
return result;
}
module.exports = {
closureType: closureType,
getAttachedComment: getAttachedComment,
getEventComments: getEventComments,
matchesCallExpression: matchesCallExpression,
objectKeyToString: objectKeyToString,
toPropertyDescriptor: toPropertyDescriptor,
};
exports.toPropertyDescriptor = toPropertyDescriptor;

@@ -10,48 +10,38 @@ /**

*/
// jshint node: true
'use strict';
var estraverse = require('estraverse');
var esutil = require('./esutil');
var numFeatures = 0;
module.exports = function featureFinder() {
/** @type {!Array<FeatureDescriptor>} The features we've found. */
var features = [];
var visitors = {
enterCallExpression: function enterCallExpression(node, parent) {
if (!esutil.matchesCallExpression(node.callee, ['Polymer', 'Base', '_addFeature'])) {
return;
}
/** @type {!FeatureDescriptor} */
var feature = {};
this._extractDesc(feature, node, parent);
this._extractProperties(feature, node, parent);
features.push(feature);
},
_extractDesc: function _extractDesc(feature, node, parent) {
feature.desc = esutil.getAttachedComment(parent);
},
_extractProperties: function _extractProperties(feature, node, parent) {
var featureNode = node.arguments[0];
if (featureNode.type !== 'ObjectExpression') {
console.warn(
'Expected first argument to Polymer.Base._addFeature to be an object.',
'Got', featureNode.type, 'instead.');
return;
}
if (!featureNode.properties) return;
feature.properties = featureNode.properties.map(esutil.toPropertyDescriptor);
},
};
return {visitors: visitors, features: features};
};
function featureFinder() {
/** The features we've found. */
var features = [];
function _extractDesc(feature, node, parent) {
feature.desc = esutil.getAttachedComment(parent);
}
function _extractProperties(feature, node, parent) {
var featureNode = node.arguments[0];
if (featureNode.type !== 'ObjectExpression') {
console.warn('Expected first argument to Polymer.Base._addFeature to be an object.', 'Got', featureNode.type, 'instead.');
return;
}
var objExpr = featureNode;
if (!objExpr.properties) return;
feature.properties = objExpr.properties.map(esutil.toPropertyDescriptor);
}
var visitors = {
enterCallExpression: function enterCallExpression(node, parent) {
var isAddFeatureCall = esutil.matchesCallExpression(node.callee, ['Polymer', 'Base', '_addFeature']);
if (!isAddFeatureCall) {
return;
}
/** @type {!FeatureDescriptor} */
var feature = {};
_extractDesc(feature, node, parent);
_extractProperties(feature, node, parent);
features.push(feature);
}
};
return { visitors: visitors, features: features };
}
exports.featureFinder = featureFinder;
;

@@ -10,133 +10,90 @@ /**

*/
// jshint node: true
'use strict';
var dom5 = require('dom5');
var p = dom5.predicates;
var isHtmlImportNode = p.AND(
p.hasTagName('link'),
p.hasAttrValue('rel', 'import'),
p.NOT(
p.hasAttrValue('type', 'css')
)
);
var isHtmlImportNode = p.AND(p.hasTagName('link'), p.hasAttrValue('rel', 'import'), p.NOT(p.hasAttrValue('type', 'css')));
var isStyleNode = p.OR(
// inline style
p.hasTagName('style'),
// external stylesheet
p.AND(
p.hasTagName('link'),
p.hasAttrValue('rel', 'stylesheet')
),
// polymer specific external stylesheet
p.AND(
p.hasTagName('link'),
p.hasAttrValue('rel', 'import'),
p.hasAttrValue('type', 'css')
)
);
var isJSScriptNode = p.AND(
p.hasTagName('script'),
p.OR(
p.NOT(p.hasAttr('type')),
p.hasAttrValue('type', 'text/javascript'),
p.hasAttrValue('type', 'application/javascript')
)
);
// inline style
p.hasTagName('style'),
// external stylesheet
p.AND(p.hasTagName('link'), p.hasAttrValue('rel', 'stylesheet')),
// polymer specific external stylesheet
p.AND(p.hasTagName('link'), p.hasAttrValue('rel', 'import'), p.hasAttrValue('type', 'css')));
var isJSScriptNode = p.AND(p.hasTagName('script'), p.OR(p.NOT(p.hasAttr('type')), p.hasAttrValue('type', 'text/javascript'), p.hasAttrValue('type', 'application/javascript')));
function addNode(node, registry) {
if (isHtmlImportNode(node)) {
registry.import.push(node);
} else if (isStyleNode(node)) {
registry.style.push(node);
} else if (isJSScriptNode(node)) {
registry.script.push(node);
} else if (node.tagName === 'base') {
registry.base.push(node);
} else if (node.tagName === 'template') {
registry.template.push(node);
} else if (node.tagName === 'dom-module') {
registry['dom-module'].push(node);
} else if (dom5.isCommentNode(node)) {
registry.comment.push(node);
}
if (isHtmlImportNode(node)) {
registry.import.push(node);
} else if (isStyleNode(node)) {
registry.style.push(node);
} else if (isJSScriptNode(node)) {
registry.script.push(node);
} else if (node.tagName === 'base') {
registry.base.push(node);
} else if (node.tagName === 'template') {
registry.template.push(node);
} else if (node.tagName === 'dom-module') {
registry['dom-module'].push(node);
} else if (dom5.isCommentNode(node)) {
registry.comment.push(node);
}
}
function getLineAndColumn(string, charNumber) {
if (charNumber > string.length) {
return undefined;
}
// TODO(ajo): Caching the line lengths of each document could be much faster.
var sliced = string.slice(0,charNumber+1);
var split = sliced.split('\n');
var line = split.length;
var column = split[split.length - 1].length;
return {line: line, column: column};
if (charNumber > string.length) {
return undefined;
}
// TODO(ajo): Caching the line lengths of each document could be much faster.
var sliced = string.slice(0, charNumber + 1);
var split = sliced.split('\n');
var line = split.length;
var column = split[split.length - 1].length;
return { line: line, column: column };
}
/**
* Parse5's representation of a parsed html document.
* @typedef {Object} DocumentAST
*/
/**
* The ASTs of the HTML elements needed to represent Polymer elements.
* @typedef {Object} ParsedImport
* @property {Array<DocumentAST>} template The entry points to the AST at each outermost template tag.
* @property {Array<DocumentAST>} script The entry points to the AST at each script tag not inside a template.
* @property {Array<DocumentAST>} style The entry points to the AST at style tag outside a template.
* @property {Array<DocumentAST>} dom-module The entry points to the AST at each outermost dom-module element.
* @property {DocumentAST} ast The full parse5 ast for the document.
*/
/**
* Parse html into ASTs.
* @param {string} htmlString A utf8, html5 document containing polymer element or module definitons.
* @param {string} href The path of the document.
* @return {ParsedImport}
*
* htmlString is a utf8, html5 document containing polymer elements
* or module definitons.
*
* href is the path of the document.
*/
var importParse = function importParse(htmlString, href) {
var doc;
try {
doc = dom5.parse(htmlString, {locationInfo: true});
} catch (err) {
console.log(err);
return null;
}
// Add line/column information
dom5.treeMap(doc, function(node) {
if (node.__location && node.__location.start >= 0) {
node.__locationDetail = getLineAndColumn(htmlString, node.__location.start);
if (href) {
node.__ownerDocument = href;
}
function importParse(htmlString, href) {
var doc;
try {
doc = dom5.parse(htmlString, { locationInfo: true });
} catch (err) {
console.log(err);
return null;
}
});
var registry = {
base: [],
template: [],
script: [],
style: [],
import: [],
'dom-module': [],
comment: []};
var queue = [].concat(doc.childNodes);
var nextNode;
while (queue.length > 0) {
nextNode = queue.shift();
if (nextNode) {
queue = queue.concat(nextNode.childNodes);
addNode(nextNode, registry);
// Add line/column information
dom5.treeMap(doc, function (node) {
if (node.__location && node.__location.start >= 0) {
node.__locationDetail = getLineAndColumn(htmlString, node.__location.start);
if (href) {
node.__ownerDocument = href;
}
}
});
var registry = {
base: [],
template: [],
script: [],
style: [],
import: [],
'dom-module': [],
comment: [],
ast: doc };
var queue = [].concat(doc.childNodes);
var nextNode;
while (queue.length > 0) {
nextNode = queue.shift();
if (nextNode) {
queue = queue.concat(nextNode.childNodes);
addNode(nextNode, registry);
}
}
}
registry.ast = doc;
return registry;
};
module.exports = importParse;
;
return registry;
}
exports.importParse = importParse;
;

@@ -13,84 +13,73 @@ /**

*/
// jshint node: true
'use strict';
var espree = require('espree');
var estraverse = require('estraverse');
var behavior_finder_1 = require('./behavior-finder');
var element_finder_1 = require('./element-finder');
var feature_finder_1 = require('./feature-finder');
function traverse(visitorRegistries) {
function applyVisitors(name, node, parent) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var behaviorFinder = require('./behavior-finder');
var elementFinder = require('./element-finder');
var featureFinder = require('./feature-finder');
try {
for (var _iterator = visitorRegistries[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var registry = _step.value;
function traverse(visitorRegistries) {
var visitor;
function applyVisitors(name, node, parent) {
var returnVal;
for (var i = 0; i < visitorRegistries.length; i++) {
if (name in visitorRegistries[i]) {
returnVal = visitorRegistries[i][name](node, parent);
if (returnVal) {
return returnVal;
if (name in registry) {
var returnVal = registry[name](node, parent);
if (returnVal) {
return returnVal;
}
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}
}
return {
enter: function(node, parent) {
visitor = 'enter' + node.type;
return applyVisitors(visitor, node, parent);
},
leave: function(node, parent) {
visitor = 'leave' + node.type;
return applyVisitors(visitor, node, parent);
},
fallback: 'iteration',
};
return {
enter: function enter(node, parent) {
return applyVisitors('enter' + node.type, node, parent);
},
leave: function leave(node, parent) {
return applyVisitors('leave' + node.type, node, parent);
},
fallback: 'iteration'
};
}
var jsParse = function jsParse(jsString) {
var script = espree.parse(jsString, {
attachComment: true,
comment: true,
loc: true,
ecmaFeatures: {
arrowFunctions: true,
blockBindings: true,
destructuring: true,
regexYFlag: true,
regexUFlag: true,
templateStrings: true,
binaryLiterals: true,
unicodeCodePointEscapes: true,
defaultParams: true,
restParams: true,
forOf: true,
objectLiteralComputedProperties: true,
objectLiteralShorthandMethods: true,
objectLiteralShorthandProperties: true,
objectLiteralDuplicateProperties: true,
generators: true,
spread: true,
classes: true,
modules: true,
jsx: true,
globalReturn: true,
}
});
var featureInfo = featureFinder();
var behaviorInfo = behaviorFinder();
var elementInfo = elementFinder();
var visitors = [featureInfo, behaviorInfo, elementInfo].map(function(info) {
return info.visitors;
});
estraverse.traverse(script, traverse(visitors));
return {
behaviors: behaviorInfo.behaviors,
elements: elementInfo.elements,
features: featureInfo.features,
parsedScript: script
};
};
module.exports = jsParse;
function jsParse(jsString) {
var script = espree.parse(jsString, {
attachComment: true,
comment: true,
loc: true,
ecmaVersion: 6
});
var featureInfo = feature_finder_1.featureFinder();
var behaviorInfo = behavior_finder_1.behaviorFinder();
var elementInfo = element_finder_1.elementFinder();
var visitors = [featureInfo, behaviorInfo, elementInfo].map(function (info) {
return info.visitors;
});
estraverse.traverse(script, traverse(visitors));
return {
behaviors: behaviorInfo.behaviors,
elements: elementInfo.elements,
features: featureInfo.features,
parsedScript: script
};
}
exports.jsParse = jsParse;
;

@@ -10,38 +10,6 @@ /**

*/
// jshint node: true
'use strict';
var doctrine = require('doctrine');
/**
* An annotated JSDoc block tag, all fields are optionally processed except for
* the tag:
*
* @TAG {TYPE} NAME DESC
*
* `line` and `col` indicate the position of the first character of text that
* the tag was extracted from - relative to the first character of the comment
* contents (e.g. the value of `desc` on a descriptor node). Lines are
* 1-indexed.
*
* @typedef {{
* tag: string,
* type: ?string,
* name: ?string,
* description: ?string,
* }}
*/
var JsdocTag;
/**
* The parsed representation of a JSDoc comment.
*
* @typedef {{
* description: ?string,
* tags: Array<JsdocTag>,
* }}
*/
var JsdocAnnotation;
/**
* doctrine configuration,

@@ -51,9 +19,6 @@ * CURRENTLY UNUSED BECAUSE PRIVATE

// function configureDoctrine() {
// // @hero [path/to/image]
// doctrine.Rules['hero'] = ['parseNamePathOptional', 'ensureEnd'];
// // // @demo [path/to/demo] [Demo title]
// doctrine.Rules['demo'] = ['parseNamePathOptional', 'parseDescription', 'ensureEnd'];
// // // @polymerBehavior [Polymer.BehaviorName]

@@ -63,90 +28,74 @@ // doctrine.Rules['polymerBehavior'] = ['parseNamePathOptional', 'ensureEnd'];

// configureDoctrine();
// @demo [path] [title]
function parseDemo(tag) {
var match = (tag.description || "").match(/^\s*(\S*)\s*(.*)$/);
return {
tag: 'demo',
type: null,
name: match ? match[1] : null,
description: match ? match[2] : null
};
var match = (tag.description || "").match(/^\s*(\S*)\s*(.*)$/);
return {
tag: 'demo',
type: null,
name: match ? match[1] : null,
description: match ? match[2] : null
};
}
// @hero [path]
function parseHero(tag) {
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
}
// @polymerBehavior [name]
function parsePolymerBehavior(tag) {
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
}
// @pseudoElement name
function parsePseudoElement(tag) {
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
return {
tag: tag.title,
type: null,
name: tag.description,
description: null
};
}
var CUSTOM_TAGS = {
demo: parseDemo,
hero: parseHero,
polymerBehavior: parsePolymerBehavior,
pseudoElement: parsePseudoElement
demo: parseDemo,
hero: parseHero,
polymerBehavior: parsePolymerBehavior,
pseudoElement: parsePseudoElement
};
/**
* Convert doctrine tags to hydrolysis tag format
* Convert doctrine tags to our tag format
*/
function _tagsToHydroTags(tags) {
if (!tags)
return null;
return tags.map( function(tag) {
if (tag.title in CUSTOM_TAGS) {
return CUSTOM_TAGS[tag.title](tag);
}
else {
return {
tag: tag.title,
type: tag.type ? doctrine.type.stringify(tag.type) : null,
name: tag.name,
description: tag.description,
};
}
});
if (!tags) return null;
return tags.map(function (tag) {
if (tag.title in CUSTOM_TAGS) {
return CUSTOM_TAGS[tag.title](tag);
} else {
return {
tag: tag.title,
type: tag.type ? doctrine.type.stringify(tag.type) : null,
name: tag.name,
description: tag.description
};
}
});
}
/**
* removes leading *, and any space before it
* @param {string} description -- js doc description
*/
function _removeLeadingAsterisks(description) {
if ((typeof description) !== 'string')
return description;
return description
.split('\n')
.map( function(line) {
// remove leading '\s*' from each line
var match = line.match(/^[\s]*\*\s?(.*)$/);
return match ? match[1] : line;
})
.join('\n');
if (typeof description !== 'string') return description;
return description.split('\n').map(function (line) {
// remove leading '\s*' from each line
var match = line.match(/^[\s]*\*\s?(.*)$/);
return match ? match[1] : line;
}).join('\n');
}
/**

@@ -157,71 +106,49 @@ * Given a JSDoc string (minus opening/closing comment delimiters), extract its

* @param {string} docs
* @return {?JsdocAnnotation}
* @return {?Annotation}
*/
function parseJsdoc(docs) {
docs = _removeLeadingAsterisks(docs);
var d = doctrine.parse(docs, {
unwrap: false,
lineNumber: true,
preserveWhitespace: true
});
return {
description: d.description,
tags: _tagsToHydroTags(d.tags)
};
docs = _removeLeadingAsterisks(docs);
var d = doctrine.parse(docs, {
unwrap: false,
lineNumber: true,
preserveWhitespace: true
});
return {
description: d.description,
tags: _tagsToHydroTags(d.tags)
};
}
exports.parseJsdoc = parseJsdoc;
// Utility
/**
* @param {JsdocAnnotation} jsdoc
* @param {string} tagName
* @return {boolean}
*/
function hasTag(jsdoc, tagName) {
if (!jsdoc || !jsdoc.tags) return false;
return jsdoc.tags.some(function(tag) { return tag.tag === tagName; });
if (!jsdoc || !jsdoc.tags) return false;
return jsdoc.tags.some(function (tag) {
return tag.tag === tagName;
});
}
/**
* Finds the first JSDoc tag matching `name` and returns its value at `key`.
*
* @param {JsdocAnnotation} jsdoc
* @param {string} tagName
* @param {string=} key If omitted, the entire tag object is returned.
* @return {?string|Object}
*/
exports.hasTag = hasTag;
function getTag(jsdoc, tagName, key) {
if (!jsdoc || !jsdoc.tags) return false;
for (var i = 0; i < jsdoc.tags.length; i++) {
var tag = jsdoc.tags[i];
if (tag.tag === tagName) {
return key ? tag[key] : tag;
if (!jsdoc || !jsdoc.tags) return null;
for (var i = 0; i < jsdoc.tags.length; i++) {
var tag = jsdoc.tags[i];
if (tag.tag === tagName) {
return key ? tag[key] : tag;
}
}
}
return null;
return null;
}
/**
* @param {?string} text
* @return {?string}
*/
exports.getTag = getTag;
function unindent(text) {
if (!text) return text;
var lines = text.replace(/\t/g, ' ').split('\n');
var indent = lines.reduce(function(prev, line) {
if (/^\s*$/.test(line)) return prev; // Completely ignore blank lines.
var lineIndent = line.match(/^(\s*)/)[0].length;
if (prev === null) return lineIndent;
return lineIndent < prev ? lineIndent : prev;
}, null);
return lines.map(function(l) { return l.substr(indent); }).join('\n');
if (!text) return text;
var lines = text.replace(/\t/g, ' ').split('\n');
var indent = lines.reduce(function (prev, line) {
if (/^\s*$/.test(line)) return prev; // Completely ignore blank lines.
var lineIndent = line.match(/^(\s*)/)[0].length;
if (prev === null) return lineIndent;
return lineIndent < prev ? lineIndent : prev;
}, null);
return lines.map(function (l) {
return l.substr(indent);
}).join('\n');
}
module.exports = {
getTag: getTag,
hasTag: hasTag,
parseJsdoc: parseJsdoc,
unindent: unindent
};
exports.unindent = unindent;

@@ -10,22 +10,37 @@ /**

*/
// jshint node:true
'use strict';
var FSResolver = require('./fs-resolver');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function ErrorSwallowingFSResolver(config) {
FSResolver.call(this, config);
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
ErrorSwallowingFSResolver.prototype = Object.create(FSResolver.prototype);
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
ErrorSwallowingFSResolver.prototype.accept = function(uri, deferred) {
var reject = deferred.reject;
deferred.reject = function(arg) {
deferred.resolve("");
};
return FSResolver.prototype.accept.call(this, uri, deferred);
};
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
module.exports = ErrorSwallowingFSResolver;
var fs_resolver_1 = require('./fs-resolver');
var ErrorSwallowingFSResolver = function (_fs_resolver_1$FSReso) {
_inherits(ErrorSwallowingFSResolver, _fs_resolver_1$FSReso);
function ErrorSwallowingFSResolver(config) {
_classCallCheck(this, ErrorSwallowingFSResolver);
return _possibleConstructorReturn(this, Object.getPrototypeOf(ErrorSwallowingFSResolver).call(this, config));
}
_createClass(ErrorSwallowingFSResolver, [{
key: 'accept',
value: function accept(uri, deferred) {
var reject = deferred.reject;
deferred.reject = function (arg) {
deferred.resolve("");
};
return fs_resolver_1.FSResolver.prototype.accept.call(this, uri, deferred);
}
}]);
return ErrorSwallowingFSResolver;
}(fs_resolver_1.FSResolver);
exports.ErrorSwallowingFSResolver = ErrorSwallowingFSResolver;

@@ -10,91 +10,76 @@ /**

*/
// jshint node:true
'use strict';
// jshint -W079
// Promise polyfill
var Promise = global.Promise || require('es6-promise').Promise;
// jshint +W079
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function Deferred() {
var self = this;
this.promise = new Promise(function(resolve, reject) {
self.resolve = resolve;
self.reject = reject;
});
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var resolver_1 = require('./resolver');
/**
* An object that knows how to resolve resources.
* @typedef {Object} Resolver
* @memberof hydrolysis
* @property {function(string, Deferred): boolean} accept Attempt to resolve
* `deferred` with the contents the specified URL. Returns false if the
* Resolver is unable to resolve the URL.
*/
/**
* A FileLoader lets you resolve URLs with a set of potential resolvers.
* @constructor
* @memberof hydrolysis
*/
function FileLoader() {
this.resolvers = [];
// map url -> Deferred
this.requests = {};
}
FileLoader.prototype = {
/**
* Add an instance of a Resolver class to the list of url resolvers
*
* Ordering of resolvers is most to least recently added
* The first resolver to "accept" the url wins.
* @param {Resolver} resolver The resolver to add.
*/
addResolver: function(resolver) {
this.resolvers.push(resolver);
},
var FileLoader = function () {
function FileLoader() {
_classCallCheck(this, FileLoader);
/**
* Return a promise for an absolute url
*
* Url requests are deduplicated by the loader, returning the same Promise for
* identical urls
*
* @param {string} url The absolute url to request.
* @return {Promise.<string>} A promise that resolves to the contents of the URL.
*/
request: function(uri) {
var promise;
this.resolvers = [];
// map url -> Deferred
this.requests = {};
}
/**
* Add an instance of a Resolver class to the list of url resolvers
*
* Ordering of resolvers is most to least recently added
* The first resolver to "accept" the url wins.
* @param {Resolver} resolver The resolver to add.
*/
if (!(uri in this.requests)) {
var handled = false;
var deferred = new Deferred();
this.requests[uri] = deferred;
// loop backwards through resolvers until one "accepts" the request
for (var i = this.resolvers.length - 1, r; i >= 0; i--) {
r = this.resolvers[i];
if (r.accept(uri, deferred)) {
handled = true;
break;
_createClass(FileLoader, [{
key: 'addResolver',
value: function addResolver(resolver) {
this.resolvers.push(resolver);
}
}
}, {
key: 'request',
if (!handled) {
deferred.reject(new Error('no resolver found for ' + uri));
}
/**
* Return a promise for an absolute url
*
* Url requests are deduplicated by the loader, returning the same Promise for
* identical urls
*
* @param {string} url The absolute url to request.
* @return {Promise.<string>} A promise that resolves to the contents of the URL.
*/
value: function request(uri) {
var promise;
if (!(uri in this.requests)) {
var handled = false;
var deferred = new resolver_1.Deferred();
this.requests[uri] = deferred;
// loop backwards through resolvers until one "accepts" the request
for (var i = this.resolvers.length - 1; i >= 0; i--) {
var r = this.resolvers[i];
if (r.accept(uri, deferred)) {
handled = true;
break;
}
}
if (!handled) {
deferred.reject(new Error('no resolver found for ' + uri));
}
promise = deferred.promise;
} else {
promise = this.requests[uri].promise;
}
return promise;
}
}]);
promise = deferred.promise;
} else {
promise = this.requests[uri].promise;
}
return FileLoader;
}();
return promise;
}
};
module.exports = FileLoader;
exports.FileLoader = FileLoader;
;

@@ -10,102 +10,91 @@ /**

*/
// jshint node:true
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var fs = require('fs');
var path = require('path');
var pathIsAbsolute = require('path-is-absolute');
var url = require('url');
function getFile(filePath, deferred, secondPath) {
fs.readFile(filePath, 'utf-8', function(err, content) {
if (err) {
if (secondPath) {
getFile(secondPath, deferred);
} else {
console.log("ERROR finding " + filePath);
deferred.reject(err);
}
} else {
deferred.resolve(content);
}
});
fs.readFile(filePath, 'utf-8', function (err, content) {
if (err) {
if (secondPath) {
getFile(secondPath, deferred);
} else {
console.log("ERROR finding " + filePath);
deferred.reject(err);
}
} else {
deferred.resolve(content);
}
});
}
/**
* Returns true if `patha` is a sibling or aunt of `pathb`.
* @return {boolean}
*/
function isSiblingOrAunt(patha, pathb) {
var parent = path.dirname(patha);
if (pathb.indexOf(patha) === -1 && pathb.indexOf(parent) === 0) {
return true;
}
return false;
var parent = path.dirname(patha);
if (pathb.indexOf(patha) === -1 && pathb.indexOf(parent) === 0) {
return true;
}
return false;
}
/**
* Change `localPath` from a sibling of `basePath` to be a child of
* `basePath` joined with `redirect`.
* @return {string}
*/
function redirectSibling(basePath, localPath, redirect) {
var parent = path.dirname(basePath);
var redirected = path.join(basePath, redirect, localPath.slice(parent.length));
return redirected;
var parent = path.dirname(basePath);
var redirected = path.join(basePath, redirect, localPath.slice(parent.length));
return redirected;
}
/**
* Resolves requests via the file system.
* @constructor
* @memberof hydrolysis
* @param {Object} config configuration options.
* @param {string} config.host Hostname to match for absolute urls.
* Matches "/" by default
* @param {string} config.basePath Prefix directory for components in url.
* Defaults to "/".
* @param {string} config.root Filesystem root to search. Defaults to the
* current working directory.
* @param {string} config.redirect Where to redirect lookups to siblings.
*/
function FSResolver(config) {
this.config = config || {};
}
FSResolver.prototype = {
accept: function(uri, deferred) {
var parsed = url.parse(uri);
var host = this.config.host;
var base = this.config.basePath && decodeURIComponent(this.config.basePath);
var root = this.config.root && path.normalize(this.config.root);
var redirect = this.config.redirect;
var local;
var FSResolver = function () {
function FSResolver(config) {
_classCallCheck(this, FSResolver);
if (!parsed.hostname || parsed.hostname === host) {
local = parsed.pathname;
this.config = config || {};
}
if (local) {
// un-escape HTML escapes
local = decodeURIComponent(local);
if (base) {
local = path.relative(base, local);
}
if (root) {
local = path.join(root, local);
}
_createClass(FSResolver, [{
key: 'accept',
value: function accept(uri, deferred) {
var parsed = url.parse(uri);
var host = this.config.host;
var base = this.config.basePath && decodeURIComponent(this.config.basePath);
var root = this.config.root && path.normalize(this.config.root);
var redirect = this.config.redirect;
var local;
if (!parsed.hostname || parsed.hostname === host) {
local = parsed.pathname;
}
if (local) {
// un-escape HTML escapes
local = decodeURIComponent(local);
if (base) {
local = path.relative(base, local);
}
if (root) {
local = path.join(root, local);
}
var backup;
if (redirect && isSiblingOrAunt(root, local)) {
backup = redirectSibling(root, local, redirect);
}
getFile(local, deferred, backup);
return true;
}
return false;
}
}]);
var backup;
if (redirect && isSiblingOrAunt(root, local)) {
backup = redirectSibling(root, local, redirect);
}
return FSResolver;
}();
getFile(local, deferred, backup);
return true;
}
return false;
}
};
module.exports = FSResolver;
exports.FSResolver = FSResolver;
;

@@ -10,36 +10,46 @@ /**

*/
// jshint node:true
'use strict';
/**
* A resolver that resolves to null any uri matching config.
* @constructor
* @memberof hydrolysis
* @param {string} config The url to `accept`.
* A resolver that resolves to empty string any uri that matches config.
*/
function NoopResolver(config) {
this.config = config;
}
NoopResolver.prototype = {
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
/**
* @param {string} uri The absolute URI being requested.
* @param {!Deferred} deferred The deferred promise that should be resolved if
* this resolver handles the URI.
* @return {boolean} Whether the URI is handled by this resolver.
*/
accept: function(uri, deferred) {
if (!this.config.test) {
if (uri.search(this.config) == -1) {
return false;
}
} else if (!this.config.test(uri)) return false;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
deferred.resolve('');
return true;
}
};
var NoopResolver = function () {
function NoopResolver(config) {
_classCallCheck(this, NoopResolver);
module.exports = NoopResolver;
this.config = config;
}
/**
* @param {string} uri The absolute URI being requested.
* @param {!Deferred} deferred The deferred promise that should be resolved if
* this resolver will handle the URI.
* @return {boolean} Whether the URI is handled by this resolver.
*/
_createClass(NoopResolver, [{
key: 'accept',
value: function accept(uri, deferred) {
var config = this.config;
if (typeof config === 'string') {
if (uri.search(config) == -1) {
return false;
}
} else {
if (!config.test(uri)) {
return false;
}
}
deferred.resolve('');
return true;
}
}]);
return NoopResolver;
}();
exports.NoopResolver = NoopResolver;

@@ -10,12 +10,15 @@ /**

*/
// jshint node:true
'use strict';
var fs = require('fs');
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var path = require('path');
var url = require('url');
var FSResolver = require('./fs-resolver');
var fs_resolver_1 = require('./fs-resolver');
/**

@@ -31,47 +34,32 @@ * A single redirect configuration

*/
function ProtocolRedirect(config){
this.protocol = config.protocol;
this.hostname = config.hostname;
this.path = config.path;
this.redirectPath = config.redirectPath;
}
ProtocolRedirect.prototype = {
/**
* The protocol this redirect matches.
* @type {string}
*/
protocol: null,
/**
* The host name this redirect matches.
* @type {string}
*/
hostname: null,
var ProtocolRedirect = function () {
function ProtocolRedirect(config) {
_classCallCheck(this, ProtocolRedirect);
/**
* The part of the path to match and replace with 'redirectPath'
* @type {string}
*/
path: null,
this.protocol = config.protocol;
this.hostname = config.hostname;
this.path = config.path;
this.redirectPath = config.redirectPath;
}
/**
* The local filesystem path that should replace "protocol://hosname/path/"
* @type {string}
*/
redirectPath: null,
_createClass(ProtocolRedirect, [{
key: 'redirect',
value: function redirect(uri) {
var parsed = url.parse(uri);
if (this.protocol !== parsed.protocol) {
return null;
} else if (this.hostname !== parsed.hostname) {
return null;
} else if (parsed.pathname.indexOf(this.path) !== 0) {
return null;
}
return path.join(this.redirectPath, parsed.pathname.slice(this.path.length));
}
}]);
redirect: function redirect(uri) {
var parsed = url.parse(uri);
if (this.protocol !== parsed.protocol) {
return null;
} else if (this.hostname !== parsed.hostname) {
return null;
} else if (parsed.pathname.indexOf(this.path) !== 0) {
return null;
}
return path.join(this.redirectPath,
parsed.pathname.slice(this.path.length));
}
};
return ProtocolRedirect;
}();
;
/**

@@ -87,23 +75,32 @@ * Resolves protocol://hostname/path to the local filesystem.

*/
function RedirectResolver(config) {
FSResolver.call(this, config);
this.redirects = config.redirects || [];
}
RedirectResolver.prototype = Object.create(FSResolver.prototype);
var RedirectResolver = function (_fs_resolver_1$FSReso) {
_inherits(RedirectResolver, _fs_resolver_1$FSReso);
RedirectResolver.prototype.accept = function(uri, deferred) {
for (var i = 0; i < this.redirects.length; i++) {
var redirected = this.redirects[i].redirect(uri);
if (redirected) {
return FSResolver.prototype.accept.call(this, redirected, deferred);
function RedirectResolver(config) {
_classCallCheck(this, RedirectResolver);
var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(RedirectResolver).call(this, config));
_this.redirects = config.redirects || [];
return _this;
}
}
return false;
};
RedirectResolver.prototype.constructor = RedirectResolver;
RedirectResolver.ProtocolRedirect = ProtocolRedirect;
_createClass(RedirectResolver, [{
key: 'accept',
value: function accept(uri, deferred) {
for (var i = 0; i < this.redirects.length; i++) {
var redirected = this.redirects[i].redirect(uri);
if (redirected) {
return fs_resolver_1.FSResolver.prototype.accept.call(this, redirected, deferred);
}
}
return false;
}
}]);
return RedirectResolver;
}(fs_resolver_1.FSResolver);
module.exports = RedirectResolver;
RedirectResolver.ProtocolRedirect = ProtocolRedirect;
exports.RedirectResolver = RedirectResolver;

@@ -10,46 +10,51 @@ /**

*/
// jshint node:true
'use strict';
/**
* A resolver that resolves to `config.content` any uri matching config.
* @constructor
* @memberof hydrolysis
* @param {string|RegExp} config.url The url or rejex to accept.
* @param {string} config.content The content to serve for `url`.
*/
function StringResolver(config) {
this.url = config.url;
this.content = config.content;
if (!this.url || !this.content) {
throw new Error("Must provide a url and content to the string resolver.");
}
}
StringResolver.prototype = {
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
/**
* @param {string} uri The absolute URI being requested.
* @param {!Deferred} deferred The deferred promise that should be resolved if
* this resolver handles the URI.
* @return {boolean} Whether the URI is handled by this resolver.
*/
accept: function(uri, deferred) {
if (this.url.test) {
// this.url is a regex
if (!this.url.test(uri)) {
return false;
}
} else {
// this.url is a string
if (uri.search(this.url) == -1) {
return false;
}
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var StringResolver = function () {
function StringResolver(config) {
_classCallCheck(this, StringResolver);
this.url = config.url;
this.content = config.content;
if (!this.url || !this.content) {
throw new Error("Must provide a url and content to the string resolver.");
}
}
deferred.resolve(this.content);
return true;
}
};
/**
* @param {string} uri The absolute URI being requested.
* @param {!Deferred} deferred The deferred promise that should be resolved if
* this resolver handles the URI.
* @return {boolean} Whether the URI is handled by this resolver.
*/
module.exports = StringResolver;
_createClass(StringResolver, [{
key: "accept",
value: function accept(uri, deferred) {
var url = this.url;
if (url instanceof RegExp) {
if (!url.test(uri)) {
return false;
}
} else {
if (uri.indexOf(url) == -1) {
return false;
}
}
deferred.resolve(this.content);
return true;
}
}]);
return StringResolver;
}();
exports.StringResolver = StringResolver;
;

@@ -10,45 +10,51 @@ /**

*/
// jshint node:true
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function getFile(url, deferred, config) {
/* global XMLHttpRequest:false */
var x = new XMLHttpRequest();
x.onload = function() {
var status = x.status || 0;
if (status >= 200 && status < 300) {
deferred.resolve(x.response);
} else {
deferred.reject('xhr status: ' + status);
/* global XMLHttpRequest:false */
var x = new XMLHttpRequest();
x.onload = function () {
var status = x.status || 0;
if (status >= 200 && status < 300) {
deferred.resolve(x.response);
} else {
deferred.reject('xhr status: ' + status);
}
};
x.onerror = function (e) {
deferred.reject(e);
};
x.open('GET', url, true);
if (config && config.responseType) {
x.responseType = config.responseType;
}
};
x.onerror = function(e) {
deferred.reject(e);
};
x.open('GET', url, true);
if (config && config.responseType) {
x.responseType = config.responseType;
}
x.send();
x.send();
}
/**
* Construct a resolver that requests resources over XHR.
* @constructor
* @memberof hydrolysis
* @param {Object} config configuration arguments.
* @param {string} config.responseType Type of object to be returned by the
* XHR. Defaults to 'text', accepts 'document', 'arraybuffer', and 'json'.
*/
function XHRResolver(config) {
this.config = config;
}
XHRResolver.prototype = {
accept: function(uri, deferred) {
getFile(uri, deferred, this.config);
return true;
}
};
module.exports = XHRResolver;
var XHRResolver = function () {
function XHRResolver(config) {
_classCallCheck(this, XHRResolver);
this.config = config;
}
_createClass(XHRResolver, [{
key: 'accept',
value: function accept(uri, deferred) {
getFile(uri, deferred, this.config);
return true;
}
}]);
return XHRResolver;
}();
exports.XHRResolver = XHRResolver;
;
{
"name": "hydrolysis",
"version": "1.22.0",
"version": "1.23.3",
"description": "Breaks polymers into monomers",

@@ -14,2 +14,5 @@ "homepage": "https://github.com/Polymer/hydrolysis",

"files": [
"hydrolysis-analyzer.html",
"hydrolysis.html",
"hydrolysis.js",
"index.js",

@@ -20,22 +23,31 @@ "LICENSE",

"scripts": {
"build": "browserify -d -r ./index.js:hydrolysis -o hydrolysis.js",
"build:watch": "watch 'npm run build' ./lib",
"release": "browserify -r ./index.js:hydrolysis -o hydrolysis.js",
"init": "typings install",
"clean": "rm -rf typings/ lib/ && find src | grep \\.js$ | xargs rm",
"compile": "if [ ! -f typings/main.d.ts ]; then npm run init; fi; tsc && babel --presets es2015 -q -d lib src",
"build": "npm run compile && browserify -d -r ./index.js:hydrolysis -o hydrolysis.js",
"release": "npm run compile && browserify -r ./index.js:hydrolysis -o hydrolysis.js",
"build:watch": "watch 'npm run build' ./src",
"apidocs": "node_modules/jsdoc-to-markdown/bin/cli.js {index.js,lib/{analyzer,loader/*}.js} > API.md",
"test": "jshint index.js lib/ && npm run build && wct && mocha test/test.js"
"test": "npm run build && wct && mocha test/test.js",
"testNode": "npm run build && mocha test/test.js"
},
"devDependencies": {
"babel-cli": "^6.4.5",
"babel-preset-es2015": "^6.1.18",
"babelify": "^7.2.0",
"browserify": "^9.0.8",
"chai": "^2.2.0",
"jsdoc-to-markdown": "^1.0.3",
"jshint": "^2.7.0",
"mocha": "^2.2.4",
"typescript": "^1.8.0",
"typings": "^0.6.3",
"watch": "latest",
"web-component-tester": "^3.3.21"
"web-component-tester": "^4.2.2"
},
"dependencies": {
"babel-polyfill": "^6.2.0",
"doctrine": "^0.7.0",
"dom5": "^1.1.0",
"es6-promise": "^2.1.0",
"espree": "^2.0.1",
"escodegen": "^1.7.0",
"espree": "^3.1.3",
"estraverse": "^3.1.0",

@@ -42,0 +54,0 @@ "path-is-absolute": "^1.0.0"

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc