New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

polymer-bundler

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

polymer-bundler - npm Package Compare versions

Comparing version 2.0.0-pre.5 to 2.0.0-pre.6

lib/bundle-strategy.d.ts

13

CHANGELOG.md

@@ -8,4 +8,15 @@ # Change Log

## Unreleased
## 2.0.0-pre.6 - 2017-02-17
- Handle `<base href="...">` values correctly when inlining imports.
- Apply `<base target="...">` to links and forms in the same document as
appropriate.
- License comments are deduplicated properly now.
- Server-Side Include comments `<!--# ... -->` are no longer stripped.
- Fixed a bug where element attributes inside `<template>` tags were not
being rewritten when html imports were inlined.
## 2.0.0-pre.5 - 2017-02-14
- Handle base tags when resolving imports.
- Handle base tags when resolving the dependency graph of imports.

@@ -12,0 +23,0 @@ ## 2.0.0-pre.4 - 2017-02-06

@@ -25,2 +25,7 @@ import { ASTNode } from 'parse5';

/**
* Return true if node is a comment node that is a server-side-include. E.g.
* <!--#directive ...-->
*/
export declare function isServerSideIncludeComment(node: ASTNode): boolean;
/**
* Inserts the node as the first child of the parent.

@@ -27,0 +32,0 @@ * TODO(usergenic): Migrate this code to polymer/dom5

34

lib/ast-utils.js

@@ -64,2 +64,10 @@ /**

/**
* 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.

@@ -111,16 +119,22 @@ * TODO(usergenic): Migrate this code to polymer/dom5

function stripComments(document) {
// Use of a Map keyed by comment text enables deduplication.
const comments = new Map();
dom5.nodeWalkAll(document, dom5.isCommentNode).forEach((comment) => {
comments.set(comment.data || '', comment);
const uniqueLicenseTexts = new Set();
const licenseComments = [];
for (const comment of dom5.nodeWalkAll(document, dom5.isCommentNode)) {
if (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 head = dom5.query(document, matchers.head);
for (const comment of comments.values()) {
if (isLicenseComment(comment)) {
prepend(head || document, comment);
}
}
const prependTarget = dom5.query(document, matchers.head) || document;
for (const comment of licenseComments.reverse()) {
prepend(prependTarget, comment);
}
}
exports.stripComments = stripComments;
//# sourceMappingURL=ast-utils.js.map

@@ -134,3 +134,3 @@ "use strict";

this._appendHtmlImportsForBundle(document, docBundle);
importUtils.rewriteImportedUrls(document, docUrl, docUrl);
importUtils.rewriteDocumentToEmulateBaseTag(docUrl, document);
yield this._inlineHtmlImports(docUrl, document, docBundle, bundleManifest);

@@ -137,0 +137,0 @@ if (this.enableScriptInlining) {

import { Analyzer } from 'polymer-analyzer';
import { UrlString } from './url-utils';
export interface DepsIndex {
entrypointToDeps: Map<string, Set<string>>;
entrypointToDeps: Map<UrlString, Set<UrlString>>;
}
export declare function buildDepsIndex(entrypoints: UrlString[], analyzer: Analyzer): Promise<DepsIndex>;

@@ -30,3 +30,3 @@ "use strict";

const lazyImports = new Set();
for (let htmlImport of imports) {
for (const htmlImport of imports) {
try {

@@ -33,0 +33,0 @@ console.assert(htmlImport.url, 'htmlImport: %s has no url', htmlImport);

@@ -19,2 +19,3 @@ import { ASTNode } from 'parse5';

export declare function inlineStylesheet(docUrl: UrlString, cssLink: ASTNode, loader: (url: UrlString) => Promise<string>): Promise<ASTNode | undefined>;
export declare function rewriteDocumentToEmulateBaseTag(docUrl: UrlString, ast: ASTNode): void;
/**

@@ -25,2 +26,2 @@ * Walk through an import document, and rewrite all urls so they are

*/
export declare function rewriteImportedUrls(importDoc: ASTNode, importUrl: UrlString, mainDocUrl: UrlString): void;
export declare function rewriteDocumentBaseUrl(ast: ASTNode, oldBaseUrl: UrlString, newBaseUrl: UrlString): void;

@@ -23,11 +23,9 @@ "use strict";

*/
const path = require("path");
const urlLib = require("url");
const pathPosix = path.posix;
const dom5 = require("dom5");
const encode_string_1 = require("./third_party/UglifyJS2/encode-string");
const parse5 = require("parse5");
const urlLib = require("url");
const astUtils = require("./ast-utils");
const constants_1 = require("./constants");
const astUtils = require("./ast-utils");
const matchers = require("./matchers");
const encode_string_1 = require("./third_party/UglifyJS2/encode-string");
const urlUtils = require("./url-utils");

@@ -88,3 +86,4 @@ // TODO(usergenic): Revisit the organization of this module and *consider*

const importDoc = parse5.parseFragment(importSource);
rewriteImportedUrls(importDoc, resolvedImportUrl, docUrl);
rewriteDocumentToEmulateBaseTag(resolvedImportUrl, importDoc);
rewriteDocumentBaseUrl(importDoc, resolvedImportUrl, docUrl);
const nestedImports = dom5.queryAll(importDoc, matchers.htmlImport);

@@ -150,3 +149,3 @@ // Move all of the import doc content after the html import.

const media = dom5.getAttribute(cssLink, 'media');
const resolvedStylesheetContent = _rewriteImportedStyleTextUrls(resolvedUrl, docUrl, stylesheetContent);
const resolvedStylesheetContent = rewriteCssTextBaseUrl(stylesheetContent, resolvedUrl, docUrl);
const styleNode = dom5.constructors.element('style');

