175 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| var broccoli = require('broccoli');
 | |
| var fs = require('fs');
 | |
| var makeBrowserTree = require('./trees/browser_tree');
 | |
| var makeNodeTree = require('./trees/node_tree');
 | |
| var path = require('path');
 | |
| var printSlowTrees = require('broccoli-slow-trees');
 | |
| var Q = require('q');
 | |
| 
 | |
| export type ProjectMap = {
 | |
|   [key: string]: boolean
 | |
| };
 | |
| 
 | |
| export type Options = {
 | |
|   projects: ProjectMap;
 | |
|   noTypeChecks: boolean;
 | |
|   generateEs6: boolean;
 | |
|   useBundles: boolean;
 | |
| };
 | |
| 
 | |
| export interface AngularBuilderOptions {
 | |
|   outputPath: string;
 | |
|   dartSDK?: any;
 | |
|   logs?: any;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * BroccoliBuilder facade for all of our build pipelines.
 | |
|  */
 | |
| export class AngularBuilder {
 | |
|   private nodeBuilder: BroccoliBuilder;
 | |
|   private browserDevBuilder: BroccoliBuilder;
 | |
|   private browserProdBuilder: BroccoliBuilder;
 | |
|   private dartBuilder: BroccoliBuilder;
 | |
|   private outputPath: string;
 | |
|   private firstResult: BuildResult;
 | |
| 
 | |
|   constructor(public options: AngularBuilderOptions) { this.outputPath = options.outputPath; }
 | |
| 
 | |
| 
 | |
|   public rebuildBrowserDevTree(opts: Options): Promise<BuildResult> {
 | |
|     this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(opts);
 | |
|     return this.rebuild(this.browserDevBuilder, 'js.dev');
 | |
|   }
 | |
| 
 | |
| 
 | |
|   public rebuildBrowserProdTree(opts: Options): Promise<BuildResult> {
 | |
|     this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(opts);
 | |
|     return this.rebuild(this.browserProdBuilder, 'js.prod');
 | |
|   }
 | |
| 
 | |
| 
 | |
|   public rebuildNodeTree(opts: Options): Promise<BuildResult> {
 | |
|     this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(opts.projects);
 | |
|     return this.rebuild(this.nodeBuilder, 'js.cjs');
 | |
|   }
 | |
| 
 | |
| 
 | |
|   public rebuildDartTree(projects: ProjectMap): Promise<BuildResult> {
 | |
|     this.dartBuilder = this.dartBuilder || this.makeDartBuilder(projects);
 | |
|     return this.rebuild(this.dartBuilder, 'dart');
 | |
|   }
 | |
| 
 | |
| 
 | |
|   cleanup(): Promise<any> {
 | |
|     return Q.all([
 | |
|       this.nodeBuilder && this.nodeBuilder.cleanup(),
 | |
|       this.browserDevBuilder && this.browserDevBuilder.cleanup(),
 | |
|       this.browserProdBuilder && this.browserProdBuilder.cleanup()
 | |
|     ]);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private makeBrowserDevBuilder(opts: Options): BroccoliBuilder {
 | |
|     let tree = makeBrowserTree(
 | |
|         {
 | |
|           name: 'dev',
 | |
|           typeAssertions: true,
 | |
|           sourceMaps: true,
 | |
|           projects: opts.projects,
 | |
|           noTypeChecks: opts.noTypeChecks,
 | |
|           generateEs6: opts.generateEs6,
 | |
|           useBundles: opts.useBundles
 | |
|         },
 | |
|         path.join(this.outputPath, 'js', 'dev'));
 | |
|     return new broccoli.Builder(tree);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private makeBrowserProdBuilder(opts: Options): BroccoliBuilder {
 | |
|     let tree = makeBrowserTree(
 | |
|         {
 | |
|           name: 'prod',
 | |
|           typeAssertions: false,
 | |
|           sourceMaps: false,
 | |
|           projects: opts.projects,
 | |
|           noTypeChecks: opts.noTypeChecks,
 | |
|           generateEs6: opts.generateEs6,
 | |
|           useBundles: opts.useBundles
 | |
|         },
 | |
|         path.join(this.outputPath, 'js', 'prod'));
 | |
|     return new broccoli.Builder(tree);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private makeNodeBuilder(projects: ProjectMap): BroccoliBuilder {
 | |
|     let tree = makeNodeTree(projects, path.join(this.outputPath, 'js', 'cjs'));
 | |
|     return new broccoli.Builder(tree);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private makeDartBuilder(projects: ProjectMap): BroccoliBuilder {
 | |
|     let options = {
 | |
|       outputPath: path.join(this.outputPath, 'dart'),
 | |
|       dartSDK: this.options.dartSDK,
 | |
|       logs: this.options.logs,
 | |
|       projects: projects
 | |
|     };
 | |
|     // Workaround for https://github.com/dart-lang/dart_style/issues/493
 | |
|     var makeDartTree = require('./trees/dart_tree');
 | |
|     let tree = makeDartTree(options);
 | |
|     return new broccoli.Builder(tree);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   private rebuild(builder: BroccoliBuilder, name: string): Promise<BuildResult> {
 | |
|     return builder.build().then<BuildResult>(
 | |
|         (result) => {
 | |
|           if (!this.firstResult) {
 | |
|             this.firstResult = result;
 | |
|           }
 | |
| 
 | |
|           printSlowTrees(result.graph);
 | |
|           writeBuildLog(result, name);
 | |
|           return result;
 | |
|         },
 | |
|         (error): any => {
 | |
|           // the build tree is the same during rebuilds, only leaf properties of the nodes change
 | |
|           // so let's traverse it and get updated values for input/cache/output paths
 | |
|           if (this.firstResult) {
 | |
|             writeBuildLog(this.firstResult, name);
 | |
|           }
 | |
|           throw error;
 | |
|         });
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function writeBuildLog(result: BuildResult, name: string) {
 | |
|   let logPath = `tmp/build.${name}.log`;
 | |
|   let prevLogPath = logPath + '.previous';
 | |
|   let formattedLogContent = JSON.stringify(broccoliNodeToBuildNode(result.graph), null, 2);
 | |
| 
 | |
|   if (fs.existsSync(prevLogPath)) fs.unlinkSync(prevLogPath);
 | |
|   if (fs.existsSync(logPath)) fs.renameSync(logPath, prevLogPath);
 | |
|   fs.writeFileSync(logPath, formattedLogContent, {encoding: 'utf-8'});
 | |
| }
 | |
| 
 | |
| 
 | |
| function broccoliNodeToBuildNode(broccoliNode: BroccoliNode): BuildNode {
 | |
|   let tree = broccoliNode.tree.newStyleTree || broccoliNode.tree;
 | |
| 
 | |
|   return new BuildNode(tree.description || (<any>tree.constructor).name,
 | |
|                        tree.inputPath ? [tree.inputPath] : tree.inputPaths, tree.cachePath,
 | |
|                        tree.outputPath, broccoliNode.selfTime / (1000 * 1000 * 1000),
 | |
|                        broccoliNode.totalTime / (1000 * 1000 * 1000),
 | |
|                        broccoliNode.subtrees.map(broccoliNodeToBuildNode));
 | |
| }
 | |
| 
 | |
| 
 | |
| class BuildNode {
 | |
|   constructor(public pluginName: string, public inputPaths: string[], public cachePath: string,
 | |
|               public outputPath: string, public selfTime: number, public totalTime: number,
 | |
|               public inputNodes: BuildNode[]) {}
 | |
| }
 |