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

Commit

Permalink
remove async loop
Browse files Browse the repository at this point in the history
  • Loading branch information
AJ Ortega committed May 17, 2016
1 parent 8a62241 commit f9dbad7
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 73 deletions.
8 changes: 6 additions & 2 deletions custom_typings/hydrolysis.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ declare module 'hydrolysis' {

// parsedScript?: estree.Program;

// html?: ParsedImport;
html?: {
script: Node[],
style: Node[],
ast: Node
};
}

/**
Expand Down Expand Up @@ -76,7 +80,7 @@ declare module 'hydrolysis' {

constructor(attachAST: boolean, loader: Loader);

metadataTree(path: string): Promise<void>;
metadataTree(path: string): Promise<DocumentDescriptor>;
annotate(): void;
elements: Element[];
behaviors: Behavior[];
Expand Down
116 changes: 56 additions & 60 deletions src/build/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
*/

import * as fs from 'fs';
import {Analyzer, Deferred, Loader, Resolver} from 'hydrolysis';
import {Analyzer, Deferred, Loader, Resolver, DocumentDescriptor} from 'hydrolysis';
import * as path from 'path';
import {Transform} from 'stream';
import File = require('vinyl');
Expand All @@ -22,8 +22,8 @@ let logger = logging.getLogger('cli.build.analyzer');

export interface DocumentDeps{
imports?: Array<string>,
scripts: Array<string>,
styles: Array<string>
scripts?: Array<string>,
styles?: Array<string>
}

