Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Commit

Permalink
Merge pull request #74 from Polymer/shell
Browse files Browse the repository at this point in the history
Bundle shared imports into the shell
  • Loading branch information
justinfagnani committed May 10, 2016
2 parents 176fac7 + 5cf687e commit d219aff
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 13 deletions.
69 changes: 62 additions & 7 deletions src/build/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

import * as dom5 from 'dom5';
import * as gulpif from 'gulp-if';
import * as path from 'path';
import {Transform} from 'stream';
Expand Down Expand Up @@ -129,17 +130,30 @@ export class Bundler {
}
}

let sharedDeps = bundles.get(this.sharedBundleUrl);
let excludes = sharedDeps ? sharedDeps.concat(this.sharedBundleUrl) : [];
let sharedDepsBundle = this.shell || this.sharedBundleUrl;
let sharedDeps = bundles.get(sharedDepsBundle);
let promises = [];

if (this.shell) {
let shellFile = this.streamResolver._files.get(this.shell);
console.assert(shellFile != null);
let newShellContent = this._addSharedImportsToShell(bundles);
shellFile.contents = new Buffer(newShellContent);
}

for (let entrypoint of this.allEntrypoints) {
let relativeSharedImportUrl = path.relative(entrypoint, this.sharedBundlePath);
let addedImports = (entrypoint == this.shell || !this.shell)
? []
: [path.relative(path.dirname(entrypoint), sharedDepsBundle)]
let excludes = (entrypoint == this.shell)
? []
: sharedDeps.concat(sharedDepsBundle);

promises.push(new Promise((resolve, reject) => {
var vulcanize = new Vulcanize({
abspath: null,
fsResolver: this.streamResolver,
addedImports: [relativeSharedImportUrl],
addedImports: addedImports,
stripExcludes: excludes,
inlineScripts: true,
inlineCss: true,
Expand All @@ -161,7 +175,7 @@ export class Bundler {
}));
}
// vulcanize the shared bundle
if (sharedDeps) {
if (!this.shell && sharedDeps) {
promises.push(this._generateSharedBundle(sharedDeps));
}

Expand All @@ -176,6 +190,42 @@ export class Bundler {
});
}

_addSharedImportsToShell(bundles: Map<string, string[]>): string {
console.assert(this.shell != null);
let shellDeps = bundles.get(this.shell)
.map((d) => path.relative(path.dirname(this.shell), d));

let file = this.streamResolver._files.get(this.shell);
console.assert(file != null);
let contents = file.contents.toString();
let doc = dom5.parse(contents);
let imports = dom5.queryAll(doc, dom5.predicates.AND(
dom5.predicates.hasTagName('link'),
dom5.predicates.hasAttrValue('rel', 'import')
));

// Remove all imports that are in the shared deps list so that we prefer
// the ordering or shared deps. Any imports left should be independent of
// ordering of shared deps.
let shellDepsSet = new Set(shellDeps);
for (let _import of imports) {
if (shellDepsSet.has(dom5.getAttribute(_import, 'href'))) {
dom5.remove(_import);
}
}

// Append all shared imports to the end of <head>
let head = dom5.query(doc, dom5.predicates.hasTagName('head'));
for (let dep of shellDeps) {
let newImport = dom5.constructors.element('link');
dom5.setAttribute(newImport, 'rel', 'import');
dom5.setAttribute(newImport, 'href', dep);
dom5.append(head, newImport);
}
let newContents = dom5.serialize(doc);
return newContents;
}

_generateSharedBundle(sharedDeps: string[]): Promise<any> {
return new Promise((resolve, reject) => {
let contents = sharedDeps
Expand Down Expand Up @@ -253,8 +303,13 @@ export class Bundler {
for (let dep of dependencies) {
let entrypointCount = depsToEntrypoints.get(dep).length;
if (entrypointCount > 1) {
addImport(this.sharedBundleUrl, dep);
addImport(entrypoint, this.sharedBundleUrl);
if (this.shell) {
addImport(this.shell, dep);
// addImport(entrypoint, this.shell);
} else {
addImport(this.sharedBundleUrl, dep);
addImport(entrypoint, this.sharedBundleUrl);
}
} else {
addImport(entrypoint, dep);
}
Expand Down
72 changes: 66 additions & 6 deletions test/build/bundle_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,73 @@ suite('Bundler', () => {
assert.isFalse(hasImport(doc, '/root/framework.html'));
}));

test('shell and 1 entrypoint', () => setupTest({
shell: '/root/shell.html',
entrypoints: ['/root/entrypointA.html'],
test('two entrypoints', () => setupTest({
entrypoints: ['/root/shell.html', '/root/entrypointA.html'],
files: [framework(), shell(), entrypointA()],
}).then((files) => {
// shell doesn't have framework
// shell doesn't import framework
let shellDoc = dom5.parse(getFile('shell.html'));
assert.isFalse(hasMarker(shellDoc, 'framework'));
assert.isFalse(hasImport(shellDoc, '/root/framework.html'));

// entrypoint doesn't have framework
// entrypoint doesn't import framework
let entrypointDoc = dom5.parse(getFile('entrypointA.html'));
assert.isFalse(hasMarker(entrypointDoc, 'framework'));
assert.isFalse(hasImport(entrypointDoc, '/root/framework.html'));

// shared-bundle has framework
// No shared-bundle bundles framework
let sharedDoc = dom5.parse(getFile('shared-bundle.html'));
assert.isTrue(hasMarker(sharedDoc, 'framework'));
assert.isFalse(hasImport(sharedDoc, '/root/framework.html'));
}));

test('shell and entrypoint', () => setupTest({
shell: '/root/shell.html',
entrypoints: ['/root/entrypointA.html'],
files: [framework(), shell(), entrypointA()],
}).then((files) => {
// shell bundles framework
let shellDoc = dom5.parse(getFile('shell.html'));
assert.isTrue(hasMarker(shellDoc, 'framework'));
assert.isFalse(hasImport(shellDoc, '/root/framework.html'));

// entrypoint doesn't import framework
let entrypointDoc = dom5.parse(getFile('entrypointA.html'));
assert.isFalse(hasMarker(entrypointDoc, 'framework'));
assert.isFalse(hasImport(entrypointDoc, '/root/framework.html'));

// No shared-bundle with a shell
assert.isNotOk(getFile('shared-bundle.html'));
}));

test('shell and entrypoints with shared dependency', () => setupTest({
shell: '/root/shell.html',
entrypoints: ['/root/entrypointB.html', '/root/entrypointC.html'],
files: [framework(), shell(), entrypointB(), entrypointC(), commonDep()],
}).then((files) => {
// shell bundles framework
let shellDoc = dom5.parse(getFile('shell.html'));
assert.isTrue(hasMarker(shellDoc, 'framework'));
assert.isFalse(hasImport(shellDoc, '/root/framework.html'));

// shell bundles commonDep
assert.isTrue(hasMarker(shellDoc, 'commonDep'));
assert.isFalse(hasImport(shellDoc, '/root/commonDep.html'));

// entrypoint B doesn't import commonDep
let entrypointBDoc = dom5.parse(getFile('entrypointB.html'));
assert.isFalse(hasMarker(entrypointBDoc, 'commonDep'));
assert.isFalse(hasImport(entrypointBDoc, '/root/commonDep.html'));

// entrypoint C doesn't import commonDep
let entrypointCDoc = dom5.parse(getFile('entrypointC.html'));
assert.isFalse(hasMarker(entrypointCDoc, 'commonDep'));
assert.isFalse(hasImport(entrypointCDoc, '/root/commonDep.html'));

// No shared-bundle with a shell
assert.isNotOk(getFile('shared-bundle.html'));
}));

});

const F = (filename, contents) => new File({
Expand All @@ -133,3 +179,17 @@ const entrypointA = () => F('entrypointA.html', `
<link rel="import" href="framework.html">
<div id="entrypointA"></div>
`);

const entrypointB = () => F('entrypointB.html', `
<link rel="import" href="commonDep.html">
<div id="entrypointB"></div>
`);

const entrypointC = () => F('entrypointC.html', `
<link rel="import" href="commonDep.html">
<div id="entrypointC"></div>
`);

const commonDep = () => F('commonDep.html', `
<div id="commonDep"></div>
`);

0 comments on commit d219aff

Please sign in to comment.