@@ -162,2 +161,29 @@ if (media) {

exports.inlineStylesheet = inlineStylesheet;
/*
* Given an import document with a base tag, transform all of its URLs and set
* link and form target attributes and remove the base tag.
*/
function rewriteDocumentToEmulateBaseTag(docUrl, ast) {
const baseTag = dom5.query(ast, matchers.base);
const p = dom5.predicates;
// If there's no base tag, there's nothing to do.
if (!baseTag) {
return;
}
for (const baseTag of dom5.queryAll(ast, matchers.base)) {
astUtils.removeElementAndNewline(baseTag);
}
if (dom5.predicates.hasAttr('href')(baseTag)) {
const baseUrl = urlLib.resolve(docUrl, dom5.getAttribute(baseTag, 'href'));
rewriteDocumentBaseUrl(ast, baseUrl, docUrl);
}
if (p.hasAttr('target')(baseTag)) {
const baseTarget = dom5.getAttribute(baseTag, 'target');
const tagsToTarget = dom5.queryAll(ast, p.AND(p.OR(p.hasTagName('a'), p.hasTagName('form')), p.NOT(p.hasAttr('target'))));
for (const tag of tagsToTarget) {
dom5.setAttribute(tag, 'target', baseTarget);
}
}
}
exports.rewriteDocumentToEmulateBaseTag = rewriteDocumentToEmulateBaseTag;
/**

@@ -168,15 +194,26 @@ * Walk through an import document, and rewrite all urls so they are

*/
function rewriteImportedUrls(importDoc, importUrl, mainDocUrl) {
_rewriteImportedElementAttrUrls(importDoc, importUrl, mainDocUrl);
_rewriteImportedStyleUrls(importDoc, importUrl, mainDocUrl);
_setImportedDomModuleAssetpaths(importDoc, importUrl, mainDocUrl);
function rewriteDocumentBaseUrl(ast, oldBaseUrl, newBaseUrl) {
rewriteElementAttrsBaseUrl(ast, oldBaseUrl, newBaseUrl);
rewriteStyleTagsBaseUrl(ast, oldBaseUrl, newBaseUrl);
setDomModuleAssetpaths(ast, oldBaseUrl, newBaseUrl);
}
exports.rewriteImportedUrls = rewriteImportedUrls;
exports.rewriteDocumentBaseUrl = rewriteDocumentBaseUrl;
/**
* Given a string of CSS, return a version where all occurrences of urls,
* have been rewritten based on the relationship of the old base url to the
* new base url.
*/
function rewriteCssTextBaseUrl(cssText, oldBaseUrl, newBaseUrl) {
return cssText.replace(constants_1.default.URL, (match) => {
let path = match.replace(/["']/g, '').slice(4, -1);
path = urlUtils.rewriteHrefBaseUrl(path, oldBaseUrl, newBaseUrl);
return 'url("' + path + '")';
});
}
/**
* Find all element attributes which express urls and rewrite them so they
* are correctly relative to the main document url as they've been
* imported from the import url.
* are based on the relationship of the old base url to the new base url.
*/
function _rewriteImportedElementAttrUrls(importDoc, importUrl, mainDocUrl) {
const nodes = dom5.queryAll(importDoc, matchers.urlAttrs);
function rewriteElementAttrsBaseUrl(ast, oldBaseUrl, newBaseUrl) {
const nodes = dom5.queryAll(ast, matchers.urlAttrs, undefined, dom5.childNodesIncludeTemplate);
for (const node of nodes) {

@@ -188,11 +225,7 @@ for (const attr of constants_1.default.URL_ATTR) {

if (attr === 'style') {
relUrl =
_rewriteImportedStyleTextUrls(importUrl, mainDocUrl, attrValue);
relUrl = rewriteCssTextBaseUrl(attrValue, oldBaseUrl, newBaseUrl);
}
else {
relUrl =
urlUtils.rewriteImportedRelPath(importUrl, mainDocUrl, attrValue);
if (attr === 'assetpath' && relUrl.slice(-1) !== '/') {
relUrl += '/';
}
urlUtils.rewriteHrefBaseUrl(attrValue, oldBaseUrl, newBaseUrl);
}

@@ -205,25 +238,10 @@ dom5.setAttribute(node, attr, relUrl);

/**
* Given a string of CSS, return a version where all occurrences of urls,
* have been rewritten based on the relationship of the import url to the
* main doc url.
* TODO(usergenic): This is a static method that should probably be moved to
* urlUtils or similar.
* Find all urls in imported style nodes and rewrite them so they are based
* on the relationship of the old base url to the new base url.
*/
function _rewriteImportedStyleTextUrls(importUrl, mainDocUrl, cssText) {
return cssText.replace(constants_1.default.URL, (match) => {
let path = match.replace(/["']/g, '').slice(4, -1);
path = urlUtils.rewriteImportedRelPath(importUrl, mainDocUrl, path);
return 'url("' + path + '")';
});
}
/**
* Find all urls in imported style nodes and rewrite them so they are now
* correctly relative to the main document url as they've been imported from
* the import url.
*/
function _rewriteImportedStyleUrls(importDoc, importUrl, mainDocUrl) {
const styleNodes = dom5.queryAll(importDoc, matchers.styleMatcher, undefined, dom5.childNodesIncludeTemplate);
function rewriteStyleTagsBaseUrl(ast, oldBaseUrl, newBaseUrl) {
const styleNodes = dom5.queryAll(ast, matchers.styleMatcher, undefined, dom5.childNodesIncludeTemplate);
for (const node of styleNodes) {
let styleText = dom5.getTextContent(node);
styleText = _rewriteImportedStyleTextUrls(importUrl, mainDocUrl, styleText);
styleText = rewriteCssTextBaseUrl(styleText, oldBaseUrl, newBaseUrl);
dom5.setTextContent(node, styleText);

@@ -234,13 +252,16 @@ }

* Set the assetpath attribute of all imported dom-modules which don't yet
* have them.
* have them if the base urls are different.
*/
function _setImportedDomModuleAssetpaths(importDoc, importUrl, mainDocUrl) {
const domModules = dom5.queryAll(importDoc, matchers.domModuleWithoutAssetpath);
function setDomModuleAssetpaths(ast, oldBaseUrl, newBaseUrl) {
const domModules = dom5.queryAll(ast, matchers.domModuleWithoutAssetpath);
for (let i = 0, node; i < domModules.length; i++) {
node = domModules[i];
let assetPathUrl = urlUtils.rewriteImportedRelPath(importUrl, mainDocUrl, '');
assetPathUrl = pathPosix.dirname(assetPathUrl) + '/';
dom5.setAttribute(node, 'assetpath', assetPathUrl);
const assetPathUrl = urlUtils.relativeUrl(newBaseUrl, urlUtils.stripUrlFileSearchAndHash(oldBaseUrl));
// There's no reason to set an assetpath on a dom-module if its different
// from the document's base.
if (assetPathUrl !== '') {
dom5.setAttribute(node, 'assetpath', assetPathUrl);
}
}
}
//# sourceMappingURL=import-utils.js.map
"use strict";
var __awaiter = (this && this.__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());
});
};
/**

@@ -33,57 +41,48 @@ * @license

function bundle(inputPath, opts) {
const bundlerOpts = opts || {};
if (!bundlerOpts.analyzer) {
bundlerOpts.analyzer = new polymer_analyzer_1.Analyzer({ urlLoader: new fs_url_loader_1.FSUrlLoader() });
}
bundler = new bundler_1.Bundler(bundlerOpts);
return bundler.bundle([inputPath])
.then((documents) => documents.get(inputPath).ast);
return __awaiter(this, void 0, void 0, function* () {
const bundlerOpts = opts || {};
if (!bundlerOpts.analyzer) {
bundlerOpts.analyzer = new polymer_analyzer_1.Analyzer({ urlLoader: new fs_url_loader_1.FSUrlLoader() });
}
bundler = new bundler_1.Bundler(bundlerOpts);
const documents = yield bundler.bundle([inputPath]);
return documents.get(inputPath).ast;
});
}
suite('Default Options', () => {
test('imports removed', () => {
test('imports removed', () => __awaiter(this, void 0, void 0, function* () {
const imports = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'), preds.hasAttr('href'), preds.NOT(preds.hasAttrValue('type', 'css')));
return bundle(inputPath).then((doc) => {
assert.equal(dom5.queryAll(doc, imports).length, 0);
});
});
test('imports were deduplicated', () => {
return bundle(inputPath).then((doc) => {
assert.equal(dom5.queryAll(doc, preds.hasTagName('dom-module')).length, 1);
});
});
assert.equal(dom5.queryAll(yield bundle(inputPath), imports).length, 0);
}));
test('imports were deduplicated', () => __awaiter(this, void 0, void 0, function* () {
assert.equal(dom5.queryAll(yield bundle(inputPath), preds.hasTagName('dom-module'))
.length, 1);
}));
});
test('svg is nested correctly', () => {
return bundle(inputPath).then((doc) => {
const svg = dom5.query(doc, matchers.template)['content'].childNodes[1];
assert.equal(svg.childNodes.filter(dom5.isElement).length, 6);
});
});
test('import bodies are in one hidden div', () => {
return bundle(inputPath).then((doc) => {
assert.equal(dom5.queryAll(doc, matchers.hiddenDiv).length, 1);
});
});
test('dom-modules have assetpath', () => {
test('svg is nested correctly', () => __awaiter(this, void 0, void 0, function* () {
const svg = dom5.query(yield bundle(inputPath), matchers.template)['content']
.childNodes[1];
assert.equal(svg.childNodes.filter(dom5.isElement).length, 6);
}));
test('import bodies are in one hidden div', () => __awaiter(this, void 0, void 0, function* () {
assert.equal(dom5.queryAll(yield bundle(inputPath), matchers.hiddenDiv).length, 1);
}));
test('dom-modules have assetpath', () => __awaiter(this, void 0, void 0, function* () {
const assetpath = preds.AND(preds.hasTagName('dom-module'), preds.hasAttrValue('assetpath', 'imports/'));
return bundle(inputPath).then((doc) => {
assert.ok(dom5.query(doc, assetpath), 'assetpath set');
});
});
test('output file is forced utf-8', () => {
assert.ok(dom5.query(yield bundle(inputPath), assetpath), 'assetpath set');
}));
test('output file is forced utf-8', () => __awaiter(this, void 0, void 0, function* () {
const meta = preds.AND(preds.hasTagName('meta'), preds.hasAttrValue('charset', 'UTF-8'));
return bundle(inputPath).then((doc) => {
assert.ok(dom5.query(doc, meta));
});
});
test.skip('Handle <base> tag', () => {
assert.ok(dom5.query(yield bundle(inputPath), meta));
}));
test('Handle <base> tag', () => __awaiter(this, void 0, void 0, function* () {
const span = preds.AND(preds.hasTagName('span'), preds.hasAttrValue('href', 'imports/hello'));
const a = preds.AND(preds.hasTagName('a'), preds.hasAttrValue('href', 'imports/sub-base/sub-base.html'));
return bundle('html/base.html').then((doc) => {
const spanHref = dom5.query(doc, span);
assert.ok(spanHref);
const anchorRef = dom5.query(doc, a);
assert.ok(anchorRef);
});
});
test('Imports in <body> are handled correctly', () => {
const doc = yield bundle('test/html/base.html');
const spanHref = dom5.query(doc, span);
assert.ok(spanHref);
const anchorRef = dom5.query(doc, a);
assert.ok(anchorRef);
}));
test('Imports in <body> are handled correctly', () => __awaiter(this, void 0, void 0, function* () {
const importMatcher = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'));

@@ -93,99 +92,87 @@ const bodyContainerMatcher = preds.AND(preds.hasTagName('div'), preds.hasAttr('hidden'), preds.hasAttr('by-polymer-bundler'));

const divExpected = preds.AND(preds.hasTagName('div'), preds.hasAttrValue('id', 'imported'));
return bundle('test/html/import-in-body.html').then(function (doc) {
const imports = dom5.queryAll(doc, importMatcher);
assert.equal(imports.length, 0);
const bodyContainer = dom5.query(doc, bodyContainerMatcher);
const scriptActual = dom5.query(doc, scriptExpected).parentNode;
const divActual = dom5.query(doc, divExpected).parentNode;
assert.equal(bodyContainer, scriptActual);
assert.equal(bodyContainer, divActual);
const doc = yield bundle('test/html/import-in-body.html');
const imports = dom5.queryAll(doc, importMatcher);
assert.equal(imports.length, 0);
const bodyContainer = dom5.query(doc, bodyContainerMatcher);
const scriptActual = dom5.query(doc, scriptExpected).parentNode;
const divActual = dom5.query(doc, divExpected).parentNode;
assert.equal(bodyContainer, scriptActual);
assert.equal(bodyContainer, divActual);
}));
test('Scripts are not inlined by default', () => __awaiter(this, void 0, void 0, function* () {
const scripts = dom5.queryAll(yield bundle('test/html/external.html'), matchers.externalJavascript);
assert.isAbove(scripts.length, 0, 'scripts were inlined');
scripts.forEach(function (s) {
assert.equal(dom5.getTextContent(s), '', 'script src should be empty');
});
});
test('Scripts are not inlined by default', () => {
const externalJS = matchers.externalJavascript;
return bundle('test/html/external.html').then((doc) => {
const scripts = dom5.queryAll(doc, externalJS);
assert.isAbove(scripts.length, 0, 'scripts were inlined');
scripts.forEach(function (s) {
assert.equal(dom5.getTextContent(s), '', 'script src should be empty');
});
});
});
test('Paths for import bodies are resolved correctly', () => {
}));
test('Paths for import bodies are resolved correctly', () => __awaiter(this, void 0, void 0, function* () {
const anchorMatcher = preds.hasTagName('a');
const input = 'test/html/multiple-imports.html';
return bundle(input).then((doc) => {
const anchor = dom5.query(doc, anchorMatcher);
const href = dom5.getAttribute(anchor, 'href');
assert.equal(href, 'imports/target.html');
});
});
test('Spaces in paths are handled correctly', () => {
const anchor = dom5.query(yield bundle(input), anchorMatcher);
const href = dom5.getAttribute(anchor, 'href');
assert.equal(href, 'imports/target.html');
}));
test('Spaces in paths are handled correctly', () => __awaiter(this, void 0, void 0, function* () {
const input = 'test/html/spaces.html';
const spacesMatcher = preds.AND(preds.hasTagName('dom-module'), preds.hasAttrValue('id', 'space-element'));
return bundle(input).then((doc) => {
const module = dom5.query(doc, spacesMatcher);
assert.ok(module);
});
});
const module = dom5.query(yield bundle(input), spacesMatcher);
assert.ok(module);
}));
suite('Script Ordering', () => {
test('Imports and scripts are ordered correctly', () => {
return bundle('test/html/order-test.html').then((doc) => {
const expectedOrder = [
'first-script',
'second-import-first-script',
'second-import-second-script',
'first-import-first-script',
'first-import-second-script',
'second-script',
'third-script'
];
const expectedSrc = [
'order/first-script.js',
'order/second-import/first-script.js',
'order/second-import/second-script.js',
'order/first-import/first-script.js',
'order/first-import/second-script.js',
'order/second-script.js',
'order/third-script.js'
];
const scripts = dom5.queryAll(doc, matchers.jsMatcher);
const actualOrder = [], actualSrc = [];
scripts.forEach(function (s) {
actualOrder.push(dom5.getAttribute(s, 'id'));
actualSrc.push(dom5.getAttribute(s, 'src'));
});
assert.deepEqual(actualOrder, expectedOrder, 'order is not as expected');
assert.deepEqual(actualSrc, expectedSrc, 'srcs are not preserved correctly');
test('Imports and scripts are ordered correctly', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/order-test.html');
const expectedOrder = [
'first-script',
'second-import-first-script',
'second-import-second-script',
'first-import-first-script',
'first-import-second-script',
'second-script',
'third-script'
];
const expectedSrc = [
'order/first-script.js',
'order/second-import/first-script.js',
'order/second-import/second-script.js',
'order/first-import/first-script.js',
'order/first-import/second-script.js',
'order/second-script.js',
'order/third-script.js'
];
const scripts = dom5.queryAll(doc, matchers.jsMatcher);
const actualOrder = [], actualSrc = [];
scripts.forEach(function (s) {
actualOrder.push(dom5.getAttribute(s, 'id'));
actualSrc.push(dom5.getAttribute(s, 'src'));
});
});
test('exhaustive script order testing', () => {
return bundle('test/html/script-order/index.html', { inlineScripts: true })
.then((doc) => {
assert(doc);
const serialized = parse5.serialize(doc);
const beforeLoc = serialized.indexOf('window.BeforeJs');
const afterLoc = serialized.indexOf('BeforeJs.value');
assert.isBelow(beforeLoc, afterLoc);
});
});
test('Paths are correct when maintaining order', () => {
return bundle('test/html/recursion/import.html').then((doc) => {
assert(doc);
const scripts = dom5.queryAll(doc, preds.AND(preds.hasTagName('script'), preds.hasAttr('src')));
scripts.forEach(function (s) {
const src = dom5.getAttribute(s, 'src');
assert.equal(src.indexOf('../order'), 0, 'path should start with ../order');
});
});
});
assert.deepEqual(actualOrder, expectedOrder, 'order is not as expected');
assert.deepEqual(actualSrc, expectedSrc, 'srcs are not preserved correctly');
}));
test('exhaustive script order testing', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/script-order/index.html', { inlineScripts: true });
assert(doc);
const serialized = parse5.serialize(doc);
const beforeLoc = serialized.indexOf('window.BeforeJs');
const afterLoc = serialized.indexOf('BeforeJs.value');
assert.isBelow(beforeLoc, afterLoc);
}));
test('Paths are correct when maintaining order', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/recursion/import.html');
assert(doc);
const scripts = dom5.queryAll(doc, preds.AND(preds.hasTagName('script'), preds.hasAttr('src')));
for (const s of scripts) {
const src = dom5.getAttribute(s, 'src');
assert.equal(src.indexOf('../order'), 0, 'path should start with ../order');
}
}));
});
suite('Redirect', () => {
test('Redirected paths load properly', () => {
test('Redirected paths load properly', () => __awaiter(this, void 0, void 0, function* () {
const options = {
redirects: ['chrome://imports/|test/html/imports/', 'biz://cool/|test/html']
};
return bundle('test/html/custom-protocol.html', options)
.then((doc) => assert(doc));
});
const doc = yield bundle('test/html/custom-protocol.html', options);
assert(doc);
}));
// TODO(usergenic): Add tests here to demo common use case of alt domains.

@@ -197,12 +184,9 @@ });

const excludes = ['test/html/imports/simple-import.html'];
test('Excluded imports are not inlined', () => {
const options = { excludes: excludes };
return bundle(inputPath, options).then((doc) => {
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 1);
});
});
test('Excluded imports are not inlined', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle(inputPath, { excludes: excludes });
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 1);
}));
const cssFromExclude = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'), preds.hasAttrValue('type', 'css'));
// TODO(ajo): Fix test with hydrolysis upgrades.
test.skip('Excluded imports are not inlined when behind a redirected URL.', () => {
test.skip('Excluded imports are not inlined when behind a redirected URL.', () => __awaiter(this, void 0, void 0, function* () {
const options = {

@@ -213,83 +197,84 @@ // TODO(usergenic): use non-redirected form of URL (?)

};
return bundle(path.resolve('test/html/custom-protocol-excluded.html'), options)
.then((doc) => {
const imports = dom5.queryAll(doc, htmlImport);
assert.equal(imports.length, 2);
const badCss = dom5.queryAll(doc, cssFromExclude);
assert.equal(badCss.length, 0);
});
});
test('Excluded imports with "Strip Excludes" are removed', () => {
const doc = yield bundle(path.resolve('test/html/custom-protocol-excluded.html'), options);
const imports = dom5.queryAll(doc, htmlImport);
assert.equal(imports.length, 2);
const badCss = dom5.queryAll(doc, cssFromExclude);
assert.equal(badCss.length, 0);
}));
test('Excluded imports with "Strip Excludes" are removed', () => __awaiter(this, void 0, void 0, function* () {
const options = { stripExcludes: excludes };
return bundle(inputPath, options).then((doc) => {
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 0);
});
});
test('Strip Excludes does not have to be exact', () => {
const doc = yield bundle(inputPath, options);
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 0);
}));
test('Strip Excludes does not have to be exact', () => __awaiter(this, void 0, void 0, function* () {
const options = { stripExcludes: ['simple-import'] };
return bundle(inputPath, options).then((doc) => {
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 0);
});
});
test('Excluded comments are removed', () => {
const doc = yield bundle(inputPath, options);
const imports = dom5.queryAll(doc, excluded);
assert.equal(imports.length, 0);
}));
test('Excluded comments are removed', () => __awaiter(this, void 0, void 0, function* () {
const options = { stripComments: true };
return bundle('test/html/comments.html', options).then((doc) => {
const comments = dom5.nodeWalkAll(doc, dom5.isCommentNode);
assert.equal(comments.length, 3);
const commentsExpected = ['@license import 2', '@license import 1', '@license main'];
const commentsActual = comments.map(function (c) {
return dom5.getTextContent(c).trim();
});
assert.deepEqual(commentsExpected, commentsActual);
});
});
test('Comments are kept by default', () => {
const doc = yield bundle('test/html/comments.html', options);
const comments = dom5.nodeWalkAll(doc, dom5.isCommentNode);
const commentsExpected = [
'#important server-side include business',
'# this could be a server-side include too',
'@license common',
'@license main',
'@license import 1',
'@license import 2'
];
const commentsActual = comments.map((c) => dom5.getTextContent(c).trim());
assert.deepEqual(commentsActual, commentsExpected);
}));
test('Comments are kept by default', () => __awaiter(this, void 0, void 0, function* () {
const options = { stripComments: false };
return bundle('test/html/comments.html', options).then((doc) => {
const comments = dom5.nodeWalkAll(doc, dom5.isCommentNode);
const expectedComments = [
'@license main',
'@license import 1',
'comment in import 1',
'@license import 2',
'comment in import 2',
'comment in main'
];
const actualComments = comments.map(function (c) {
return dom5.getTextContent(c).trim();
});
assert.deepEqual(expectedComments, actualComments);
});
});
test('Folder can be excluded', () => {
const doc = yield bundle('test/html/comments.html', options);
const comments = dom5.nodeWalkAll(doc, dom5.isCommentNode);
// NOTE: Explicitly not trimming the expected comments to ensure we keep
// the test fixtures with the same whitespace they currently have.
const expectedComments = [
'#important server-side include business ',
'# this could be a server-side include too ',
' #this is not a server-side include ',
' @license common ',
' @license main ',
'\n@license common\n',
' @license import 1 ',
'\n @license common\n ',
' comment in import 1 ',
' @license import 2 ',
' comment in import 2 ',
' comment in main '
];
const actualComments = comments.map((c) => dom5.getTextContent(c));
assert.deepEqual(actualComments, expectedComments);
}));
test('Folder can be excluded', () => __awaiter(this, void 0, void 0, function* () {
const linkMatcher = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'));
const options = { excludes: ['test/html/imports/'] };
return bundle('test/html/default.html', options).then((doc) => {
const links = dom5.queryAll(doc, linkMatcher);
// one duplicate import is removed. default.html contains this
// duplication:
// <link rel="import" href="imports/simple-import.html">
// <link rel="import" href="imports/simple-import.html">
assert.equal(links.length, 1);
});
});
const doc = yield bundle('test/html/default.html', options);
const links = dom5.queryAll(doc, linkMatcher);
// one duplicate import is removed. default.html contains this
// duplication:
// <link rel="import" href="imports/simple-import.html">
// <link rel="import" href="imports/simple-import.html">
assert.equal(links.length, 1);
}));
});
suite('Inline Scripts', () => {
const options = { inlineScripts: true };
test('All scripts are inlined', () => {
return bundle('test/html/external.html', options).then((doc) => {
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 0);
});
});
test('Remote scripts are kept', () => {
return bundle('test/html/scripts.html', options).then((doc) => {
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 1);
assert.equal(dom5.getAttribute(scripts[0], 'src'), 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js');
});
});
test.skip('Absolute paths are correct for excluded links', () => {
test('All scripts are inlined', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/external.html', options);
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 0);
}));
test('Remote scripts are kept', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/scripts.html', options);
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 1);
assert.equal(dom5.getAttribute(scripts[0], 'src'), 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js');
}));
test.skip('Absolute paths are correct for excluded links', () => __awaiter(this, void 0, void 0, function* () {
const target = 'test/html/external.html';

@@ -301,100 +286,87 @@ const options = {

};
return bundle(target, options).then((doc) => {
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 1);
// TODO(usergenic): assert the src attribute is now
// /myapp/external/external.js
});
});
test('Escape inline <script>', () => {
return bundle('test/html/xss.html', options).then((doc) => {
const script = dom5.query(doc, matchers.inlineJavascript);
assert.include(dom5.getTextContent(script), 'var b = 0<\\/script><script>alert(\'XSS\'); //2;', 'Inline <script> should be escaped');
});
});
test('Inlined Scripts are in the expected order', () => {
return bundle('test/html/reordered/in.html', options).then((doc) => {
const scripts = dom5.queryAll(doc, matchers.inlineJavascript);
const contents = scripts.map(function (script) {
return dom5.getTextContent(script);
});
assert.deepEqual(['"First"', '"Second"'], contents);
});
});
test('Firebase works inlined', () => {
return bundle('test/html/firebase.html', options).then((doc) => {
const scripts = dom5.queryAll(doc, matchers.inlineJavascript);
assert.equal(scripts.length, 1);
const idx = dom5.getTextContent(scripts[0]).indexOf('</script>');
assert(idx === -1, '/script found, should be escaped');
});
});
const doc = yield bundle(target, options);
const scripts = dom5.queryAll(doc, matchers.externalJavascript);
assert.equal(scripts.length, 1);
// TODO(usergenic): assert the src attribute is now
// /myapp/external/external.js
}));
test('Escape inline <script>', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/xss.html', options);
const script = dom5.query(doc, matchers.inlineJavascript);
assert.include(dom5.getTextContent(script), 'var b = 0<\\/script><script>alert(\'XSS\'); //2;', 'Inline <script> should be escaped');
}));
test('Inlined Scripts are in the expected order', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/reordered/in.html', options);
const scripts = dom5.queryAll(doc, matchers.inlineJavascript);
const contents = scripts.map((script) => dom5.getTextContent(script));
assert.deepEqual(['"First"', '"Second"'], contents);
}));
test('Firebase works inlined', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/firebase.html', options);
const scripts = dom5.queryAll(doc, matchers.inlineJavascript);
assert.equal(scripts.length, 1);
const idx = dom5.getTextContent(scripts[0]).indexOf('</script>');
assert(idx === -1, '/script found, should be escaped');
}));
});
suite('Inline CSS', () => {
const options = { inlineCss: true };
test('All styles are inlined', () => {
return bundle(inputPath, options).then((doc) => {
const links = dom5.queryAll(doc, matchers.stylesheetImport);
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(links.length, 0);
assert.equal(styles.length, 2);
});
});
test('Inlined styles have proper paths', () => {
return bundle('test/html/inline-styles.html', options).then((doc) => {
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(styles.length, 2);
const content = dom5.getTextContent(styles[1]);
assert(content.search('imports/foo.jpg') > -1, 'path adjusted');
assert(content.search('@apply') > -1, '@apply kept');
});
});
test('Remote scripts, styles and media queries are preserved', () => {
test('All styles are inlined', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle(inputPath, options);
const links = dom5.queryAll(doc, matchers.stylesheetImport);
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(links.length, 0);
assert.equal(styles.length, 2);
}));
test('Inlined styles have proper paths', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/inline-styles.html', options);
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(styles.length, 2);
const content = dom5.getTextContent(styles[1]);
assert(content.search('imports/foo.jpg') > -1, 'path adjusted');
assert(content.search('@apply') > -1, '@apply kept');
}));
test('Remote scripts, styles and media queries are preserved', () => __awaiter(this, void 0, void 0, function* () {
const input = 'test/html/imports/remote-stylesheet.html';
return bundle(input, options).then((doc) => {
const links = dom5.queryAll(doc, matchers.externalStyle);
assert.equal(links.length, 1);
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(styles.length, 1);
assert.equal(dom5.getAttribute(styles[0], 'media'), '(min-width: 800px)');
});
});
test.skip('Absolute paths are correct', () => {
const doc = yield bundle(input, options);
const links = dom5.queryAll(doc, matchers.externalStyle);
assert.equal(links.length, 1);
const styles = dom5.queryAll(doc, matchers.styleMatcher);
assert.equal(styles.length, 1);
assert.equal(dom5.getAttribute(styles[0], 'media'), '(min-width: 800px)');
}));
test.skip('Absolute paths are correct', () => __awaiter(this, void 0, void 0, function* () {
const root = path.resolve(inputPath, '../..');
const options = { absPathPrefix: root, inlineCss: true };
return bundle('/test/html/default.html', options).then((doc) => {
const links = dom5.queryAll(doc, matchers.ALL_CSS_LINK);
assert.equal(links.length, 0);
});
});
test('Inlined Polymer styles are moved into the <template>', () => {
return bundle('test/html/default.html', options).then((doc) => {
const domModule = dom5.query(doc, preds.hasTagName('dom-module'));
assert(domModule);
const template = dom5.query(domModule, matchers.template);
assert(template);
const style = dom5.queryAll(template.childNodes[0], matchers.styleMatcher);
assert.equal(style.length, 1);
});
});
test('Inlined Polymer styles will force a dom-module to have a template', () => {
return bundle('test/html/inline-styles.html', options).then((doc) => {
const domModule = dom5.query(doc, preds.hasTagName('dom-module'));
assert(domModule);
const template = dom5.query(domModule, matchers.template);
assert(template);
const style = dom5.query(template.childNodes[0], matchers.styleMatcher);
assert(style);
});
});
const doc = yield bundle('/test/html/default.html', options);
const links = dom5.queryAll(doc, matchers.ALL_CSS_LINK);
assert.equal(links.length, 0);
}));
test('Inlined Polymer styles are moved into the <template>', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/default.html', options);
const domModule = dom5.query(doc, preds.hasTagName('dom-module'));
assert(domModule);
const template = dom5.query(domModule, matchers.template);
assert(template);
const style = dom5.queryAll(template.childNodes[0], matchers.styleMatcher);
assert.equal(style.length, 1);
}));
test('Inlined Polymer styles force dom-module to have template', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/inline-styles.html', options);
const domModule = dom5.query(doc, preds.hasTagName('dom-module'));
assert(domModule);
const template = dom5.query(domModule, matchers.template);
assert(template);
const style = dom5.query(template.childNodes[0], matchers.styleMatcher);
assert(style);
}));
});
suite.skip('Add import', () => {
const options = { addedImports: ['imports/comment-in-import.html'] };
test('added import is added to bundled doc', () => {
return bundle('test/html/default.html', options).then((doc) => {
assert(doc);
const hasAddedImport = preds.hasAttrValue('href', 'imports/comment-in-import.html');
assert.equal(dom5.queryAll(doc, hasAddedImport).length, 1);
});
});
test('added import is added to bundled doc', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/default.html', options);
assert(doc);
const hasAddedImport = preds.hasAttrValue('href', 'imports/comment-in-import.html');
assert.equal(dom5.queryAll(doc, hasAddedImport).length, 1);
}));
});

@@ -407,8 +379,7 @@ // TODO(usergenic): These tests only prove that the `inputUrl` has precedence

const options = { inputUrl: 'test/html/default.html' };
test.skip('inputURL is used instead of argument to process', () => {
return bundle('flibflabfloom!', options).then((doc) => {
assert(doc);
});
});
test.skip('gulp-vulcanize invocation with absPathPrefix', () => {
test.skip('inputURL is used instead of argument to process', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('flibflabfloom!', options);
assert(doc);
}));
test.skip('gulp-vulcanize invocation with absPathPrefix', () => __awaiter(this, void 0, void 0, function* () {
const options = {

@@ -418,58 +389,67 @@ abspath: path.resolve('test/html'),

};
return bundle('C:\\Users\\PolymerBundlerTester\\polymer-bundler\\test\\html\\default.html', options)
.then((doc) => assert(doc));
});
const doc = yield bundle('C:\\Users\\PolymerBundlerTester\\polymer-bundler\\test\\html\\default.html', options);
assert(doc);
}));
});
suite('Regression Testing', () => {
test('Complicated Ordering', () => {
test('Base tag emulation should not leak to other imports', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/base.html');
const clickMe = dom5.query(doc, preds.hasTextValue('CLICK ME'));
assert.ok(clickMe);
// The base target from `test/html/imports/base.html` should apply to the
// anchor tag in it.
assert.equal(dom5.getAttribute(clickMe, 'target'), 'foo-frame');
const doNotClickMe = dom5.query(doc, preds.hasTextValue('DO NOT CLICK ME'));
assert.ok(doNotClickMe);
// The base target from `test/html/imports/base.html` should NOT apply to
// the anchor tag in `test/html/imports/base-foo/sub-base.html`
assert.isFalse(dom5.hasAttribute(doNotClickMe, 'target'));
}));
test('Complicated Ordering', () => __awaiter(this, void 0, void 0, function* () {
// refer to
// https://github.com/Polymer/polymer-bundler/tree/master/test/html/complicated/ordering.svg
// for visual reference on the document structure for this example
return bundle('test/html/complicated/A.html', { inlineScripts: true })
.then((doc) => {
assert(doc);
const expected = ['A1', 'C', 'E', 'B', 'D', 'A2'];
const scripts = dom5.queryAll(doc, matchers.jsMatcher);
const contents = scripts.map(function (s) {
return dom5.getTextContent(s).trim();
});
assert.deepEqual(contents, expected);
const doc = yield bundle('test/html/complicated/A.html', { inlineScripts: true });
assert(doc);
const expected = ['A1', 'C', 'E', 'B', 'D', 'A2'];
const scripts = dom5.queryAll(doc, matchers.jsMatcher);
const contents = scripts.map(function (s) {
return dom5.getTextContent(s).trim();
});
});
test('Assetpath rewriting', () => {
return bundle('test/html/path-rewriting/src/app-main/app-main.html')
.then((doc) => {
assert(doc);
const domModules = dom5.queryAll(doc, preds.hasTagName('dom-module'));
const assetpaths = domModules.map((domModule) => [dom5.getAttribute(domModule, 'id'),
dom5.getAttribute(domModule, 'assetpath')]);
assert.deepEqual(assetpaths, [
['test-c', '../../bower_components/test-component/'],
['test-b', '../../bower_components/test-component/src/elements/'],
['test-a', '../../bower_components/test-component/'],
['app-main', './']
]);
});
});
test('Entrypoint body content should not be wrapped by bundler', () => {
return bundle('test/html/default.html').then((doc) => {
assert(doc);
const myElement = dom5.query(doc, preds.hasTagName('my-element'));
assert(myElement);
assert(preds.NOT(preds.parentMatches(preds.hasAttr('by-polymer-bundler')))(myElement));
});
});
test.skip('Imports in templates should not inline', () => {
return bundle('test/html/inside-template.html').then((doc) => {
const importMatcher = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'), preds.hasAttr('href'));
const externalScriptMatcher = preds.AND(preds.hasTagName('script'), preds.hasAttrValue('src', 'external/external.js'));
assert(doc);
const imports = dom5.queryAll(doc, importMatcher);
assert.equal(imports.length, 1, 'import in template was inlined');
const unexpectedScript = dom5.query(doc, externalScriptMatcher);
assert.equal(unexpectedScript, null, 'script in external.html should not be present');
});
});
assert.deepEqual(contents, expected);
}));
test('Assetpath rewriting', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/path-rewriting/src/app-main/app-main.html');
assert(doc);
const domModules = dom5.queryAll(doc, preds.hasTagName('dom-module'));
const assetpaths = domModules.map((domModule) => [dom5.getAttribute(domModule, 'id'),
dom5.getAttribute(domModule, 'assetpath')]);
assert.deepEqual(assetpaths, [
['test-c', '../../bower_components/test-component/'],
['test-b', '../../bower_components/test-component/src/elements/'],
['test-a', '../../bower_components/test-component/'],
// We don't need an assetpath on app-main because its not been
// moved/inlined from another location.
['app-main', null]
]);
}));
test('Entrypoint body content should not be wrapped', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/default.html');
assert(doc);
const myElement = dom5.query(doc, preds.hasTagName('my-element'));
assert(myElement);
assert(preds.NOT(preds.parentMatches(preds.hasAttr('by-polymer-bundler')))(myElement));
}));
test.skip('Imports in templates should not inline', () => __awaiter(this, void 0, void 0, function* () {
const doc = yield bundle('test/html/inside-template.html');
const importMatcher = preds.AND(preds.hasTagName('link'), preds.hasAttrValue('rel', 'import'), preds.hasAttr('href'));
const externalScriptMatcher = preds.AND(preds.hasTagName('script'), preds.hasAttrValue('src', 'external/external.js'));
assert(doc);
const imports = dom5.queryAll(doc, importMatcher);
assert.equal(imports.length, 1, 'import in template was inlined');
const unexpectedScript = dom5.query(doc, externalScriptMatcher);
assert.equal(unexpectedScript, null, 'script in external.html should not be present');
}));
});
});
//# sourceMappingURL=bundler_test.js.map

@@ -24,2 +24,3 @@ "use strict";

const assert = chai.assert;
const stripSpace = (html) => html.replace(/>\s+/g, '>').replace(/>/g, '>\n').trim();
suite('import-utils', () => {

@@ -30,136 +31,156 @@ suite('Path rewriting', () => {

test('Rewrite URLs', () => {
const css = [
'x-element {',
' background-image: url(foo.jpg);',
'}',
'x-bar {',
' background-image: url(data:xxxxx);',
'}',
'x-quuz {',
' background-image: url(\'https://foo.bar/baz.jpg\');',
'}'
].join('\n');
const expected = [
'x-element {',
' background-image: url("my-element/foo.jpg");',
'}',
'x-bar {',
' background-image: url("data:xxxxx");',
'}',
'x-quuz {',
' background-image: url("https://foo.bar/baz.jpg");',
'}'
].join('\n');
const _rewriteImportedStyleTextUrls = importUtils.__get__('_rewriteImportedStyleTextUrls');
const actual = _rewriteImportedStyleTextUrls(importDocPath, mainDocPath, css);
const css = `
x-element {
background-image: url(foo.jpg);
}
x-bar {
background-image: url(data:xxxxx);
}
x-quuz {
background-image: url(\'https://foo.bar/baz.jpg\');
}
`;
const expected = `
x-element {
background-image: url("my-element/foo.jpg");
}
x-bar {
background-image: url("data:xxxxx");
}
x-quuz {
background-image: url("https://foo.bar/baz.jpg");
}
`;
const rewriteCssTextBaseUrl = importUtils.__get__('rewriteCssTextBaseUrl');
const actual = rewriteCssTextBaseUrl(css, importDocPath, mainDocPath);
assert.equal(actual, expected);
});
test('Resolve Paths', () => {
const html = [
'<link rel="import" href="../polymer/polymer.html">',
'<link rel="stylesheet" href="my-element.css">',
'<dom-module id="my-element">',
'<template>',
'<style>:host { background-image: url(background.svg); }</style>',
'<div style="position: absolute;"></div>',
'</template>',
'</dom-module>',
'<script>Polymer({is: "my-element"})</script>'
].join('\n');
const expected = [
'<html><head><link rel="import" href="polymer/polymer.html">',
'<link rel="stylesheet" href="my-element/my-element.css">',
'</head><body><dom-module id="my-element" assetpath="my-element/">',
'<template>',
'<style>:host { background-image: url("my-element/background.svg"); }</style>',
'<div style="position: absolute;"></div>',
'</template>',
'</dom-module>',
'<script>Polymer({is: "my-element"})</script></body></html>'
].join('\n');
const html = `
<link rel="import" href="../polymer/polymer.html">
<link rel="stylesheet" href="my-element.css">
<dom-module id="my-element">
<template>
<style>:host { background-image: url(background.svg); }</style>
<div style="position: absolute;"></div>
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script>
`;
const expected = `
<html><head><link rel="import" href="polymer/polymer.html">
<link rel="stylesheet" href="my-element/my-element.css">
</head><body><dom-module id="my-element" assetpath="my-element/">
<template>
<style>:host { background-image: url("my-element/background.svg"); }</style>
<div style="position: absolute;"></div>
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script></body></html>
`;
const ast = parse5.parse(html);
importUtils.rewriteImportedUrls(ast, importDocPath, mainDocPath);
importUtils.rewriteDocumentBaseUrl(ast, importDocPath, mainDocPath);
const actual = parse5.serialize(ast);
assert.equal(actual, expected, 'relative');
assert.equal(stripSpace(actual), stripSpace(expected), 'relative');
});
test.skip('Resolve Paths with <base>', () => {
const htmlBase = [
'<base href="zork">',
'<link rel="import" href="../polymer/polymer.html">',
'<link rel="stylesheet" href="my-element.css">',
'<dom-module id="my-element">',
'<template>',
'<style>:host { background-image: url(background.svg); }</style>',
'</template>',
'</dom-module>',
'<script>Polymer({is: "my-element"})</script>'
].join('\n');
const expectedBase = [
'<html><head>',
'<link rel="import" href="my-element/polymer/polymer.html">',
'<link rel="stylesheet" href="my-element/zork/my-element.css">',
'</head><body><dom-module id="my-element" assetpath="my-element/zork/">',
'<template>',
'<style>:host { background-image: url("my-element/zork/background.svg"); }</style>',
'</template>',
'</dom-module>',
'<script>Polymer({is: "my-element"})</script></body></html>'
].join('\n');
const ast = parse5.parse(htmlBase);
// pathRewriter.acid(ast, inputPath);
importUtils.rewriteImportedUrls(ast, importDocPath, mainDocPath);
test('Leave Templated URLs', () => {
const base = `
<html><head></head><body>
<a href="{{foo}}"></a>
<img src="[[bar]]">
</body></html>
`;
const ast = parse5.parse(base);
importUtils.rewriteDocumentBaseUrl(ast, importDocPath, mainDocPath);
const actual = parse5.serialize(ast);
assert.equal(actual, expectedBase, 'base');
assert.deepEqual(stripSpace(actual), stripSpace(base), 'templated urls');
});
test.skip('Resolve Paths with <base> having a trailing /', () => {
const htmlBase = [
'<base href="zork/">',
'<link rel="import" href="../polymer/polymer.html">',
'<link rel="stylesheet" href="my-element.css">',
'<dom-module id="my-element">',
'<template>',
'<style>:host { background-image: url(background.svg); }</style>',
'</template>',
'</dom-module>',
'<script>Polymer({is: "my-element"})</script>'
].join('\n');
const expectedBase = [
`<html><head>
<link rel="import" href="my-element/polymer/polymer.html">
<link rel="stylesheet" href="my-element/zork/my-element.css">
</head><body><dom-module id="my-element" assetpath="my-element/zork/">
});
suite('Document <base> tag emulation', () => {
// The trailing slash is meaningful.
test('Resolve Paths with <base href> having a trailing /', () => {
const htmlBase = `
<base href="components/my-element/">
<link rel="import" href="../polymer/polymer.html">
<link rel="stylesheet" href="my-element.css">
<dom-module id="my-element">
<template>
<style>:host { background-image: url("my-element/zork/background.svg"); }</style>
<style>:host { background-image: url(background.svg); }</style>
<img src="bloop.gif">
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script></body></html>`
].join('\n');
<script>Polymer({is: "my-element"})</script>`;
const expectedBase = `
<html><head>
<link rel="import" href="components/polymer/polymer.html">
<link rel="stylesheet" href="components/my-element/my-element.css">
</head><body>
<dom-module id="my-element" assetpath="components/my-element/">
<template>
<style>:host { background-image: url("components/my-element/background.svg"); }</style>
<img src="components/my-element/bloop.gif">
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script>
</body></html>`;
const ast = parse5.parse(htmlBase);
// pathRewriter.acid(ast, inputPath);
importUtils.rewriteImportedUrls(ast, importDocPath, mainDocPath);
importUtils.rewriteDocumentToEmulateBaseTag('the/doc/url', ast);
const actual = parse5.serialize(ast);
assert.equal(actual, expectedBase, 'base');
assert.deepEqual(stripSpace(actual), stripSpace(expectedBase), 'base');
});
test.skip('Resolve <base target>', () => {
const htmlBase = ['<base target="_blank">', '<a href="foo.html">LINK</a>'].join('\n');
const expectedBase = [
'<html><head>',
'</head><body><a href="my-element/foo.html" target="_blank">LINK</a></body></html>'
].join('\n');
// Old vulcanize did the wrong thing with base href that had no trailing
// slash, so this proves the behavior of bundler is correct in this case.
test('Resolve Paths with <base href> with no trailing slash', () => {
const htmlBase = `
<base href="components/my-element">
<link rel="import" href="../polymer/polymer.html">
<link rel="stylesheet" href="my-element.css">
<dom-module id="my-element">
<template>
<style>:host { background-image: url(background.svg); }</style>
<img src="bloop.gif">
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script>
`;
const expectedBase = `
<html><head>
<link rel="import" href="polymer/polymer.html">
<link rel="stylesheet" href="components/my-element.css">
</head><body><dom-module id="my-element" assetpath="components/">
<template>
<style>:host { background-image: url("components/background.svg"); }</style>
<img src="components/bloop.gif">
</template>
</dom-module>
<script>Polymer({is: "my-element"})</script></body></html>
`;
const ast = parse5.parse(htmlBase);
importUtils.rewriteImportedUrls(ast, importDocPath, mainDocPath);
importUtils.rewriteDocumentToEmulateBaseTag('the/doc/url', ast);
const actual = parse5.serialize(ast);
assert.equal(actual, expectedBase, 'base target');
assert.deepEqual(stripSpace(actual), stripSpace(expectedBase), 'base');
});
test('Leave Templated URLs', () => {
const base = [
'<html><head></head><body>',
'<a href="{{foo}}"></a>',
'<img src="[[bar]]">',
'</body></html>'
].join('\n');
const ast = parse5.parse(base);
importUtils.rewriteImportedUrls(ast, importDocPath, mainDocPath);
test('Apply <base target> to all links and forms without target', () => {
const htmlBase = `
<base target="_blank">
<a href="foo.html">LINK</a>
<a href="bar.html" target="leavemealone">OTHERLINK</a>
<form action="doit"></form>
<form action="doitagain" target="leavemealone"></form>
<div>Just a div. I don't need a target</div>
`;
const expectedBase = `
<html><head>
</head><body>
<a href="foo.html" target="_blank">LINK</a>
<a href="bar.html" target="leavemealone">OTHERLINK</a>
<form action="doit" target="_blank"></form>
<form action="doitagain" target="leavemealone"></form>
<div>Just a div. I don't need a target</div>
</body></html>
`;
const ast = parse5.parse(htmlBase);
importUtils.rewriteDocumentToEmulateBaseTag('the/doc/url', ast);
const actual = parse5.serialize(ast);
assert.equal(actual, base, 'templated urls');
assert.deepEqual(stripSpace(actual), stripSpace(expectedBase), 'base target');
});

@@ -166,0 +187,0 @@ });

@@ -22,2 +22,16 @@ /**

suite('URL Utils', () => {
suite('stripUrlFileSearchAndHash', () => {
test('Strips "file.html" basename off url', () => {
assert.equal(urlUtils.stripUrlFileSearchAndHash('https://example.com/path/to/file.html'), 'https://example.com/path/to/');
});
test('Strips "something?a=b&c=d" basename and search off url', () => {
assert.equal(urlUtils.stripUrlFileSearchAndHash('https://example.com/path/to/something?a=b&c=d'), 'https://example.com/path/to/');
});
test('Strips "#some-hash-value" off url', () => {
assert.equal(urlUtils.stripUrlFileSearchAndHash('https://example.com/path/#some-hash-value'), 'https://example.com/path/');
});
test('Handles relative paths', () => {
assert.equal(urlUtils.stripUrlFileSearchAndHash('relative/path/to/file'), 'relative/path/to/');
});
});
suite('Rewrite imported relative paths', () => {

@@ -27,3 +41,3 @@ const importDocPath = '/foo/bar/my-element/index.html';

function testRewrite(val, expected, msg) {
const actual = urlUtils.rewriteImportedRelPath(importDocPath, mainDocPath, val);
const actual = urlUtils.rewriteHrefBaseUrl(val, importDocPath, mainDocPath);
assert.equal(actual, expected, msg);

@@ -79,4 +93,4 @@ }

// TODO(usergenic): Update resolveUrl to interpret scheme-less URLs the
// same way browsers do, where '//' prefix implies preserved scheme and the
// first path segment is actually the host.
// same way browsers do, where '//' prefix implies preserved scheme and
// the first path segment is actually the host.
test.skip('Scheme-less URLs should be interpreted as browsers do', () => {

@@ -83,0 +97,0 @@ assert.equal(urlUtils.relativeUrl('//a/b', '/c/d'), 'c/d');

@@ -6,2 +6,7 @@ /**

/**
* Returns a URL with the basename removed from the pathname. Strips the
* search off of the URL as well, since it will not apply.
*/
export declare function stripUrlFileSearchAndHash(href: UrlString): UrlString;
/**
* Returns true if the href is an absolute path.

@@ -21,6 +26,5 @@ */

/**
* Modifies an href by the relative difference between the `mainDocUrl` and
* `importUrl` which is the location of the imported document containing the
* href.
* Modifies an href by the relative difference between the old base url and
* the new base url.
*/
export declare function rewriteImportedRelPath(importUrl: UrlString, mainDocUrl: UrlString, href: UrlString): UrlString;
export declare function rewriteHrefBaseUrl(href: UrlString, oldBaseUrl: UrlString, newBaseUrl: UrlString): UrlString;

@@ -22,2 +22,22 @@ /**

/**
* Returns a URL with the basename removed from the pathname. Strips the
* search off of the URL as well, since it will not apply.
*/
function stripUrlFileSearchAndHash(href) {
const u = url.parse(href);
// Using != so tests for null AND undefined
if (u.pathname != null) {
// Suffix path with `_` so that `/a/b/` is treated as `/a/b/_` and that
// `path.dirname()` returns `/a/b` because it would otherwise return `/a`
// incorrectly.
u.pathname = path.dirname(u.pathname + '_') + '/';
}
// Assigning to undefined because TSC says type of these is
// `string | undefined` as opposed to `string | null`
u.search = undefined;
u.hash = undefined;
return url.format(u);
}
exports.stripUrlFileSearchAndHash = stripUrlFileSearchAndHash;
/**
* Returns true if the href is an absolute path.

@@ -63,12 +83,11 @@ */

/**
* Modifies an href by the relative difference between the `mainDocUrl` and
* `importUrl` which is the location of the imported document containing the
* href.
* Modifies an href by the relative difference between the old base url and
* the new base url.
*/
function rewriteImportedRelPath(importUrl, mainDocUrl, href) {
function rewriteHrefBaseUrl(href, oldBaseUrl, newBaseUrl) {
if (isAbsolutePath(href)) {
return href;
}
const absUrl = url.resolve(importUrl, href);
const parsedFrom = url.parse(mainDocUrl);
const absUrl = url.resolve(oldBaseUrl, href);
const parsedFrom = url.parse(newBaseUrl);
const parsedTo = url.parse(absUrl);

@@ -82,3 +101,3 @@ if (parsedFrom.protocol === parsedTo.protocol &&

}
exports.rewriteImportedRelPath = rewriteImportedRelPath;
exports.rewriteHrefBaseUrl = rewriteHrefBaseUrl;
//# sourceMappingURL=url-utils.js.map
{
"name": "polymer-bundler",
"version": "2.0.0-pre.5",
"version": "2.0.0-pre.6",
"description": "Process Web Components into one output file",

@@ -5,0 +5,0 @@ "main": "lib/bundler.js",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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