export class StreamAnalyzer extends Transform {
Expand Down Expand Up @@ -125,14 +125,14 @@ export class StreamAnalyzer extends Transform {

return Promise.all(depsPromises).then((value: any) => {
// tsc was giving a spurious error with `allDeps` as the parameter
let allDeps: DocumentDeps[] = <DocumentDeps[]>value;
let allDeps: DocumentDeps[] = <DocumentDeps[]>value;

// An index of dependency -> fragments that depend on it
let depsToFragments = new Map<string, string[]>();

// An index of fragments -> dependencies
let fragmentToDeps = new Map<string, string[]>();

let fragmentToFullDeps = new Map<string, DocumentDeps>();

console.assert(this.allFragments.length === allDeps.length);
Expand Down Expand Up @@ -163,77 +163,73 @@ export class StreamAnalyzer extends Transform {
};
});
}
_collectScriptsAndStyles(tree: Node): DocumentDeps {
const externalScriptPredicate = predicates.AND(
predicates.hasTagName('script')
);
const externalStylePredicate = predicates.AND(
predicates.hasTagName('style'),
predicates.hasAttrValue('rel', 'stylesheet'),
predicates.hasAttr('href')
)
let scriptNodes = queryAll(tree, externalScriptPredicate);
let styleNodes = queryAll(tree, externalStylePredicate);
let scripts: string[] = scriptNodes.map((s) => <any>s.__hydrolysisInlined).filter((s) => !!s);
let styles = styleNodes.map((s) => getAttribute(s, 'href'));
return {
scripts,
styles
}
}

/**
* Attempts to retreive document-order transitive dependencies for `url`.
*/
_getDependencies(url: string): Promise<DocumentDeps> {
let visited = new Set<string>();
let allHtmlDeps = new Set<string>();
let documents = this.analyzer.parsedDocuments;
let dir = path.dirname(url);
return this.analyzer.metadataTree(url)
.then((tree) => this._getDependenciesFromDescriptor(tree, dir));
}

_getDependenciesFromDescriptor(descriptor: DocumentDescriptor, dir: string): DocumentDeps {
let allHtmlDeps = [];
let allScriptDeps = new Set<string>();
let allStyleDeps = new Set<string>();
// async depth-first traversal: waits for document load, then async
// iterates on dependencies. No return values are used, writes to visited
// and list.
//
// document.depHrefs is _probably_ document order, if all html imports are
// at the same level in the tree.
// See: https://github.com/Polymer/hydrolysis/issues/240
let documents = this.analyzer.parsedDocuments;
let _getDeps = (url: string) =>
this.analyzer.load(url).then((d) => {
let document = documents[d.href];
let dir = path.dirname(url);
let deps: DocumentDeps = this._collectScriptsAndStyles(document);
deps.scripts.forEach(s => allScriptDeps.add(path.resolve(dir, s)));
deps.styles.forEach(s => allStyleDeps.add(path.resolve(dir, s)));
return _iterate(d.depHrefs.values());
});

// async iteration: waits for _getDeps on a value to return before
// recursing to call _getDeps on the next value.
let _iterate = (iterator: Iterator<string>) => {
let next = iterator.next();
if (next.done || visited.has(next.value)) {
return Promise.resolve();
} else {
allHtmlDeps.add(next.value);
visited.add(url);
return _getDeps(next.value).then(() => _iterate(iterator));

let deps: DocumentDeps = this._collectScriptsAndStyles(descriptor);
deps.scripts.forEach((s) => allScriptDeps.add(path.resolve(dir, s)));
deps.styles.forEach((s) => allStyleDeps.add(path.resolve(dir, s)));
if (descriptor.imports) {
let queue = descriptor.imports.slice();
while (queue.length > 0) {
let next = queue.shift();
if (!next.href) {
continue;
}
allHtmlDeps.push(next.href);
let childDeps = this._getDependenciesFromDescriptor(next, path.dirname(next.href));
allHtmlDeps = allHtmlDeps.concat(childDeps.imports);
childDeps.scripts.forEach((s) => allScriptDeps.add(s));
childDeps.styles.forEach((s) => allStyleDeps.add(s));
}
}
// kick off the traversal from root, then resolve the list of dependencies
return _getDeps(url).then(() => {
return {
imports: Array.from(allHtmlDeps),
scripts: Array.from(allScriptDeps),
styles: Array.from(allStyleDeps),

return {
scripts: Array.from(allScriptDeps),
styles: Array.from(allStyleDeps),
imports: allHtmlDeps,
};
}

_collectScriptsAndStyles(tree: DocumentDescriptor): DocumentDeps {
let scripts = [];
let styles = [];
tree.html.script.forEach((script) => {
if (script['__hydrolysisInlined']) {
scripts.push(script['__hydrolysisInlined']);
}
});
tree.html.style.forEach((style) => {
let href = getAttribute(style, 'href');
if (href) {
styles.push(href);
}
});
return {
scripts,
styles
}
}
}

export interface DepsIndex {
depsToFragments: Map<string, string[]>;
// TODO(garlicnation): Remove this map.
// A legacy map from framents to html dependencies.
fragmentToDeps: Map<string, string[]>;
// A map from frament urls to html, js, and css dependencies.
fragmentToFullDeps: Map<string, DocumentDeps>;
}

Expand Down
17 changes: 10 additions & 7 deletions src/build/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ export function build(options?: BuildOptions, config?: ProjectConfig): Promise<a
.pipe(bundler)
.pipe(vfs.dest('build/bundled'));

let genSW = (buildRoot: string, deps: string[], swConfig: SWConfig, scriptDeps?: string[]) => {
let genSW = (buildRoot: string, deps: string[], swConfig: SWConfig, scriptAndStyleDeps?: string[]) => {
logger.debug(`Generating service worker for ${buildRoot}...`);
logger.info(`Script deps: ${scriptDeps}`);
logger.debug(`Script and style deps: ${scriptAndStyleDeps}`);
return generateServiceWorker({
root,
entrypoint,
deps,
scriptDeps,
scriptAndStyleDeps,
buildRoot,
swConfig: clone(swConfig),
serviceWorkerPath: path.join(root, buildRoot, serviceWorkerName)
Expand All @@ -174,10 +174,13 @@ export function build(options?: BuildOptions, config?: ProjectConfig): Promise<a
.then((depsIndex) => {
let unbundledDeps = analyzer.allFragments
.concat(Array.from(depsIndex.depsToFragments.keys()));

let fullDeps = Array.from(depsIndex.fragmentToFullDeps.values());
let scriptDeps = new Set<string>();
fullDeps.forEach(d => d.scripts.forEach(s => scriptDeps.add(s)));
let scriptAndStyleDeps = new Set<string>();
fullDeps.forEach(d => {
d.scripts.forEach((s) => scriptAndStyleDeps.add(s));
d.styles.forEach((s) => scriptAndStyleDeps.add(s));
});

let bundledDeps = analyzer.allFragments
.concat(bundler.sharedBundleUrl);
Expand All @@ -191,7 +194,7 @@ export function build(options?: BuildOptions, config?: ProjectConfig): Promise<a

logger.info(`Generating service workers...`);
return Promise.all([
genSW('build/unbundled', unbundledDeps, swConfig, Array.from(scriptDeps)),
genSW('build/unbundled', unbundledDeps, swConfig, Array.from(scriptAndStyleDeps)),
genSW('build/bundled', bundledDeps, swConfig)
]);
})
Expand Down
8 changes: 4 additions & 4 deletions src/build/sw-precache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ export function generateServiceWorker(options: GenerateServiceWorkerOptions)
let swConfig = options.swConfig || <SWConfig>{};
// strip root prefix, so buildRoot prefix can be added safely
let scriptsAndImports = options.deps;
if (options.scriptDeps) {
scriptsAndImports = scriptsAndImports.concat(options.scriptDeps);
if (options.scriptAndStyleDeps) {
scriptsAndImports = scriptsAndImports.concat(options.scriptAndStyleDeps);
}
let deps = scriptsAndImports.map((p) => {
if (p.startsWith(options.root)) {
Expand Down Expand Up @@ -126,9 +126,9 @@ export interface GenerateServiceWorkerOptions {
*/
deps: string[];
/**
* List of script dependencies.
* List of script and style dependencies.
*/
scriptDeps: string[];
scriptAndStyleDeps: string[];
/**
* Existing config to use as a base for the serivce worker generation.
*/
Expand Down

0 comments on commit f9dbad7

Please sign in to comment.