polymer-bundler
Version:
Process Web Components into one output file
200 lines • 6.97 kB
JavaScript
/**
* @license
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* The complete set of authors may be found at
* http://polymer.github.io/AUTHORS.txt
* The complete set of contributors may be found at
* http://polymer.github.io/CONTRIBUTORS.txt
* Code distributed by Google as part of the polymer project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
Object.defineProperty(exports, "__esModule", { value: true });
const dom5 = require("dom5");
const parse5_1 = require("parse5");
const matchers = require("./matchers");
/**
* Walk the ancestor nodes from parentNode up to document root, returning the
* first one matching the predicate function.
*/
function findAncestor(ast, predicate) {
// The visited set protects us against circular references.
const visited = new Set();
while (ast.parentNode && !visited.has(ast.parentNode)) {
if (predicate(ast.parentNode)) {
return ast.parentNode;
}
visited.add(ast.parentNode);
ast = ast.parentNode;
}
return undefined;
}
exports.findAncestor = findAncestor;
/**
* Move the `node` to be the immediate sibling after the `target` node.
* TODO(usergenic): Migrate this code to polymer/dom5 and when you do, use
* insertNode which will handle the remove and the splicing in once you have
* the index.
*/
function insertAfter(target, node) {
dom5.remove(node);
const index = target.parentNode.childNodes.indexOf(target);
target.parentNode.childNodes.splice(index + 1, 0, node);
node.parentNode = target.parentNode;
}
exports.insertAfter = insertAfter;
/**
* Move the entire collection of nodes to be the immediate sibling before the
* `after` node.
*/
function insertAllBefore(target, after, nodes) {
let lastNode = after;
for (let n = nodes.length - 1; n >= 0; n--) {
const node = nodes[n];
dom5.insertBefore(target, lastNode, node);
lastNode = node;
}
}
exports.insertAllBefore = insertAllBefore;
/**
* Return true if node is a text node that is empty or consists only of white
* space.
*/
function isBlankTextNode(node) {
return node && dom5.isTextNode(node) &&
dom5.getTextContent(node).trim() === '';
}
exports.isBlankTextNode = isBlankTextNode;
/**
* Return true if comment starts with a `!` character indicating it is an
* "important" comment, needing preservation.
*/
function isImportantComment(node) {
return !!node.data && !!node.data.match(/^!/);
}
exports.isImportantComment = isImportantComment;
/**
* Return true if node is a comment node consisting of a license (annotated by
* the `@license` string.)
*/
function isLicenseComment(node) {
if (dom5.isCommentNode(node)) {
return dom5.getTextContent(node).indexOf('@license') > -1;
}
return false;
}
exports.isLicenseComment = isLicenseComment;
/**
* Return true if node is a comment node that is a server-side-include. E.g.
* <!--#directive ...-->
*/
function isServerSideIncludeComment(node) {
return !!node.data && !!node.data.match(/^#/);
}
exports.isServerSideIncludeComment = isServerSideIncludeComment;
/**
* Inserts the node as the first child of the parent.
* TODO(usergenic): Migrate this code to polymer/dom5
*/
function prepend(parent, node) {
if (parent.childNodes && parent.childNodes.length) {
dom5.insertBefore(parent, parent.childNodes[0], node);
}
else {
dom5.append(parent, node);
}
}
exports.prepend = prepend;
/**
* Removes an AST Node and the whitespace-only text node following it, if
* present.
*/
function removeElementAndNewline(node, replacement) {
const siblings = Array.from(node.parentNode.childNodes);
let nextIdx = siblings.indexOf(node) + 1;
let next = siblings[nextIdx];
while (next && isBlankTextNode(next)) {
dom5.remove(next);
next = siblings[++nextIdx];
}
if (replacement) {
dom5.replace(node, replacement);
}
else {
dom5.remove(node);
}
}
exports.removeElementAndNewline = removeElementAndNewline;
/**
* A common pattern is to parse html and then remove the fake nodes.
* This function dries up that pattern.
*/
function parse(html, options) {
const ast = parse5_1.parse(html, Object.assign({ locationInfo: true }, options));
dom5.removeFakeRootElements(ast);
return ast;
}
exports.parse = parse;
/**
* Returns true if the nodes are given in order as they appear in the source
* code.
* TODO(usergenic): Port this to `dom5` and do it with typings for location info
* instead of all of these string-based lookups.
*/
function inSourceOrder(left, right) {
const l = left.__location, r = right.__location;
return l && r && l['line'] && r['line'] &&
(l['line'] < r['line'] ||
(l['line'] === r['line'] && l['col'] < r['col']));
}
exports.inSourceOrder = inSourceOrder;
/**
* Returns true if both nodes have the same line and column according to their
* location info.
* TODO(usergenic): Port this to `dom5` and do it with typings for location info
* instead of all of these string-based lookups.
*/
function isSameNode(node1, node2) {
const l1 = node1.__location, l2 = node2.__location;
return !!(l1 && l2 && l1['line'] && l1['col'] && l1['line'] === l2['line'] &&
l1['col'] === l2['col']);
}
exports.isSameNode = isSameNode;
/**
* Return all sibling nodes following node.
*/
function siblingsAfter(node) {
const siblings = Array.from(node.parentNode.childNodes);
return siblings.slice(siblings.indexOf(node) + 1);
}
exports.siblingsAfter = siblingsAfter;
/**
* Find all comment nodes in the document, removing them from the document
* if they are note license comments, and if they are license comments,
* deduplicate them and prepend them in document's head.
*/
function stripComments(document) {
const uniqueLicenseTexts = new Set();
const licenseComments = [];
for (const comment of dom5.nodeWalkAll(document, dom5.isCommentNode, undefined, dom5.childNodesIncludeTemplate)) {
if (isImportantComment(comment) || isServerSideIncludeComment(comment)) {
continue;
}
// Make whitespace uniform so we can deduplicate based on actual content.
const commentText = (comment.data || '').replace(/\s+/g, ' ').trim();
if (isLicenseComment(comment) && !uniqueLicenseTexts.has(commentText)) {
uniqueLicenseTexts.add(commentText);
licenseComments.push(comment);
}
removeElementAndNewline(comment);
}
const prependTarget = dom5.query(document, matchers.head) || document;
for (const comment of licenseComments.reverse()) {
prepend(prependTarget, comment);
}
}
exports.stripComments = stripComments;
//# sourceMappingURL=parse5-utils.js.map
;