diff --git a/tools/analytics/analytics.js b/tools/analytics/analytics.js deleted file mode 100644 index e9c4075852..0000000000 --- a/tools/analytics/analytics.js +++ /dev/null @@ -1,176 +0,0 @@ -'use strict'; - -let execSync = require('child_process').execSync; -let fs = require('fs'); - -let minimist; -try { - minimist = require('minimist'); -} catch (e) { - minimist = function(){ return {projects: ""}; }; -} - -let path = require('path'); -let os = require('os'); -let ua; - -try { - ua = require('universal-analytics'); -} catch(e) { - // ignore errors due to invoking analytics before the first npm install -} - -const analyticsFile = path.resolve(path.join(__dirname, '..', '..', '.build-analytics')); -const analyticsId = "UA-8594346-17"; // Owned by the Angular account -const analyticsOptions = { - https: true, - debug: false -}; - -let cid = fs.existsSync(analyticsFile) ? fs.readFileSync(analyticsFile, 'utf-8') : null; -let visitor; - -if (ua) { - if (cid) { - visitor = ua(analyticsId, cid, analyticsOptions); - } else { - visitor = ua(analyticsId, analyticsOptions); - cid = visitor.cid; - fs.writeFileSync(analyticsFile, cid, 'utf-8'); - } -} - -// https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters -let customParams = { - // OS Platform (darwin, win32, linux) - cd1: os.platform(), - // Node.js Version (v4.1.2) - cd2: process.version, - // npm Version (2.14.7) - cd10: getNpmVersion(), - // TypeScript Version (1.6.2) - cd3: getTypeScriptVersion(), - // Dart Version - cd11: getDartVersion(), - // Dev Environment - cd4: process.env.TRAVIS ? 'Travis CI' : 'Local Dev', - // Travis - Pull Request? - cd5: (process.env.TRAVIS_PULL_REQUEST == 'true') ? 'true' : 'false', - // Travis - Branch Name (master) - cd6: process.env.TRAVIS_BRANCH, - // Travis - Repo Slug (angular/angular) - cd7: process.env.TRAVIS_REPO_SLUG, - // Travis - Job ID (1, 2, 3, 4, ...) - cd12: process.env.TRAVIS_JOB_NUMBER ? process.env.TRAVIS_JOB_NUMBER.split('.')[1] : undefined, - // HW - CPU Info - cd8: `${os.cpus().length} x ${os.cpus()[0].model}`, - // HW - Memory Info - cd9: `${Math.round(os.totalmem()/1024/1024/1024)}GB`, - // gulp --projects (angular2) - cd13: minimist(process.argv.slice(2)).projects -}; - - - -function getTypeScriptVersion() { - try { - return require('typescript').version; - } catch (e) { - return 'unknown'; - } -} - - -function getNpmVersion() { - try { - return execSync('npm -v').toString().replace(/\s/, ''); - } catch (e) { - return 'unknown'; - } -} - - -function getDartVersion() { - try { - return execSync('dart --version 2>&1').toString().replace(/.+: (\S+) [\s\S]+/, '$1') - } catch (e) { - return 'unknown'; - } -} - - -function recordEvent(eventType, actionCategory, actionName, duration, label) { - // if universal-analytics is not yet installed, don't bother doing anything (e.g. when tracking initial npm install) - // build-analytics will however store the starting timestamp, so at least we can record the success/error event with duration - if (!ua) return; - - if (duration) { - duration = Math.round(duration); - } - - switch (eventType) { - case 'start': - visitor. - event(actionCategory, actionName + ' (start)', label, null, customParams). - send(); - break; - case 'success': - visitor. - event(actionCategory, actionName, label, duration, customParams). - timing(actionCategory, actionName, duration, label, customParams). - send(); - break; - case 'error': - visitor. - event(actionCategory, actionName + ' (errored)', label, duration, customParams). - timing(actionCategory, actionName, duration, label, customParams). - send(); - break; - default: - throw new Error(`unknown event type "${eventType}"`); - } -} - - -module.exports = { - - installStart: (actionName) => { - recordEvent('start', 'install', actionName); - }, - - installSuccess: (actionName, duration) => { - recordEvent('success', 'install', actionName, duration); - }, - - installError: (actionName, duration) => { - recordEvent('error', 'install', actionName, duration); - }, - - buildStart: (actionName) => { - recordEvent('start', 'build', actionName); - }, - - buildSuccess: (actionName, duration) => { - recordEvent('success', 'build', actionName, duration); - }, - - buildError: (actionName, duration) => { - recordEvent('error', 'build', actionName, duration); - }, - - ciStart: (actionName) => { - recordEvent('start', 'ci', actionName); - }, - - ciSuccess: (actionName, duration) => { - recordEvent('success', 'ci', actionName, duration); - }, - - ciError: (actionName, duration) => { - recordEvent('error', 'ci', actionName, duration); - }, - - bundleSize: (filePath, sizeInBytes, compressionLevel) => { - recordEvent('success', 'payload', compressionLevel, sizeInBytes, filePath); - } -}; diff --git a/tools/analytics/build-analytics b/tools/analytics/build-analytics deleted file mode 100755 index 4271c8af29..0000000000 --- a/tools/analytics/build-analytics +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env node - -// Usage: -// -// build analytics start|success|error| - - -'use strict'; - - -var analytics = require('./analytics'); -var fs = require('fs'); -var path = require('path'); - -var eventType = process.argv[2]; -var actionCategory = process.argv[3]; -var actionName = process.argv[4]; - - -if (!analytics[actionCategory + 'Start']) { - throw new Error('Unknown build-analytics actionCategory "' + actionCategory + '"'); -} - -var exitCode = Number.parseInt(eventType, 10); -if (!Number.isNaN(exitCode)) { - eventType = (exitCode === 0) ? 'success' : 'error'; -} - -if (eventType != 'start' && eventType != 'success' && eventType != 'error') { - throw new Error('Unknown build-analytics eventType "' + eventType + '"'); -} - - -var startTimestampFilePath = path.resolve(path.join(__dirname, '..', '..', 'tmp', 'analytics', actionCategory + '-' + actionName)); -var analyticsDirPath = path.dirname(startTimestampFilePath); -var tmpDirPath = path.dirname(analyticsDirPath); - - -if (!fs.existsSync(tmpDirPath)) { - fs.mkdirSync(tmpDirPath); -} -if (!fs.existsSync(analyticsDirPath)) { - fs.mkdirSync(analyticsDirPath); -} - - -switch (eventType) { - case 'start': - analytics[actionCategory + 'Start'](actionName); - fs.writeFileSync(startTimestampFilePath, Date.now(), 'utf-8'); - break; - case 'success': - try { - var startTime = fs.readFileSync(startTimestampFilePath, 'utf-8'); - analytics[actionCategory + 'Success'](actionName, Date.now() - startTime); - fs.unlinkSync(startTimestampFilePath); - } catch(e) { - console.log('No start timestamp file "' + startTimestampFilePath + '" found, skipping analytics.'); - } - break; - case 'error': - var startTime = fs.readFileSync(startTimestampFilePath, 'utf-8'); - analytics[actionCategory + 'Error'](actionName, Date.now() - startTime); - fs.unlinkSync(startTimestampFilePath); -} diff --git a/tools/analytics/reportsize.js b/tools/analytics/reportsize.js deleted file mode 100644 index b642c7bf50..0000000000 --- a/tools/analytics/reportsize.js +++ /dev/null @@ -1,139 +0,0 @@ -'use strict'; - -let analytics = require('./analytics'); -let gulp = require('gulp'); -let gzip = require('gulp-gzip'); -let merge2 = require('merge2'); -let path = require('path'); -let Stream = require('stream'); - -// Keys are a text description of compressionLevel. -// Values are "gzipOptions" passed to gzip. -// We report one `analytics.bundleSize` event -// * each file in `glob` -// * each entry in `_gzipConfigs`. -const _gzipConfigs = { - 'uncompressed': null, - 'gzip level=1': {level: 1}, // code.angular.js - 'gzip level=2': {level: 2}, // github pages, most common - 'gzip level=6': {level: 6}, // default gzip level - 'gzip level=9': {level: 9} // max gzip level -}; - -const _defaultOptions = { - // @type {Object} - // - Key(s) must match one of `_gzipConfigs` keys. - // - Values are the max size (in bytes) allowed for that configuration. - failConditions: {}, - prefix: '', - // @type {Array|boolean} - // Entries must match one of `_gzipConfigs` keys. These values will be - // printed to the screen. - // If this is the boolean value `true`, will print all to screen. - printToConsole: ['gzip level=6'], - reportAnalytics: true -}; - -// `glob` is a string representing a glob of files. -// options is an object containing zero or more of -// - printToConsole: Write debug to console. Default: false. -// - reportAnalytics: Report to Google Analytics. Default: true. -function reportSize(glob, options) { - options = options || {}; - for (const key in _defaultOptions) { - if (!options.hasOwnProperty(key)) { - options[key] = _defaultOptions[key]; - } - } - var errStream = _checkConfig(options); - if (errStream) { - return errStream; - } - - const allStreams = []; - for (const compressionLevel in _gzipConfigs) { - if (_gzipConfigs.hasOwnProperty(compressionLevel)) { - let stream = gulp.src(glob); - if (_gzipConfigs[compressionLevel]) { - stream = stream.pipe(gzip({gzipOptions: _gzipConfigs[compressionLevel]})); - } - allStreams.push(stream.on('data', checkFileSizeFactory(compressionLevel))); - } - } - - let didRun = false; - var errs = []; - return merge2(allStreams, {end: false}) - .on('queueDrain', function() { - if (errs.length) { - errs.unshift(`Failed with ${errs.length} error(s).`); - this.emit('error', new Error(errs.join('\n '))); - } - if (!didRun) { - this.emit('error', new Error(`No file found for pattern "${glob}".`)); - } - this.emit('end'); - }); - - function checkFileSizeFactory(compressionLevel) { - return function checkFileSize(file) { - if (file.isNull()) return; - didRun = true; - var filePath = path.basename(file.path).replace('\.gz', ''); - if (options.prefix) { - filePath = path.join(options.prefix, filePath); - } - const fileLen = file.contents.length; - if (options.reportAnalytics) { - analytics.bundleSize(filePath, fileLen, compressionLevel); - } - if (_shouldPrint(options, compressionLevel)) { - console.log(` ${filePath} => ${fileLen} bytes (${compressionLevel})`) - } - if (options.failConditions.hasOwnProperty(compressionLevel)) { - if (options.failConditions[compressionLevel] < fileLen) { - errs.push(`Max size for "${compressionLevel}" is ` + - `${options.failConditions[compressionLevel]}, but the size is now ${fileLen}.`); - } - } - } - } -} - -function _shouldPrint(options, compressionLevel) { - const printAll = typeof options.printToConsole == 'boolean' && options.printToConsole; - return printAll || options.printToConsole.indexOf(compressionLevel) >= 0; -} - -// Returns an error stream if the fail conditions are not provided property. -// Returns `null` if everything is fine. -function _checkConfig(config) { - for (const key in config.failConditions) { - if (config.failConditions.hasOwnProperty(key)) { - if (!_gzipConfigs.hasOwnProperty(key)) { - var stream = new Stream(); - stream.emit( - 'error', - new Error(`failCondition for "${key}" will not be tested. Check _gzipConfigs.`)); - stream.emit('end'); - return stream; - } - } - } - if (typeof config.printToConsole != 'boolean') { - for (var i = 0; i < config.printToConsole.length; ++i) { - const key = config.printToConsole[i]; - if (!_gzipConfigs.hasOwnProperty(key)) { - var stream = new Stream(); - stream.emit( - 'error', - new Error(`Incorrect value "${key}" in printToConsole. Check _gzipConfigs.`)); - stream.emit('end'); - return stream; - } - } - } - return null; -} - -module.exports = reportSize; diff --git a/tools/broccoli/angular_builder.ts b/tools/broccoli/angular_builder.ts deleted file mode 100644 index c08eeb82e0..0000000000 --- a/tools/broccoli/angular_builder.ts +++ /dev/null @@ -1,174 +0,0 @@ -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 { - this.browserDevBuilder = this.browserDevBuilder || this.makeBrowserDevBuilder(opts); - return this.rebuild(this.browserDevBuilder, 'js.dev'); - } - - - public rebuildBrowserProdTree(opts: Options): Promise { - this.browserProdBuilder = this.browserProdBuilder || this.makeBrowserProdBuilder(opts); - return this.rebuild(this.browserProdBuilder, 'js.prod'); - } - - - public rebuildNodeTree(opts: Options): Promise { - this.nodeBuilder = this.nodeBuilder || this.makeNodeBuilder(opts.projects); - return this.rebuild(this.nodeBuilder, 'js.cjs'); - } - - - public rebuildDartTree(projects: ProjectMap): Promise { - this.dartBuilder = this.dartBuilder || this.makeDartBuilder(projects); - return this.rebuild(this.dartBuilder, 'dart'); - } - - - cleanup(): Promise { - 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 { - return builder.build().then( - (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 || (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[]) {} -} diff --git a/tools/broccoli/broccoli-check-imports.ts b/tools/broccoli/broccoli-check-imports.ts deleted file mode 100644 index 54a439f8f6..0000000000 --- a/tools/broccoli/broccoli-check-imports.ts +++ /dev/null @@ -1,109 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -/** - * Checks that modules do not import files that are not supposed to import. - * - * This guarantees that platform-independent modules remain platoform-independent. - */ -class CheckImports implements DiffingBroccoliPlugin { - static IMPORT_DECL_REGEXP = new RegExp(`^import[^;]+;`, 'mg'); - static IMPORT_PATH_REGEXP = new RegExp(`['"]([^'"]+)+['"]`, 'm'); - - static ALLOWED_IMPORTS: {[s: string]: string[]} = { - 'angular2/src/core': ['angular2/src/facade'], - 'angular2/src/facade': ['rxjs'], - 'angular2/src/common': ['angular2/core', 'angular2/src/facade'], - 'angular2/src/http': ['angular2/core', 'angular2/src/facade', 'rxjs'], - 'angular2/src/upgrade': - ['angular2/core', 'angular2/src/facade', 'angular2/platform/browser', 'angular2/compiler'] - //"angular2/src/render": [ - // "angular2/animate", - // "angular2/core", - // "angular2/src/facade", - //] - }; - - private initRun = true; - - constructor(private inputPath: string, private cachePath: string, private options: number) {} - - rebuild(treeDiff: DiffResult) { - const errors = this.checkAllPaths(treeDiff); - if (errors.length > 0) { - throw new Error( - `The following imports are not allowed because they break barrel boundaries:\n${errors.join("\n")}`); - } - this.symlinkInputAndCacheIfNeeded(); - return treeDiff; - } - - private checkAllPaths(treeDiff: DiffResult) { - const changesFiles = treeDiff.addedPaths.concat(treeDiff.changedPaths); - return flatMap(changesFiles, _ => this.checkFilePath(_)); - } - - private symlinkInputAndCacheIfNeeded() { - if (this.initRun) { - fs.rmdirSync(this.cachePath); - fs.symlinkSync(this.inputPath, this.cachePath); - } - this.initRun = false; - } - - private checkFilePath(filePath: string) { - const sourceFilePath = path.join(this.inputPath, filePath); - if (endsWith(sourceFilePath, '.ts') && fs.existsSync(sourceFilePath)) { - const content = fs.readFileSync(sourceFilePath, 'UTF-8'); - const imports = content.match(CheckImports.IMPORT_DECL_REGEXP); - if (imports) { - return imports.filter(i => !this.isAllowedImport(filePath, i)) - .map(i => this.formatError(filePath, i)); - } else { - return []; - } - } - return []; - } - - private isAllowedImport(sourceFile: string, importDecl: string): boolean { - const res = CheckImports.IMPORT_PATH_REGEXP.exec(importDecl); - if (!res || res.length < 2) return true; // non-es6 import - const importPath = res[1]; - - if (startsWith(importPath, './') || startsWith(importPath, '../')) return true; - - const c = CheckImports.ALLOWED_IMPORTS; - for (var prop in c) { - if (c.hasOwnProperty(prop) && startsWith(sourceFile, prop)) { - const allowedPaths = c[prop]; - return startsWith(importPath, prop) || - allowedPaths.filter(p => startsWith(importPath, p)).length > 0; - } - } - - return true; - } - - private formatError(filePath: string, importPath: string): string { - const i = importPath.replace(new RegExp(`\n`, 'g'), '\\n'); - return `${filePath}: ${i}`; - } -} - - -function startsWith(str: string, substring: string): boolean { - return str.substring(0, substring.length) === substring; -} - -function endsWith(str: string, substring: string): boolean { - return str.indexOf(substring, str.length - substring.length) !== -1; -} - -function flatMap(arr: T[], fn: (t: T) => U[]): U[] { - return [].concat(...arr.map(fn)); -} - -export default wrapDiffingPlugin(CheckImports); diff --git a/tools/broccoli/broccoli-dartfmt.ts b/tools/broccoli/broccoli-dartfmt.ts deleted file mode 100644 index 060a9457ba..0000000000 --- a/tools/broccoli/broccoli-dartfmt.ts +++ /dev/null @@ -1,106 +0,0 @@ -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; -import {AngularBuilderOptions} from './angular_builder'; - -var spawn = require('child_process').spawn; -var exec = require('child_process').exec; - -function processToPromise(process: NodeJS.Process) { - return new Promise(function(resolve, reject) { - process.on('close', function(code: number) { - if (code) { - reject(code); - } else { - resolve(); - } - }); - }); -} - -class DartFormatter implements DiffingBroccoliPlugin { - private DARTFMT: string; - private verbose: boolean; - private firstBuild: boolean = true; - - constructor(public inputPath: string, public cachePath: string, options: AngularBuilderOptions) { - if (!options.dartSDK) throw new Error('Missing Dart SDK'); - this.DARTFMT = options.dartSDK.DARTFMT; - this.verbose = options.logs.dartfmt; - } - - rebuild(treeDiff: DiffResult): Promise { - let args = ['-w']; - let argsLength = 2; - let argPackages: string[][] = []; - let firstBuild = this.firstBuild; - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFile) => { - let sourcePath = path.join(this.inputPath, changedFile); - let destPath = path.join(this.cachePath, changedFile); - if (!firstBuild && /\.dart$/.test(changedFile)) { - if ((argsLength + destPath.length + 2) >= 0x2000) { - // Win32 command line arguments length - argPackages.push(args); - args = ['-w']; - argsLength = 2; - } - args.push(destPath); - argsLength += destPath.length + 2; - } - fse.copySync(sourcePath, destPath); - }); - treeDiff.removedPaths.forEach((removedFile) => { - let destPath = path.join(this.cachePath, removedFile); - fse.removeSync(destPath); - }); - - if (!firstBuild && args.length > 1) { - argPackages.push(args); - } - - let execute = (args: string[]) => { - if (args.length < 2) return Promise.resolve(); - return new Promise((resolve, reject) => { - exec(this.DARTFMT + ' ' + args.join(' '), (err: Error, stdout: string, stderr: string) => { - if (this.verbose) { - console.log(stdout); - } - if (err) { - console.error(shortenFormatterOutput(stderr)); - reject('Formatting failed.'); - } else { - resolve(); - } - }); - }); - }; - - if (firstBuild) { - // On firstBuild, format the entire cachePath - this.firstBuild = false; - return execute(['-w', this.cachePath]); - } - - return Promise.all(argPackages.map(execute)); - } -} - -export default wrapDiffingPlugin(DartFormatter); - -var ARROW_LINE = /^(\s+)\^+/; -var BEFORE_CHARS = 15; -var stripAnsi = require('strip-ansi'); -function shortenFormatterOutput(formatterOutput: string) { - var lines = formatterOutput.split('\n'); - var match: string, line: string; - for (var i = 0; i < lines.length; i += 1) { - line = lines[i]; - if (match = stripAnsi(line).match(ARROW_LINE)) { - let leadingWhitespace = match[1].length; - let leadingCodeChars = Math.min(leadingWhitespace, BEFORE_CHARS); - lines[i] = line.substr(leadingWhitespace - leadingCodeChars); - lines[i - 1] = lines[i - 1].substr(leadingWhitespace - leadingCodeChars, 80) + '…'; - } - } - return lines.join('\n'); -} diff --git a/tools/broccoli/broccoli-dest-copy.ts b/tools/broccoli/broccoli-dest-copy.ts deleted file mode 100644 index 34c1b661e7..0000000000 --- a/tools/broccoli/broccoli-dest-copy.ts +++ /dev/null @@ -1,32 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -/** - * Intercepts each file as it is copied to the destination tempdir, - * and tees a copy to the given path outside the tmp dir. - */ -class DestCopy implements DiffingBroccoliPlugin { - constructor(private inputPath: string, private cachePath: string, private outputRoot: string) {} - - - rebuild(treeDiff: DiffResult) { - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFilePath) => { - var destFilePath = path.join(this.outputRoot, changedFilePath); - - var destDirPath = path.dirname(destFilePath); - fse.mkdirsSync(destDirPath); - fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath); - }); - - treeDiff.removedPaths.forEach((removedFilePath) => { - var destFilePath = path.join(this.outputRoot, removedFilePath); - - // TODO: what about obsolete directories? we are not cleaning those up yet - fs.unlinkSync(destFilePath); - }); - } -} - -export default wrapDiffingPlugin(DestCopy); diff --git a/tools/broccoli/broccoli-filter.d.ts b/tools/broccoli/broccoli-filter.d.ts deleted file mode 100644 index 496b1fc5a7..0000000000 --- a/tools/broccoli/broccoli-filter.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -interface FilterOptions { - extensions?: string[] -} - -declare class Filter { - constructor(inputTree: any, options?: FilterOptions); - processString(contents: string, relativePath: string): string; - // NB: This function is probably not intended as part of the public API - processFile(srcDir: string, destDir: string, relativePath: string): Promise; -} - -export = Filter; diff --git a/tools/broccoli/broccoli-flatten.spec.ts b/tools/broccoli/broccoli-flatten.spec.ts deleted file mode 100644 index 04738c789a..0000000000 --- a/tools/broccoli/broccoli-flatten.spec.ts +++ /dev/null @@ -1,74 +0,0 @@ -let mockfs = require('mock-fs'); -import fs = require('fs'); -import path = require('path'); -import {TreeDiffer} from './tree-differ'; -import {DiffingFlatten} from './broccoli-flatten'; - -describe('Flatten', () => { - afterEach(() => mockfs.restore()); - - let flatten = (inputPaths: string) => new DiffingFlatten(inputPaths, 'output', null); - let read = (path: string) => fs.readFileSync(path, {encoding: 'utf-8'}); - let rm = (path: string) => fs.unlinkSync(path); - let write = - (path: string, content: string) => { fs.writeFileSync(path, content, {encoding: 'utf-8'}); } - - - it('should flatten files and be incremental', () => { - let testDir = { - 'input': { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-2.txt content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) - }, - 'empty-dir': {} - }, - }, - 'output': {} - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'input'); - let flattenedTree = flatten('input'); - flattenedTree.rebuild(differ.diffTree()); - - expect(fs.readdirSync('output')).toEqual(['file-1.1.txt', 'file-1.txt', 'file-2.txt']); - // fails due to a mock-fs bug related to reading symlinks? - // expect(read('output/file-1.1.txt')).toBe('file-1.1.txt content'); - - - // delete a file - rm('input/dir1/file-1.txt'); - // add a new one - write('input/dir1/file-3.txt', 'file-3.txt content'); - - flattenedTree.rebuild(differ.diffTree()); - - expect(fs.readdirSync('output')).toEqual(['file-1.1.txt', 'file-2.txt', 'file-3.txt']); - }); - - - it('should throw an exception if duplicates are found', () => { - let testDir = { - 'input': { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'subdir-1': - {'file-1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)})}, - 'empty-dir': {} - }, - }, - 'output': {} - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'input'); - let flattenedTree = flatten('input'); - expect(() => flattenedTree.rebuild(differ.diffTree())) - .toThrowError( - 'Duplicate file \'file-1.txt\' found in path \'dir1' + path.sep + 'subdir-1' + - path.sep + 'file-1.txt\''); - }); -}); diff --git a/tools/broccoli/broccoli-flatten.ts b/tools/broccoli/broccoli-flatten.ts deleted file mode 100644 index 511e29de8d..0000000000 --- a/tools/broccoli/broccoli-flatten.ts +++ /dev/null @@ -1,56 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; -import {AngularBuilderOptions} from './angular_builder'; - -var symlinkOrCopy = require('symlink-or-copy').sync; - -var isWindows = process.platform === 'win32'; - - -/** - * Intercepts each changed file and replaces its contents with - * the associated changes. - */ -export class DiffingFlatten implements DiffingBroccoliPlugin { - constructor( - private inputPath: string, private cachePath: string, - private options: AngularBuilderOptions) {} - - - rebuild(treeDiff: DiffResult) { - let pathsToUpdate = treeDiff.addedPaths; - - // since we need to run on Windows as well we can't rely on symlinks being available, - // which means that we need to respond to both added and changed paths - if (isWindows) { - pathsToUpdate = pathsToUpdate.concat(treeDiff.changedPaths); - } - - pathsToUpdate.forEach((changedFilePath) => { - var sourceFilePath = path.join(this.inputPath, changedFilePath); - var destFilePath = path.join(this.cachePath, path.basename(changedFilePath)); - var destDirPath = path.dirname(destFilePath); - - if (!fs.existsSync(destDirPath)) { - fse.mkdirpSync(destDirPath); - } - - if (!fs.existsSync(destFilePath)) { - symlinkOrCopy(sourceFilePath, destFilePath); - } else { - throw new Error( - `Duplicate file '${path.basename(changedFilePath)}' ` + - `found in path '${changedFilePath}'`); - } - }); - - treeDiff.removedPaths.forEach((removedFilePath) => { - var destFilePath = path.join(this.cachePath, path.basename(removedFilePath)); - fs.unlinkSync(destFilePath); - }); - } -} - -export default wrapDiffingPlugin(DiffingFlatten); diff --git a/tools/broccoli/broccoli-generate-for-test.ts b/tools/broccoli/broccoli-generate-for-test.ts deleted file mode 100644 index 9c4d28abbf..0000000000 --- a/tools/broccoli/broccoli-generate-for-test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import childProcess = require('child_process'); -var glob = require('glob'); - -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -/** - * Intercepts each changed file and replaces its contents with - * the output of the generator. - */ -class GeneratorForTest implements DiffingBroccoliPlugin { - private seenFiles: {[key: string]: boolean} = {}; - - constructor(private inputPath: string, private outputPath: string, private options: { - files: string[], - dartPath: string - }) {} - - rebuild(treeDiff: DiffResult) { - var matchedFiles: string[] = []; - this.options.files.forEach( - (file) => { matchedFiles = matchedFiles.concat(glob.sync(file, {cwd: this.inputPath})); }); - return Promise - .all(matchedFiles.map((matchedFile) => { - var inputFilePath = path.join(this.inputPath, matchedFile); - var outputFilePath = path.join(this.outputPath, matchedFile); - - var outputDirPath = path.dirname(outputFilePath); - if (!fs.existsSync(outputDirPath)) { - fse.mkdirpSync(outputDirPath); - } - return this.invokeGenerator(matchedFile, inputFilePath, outputFilePath) - })) - .then(() => { - var result = new DiffResult(); - matchedFiles.forEach((file) => { - if (!this.seenFiles[file]) { - result.addedPaths.push(file); - this.seenFiles[file] = true; - } else { - result.changedPaths.push(file); - } - }); - return result; - }); - } - - private invokeGenerator(file: string, inputFilePath: string, outputFilePath: string): - Promise { - return new Promise((resolve, reject) => { - var args: string[]; - var vmPath: string; - var env: {[key: string]: string}; - if (this.options.dartPath) { - vmPath = this.options.dartPath; - args = [`--package-root=${this.inputPath}`, '--checked', inputFilePath, file]; - env = {}; - } else { - vmPath = process.execPath; - var script = `require('reflect-metadata');require('${inputFilePath}').main(['${file}']);`; - args = ['-e', script]; - env = {'NODE_PATH': this.inputPath}; - } - - var stdoutStream = fs.createWriteStream(outputFilePath); - var proc = childProcess.spawn(vmPath, args, { - stdio: ['ignore', 'pipe', 'inherit'], - env: (Object)['assign']({}, process.env, env) - }); - proc.on('error', function(code: any) { - console.error(code); - reject(new Error( - 'Failed while generating code. Please run manually: ' + vmPath + ' ' + args.join(' '))); - }); - proc.on('close', function() { - stdoutStream.close(); - resolve(); - }); - proc.stdout.pipe(stdoutStream); - }); - } -} - -export default wrapDiffingPlugin(GeneratorForTest); diff --git a/tools/broccoli/broccoli-lodash.ts b/tools/broccoli/broccoli-lodash.ts deleted file mode 100644 index abca54ec04..0000000000 --- a/tools/broccoli/broccoli-lodash.ts +++ /dev/null @@ -1,50 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -var _ = require('lodash'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -export interface LodashRendererOptions { - encoding?: string; - context?: any; - // files option unsupported --- use Funnel on inputTree instead. - files?: string[]; -} - -const kDefaultOptions: LodashRendererOptions = { - encoding: 'utf-8', - context: {}, - files: [] -}; - - -/** - * Intercepts each changed file and replaces its contents with - * the associated changes. - */ -export class LodashRenderer implements DiffingBroccoliPlugin { - constructor( - private inputPath: string, private cachePath: string, - private options: LodashRendererOptions = kDefaultOptions) {} - - rebuild(treeDiff: DiffResult) { - let {encoding = 'utf-8', context = {}} = this.options; - let processFile = (relativePath: string) => { - let sourceFilePath = path.join(this.inputPath, relativePath); - let destFilePath = path.join(this.cachePath, relativePath); - let content = fs.readFileSync(sourceFilePath, {encoding}); - let transformedContent = _.template(content)(context); - fse.outputFileSync(destFilePath, transformedContent); - }; - - let removeFile = (relativePath: string) => { - let destFilePath = path.join(this.cachePath, relativePath); - fs.unlinkSync(destFilePath); - }; - - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach(processFile); - treeDiff.removedPaths.forEach(removeFile); - } -} - -export default wrapDiffingPlugin(LodashRenderer); diff --git a/tools/broccoli/broccoli-merge-trees.spec.ts b/tools/broccoli/broccoli-merge-trees.spec.ts deleted file mode 100644 index 2d13aa8a8e..0000000000 --- a/tools/broccoli/broccoli-merge-trees.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -let mockfs = require('mock-fs'); -import fs = require('fs'); -import {TreeDiffer, DiffResult} from './tree-differ'; -import {MergeTrees} from './broccoli-merge-trees'; - -describe('MergeTrees', () => { - afterEach(() => mockfs.restore()); - - function mergeTrees(inputPaths: string[], cachePath: string, options: {}) { - return new MergeTrees(inputPaths, cachePath, options); - } - - function MakeTreeDiffers(rootDirs: string[]): TreeDiffer[] { - return rootDirs.map((rootDir) => new TreeDiffer('MergeTrees', rootDir)); - } - - let diffTrees = (differs: TreeDiffer[]): DiffResult[] => differs.map(tree => tree.diffTree()); - function read(path: string) { return fs.readFileSync(path, 'utf-8'); } - - it('should copy the file from the right-most inputTree with overwrite=true', () => { - let testDir: any = { - 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, - 'tree2': {'foo.js': mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)})}, - 'tree3': {'foo.js': mockfs.file({content: 'tree3/foo.js content', mtime: new Date(1000)})} - }; - mockfs(testDir); - let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']); - let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {overwrite: true}); - treeMerger.rebuild(diffTrees(treeDiffer)); - expect(read('dest/foo.js')).toBe('tree3/foo.js content'); - - delete testDir.tree2['foo.js']; - delete testDir.tree3['foo.js']; - mockfs(testDir); - treeMerger.rebuild(diffTrees(treeDiffer)); - expect(read('dest/foo.js')).toBe('tree1/foo.js content'); - - testDir.tree2['foo.js'] = mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)}); - mockfs(testDir); - treeMerger.rebuild(diffTrees(treeDiffer)); - expect(read('dest/foo.js')).toBe('tree2/foo.js content'); - }); - - it('should throw if duplicates are found during the initial build', () => { - let testDir: any = { - 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, - 'tree2': {'foo.js': mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)})}, - 'tree3': {'foo.js': mockfs.file({content: 'tree3/foo.js content', mtime: new Date(1000)})} - }; - mockfs(testDir); - let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']); - let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {}); - expect(() => treeMerger.rebuild(diffTrees(treeDiffer))) - .toThrowError( - 'Duplicate path found while merging trees. Path: "foo.js".\n' + - 'Either remove the duplicate or enable the "overwrite" option for this merge.'); - - testDir = { - 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, - 'tree2': {}, - 'tree3': {} - }; - mockfs(testDir); - }); - - - it('should throw if duplicates are found during rebuild', () => { - let testDir: any = { - 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, - 'tree2': {}, - 'tree3': {} - }; - mockfs(testDir); - - let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']); - let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {}); - expect(() => treeMerger.rebuild(diffTrees(treeDiffer))).not.toThrow(); - - - testDir.tree2['foo.js'] = mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)}); - mockfs(testDir); - expect(() => treeMerger.rebuild(diffTrees(treeDiffer))) - .toThrowError( - 'Duplicate path found while merging trees. Path: "foo.js".\n' + - 'Either remove the duplicate or enable the "overwrite" option for this merge.'); - }); -}); diff --git a/tools/broccoli/broccoli-merge-trees.ts b/tools/broccoli/broccoli-merge-trees.ts deleted file mode 100644 index ec839267b7..0000000000 --- a/tools/broccoli/broccoli-merge-trees.ts +++ /dev/null @@ -1,135 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -var symlinkOrCopySync = require('symlink-or-copy').sync; -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -var isWindows = process.platform === 'win32'; - -export interface MergeTreesOptions { overwrite?: boolean; } - -function outputFileSync(sourcePath: string, destPath: string) { - let dirname = path.dirname(destPath); - fse.mkdirsSync(dirname, {fs: fs}); - symlinkOrCopySync(sourcePath, destPath); -} - -function pathOverwrittenError(path: string) { - const msg = 'Either remove the duplicate or enable the "overwrite" option for this merge.'; - return new Error(`Duplicate path found while merging trees. Path: "${path}".\n${msg}`); -} - -export class MergeTrees implements DiffingBroccoliPlugin { - private pathCache: {[key: string]: number[]} = Object.create(null); - public options: MergeTreesOptions; - private firstBuild: boolean = true; - - constructor( - public inputPaths: string[], public cachePath: string, options: MergeTreesOptions = {}) { - this.options = options || {}; - } - - rebuild(treeDiffs: DiffResult[]) { - let overwrite = this.options.overwrite; - let pathsToEmit: string[] = []; - let pathsToRemove: string[] = []; - let emitted: {[key: string]: boolean} = Object.create(null); - let contains = (cache: number[], val: number) => { - for (let i = 0, ii = cache.length; i < ii; ++i) { - if (cache[i] === val) return true; - } - return false; - }; - - let emit = (relativePath: string) => { - // ASSERT(!emitted[relativePath]); - pathsToEmit.push(relativePath); - emitted[relativePath] = true; - }; - - if (this.firstBuild) { - this.firstBuild = false; - - // Build initial cache - treeDiffs.reverse().forEach((treeDiff: DiffResult, index: number) => { - index = treeDiffs.length - 1 - index; - treeDiff.addedPaths.forEach((changedPath) => { - let cache = this.pathCache[changedPath]; - if (cache === undefined) { - this.pathCache[changedPath] = [index]; - pathsToEmit.push(changedPath); - } else if (overwrite) { - // ASSERT(contains(pathsToEmit, changedPath)); - cache.unshift(index); - } else { - throw pathOverwrittenError(changedPath); - } - }); - }); - - } else { - // Update cache - treeDiffs.reverse().forEach((treeDiff: DiffResult, index: number) => { - index = treeDiffs.length - 1 - index; - if (treeDiff.removedPaths) { - treeDiff.removedPaths.forEach((removedPath) => { - let cache = this.pathCache[removedPath]; - // ASSERT(cache !== undefined); - // ASSERT(contains(cache, index)); - if (cache[cache.length - 1] === index) { - pathsToRemove.push(path.join(this.cachePath, removedPath)); - cache.pop(); - if (cache.length === 0) { - this.pathCache[removedPath] = undefined; - } else if (!emitted[removedPath]) { - if (cache.length === 1 && !overwrite) { - throw pathOverwrittenError(removedPath); - } - emit(removedPath); - } - } - }); - } - - let pathsToUpdate = treeDiff.addedPaths; - - if (isWindows) { - pathsToUpdate = pathsToUpdate.concat(treeDiff.changedPaths); - } - - pathsToUpdate.forEach((changedPath) => { - let cache = this.pathCache[changedPath]; - if (cache === undefined) { - // File was added - this.pathCache[changedPath] = [index]; - emit(changedPath); - } else if (!contains(cache, index)) { - cache.push(index); - cache.sort((a, b) => a - b); - if (cache.length > 1 && !overwrite) { - throw pathOverwrittenError(changedPath); - } - if (cache[cache.length - 1] === index && !emitted[changedPath]) { - emit(changedPath); - } - } - }); - }); - } - - pathsToRemove.forEach((destPath) => fse.removeSync(destPath)); - pathsToEmit.forEach((emittedPath) => { - let cache = this.pathCache[emittedPath]; - let destPath = path.join(this.cachePath, emittedPath); - let sourceIndex = cache[cache.length - 1]; - let sourceInputPath = this.inputPaths[sourceIndex]; - let sourcePath = path.join(sourceInputPath, emittedPath); - if (cache.length > 1) { - fse.removeSync(destPath); - } - outputFileSync(sourcePath, destPath); - }); - } -} - -export default wrapDiffingPlugin(MergeTrees); diff --git a/tools/broccoli/broccoli-replace.ts b/tools/broccoli/broccoli-replace.ts deleted file mode 100644 index bb7cb0076c..0000000000 --- a/tools/broccoli/broccoli-replace.ts +++ /dev/null @@ -1,59 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -var minimatch = require('minimatch'); -var FILE_ENCODING = {encoding: 'utf-8'}; - -/** - * Intercepts each changed file and replaces its contents with - * the associated changes. - */ -class DiffingReplace implements DiffingBroccoliPlugin { - // TODO: define an interface for options - constructor(private inputPath: string, private cachePath: string, private options: any) {} - - rebuild(treeDiff: DiffResult) { - var patterns = this.options.patterns; - var files = this.options.files; - - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedFilePath) => { - var sourceFilePath = path.join(this.inputPath, changedFilePath); - var destFilePath = path.join(this.cachePath, changedFilePath); - var destDirPath = path.dirname(destFilePath); - - if (!fs.existsSync(destDirPath)) { - fse.mkdirpSync(destDirPath); - } - - var fileMatches = files.some((filePath: string) => minimatch(changedFilePath, filePath)); - if (fileMatches) { - var content = fs.readFileSync(sourceFilePath, FILE_ENCODING); - patterns.forEach((pattern: any) => { - var replacement = pattern.replacement; - if (typeof replacement === 'function') { - replacement = function(content: string) { - return pattern.replacement(content, changedFilePath); - }; - } - content = content.replace(pattern.match, replacement); - }); - fs.writeFileSync(destFilePath, content, FILE_ENCODING); - } else if (!fs.existsSync(destFilePath)) { - try { - fs.symlinkSync(sourceFilePath, destFilePath); - } catch (e) { - fs.writeFileSync(destFilePath, fs.readFileSync(sourceFilePath)); - } - } - }); - - treeDiff.removedPaths.forEach((removedFilePath) => { - var destFilePath = path.join(this.cachePath, removedFilePath); - fs.unlinkSync(destFilePath); - }); - } -} - -export default wrapDiffingPlugin(DiffingReplace); diff --git a/tools/broccoli/broccoli-tree-stabilizer.ts b/tools/broccoli/broccoli-tree-stabilizer.ts deleted file mode 100644 index 3d2444bf11..0000000000 --- a/tools/broccoli/broccoli-tree-stabilizer.ts +++ /dev/null @@ -1,42 +0,0 @@ -import fs = require('fs'); -let symlinkOrCopy = require('symlink-or-copy'); - - -/** - * Stabilizes the inputPath for the following plugins in the build tree. - * - * All broccoli plugins that inherit from `broccoli-writer` or `broccoli-filter` change their - * outputPath during each rebuild. - * - * This means that all following plugins in the build tree can't rely on their inputPath being - * immutable. This results in breakage of any plugin that is not expecting such behavior. - * - * For example all `DiffingBroccoliPlugin`s expect their inputPath to be stable. - * - * By inserting this plugin into the tree after any misbehaving plugin, we can stabilize the - * inputPath for the following plugin in the tree and correct the surprising behavior. - */ -class TreeStabilizer implements BroccoliTree { - inputPath: string; - outputPath: string; - - - constructor(public inputTree: BroccoliTree) {} - - - rebuild() { - fs.rmdirSync(this.outputPath); - - // TODO: investigate if we can use rename the directory instead to improve performance on - // Windows - symlinkOrCopy.sync(this.inputPath, this.outputPath); - } - - - cleanup() {} -} - - -export default function stabilizeTree(inputTree: BroccoliTree): BroccoliTree { - return new TreeStabilizer(inputTree); -} diff --git a/tools/broccoli/broccoli-ts2dart.ts b/tools/broccoli/broccoli-ts2dart.ts deleted file mode 100644 index 5b13cbf055..0000000000 --- a/tools/broccoli/broccoli-ts2dart.ts +++ /dev/null @@ -1,53 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; - -class TSToDartTranspiler implements DiffingBroccoliPlugin { - static includeExtensions = ['.ts']; - - private transpiler: any /*ts2dart.Transpiler*/; - - constructor( - public inputPath: string, public cachePath: string, - public options: any /*ts2dart.TranspilerOptions*/) { - options.basePath = inputPath; - options.tsconfig = path.join(inputPath, options.tsconfig); - // Workaround for https://github.com/dart-lang/dart_style/issues/493 - var ts2dart = require('ts2dart'); - this.transpiler = new ts2dart.Transpiler(options); - } - - rebuild(treeDiff: DiffResult) { - let toEmit = [ - path.resolve(this.inputPath, 'angular2/manual_typings/globals.d.ts'), - path.resolve(this.inputPath, 'angular2/typings/es6-promise/es6-promise.d.ts'), - path.resolve(this.inputPath, 'angular2/typings/es6-collections/es6-collections.d.ts') - ]; - let getDartFilePath = (path: string) => path.replace(/((\.js)|(\.ts))$/i, '.dart'); - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((changedPath) => { - let inputFilePath = path.resolve(this.inputPath, changedPath); - - // Ignore files which don't need to be transpiled to Dart - let dartInputFilePath = getDartFilePath(inputFilePath); - if (fs.existsSync(dartInputFilePath)) return; - - // Prepare to rebuild - toEmit.push(path.resolve(this.inputPath, changedPath)); - }); - - treeDiff.removedPaths.forEach((removedPath) => { - let absolutePath = path.resolve(this.inputPath, removedPath); - - // Ignore files which don't need to be transpiled to Dart - let dartInputFilePath = getDartFilePath(absolutePath); - if (fs.existsSync(dartInputFilePath)) return; - - let dartOutputFilePath = getDartFilePath(removedPath); - fs.unlinkSync(path.join(this.cachePath, dartOutputFilePath)); - }); - this.transpiler.transpile(toEmit, this.cachePath); - } -} - -export default wrapDiffingPlugin(TSToDartTranspiler); diff --git a/tools/broccoli/broccoli-typescript.ts b/tools/broccoli/broccoli-typescript.ts deleted file mode 100644 index 50e53c053a..0000000000 --- a/tools/broccoli/broccoli-typescript.ts +++ /dev/null @@ -1,387 +0,0 @@ -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import * as ts from 'typescript'; -import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; -import {MetadataCollector} from '../@angular/tsc-wrapped'; - -type FileRegistry = ts.Map<{version: number}>; - -const FS_OPTS = { - encoding: 'utf-8' -}; - -// Sub-directory where the @internal typing files (.d.ts) are stored -export const INTERNAL_TYPINGS_PATH: string = 'internal_typings'; - -// Monkey patch the TS compiler to be able to re-emit files with @internal symbols -let tsEmitInternal: boolean = false; - -const originalEmitFiles: Function = (ts).emitFiles; - -(ts).emitFiles = function(resolver: any, host: any, targetSourceFile: any): any { - if (tsEmitInternal) { - const orignalgetCompilerOptions = host.getCompilerOptions; - host.getCompilerOptions = () => { - let options = clone(orignalgetCompilerOptions.call(host)); - options.stripInternal = false; - options.outDir = `${options.outDir}/${INTERNAL_TYPINGS_PATH}`; - return options; - } - } - return originalEmitFiles(resolver, host, targetSourceFile); -}; - -/** - * Broccoli plugin that implements incremental Typescript compiler. - * - * It instantiates a typescript compiler instance that keeps all the state about the project and - * can re-emit only the files that actually changed. - * - * Limitations: only files that map directly to the changed source file via naming conventions are - * re-emitted. This primarily affects code that uses `const enum`s, because changing the enum value - * requires global emit, which can affect many files. - */ -class DiffingTSCompiler implements DiffingBroccoliPlugin { - private tsOpts: ts.CompilerOptions; - private fileRegistry: FileRegistry = Object.create(null); - private rootFilePaths: string[]; - private tsServiceHost: ts.LanguageServiceHost; - private tsService: ts.LanguageService; - private metadataCollector: MetadataCollector; - private firstRun: boolean = true; - private previousRunFailed: boolean = false; - // Whether to generate the @internal typing files (they are only generated when `stripInternal` is - // true) - private genInternalTypings: boolean = false; - - static includeExtensions = ['.ts']; - - constructor(public inputPath: string, public cachePath: string, public options: any) { - // TODO: define an interface for options - if (options.rootFilePaths) { - this.rootFilePaths = options.rootFilePaths.splice(0); - delete options.rootFilePaths; - } else { - this.rootFilePaths = []; - } - - if (options.internalTypings) { - this.genInternalTypings = true; - delete options.internalTypings; - } - - // the conversion is a bit awkward, see https://github.com/Microsoft/TypeScript/issues/5276 - // in 1.8 use convertCompilerOptionsFromJson - this.tsOpts = - ts.parseJsonConfigFileContent({compilerOptions: options, files: []}, null, null).options; - - if ((this.tsOpts).stripInternal === false) { - // @internal are included in the generated .d.ts, do not generate them separately - this.genInternalTypings = false; - } - - this.tsOpts.rootDir = inputPath; - this.tsOpts.baseUrl = inputPath; - this.tsOpts.outDir = this.cachePath; - - this.tsServiceHost = new CustomLanguageServiceHost( - this.tsOpts, this.rootFilePaths, this.fileRegistry, this.inputPath); - this.tsService = ts.createLanguageService(this.tsServiceHost, ts.createDocumentRegistry()); - this.metadataCollector = new MetadataCollector(); - } - - - rebuild(treeDiff: DiffResult) { - let pathsToEmit: string[] = []; - let pathsWithErrors: string[] = []; - let errorMessages: string[] = []; - - treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach((tsFilePath) => { - if (!this.fileRegistry[tsFilePath]) { - this.fileRegistry[tsFilePath] = {version: 0}; - this.rootFilePaths.push(tsFilePath); - } else { - this.fileRegistry[tsFilePath].version++; - } - - pathsToEmit.push(path.join(this.inputPath, tsFilePath)); - }); - - treeDiff.removedPaths.forEach((tsFilePath) => { - console.log('removing outputs for', tsFilePath); - - this.rootFilePaths.splice(this.rootFilePaths.indexOf(tsFilePath), 1); - this.fileRegistry[tsFilePath] = null; - this.removeOutputFor(tsFilePath); - }); - - if (this.firstRun) { - this.firstRun = false; - this.doFullBuild(); - } else { - let program = this.tsService.getProgram(); - tsEmitInternal = false; - pathsToEmit.forEach((tsFilePath) => { - let output = this.tsService.getEmitOutput(tsFilePath); - - if (output.emitSkipped) { - let errorFound = this.collectErrors(tsFilePath); - if (errorFound) { - pathsWithErrors.push(tsFilePath); - errorMessages.push(errorFound); - } - } else { - output.outputFiles.forEach(o => { - let destDirPath = path.dirname(o.name); - fse.mkdirsSync(destDirPath); - fs.writeFileSync(o.name, this.fixSourceMapSources(o.text), FS_OPTS); - if (endsWith(o.name, '.d.ts')) { - const sourceFile = program.getSourceFile(tsFilePath); - this.emitMetadata(o.name, sourceFile); - } - }); - } - }); - - if (pathsWithErrors.length) { - this.previousRunFailed = true; - var error = - new Error('Typescript found the following errors:\n' + errorMessages.join('\n')); - (error)['showStack'] = false; - throw error; - } else if (this.previousRunFailed) { - this.doFullBuild(); - } else if (this.genInternalTypings) { - // serialize the .d.ts files containing @internal symbols - tsEmitInternal = true; - pathsToEmit.forEach((tsFilePath) => { - let output = this.tsService.getEmitOutput(tsFilePath); - if (!output.emitSkipped) { - output.outputFiles.forEach(o => { - if (endsWith(o.name, '.d.ts')) { - let destDirPath = path.dirname(o.name); - fse.mkdirsSync(destDirPath); - fs.writeFileSync(o.name, this.fixSourceMapSources(o.text), FS_OPTS); - } - }); - } - }); - tsEmitInternal = false; - } - } - } - - private collectErrors(tsFilePath: string): string { - let allDiagnostics = this.tsService.getCompilerOptionsDiagnostics() - .concat(this.tsService.getSyntacticDiagnostics(tsFilePath)) - .concat(this.tsService.getSemanticDiagnostics(tsFilePath)); - let errors: string[] = []; - - allDiagnostics.forEach(diagnostic => { - let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); - if (diagnostic.file) { - let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); - errors.push(` ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); - } else { - errors.push(` Error: ${message}`); - } - }); - - if (errors.length) { - return errors.join('\n'); - } - } - - private doFullBuild() { - let program = this.tsService.getProgram(); - let typeChecker = program.getTypeChecker(); - let diagnostics: ts.Diagnostic[] = []; - tsEmitInternal = false; - - let emitResult = program.emit(undefined, (absoluteFilePath, fileContent) => { - fse.mkdirsSync(path.dirname(absoluteFilePath)); - fs.writeFileSync(absoluteFilePath, this.fixSourceMapSources(fileContent), FS_OPTS); - if (endsWith(absoluteFilePath, '.d.ts')) { - // TODO: Use sourceFile from the callback if - // https://github.com/Microsoft/TypeScript/issues/7438 - // is taken - const originalFile = absoluteFilePath.replace(this.tsOpts.outDir, this.tsOpts.rootDir) - .replace(/\.d\.ts$/, '.ts'); - const sourceFile = program.getSourceFile(originalFile); - this.emitMetadata(absoluteFilePath, sourceFile); - } - }); - - if (this.genInternalTypings) { - // serialize the .d.ts files containing @internal symbols - tsEmitInternal = true; - program.emit(undefined, (absoluteFilePath, fileContent) => { - if (endsWith(absoluteFilePath, '.d.ts')) { - fse.mkdirsSync(path.dirname(absoluteFilePath)); - fs.writeFileSync(absoluteFilePath, fileContent, FS_OPTS); - } - }); - tsEmitInternal = false; - } - - if (emitResult.emitSkipped) { - let allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); - let errorMessages: string[] = []; - - allDiagnostics.forEach(diagnostic => { - var pos = ''; - if (diagnostic.file) { - var {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); - pos = `${diagnostic.file.fileName} (${line + 1}, ${character + 1}): ` - } - var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); - errorMessages.push(` ${pos}${message}`); - }); - - if (errorMessages.length) { - this.previousRunFailed = true; - var error = - new Error('Typescript found the following errors:\n' + errorMessages.join('\n')); - (error)['showStack'] = false; - throw error; - } else { - this.previousRunFailed = false; - } - } - } - - /** - * Emit a .metadata.json file to correspond to the .d.ts file if the module contains classes that - * use decorators or exported constants. - */ - private emitMetadata(dtsFileName: string, sourceFile: ts.SourceFile) { - if (sourceFile) { - const metadata = this.metadataCollector.getMetadata(sourceFile); - if (metadata && metadata.metadata) { - const metadataText = JSON.stringify(metadata); - const metadataFileName = dtsFileName.replace(/\.d.ts$/, '.metadata.json'); - fs.writeFileSync(metadataFileName, metadataText, FS_OPTS); - } - } - } - - /** - * There is a bug in TypeScript 1.6, where the sourceRoot and inlineSourceMap properties - * are exclusive. This means that the sources property always contains relative paths - * (e.g, ../../../../angular2/src/di/injector.ts). - * - * Here, we normalize the sources property and remove the ../../../ - * - * This issue is fixed in https://github.com/Microsoft/TypeScript/pull/5620. - * Once we switch to TypeScript 1.8, we can remove this method. - */ - private fixSourceMapSources(content: string): string { - try { - const marker = '//# sourceMappingURL=data:application/json;base64,'; - const index = content.indexOf(marker); - if (index == -1) return content; - - const base = content.substring(0, index + marker.length); - const sourceMapBit = - new Buffer(content.substring(index + marker.length), 'base64').toString('utf8'); - const sourceMaps = JSON.parse(sourceMapBit); - const source = sourceMaps.sources[0]; - sourceMaps.sources = [source.substring(source.lastIndexOf('../') + 3)]; - return `${base}${new Buffer(JSON.stringify(sourceMaps)).toString('base64')}`; - } catch (e) { - return content; - } - } - - private removeOutputFor(tsFilePath: string) { - let absoluteJsFilePath = path.join(this.cachePath, tsFilePath.replace(/\.ts$/, '.js')); - let absoluteMapFilePath = path.join(this.cachePath, tsFilePath.replace(/.ts$/, '.js.map')); - let absoluteDtsFilePath = path.join(this.cachePath, tsFilePath.replace(/\.ts$/, '.d.ts')); - - if (fs.existsSync(absoluteJsFilePath)) { - fs.unlinkSync(absoluteJsFilePath); - if (fs.existsSync(absoluteMapFilePath)) { - // source map could be inline or not generated - fs.unlinkSync(absoluteMapFilePath); - } - fs.unlinkSync(absoluteDtsFilePath); - } - } -} - - -class CustomLanguageServiceHost implements ts.LanguageServiceHost { - private currentDirectory: string; - private defaultLibFilePath: string; - - - constructor( - private compilerOptions: ts.CompilerOptions, private fileNames: string[], - private fileRegistry: FileRegistry, private treeInputPath: string) { - this.currentDirectory = process.cwd(); - this.defaultLibFilePath = ts.getDefaultLibFilePath(compilerOptions).replace(/\\/g, '/'); - } - - - getScriptFileNames(): string[] { - return this.fileNames.map(f => path.join(this.treeInputPath, f)); - } - - - getScriptVersion(fileName: string): string { - if (startsWith(fileName, this.treeInputPath)) { - const key = fileName.substr(this.treeInputPath.length + 1); - return this.fileRegistry[key] && this.fileRegistry[key].version.toString(); - } - } - - - getScriptSnapshot(tsFilePath: string): ts.IScriptSnapshot { - // TypeScript seems to request lots of bogus paths during import path lookup and resolution, - // so we we just return undefined when the path is not correct. - - // Ensure it is in the input tree or a lib.d.ts file. - if (!startsWith(tsFilePath, this.treeInputPath) && !tsFilePath.match(/\/lib(\..*)*.d\.ts$/)) { - if (fs.existsSync(tsFilePath)) { - console.log('Rejecting', tsFilePath, '. File is not in the input tree.'); - } - return undefined; - } - - // Ensure it exists - if (!fs.existsSync(tsFilePath)) { - return undefined; - } - - return ts.ScriptSnapshot.fromString(fs.readFileSync(tsFilePath, FS_OPTS)); - } - - - getCurrentDirectory(): string { return this.currentDirectory; } - - getCompilationSettings(): ts.CompilerOptions { return this.compilerOptions; } - - getDefaultLibFileName(options: ts.CompilerOptions): string { - // ignore options argument, options should not change during the lifetime of the plugin - return this.defaultLibFilePath; - } -} - -export default wrapDiffingPlugin(DiffingTSCompiler); - -function clone(object: T): T { - const result: any = {}; - for (const id in object) { - result[id] = (object)[id]; - } - return result; -} - -function startsWith(str: string, substring: string): boolean { - return str.substring(0, substring.length) === substring; -} - -function endsWith(str: string, substring: string): boolean { - return str.indexOf(substring, str.length - substring.length) !== -1; -} diff --git a/tools/broccoli/broccoli-writer.d.ts b/tools/broccoli/broccoli-writer.d.ts deleted file mode 100644 index 5a873849f3..0000000000 --- a/tools/broccoli/broccoli-writer.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'broccoli-writer' { - class Writer { - write(readTree: (tree: BroccoliTree) => Promise, destDir: string): Promise; - } - export = Writer; -} diff --git a/tools/broccoli/broccoli.d.ts b/tools/broccoli/broccoli.d.ts deleted file mode 100644 index 01a9623756..0000000000 --- a/tools/broccoli/broccoli.d.ts +++ /dev/null @@ -1,157 +0,0 @@ -interface BroccoliTree { - /** - * Contains the fs path for the input tree when the plugin takes only one input tree. - * - * For plugins that take multiple trees see the `inputPaths` property. - * - * This property is set just before the first rebuild and doesn't change afterwards. - */ - inputPath: string; - - /** - * Contains the array of fs paths for input trees. - * - * For plugins that take only one input tree, it might be more convenient to use the `inputPath` - *property instead. - * - * This property is set just before the first rebuild and doesn't change afterwards, unless - * plugins themselves change it. - * - * If the inputPath is outside of broccoli's temp directory, then it's lifetime is not managed by - *the builder. - * If the inputPath is within broccoli's temp directory it is an outputPath (and output directory) - *of another plugin. - * This means that while the `outputPath` doesn't change, the underlying directory is frequently - *recreated. - */ - inputPaths?: string[]; - - /** - * Contains the fs paths for the output trees. - * - * This property is set just before the first rebuild and doesn't change afterwards, unless the - * plugins themselves change it. - * - * The underlying directory is also created by the builder just before the first rebuild. - * This directory is destroyed and recreated upon each rebuild. - */ - outputPath?: string; - - /** - * Contains the fs paths for a cache directory available to the plugin. - * - * This property is set just before the first rebuild and doesn't change afterwards. - * - * The underlying directory is also created by the builder just before the first rebuild. - * The lifetime of the directory is associated with the lifetime of the plugin. - */ - cachePath?: string; - - inputTree?: BroccoliTree; - inputTrees?: BroccoliTree[]; - - /** - * Trees which implement the rebuild api are wrapped automatically for api compat, - * and `newStyleTree` keeps a reference to the original unwrapped tree. - */ - newStyleTree?: BroccoliTree; - - /** - * Description or name of the plugin used for reporting. - * - * If missing `tree.constructor.name` is usually used instead. - */ - description?: string; - - rebuild(): (Promise|void); - cleanup(): void; -} - - -interface OldBroccoliTree { - read?(readTree: (tree: BroccoliTree) => Promise): (Promise|string); -} - - - -interface BroccoliBuilder { - /** - * Triggers a build and returns a promise for the build result - */ - build(): Promise; - - - /** - * Cleans up the whole build tree by calling `.cleanup()` method on all trees that are part of the - * pipeline. - */ - cleanup(): Promise; -} - - -interface BuildResult { - /** - * Directory that contains result of the build. - * - * This directory will contains symlinks, so it is not safe to just use it as is. - * - * Use `copy-dereference` npm package to create a safe-to-use replica of the build artifacts. - */ - directory: string; - - - /** - * The DAG (graph) of all trees in the build pipeline. - */ - graph: BroccoliNode; - - /** - * Total time it took to make the build. - */ - totalTime: number; -} - - - -interface BroccoliNode { - ///** - // * Id of the current node - // */ - // id: number; //only in master - - /** - * Time spent processing the current node during a single rebuild. - */ - selfTime: number; - - - /** - * Time spent processing the current node and its subtrees during a single rebuild. - */ - totalTime: number; - - - /** - * Tree associated with the current node. - */ - tree: BroccoliTree; - - - /** - * Child nodes with references to trees that are input for the tree of the current node. - */ - subtrees: BroccoliNode[]; - - - /** - * Parent nodes with references to trees that are consume the output of processing the current - * tree. - */ - parents: BroccoliNode[]; - - - /** - * Path to the directory containing the output of processing the current tree. - */ - directory: string; -} diff --git a/tools/broccoli/diffing-broccoli-plugin.ts b/tools/broccoli/diffing-broccoli-plugin.ts deleted file mode 100644 index 9c8ba37349..0000000000 --- a/tools/broccoli/diffing-broccoli-plugin.ts +++ /dev/null @@ -1,186 +0,0 @@ -/// - -import fs = require('fs'); -import fse = require('fs-extra'); -import path = require('path'); -import {TreeDiffer, DiffResult} from './tree-differ'; -import stabilizeTree from './broccoli-tree-stabilizer'; -let symlinkOrCopy = require('symlink-or-copy'); - -export {DiffResult} from './tree-differ'; - -export type PluginClass = any; - -/** - * Makes writing diffing plugins easy. - * - * Factory method that takes a class that implements the DiffingBroccoliPlugin interface and returns - * an instance of BroccoliTree. - */ -export function wrapDiffingPlugin(pluginClass: PluginClass): DiffingPluginWrapperFactory { - return function() { return new DiffingPluginWrapper(pluginClass, arguments) }; -} - - -export interface DiffingBroccoliPlugin { - rebuild(diff: (DiffResult|DiffResult[])): (Promise|DiffResult|void); - cleanup?(): void; -} - - -export type DiffingPluginWrapperFactory = - (inputTrees: (BroccoliTree | BroccoliTree[]), options?: any) => BroccoliTree; - - -class DiffingPluginWrapper implements BroccoliTree { - treeDiffer: TreeDiffer = null; - treeDiffers: TreeDiffer[] = null; - initialized = false; - wrappedPlugin: DiffingBroccoliPlugin = null; - inputTree: BroccoliTree = null; - inputTrees: BroccoliTree[] = null; - description: string = null; - - // props monkey-patched by broccoli builder: - inputPath: string = null; - inputPaths: string[] = null; - cachePath: string = null; - outputPath: string = null; - - private diffResult: DiffResult = null; - - constructor(private pluginClass: PluginClass, private wrappedPluginArguments: IArguments) { - if (Array.isArray(wrappedPluginArguments[0])) { - this.inputTrees = this.stabilizeTrees(wrappedPluginArguments[0]); - } else { - this.inputTree = this.stabilizeTree(wrappedPluginArguments[0]); - } - - this.description = this.pluginClass.name; - } - - private getDiffResult(): (DiffResult|DiffResult[]) { - let returnOrCalculateDiffResult = (tree: BroccoliTree, index: number) => { - // returnOrCalculateDiffResult will do one of two things: - // - // If `this.diffResult` is null, calculate a DiffResult using TreeDiffer - // for the input tree. - // - // Otherwise, `this.diffResult` was produced from the output of the - // inputTree's rebuild() method, and can be used without being checked. - // Set `this.diffResult` to null and return the previously stored value. - let diffResult = this.diffResult; - if (diffResult) return diffResult; - let differ = index === -1 ? this.treeDiffer : this.treeDiffers[index]; - return differ.diffTree(); - }; - - if (this.inputTrees) { - return this.inputTrees.map(returnOrCalculateDiffResult); - } else if (this.inputTree) { - return returnOrCalculateDiffResult(this.inputTree, -1); - } else { - throw new Error('Missing TreeDiffer'); - } - } - - private maybeStoreDiffResult(value: (DiffResult|void)) { - if (!(value instanceof DiffResult)) value = null; - this.diffResult = (value); - } - - rebuild(): (Promise|void) { - try { - let firstRun = !this.initialized; - this.init(); - - let diffResult = this.getDiffResult(); - - let result = this.wrappedPlugin.rebuild(diffResult); - - if (result) { - let resultPromise = >(result); - if (resultPromise.then) { - // rebuild() -> Promise<> - return resultPromise.then((result: (DiffResult | void)) => { - this.maybeStoreDiffResult(result); - this.relinkOutputAndCachePaths(); - }); - } - } - - this.maybeStoreDiffResult(<(DiffResult | void)>(result)); - this.relinkOutputAndCachePaths(); - } catch (e) { - e.message = `[${this.description}]: ${e.message}`; - throw e; - } - } - - - cleanup() { - if (this.wrappedPlugin && this.wrappedPlugin.cleanup) { - this.wrappedPlugin.cleanup(); - } - } - - - private relinkOutputAndCachePaths() { - // just symlink the cache and output tree - fs.rmdirSync(this.outputPath); - symlinkOrCopy.sync(this.cachePath, this.outputPath); - } - - - private init() { - if (!this.initialized) { - let includeExtensions = this.pluginClass.includeExtensions || []; - let excludeExtensions = this.pluginClass.excludeExtensions || []; - let description = this.description; - this.initialized = true; - if (this.inputPaths) { - this.treeDiffers = this.inputPaths.map( - (inputPath) => - new TreeDiffer(description, inputPath, includeExtensions, excludeExtensions)); - } else if (this.inputPath) { - this.treeDiffer = - new TreeDiffer(description, this.inputPath, includeExtensions, excludeExtensions); - } - this.wrappedPlugin = new this.pluginClass( - this.inputPaths || this.inputPath, this.cachePath, this.wrappedPluginArguments[1]); - } - } - - - private stabilizeTrees(trees: BroccoliTree[]) { - // Prevent extensions to prevent array from being mutated from the outside. - // For-loop used to avoid re-allocating a new array. - var stableTrees: BroccoliTree[] = []; - for (let i = 0; i < trees.length; ++i) { - // ignore null/undefined input tries in order to support conditional build pipelines - if (trees[i]) { - stableTrees.push(this.stabilizeTree(trees[i])); - } - } - - if (stableTrees.length === 0) { - throw new Error('No input trees provided!'); - } - - return Object.freeze(stableTrees); - } - - - private stabilizeTree(tree: BroccoliTree) { - // Ignore all DiffingPlugins as they are already stable, for others we don't know for sure - // so we need to stabilize them. - // Since it's not safe to use instanceof operator in node, we are checking the constructor.name. - // - // New-style/rebuild trees should always be stable. - let isNewStyleTree = - !!(tree['newStyleTree'] || typeof tree.rebuild === 'function' || - (tree)['isReadAPICompatTree'] || (tree).constructor['name'] === 'Funnel'); - - return isNewStyleTree ? tree : stabilizeTree(tree); - } -} diff --git a/tools/broccoli/html-replace/SCRIPTS.html b/tools/broccoli/html-replace/SCRIPTS.html deleted file mode 100644 index 43ed12f605..0000000000 --- a/tools/broccoli/html-replace/SCRIPTS.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - diff --git a/tools/broccoli/html-replace/SCRIPTS_benchmarks.html b/tools/broccoli/html-replace/SCRIPTS_benchmarks.html deleted file mode 100644 index 113460231e..0000000000 --- a/tools/broccoli/html-replace/SCRIPTS_benchmarks.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/tools/broccoli/html-replace/SCRIPTS_benchmarks_external.html b/tools/broccoli/html-replace/SCRIPTS_benchmarks_external.html deleted file mode 100644 index 1c13a637eb..0000000000 --- a/tools/broccoli/html-replace/SCRIPTS_benchmarks_external.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/tools/broccoli/html-replace/index.ts b/tools/broccoli/html-replace/index.ts deleted file mode 100644 index a07e8ffc17..0000000000 --- a/tools/broccoli/html-replace/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -var fs = require('fs'); -var path = require('path'); - -module.exports = read; -function read(file: string) { - var content = fs.readFileSync( - path.join('tools/broccoli/html-replace', file + '.html'), {encoding: 'utf-8'}); - // TODO(broccoli): we don't really need this, it's here to make the output match the - // tools/build/html - return content.substring(0, content.lastIndexOf('\n')); -} diff --git a/tools/broccoli/js-replace/SCRIPTS.js b/tools/broccoli/js-replace/SCRIPTS.js deleted file mode 100644 index 371769eff9..0000000000 --- a/tools/broccoli/js-replace/SCRIPTS.js +++ /dev/null @@ -1,2 +0,0 @@ -importScripts("es6-shim.js", "zone.js", "long-stack-trace-zone.js", "system.src.js", - "Reflect.js"); diff --git a/tools/broccoli/js-replace/index.ts b/tools/broccoli/js-replace/index.ts deleted file mode 100644 index 22628dcfc7..0000000000 --- a/tools/broccoli/js-replace/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -var fs = require('fs'); -var path = require('path'); - -module.exports = readJs; -function readJs(file: string) { - var content = - fs.readFileSync(path.join('tools/broccoli/js-replace', file + '.js'), {encoding: 'utf-8'}); - // TODO(broccoli): we don't really need this, it's here to make the output match the - // tools/build/html - return content.substring(0, content.lastIndexOf('\n')); -} diff --git a/tools/broccoli/multi_copy.ts b/tools/broccoli/multi_copy.ts deleted file mode 100644 index 1e8fb1d52b..0000000000 --- a/tools/broccoli/multi_copy.ts +++ /dev/null @@ -1,46 +0,0 @@ -/// - -import Writer = require('broccoli-writer'); -import fs = require('fs'); -import fsx = require('fs-extra'); -var minimatch = require('minimatch'); -var path = require('path'); -var glob = require('glob'); - -export interface MultiCopyOptions { - /** The path of the file to copy. */ - srcPath: string; - /** A list of glob patterns of folders to copy to, matched against the input tree. */ - targetPatterns: string[]; - /** List of glob patterns to *not* copy to, matched against the matches from `targetPatterns`. */ - exclude?: string[]; -} - -/** - * A writer that copies an input file from an input path into (potentially many) output locations - * given by glob patterns, . - */ -export class MultiCopy extends Writer { - constructor(private inputTree: BroccoliTree, private options: MultiCopyOptions) { super(); } - - write(readTree: (tree: BroccoliTree) => Promise, destDir: string): Promise { - return readTree(this.inputTree).then((inputPath: string) => { - var fileName = path.basename(this.options.srcPath); - var data = fs.readFileSync(path.join(inputPath, this.options.srcPath), 'utf-8'); - - this.options.targetPatterns.forEach(pattern => { - var paths: string[] = glob.sync(pattern); - paths = paths.filter(p => fs.statSync(p).isDirectory()); - if (this.options.exclude) { - paths = paths.filter(p => !this.options.exclude.some((excl) => minimatch(p, excl))); - } - paths.forEach(p => { - var folder = path.join(destDir, p); - fsx.mkdirsSync(folder); - var outputPath = path.join(folder, fileName); - fs.writeFileSync(outputPath, data); - }); - }); - }); - } -} diff --git a/tools/broccoli/tree-differ.spec.ts b/tools/broccoli/tree-differ.spec.ts deleted file mode 100644 index 4eb94f3ecc..0000000000 --- a/tools/broccoli/tree-differ.spec.ts +++ /dev/null @@ -1,380 +0,0 @@ -let mockfs = require('mock-fs'); -import fs = require('fs'); -import path = require('path'); -import {TreeDiffer} from './tree-differ'; - - -describe('TreeDiffer', () => { - - afterEach(() => mockfs.restore()); - - - describe('diff of added and changed files', () => { - - it('should list all files (but no directories) during the first diff', () => { - let testDir = { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-2.txt content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) - }, - 'empty-dir': {} - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([ - 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt' - ]); - - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - }); - - - it('should return empty diff if nothing has changed', () => { - let testDir = { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-2.txt content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) - }, - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).not.toEqual([]); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - - diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([]); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - }); - - - it('should list only changed files during the subsequent diffs', () => { - let testDir = { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-2.txt content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.txt': - mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) - } - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([ - 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt' - ]); - - // change two files - testDir['dir1']['file-1.txt'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['subdir-1']['file-1.1.txt'] = - mockfs.file({content: 'file-1.1.txt content', mtime: new Date(9999)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - - expect(diffResult.changedPaths).toEqual([ - 'file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt' - ]); - - expect(diffResult.removedPaths).toEqual([]); - - // change one file - testDir['dir1']['file-1.txt'] = mockfs.file({content: 'super new', mtime: new Date(1000)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.txt']); - }); - - - it('should handle changes via symbolic links', () => { - let testDir = { - 'orig_path': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-2.txt content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) - } - }, - 'symlinks': { - 'file-1.txt': mockfs.symlink({path: '../orig_path/file-1.txt'}), - 'file-2.txt': mockfs.symlink({path: '../orig_path/file-2.txt'}), - 'subdir-1': - {'file-1.1.txt': mockfs.symlink({path: '../../orig_path/subdir-1/file-1.1.txt'})} - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'symlinks'); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([ - 'file-1.txt', 'file-2.txt', 'subdir-1' + path.sep + 'file-1.1.txt' - ]); - - // change two files - testDir['orig_path']['file-1.txt'] = - mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['orig_path']['subdir-1']['file-1.1.txt'] = - mockfs.file({content: 'file-1.1.txt content', mtime: new Date(9999)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([]); - expect(diffResult.changedPaths).toEqual([ - 'file-1.txt', 'subdir-1' + path.sep + 'file-1.1.txt' - ]); - expect(diffResult.removedPaths).toEqual([]); - - // change one file - testDir['orig_path']['file-1.txt'] = - mockfs.file({content: 'super new', mtime: new Date(1000)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.txt']); - - // remove a link - delete testDir['orig_path']['file-1.txt']; - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.addedPaths).toEqual([]); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual(['file-1.txt']); - - // don't report it as a removal twice - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - - // re-add it. - testDir['orig_path']['file-1.txt'] = - mockfs.file({content: 'super new', mtime: new Date(1000)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.addedPaths).toEqual(['file-1.txt']); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - }); - - - it('should throw an error if an extension isn\'t prefixed with doc', () => { - // includeExtensions - expect(() => new TreeDiffer('testLabel', 'dir1', ['js'])) - .toThrowError('Extension must begin with \'.\'. Was: \'js\''); - - // excludeExtentions - expect(() => new TreeDiffer('testLabel', 'dir1', [], ['js'])) - .toThrowError('Extension must begin with \'.\'. Was: \'js\''); - }); - - - it('should ignore files with extensions not listed in includeExtensions', () => { - let testDir = { - 'dir1': { - 'file-1.js': mockfs.file({content: 'file-1.js content', mtime: new Date(1000)}), - 'file-2.md': mockfs.file({content: 'file-2.md content', mtime: new Date(1000)}), - 'file-3.coffee': mockfs.file({content: 'file-3.coffee content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.cc': mockfs.file({content: 'file-1.1.cc content', mtime: new Date(1000)}) - } - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1', ['.js', '.coffee']); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual(['file-1.js', 'file-3.coffee']); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - - // change two files - testDir['dir1']['file-1.js'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['file-3.coffee'] = - mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['subdir-1']['file-1.1.cc'] = - mockfs.file({content: 'file-1.1.cc content', mtime: new Date(9999)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([]); - expect(diffResult.changedPaths).toEqual(['file-1.js', 'file-3.coffee']); - expect(diffResult.removedPaths).toEqual([]); - - // change one file - testDir['dir1']['file-1.js'] = mockfs.file({content: 'super new', mtime: new Date(1000)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.js']); - }); - - - it('should ignore files with extensions listed in excludeExtensions', () => { - let testDir = { - 'dir1': { - 'file-1.ts': mockfs.file({content: 'file-1.ts content', mtime: new Date(1000)}), - 'file-1.cs': mockfs.file({content: 'file-1.cs content', mtime: new Date(1000)}), - 'file-1d.cs': mockfs.file({content: 'file-1d.cs content', mtime: new Date(1000)}), - 'file-1.d.cs': mockfs.file({content: 'file-1.d.cs content', mtime: new Date(1000)}), - 'file-2.md': mockfs.file({content: 'file-2.md content', mtime: new Date(1000)}), - 'file-3.ts': mockfs.file({content: 'file-3.ts content', mtime: new Date(1000)}), - 'file-4.d.ts': mockfs.file({content: 'file-4.d.ts content', mtime: new Date(1000)}), - 'subdir-1': { - 'file-1.1.cc': mockfs.file({content: 'file-1.1.cc content', mtime: new Date(1000)}) - } - } - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1', ['.ts', '.cs'], ['.d.ts', '.d.cs']); - - let diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual(['file-1.cs', 'file-1.ts', 'file-1d.cs', 'file-3.ts']); - - // change two files - testDir['dir1']['file-1.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['file-1.cs'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['file-1.d.cs'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['file-3.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['file-4.d.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); - testDir['dir1']['subdir-1']['file-1.1.cc'] = - mockfs.file({content: 'file-1.1.cc content', mtime: new Date(9999)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - - expect(diffResult.addedPaths).toEqual([]); - expect(diffResult.changedPaths).toEqual(['file-1.cs', 'file-1.ts', 'file-3.ts']); - expect(diffResult.removedPaths).toEqual([]); - - // change one file - testDir['dir1']['file-4.d.ts'] = mockfs.file({content: 'super new', mtime: new Date(1000)}); - mockfs(testDir); - - diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual([]); - }); - }); - - describe('diff of new files', () => { - - it('should detect file additions', () => { - let testDir: any = { - 'dir1': - {'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)})} - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - differ.diffTree(); - - testDir['dir1']['file-2.txt'] = 'new file'; - mockfs(testDir); - - let diffResult = differ.diffTree(); - expect(diffResult.addedPaths).toEqual(['file-2.txt']); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual([]); - }); - - - it('should detect file additions mixed with file changes', () => { - let testDir: any = { - 'dir1': - {'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)})} - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - differ.diffTree(); - - testDir['dir1']['file-1.txt'] = 'new content'; - testDir['dir1']['file-2.txt'] = 'new file'; - mockfs(testDir); - - let diffResult = differ.diffTree(); - expect(diffResult.addedPaths).toEqual(['file-2.txt']); - expect(diffResult.changedPaths).toEqual(['file-1.txt']); - }); - }); - - - describe('diff of removed files', () => { - - it('should detect file removals and report them as removed files', () => { - let testDir = { - 'dir1': - {'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)})} - }; - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - differ.diffTree(); - - delete testDir['dir1']['file-1.txt']; - mockfs(testDir); - - let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual([]); - expect(diffResult.removedPaths).toEqual(['file-1.txt']); - }); - - - it('should detect file removals mixed with file changes and additions', () => { - let testDir: any = { - 'dir1': { - 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), - 'file-2.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}) - } - }; - - mockfs(testDir); - - let differ = new TreeDiffer('testLabel', 'dir1'); - differ.diffTree(); - - testDir['dir1']['file-1.txt'] = 'changed content'; - delete testDir['dir1']['file-2.txt']; - testDir['dir1']['file-3.txt'] = 'new content'; - mockfs(testDir); - - let diffResult = differ.diffTree(); - expect(diffResult.addedPaths).toEqual(['file-3.txt']); - expect(diffResult.changedPaths).toEqual(['file-1.txt']); - expect(diffResult.removedPaths).toEqual(['file-2.txt']); - }); - }); -}); diff --git a/tools/broccoli/tree-differ.ts b/tools/broccoli/tree-differ.ts deleted file mode 100644 index a2e4d081ed..0000000000 --- a/tools/broccoli/tree-differ.ts +++ /dev/null @@ -1,175 +0,0 @@ -import fs = require('fs'); -import path = require('path'); - - -function tryStatSync(path: string) { - try { - return fs.statSync(path); - } catch (e) { - if (e.code === 'ENOENT') return null; - throw e; - } -} - - -export class TreeDiffer { - private fingerprints: {[key: string]: string} = Object.create(null); - private nextFingerprints: {[key: string]: string} = Object.create(null); - private rootDirName: string; - private include: RegExp = null; - private exclude: RegExp = null; - - constructor( - private label: string, private rootPath: string, includeExtensions?: string[], - excludeExtensions?: string[]) { - this.rootDirName = path.basename(rootPath); - - let buildRegexp = (arr: string[]) => new RegExp(`(${arr.reduce(combine, "")})$`, 'i'); - - this.include = (includeExtensions || []).length ? buildRegexp(includeExtensions) : null; - this.exclude = (excludeExtensions || []).length ? buildRegexp(excludeExtensions) : null; - - function combine(prev: string, curr: string) { - if (curr.charAt(0) !== '.') { - throw new Error(`Extension must begin with '.'. Was: '${curr}'`); - } - let kSpecialRegexpChars = /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g; - curr = '(' + curr.replace(kSpecialRegexpChars, '\\$&') + ')'; - return prev ? (prev + '|' + curr) : curr; - } - } - - - public diffTree(): DiffResult { - let result = new DirtyCheckingDiffResult(this.label, this.rootDirName); - this.dirtyCheckPath(this.rootPath, result); - this.detectDeletionsAndUpdateFingerprints(result); - result.endTime = Date.now(); - return result; - } - - - private dirtyCheckPath(rootDir: string, result: DirtyCheckingDiffResult) { - fs.readdirSync(rootDir).forEach((segment) => { - let absolutePath = path.join(rootDir, segment); - let pathStat = fs.lstatSync(absolutePath); - if (pathStat.isSymbolicLink()) { - pathStat = tryStatSync(absolutePath); - if (pathStat === null) return; - } - - if (pathStat.isDirectory()) { - result.directoriesChecked++; - this.dirtyCheckPath(absolutePath, result); - } else { - if (!(this.include && !absolutePath.match(this.include)) && - !(this.exclude && absolutePath.match(this.exclude))) { - result.filesChecked++; - let relativeFilePath = path.relative(this.rootPath, absolutePath); - - switch (this.isFileDirty(absolutePath, pathStat)) { - case FileStatus.Added: - result.addedPaths.push(relativeFilePath); - break; - case FileStatus.Changed: - result.changedPaths.push(relativeFilePath); - } - } - } - }); - - return result; - } - - - private isFileDirty(path: string, stat: fs.Stats): FileStatus { - let oldFingerprint = this.fingerprints[path]; - let newFingerprint = `${stat.mtime.getTime()} # ${stat.size}`; - - this.nextFingerprints[path] = newFingerprint; - - if (oldFingerprint) { - this.fingerprints[path] = null; - - if (oldFingerprint === newFingerprint) { - // nothing changed - return FileStatus.Unchanged; - } - - return FileStatus.Changed; - } - - return FileStatus.Added; - } - - - private detectDeletionsAndUpdateFingerprints(result: DiffResult) { - for (let absolutePath in this.fingerprints) { - if (!(this.include && !absolutePath.match(this.include)) && - !(this.exclude && absolutePath.match(this.exclude))) { - if (this.fingerprints[absolutePath] !== null) { - let relativePath = path.relative(this.rootPath, absolutePath); - result.removedPaths.push(relativePath); - } - } - } - - this.fingerprints = this.nextFingerprints; - this.nextFingerprints = Object.create(null); - } -} - - -export class DiffResult { - public addedPaths: string[] = []; - public changedPaths: string[] = []; - public removedPaths: string[] = []; - - constructor(public label: string = '') {} - - log(verbose: boolean): void {} - - toString(): string { - // TODO(@caitp): more meaningful logging - return ''; - } -} - -class DirtyCheckingDiffResult extends DiffResult { - public filesChecked: number = 0; - public directoriesChecked: number = 0; - public startTime: number = Date.now(); - public endTime: number = null; - - constructor(label: string, public directoryName: string) { super(label); } - - toString() { - return `${pad(this.label, 30)}, ${pad(this.endTime - this.startTime, 5)}ms, ` + - `${pad(this.addedPaths.length + this.changedPaths.length + this.removedPaths.length, 5)} changes ` + - `(files: ${pad(this.filesChecked, 5)}, dirs: ${pad(this.directoriesChecked, 4)})`; - } - - log(verbose: boolean) { - let prefixedPaths = this.addedPaths.map(p => `+ ${p}`) - .concat(this.changedPaths.map(p => `* ${p}`)) - .concat(this.removedPaths.map(p => `- ${p}`)); - console.log( - `Tree diff: ${this}` + - ((verbose && prefixedPaths.length) ? ` [\n ${prefixedPaths.join('\n ')}\n]` : '')); - } -} - - -function pad(v: string | number, length: number) { - let value = '' + v; - let whitespaceLength = (value.length < length) ? length - value.length : 0; - whitespaceLength = whitespaceLength + 1; - return new Array(whitespaceLength).join(' ') + value; -} - - -enum FileStatus { - Added, - Unchanged, - Changed -} diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts deleted file mode 100644 index add4c1d8f1..0000000000 --- a/tools/broccoli/trees/browser_tree.ts +++ /dev/null @@ -1,325 +0,0 @@ -'use strict'; - -var Funnel = require('broccoli-funnel'); -var htmlReplace = require('../html-replace'); -var jsReplace = require('../js-replace'); -var path = require('path'); -var stew = require('broccoli-stew'); - -import compileWithTypescript from '../broccoli-typescript'; -import destCopy from '../broccoli-dest-copy'; -import flatten from '../broccoli-flatten'; -import mergeTrees from '../broccoli-merge-trees'; -import replace from '../broccoli-replace'; -import checkImports from '../broccoli-check-imports'; - - -const kServedPaths = [ - // Relative (to /modules) paths to benchmark directories - 'benchmarks/src', - 'benchmarks/src/change_detection', - 'benchmarks/src/compiler', - 'benchmarks/src/costs', - 'benchmarks/src/di', - 'benchmarks/src/element_injector', - 'benchmarks/src/largetable', - 'benchmarks/src/naive_infinite_scroll', - 'benchmarks/src/page_load', - 'benchmarks/src/tree', - 'benchmarks/src/static_tree', - - // Relative (to /modules) paths to external benchmark directories - 'benchmarks_external/src', - 'benchmarks_external/src/compiler', - 'benchmarks_external/src/largetable', - 'benchmarks_external/src/naive_infinite_scroll', - 'benchmarks_external/src/tree', - 'benchmarks_external/src/tree/react', - 'benchmarks_external/src/static_tree', - - // Relative (to /modules) paths to example directories - 'playground/src/animate', - 'playground/src/benchpress', - 'playground/src/model_driven_forms', - 'playground/src/template_driven_forms', - 'playground/src/person_management', - 'playground/src/order_management', - 'playground/src/gestures', - 'playground/src/hash_routing', - 'playground/src/hello_world', - 'playground/src/http', - 'playground/src/jsonp', - 'playground/src/key_events', - 'playground/src/relative_assets', - 'playground/src/routing', - 'playground/src/sourcemap', - 'playground/src/svg', - 'playground/src/todo', - 'playground/src/upgrade', - 'playground/src/zippy_component', - 'playground/src/async', - 'playground/src/web_workers/kitchen_sink', - 'playground/src/web_workers/todo', - 'playground/src/web_workers/images', - 'playground/src/web_workers/message_broker', - 'playground/src/web_workers/router', - 'playground/src/web_workers/input', -]; - - -module.exports = function makeBrowserTree(options: any, destinationPath: string) { - // TODO: define an interface for the options - const modules = options.projects; - const noTypeChecks = options.noTypeChecks; - const generateEs6 = options.generateEs6; - const sourceMaps = options.sourceMaps; - const useBundles = options.useBundles; - - if (modules.angular2) { - var angular2Tree = new Funnel('modules/angular2', { - include: ['**/**'], - exclude: [ - // Exclude ES6 polyfill typings when tsc target=ES6 - 'typings/es6-*/**', - ], - destDir: '/angular2/' - }); - } - - if (modules.benchmarks) { - var benchmarksTree = new Funnel( - 'modules/benchmarks', - {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks/'}); - } - - if (modules.benchmarks_external) { - var benchmarksExternalTree = new Funnel( - 'modules/benchmarks_external', - {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchmarks_external/'}); - } - - if (modules.payload_tests) { - var payloadTestsTree = new Funnel( - 'modules/payload_tests', - {include: ['**/ts/**'], exclude: ['e2e_test/**'], destDir: '/payload_tests/'}); - } - - if (modules.playground) { - var playgroundTree = new Funnel( - 'modules/playground', - {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/playground/'}); - } - - if (modules.benchpress) { - var benchpressTree = new Funnel( - 'modules/benchpress', - {include: ['**/**'], exclude: ['e2e_test/**'], destDir: '/benchpress/'}); - } - - let externalTypings = - new Funnel('node_modules', {include: ['rxjs/**/*.d.ts', 'zone.js/**/*.d.ts']}); - - - var modulesTree = mergeTrees([ - angular2Tree, - benchmarksTree, - benchmarksExternalTree, - payloadTestsTree, - playgroundTree, - benchpressTree, - externalTypings, - ]); - - var es6PolyfillTypings = - new Funnel('modules', {include: ['angular2/typings/es6-*/**'], destDir: '/'}); - - var es5ModulesTree = mergeTrees([modulesTree, es6PolyfillTypings]); - - var scriptPathPatternReplacement = { - match: '@@PATH', - replacement: function(replacement: string, relativePath: string) { - var parts = relativePath.replace(/\\/g, '/').split('/'); - return parts.splice(0, parts.length - 1).join('/'); - } - }; - - var scriptFilePatternReplacement = { - match: '@@FILENAME', - replacement: function(replacement: string, relativePath: string) { - var parts = relativePath.replace(/\\/g, '/').split('/'); - return parts[parts.length - 1].replace('html', 'js'); - } - }; - - var useBundlesPatternReplacement = { - match: '@@USE_BUNDLES', - replacement: function(replacement: string, relativePath: string) { return useBundles; } - }; - - // Check that imports do not break barrel boundaries - modulesTree = checkImports(modulesTree); - - modulesTree = replace(modulesTree, { - files: ['playground*/**/*.js'], - patterns: [{match: /\$SCRIPTS\$/, replacement: jsReplace('SCRIPTS')}] - }); - - let ambientTypings = [ - 'angular2/typings/hammerjs/hammerjs.d.ts', - 'angular2/typings/node/node.d.ts', - 'node_modules/zone.js/dist/zone.js.d.ts', - 'angular2/manual_typings/globals.d.ts', - 'angular2/typings/es6-collections/es6-collections.d.ts', - 'angular2/typings/es6-promise/es6-promise.d.ts', - ]; - - // Use TypeScript to transpile the *.ts files to ES5 - var es5Tree = compileWithTypescript(es5ModulesTree, { - declaration: false, - emitDecoratorMetadata: true, - experimentalDecorators: true, - module: 'commonjs', - moduleResolution: 'classic', - noEmitOnError: !noTypeChecks, - rootDir: './', - rootFilePaths: ambientTypings, - inlineSourceMap: sourceMaps, - inlineSources: sourceMaps, - target: 'es5' - }); - - var vendorScriptsTree = flatten(new Funnel('.', { - files: [ - 'node_modules/es6-shim/es6-shim.js', - 'node_modules/zone.js/dist/zone.js', - 'node_modules/zone.js/dist/long-stack-trace-zone.js', - 'node_modules/systemjs/dist/system.src.js', - 'node_modules/base64-js/lib/b64.js', - 'node_modules/reflect-metadata/Reflect.js', - ] - })); - - var vendorScripts_benchmark = - new Funnel('tools/build/snippets', {files: ['url_params_to_form.js'], destDir: '/'}); - var vendorScripts_benchmarks_external = - new Funnel('node_modules/angular', {files: ['angular.js'], destDir: '/'}); - - // Get scripts for each benchmark or example - let servingTrees = kServedPaths.reduce(getServedFunnels, []); - function getServedFunnels(funnels: BroccoliTree[], destDir: string) { - let options = {srcDir: '/', destDir: destDir}; - funnels.push(new Funnel(vendorScriptsTree, options)); - if (destDir.indexOf('benchmarks') > -1) { - funnels.push(new Funnel(vendorScripts_benchmark, options)); - } - if (destDir.indexOf('benchmarks_external') > -1) { - funnels.push(new Funnel(vendorScripts_benchmarks_external, options)); - } - return funnels; - } - - - if (modules.benchmarks || modules.benchmarks_external || modules.playground) { - var assetsTree = new Funnel( - modulesTree, {include: ['**/*'], exclude: ['**/*.{html,ts,dart}'], destDir: '/'}); - } - - var htmlTree = new Funnel(modulesTree, { - include: ['*/src/**/*.html', '**/playground/**/*.html', '**/payload_tests/**/ts/**/*.html'], - destDir: '/' - }); - - if (modules.playground) { - htmlTree = replace(htmlTree, { - files: ['playground*/**/*.html'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS')}, scriptPathPatternReplacement, - scriptFilePatternReplacement, useBundlesPatternReplacement - ] - }); - } - - if (modules.benchmarks) { - htmlTree = replace(htmlTree, { - files: ['benchmarks/**'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks')}, - scriptPathPatternReplacement, scriptFilePatternReplacement, useBundlesPatternReplacement - ] - }); - } - - if (modules.benchmarks_external) { - htmlTree = replace(htmlTree, { - files: ['benchmarks_external/**'], - patterns: [ - {match: /\$SCRIPTS\$/, replacement: htmlReplace('SCRIPTS_benchmarks_external')}, - scriptPathPatternReplacement, scriptFilePatternReplacement, useBundlesPatternReplacement - ] - }); - } - - if (modules.playground) { - // We need to replace the regular angular bundle with the web-worker bundle - // for web-worker e2e tests. - htmlTree = replace(htmlTree, { - files: ['playground*/**/web_workers/**/*.html'], - patterns: [{match: '/bundle/angular2.dev.js', replacement: '/bundle/web_worker/ui.dev.js'}] - }); - } - - if (modules.benchmarks || modules.benchmarks_external) { - var scripts = mergeTrees(servingTrees); - } - - if (modules.benchmarks_external) { - var polymerFiles = new Funnel('.', { - files: [ - 'bower_components/polymer/polymer.html', - 'bower_components/polymer/polymer-micro.html', - 'bower_components/polymer/polymer-mini.html', - 'tools/build/snippets/url_params_to_form.js', - ] - }); - var polymer = stew.mv(flatten(polymerFiles), 'benchmarks_external/src/tree/polymer'); - - var reactFiles = new Funnel('.', {files: ['node_modules/react/dist/react.min.js']}); - var react = stew.mv(flatten(reactFiles), 'benchmarks_external/src/tree/react'); - } - - if (modules.benchmarks || modules.benchmarks_external || modules.playground) { - htmlTree = mergeTrees([htmlTree, scripts, polymer, react]); - } - - // this is needed only for creating a bundle - // typescript resolves dependencies automatically - if (modules.bundle_deps) { - var nodeModules = new Funnel( - 'node_modules', {include: ['rxjs/**/**', 'parse5/**/**', 'css/**/**'], destDir: '/'}); - } - - if (generateEs6) { - // Use TypeScript to transpile the *.ts files to ES6 - var es6Tree = compileWithTypescript(modulesTree, { - declaration: false, - emitDecoratorMetadata: true, - experimentalDecorators: true, - noEmitOnError: false, - rootDir: './', - rootFilePaths: [ - 'angular2/typings/zone.js/zone.js.d.ts', - 'angular2/typings/hammerjs/hammerjs.d.ts', - 'angular2/typings/node/node.d.ts', - ], - inlineSourceMap: sourceMaps, - inlineSources: sourceMaps, - target: 'es6' - }); - - es6Tree = stew.mv(mergeTrees([es6Tree, htmlTree, assetsTree, nodeModules]), '/es6'); - } - es5Tree = stew.mv(mergeTrees([es5Tree, htmlTree, assetsTree, nodeModules]), '/es5'); - - var mergedTree = mergeTrees([es6Tree, es5Tree]); - return destCopy(mergedTree, destinationPath); -}; diff --git a/tools/broccoli/trees/dart_tree.ts b/tools/broccoli/trees/dart_tree.ts deleted file mode 100644 index e746e1c71e..0000000000 --- a/tools/broccoli/trees/dart_tree.ts +++ /dev/null @@ -1,213 +0,0 @@ -'use strict'; - -import {MultiCopy} from './../multi_copy'; -import destCopy from '../broccoli-dest-copy'; -var Funnel = require('broccoli-funnel'); -import mergeTrees from '../broccoli-merge-trees'; -var path = require('path'); -import renderLodashTemplate from '../broccoli-lodash'; -var stew = require('broccoli-stew'); -import ts2dart from '../broccoli-ts2dart'; -import dartfmt from '../broccoli-dartfmt'; -import replace from '../broccoli-replace'; -import {AngularBuilderOptions} from '../angular_builder'; -import generateForTest from '../broccoli-generate-for-test'; - -var global_excludes = [ - 'angular2/examples/**/ts/**/*', - 'angular2/http*', - 'angular2/http/**/*', - 'angular2/src/http/**/*', - 'angular2/src/upgrade/**/*', - 'angular2/test/http/**/*', - 'angular2/test/upgrade/**/*', - 'angular2/upgrade*', - 'payload_tests/**/ts/**/*', - 'playground/src/http/**/*', - 'playground/src/jsonp/**/*', - 'playground/test/http/**/*', - 'playground/test/jsonp/**/*', -]; - - -/** - * A funnel starting at modules, including the given filters, and moving into the root. - * @param include Include glob filters. - */ -function modulesFunnel(include: string[], exclude?: string[]) { - exclude = exclude || []; - exclude = exclude.concat(global_excludes); - return new Funnel('modules', {include, destDir: '/', exclude}); -} - -/** - * Replaces $SCRIPT$ in .html files with actual \n'; - } - var scriptName = relativePath.replace(/\\/g, '/').replace(/.*\/([^/]+)\.html$/, '$1.dart'); - scriptTags += '\n' + - ''; - return scriptTags; -} - -function stripModulePrefix(relativePath: string): string { - if (!relativePath.match(/^modules\//)) { - throw new Error('Expected path to root at modules/: ' + relativePath); - } - return relativePath.replace(/^modules\//, ''); -} - -function getSourceTree(options: AngularBuilderOptions) { - var tsInputTree = modulesFunnel( - [ - 'tsconfig-ts2dart.json', - 'upgrade-ts2dart.d.ts', - 'zone-ts2dart.d.ts', - '**/*.js', - '**/*.ts', - '**/*.dart', - ], - [ - 'rollup-test/**/*', - 'angular1_router/**/*', - 'angular2/upgrade/**/*', - 'angular2/core/test/typings.d.ts', - 'angular2/manual_typings/globals.d.ts', - 'angular2/typings/es6-collections/es6-collections.d.ts', - 'angular2/typings/es6-promise/es6-promise.d.ts', - 'angular2/typings/tsd.d.ts', - 'angular2/typings.d.ts', - ]); - var transpiled = ts2dart(tsInputTree, { - generateLibraryName: true, - generateSourceMap: false, - translateBuiltins: true, - tsconfig: 'tsconfig-ts2dart.json' - }); - - // Native sources, dart only examples, etc. - var dartSrcs = modulesFunnel( - ['**/*.dart', '**/*.ng_meta.json', '**/*.aliases.json', '**/css/**', '**/*.css']); - - var compiledTree = mergeTrees([transpiled, dartSrcs]); - - // Generate test files - let generatedDartTestFiles = generateForTest( - mergeTrees([compiledTree, new Funnel('packages', {include: ['path/**', 'stack_trace/**']})]), - {files: ['*/test/**/*_codegen_typed.dart'], dartPath: options.dartSDK.VM}); - - return mergeTrees([compiledTree, generatedDartTestFiles], {overwrite: true}); -} - -function fixDartFolderLayout(sourceTree: BroccoliTree) { - // Move around files to match Dart's layout expectations. - return stew.rename(sourceTree, function(relativePath: string) { - // If a file matches the `pattern`, insert the given `insertion` as the second path part. - var replacements = [ - {pattern: /^benchmarks\/test\//, insertion: ''}, - {pattern: /^benchmarks\//, insertion: 'web'}, - {pattern: /^benchmarks_external\/test\//, insertion: ''}, - {pattern: /^benchmarks_external\//, insertion: 'web'}, - {pattern: /^playground\/test\//, insertion: ''}, - {pattern: /^playground\//, insertion: 'web/'}, - {pattern: /^[^\/]*\/test\//, insertion: ''}, - // catch all. - {pattern: /^./, insertion: 'lib'}, - ]; - - for (var i = 0; i < replacements.length; i++) { - var repl = replacements[i]; - if (relativePath.match(repl.pattern)) { - var parts = relativePath.split('/'); - parts.splice(1, 0, repl.insertion); - return path.join.apply(path, parts); - } - } - throw new Error('Failed to match any path: ' + relativePath); - }); -} - -function getHtmlSourcesTree() { - // Replace $SCRIPT$ markers in HTML files. - var htmlSrcsTree = modulesFunnel(['*/src/**/*.html']); - htmlSrcsTree = replace( - htmlSrcsTree, - {files: ['*/**'], patterns: [{match: '$SCRIPTS$', replacement: replaceScriptTagInHtml}]}); - - // Copy a url_params_to_form.js for each benchmark html file. - var urlParamsToFormTree = new MultiCopy('', { - srcPath: 'tools/build/snippets/url_params_to_form.js', - targetPatterns: ['modules/benchmarks*/src/*', 'modules/benchmarks*/src/*/*'], - }); - urlParamsToFormTree = stew.rename(urlParamsToFormTree, stripModulePrefix); - return mergeTrees([htmlSrcsTree, urlParamsToFormTree]); -} - -function getExamplesJsonTree() { - // Copy JSON files - return modulesFunnel(['playground/**/*.json']); -} - - -function getTemplatedPubspecsTree() { - // The JSON structure for templating pubspec.yaml files. - var BASE_PACKAGE_JSON = require('../../../../package.json'); - var COMMON_PACKAGE_JSON = { - version: BASE_PACKAGE_JSON.version, - homepage: BASE_PACKAGE_JSON.homepage, - bugs: BASE_PACKAGE_JSON.bugs, - license: BASE_PACKAGE_JSON.license, - contributors: BASE_PACKAGE_JSON.contributors, - dependencies: BASE_PACKAGE_JSON.dependencies, - devDependencies: {} - }; - // Generate pubspec.yaml from templates. - var pubspecs = modulesFunnel(['**/pubspec.yaml']); - // Then render the templates. - return renderLodashTemplate(pubspecs, {context: {'packageJson': COMMON_PACKAGE_JSON}}); -} - -function getDocsTree() { - // LICENSE files - var licenses = new MultiCopy('', { - srcPath: 'LICENSE', - targetPatterns: ['modules/*'], - exclude: [ - '*/@angular', - '*/angular2', - '*/angular1_router', - '*/angular2/src/http', - '*/payload_tests', - '*/upgrade', - ] // Not in dart. - }); - licenses = stew.rename(licenses, stripModulePrefix); - - // Documentation. - // Rename *.dart.md -> *.dart. - var mdTree = stew.rename( - modulesFunnel(['**/*.dart.md']), - (relativePath: string) => relativePath.replace(/\.dart\.md$/, '.md')); - // Copy all assets, ignore .js. and .dart. (handled above). - var docs = modulesFunnel( - ['**/*.md', '**/*.png', '**/*.html', '**/*.css', '**/*.scss'], - ['**/*.js.md', '**/*.dart.md', 'angular1_router/**/*']); - - var assets = modulesFunnel(['playground/**/*.json']); - - return mergeTrees([licenses, mdTree, docs, assets]); -} - -module.exports = function makeDartTree(options: AngularBuilderOptions) { - var dartSources = dartfmt(getSourceTree(options), {dartSDK: options.dartSDK, logs: options.logs}); - var sourceTree = mergeTrees([dartSources, getHtmlSourcesTree(), getExamplesJsonTree()]); - sourceTree = fixDartFolderLayout(sourceTree); - - var dartTree = mergeTrees([sourceTree, getTemplatedPubspecsTree(), getDocsTree()]); - - return destCopy(dartTree, options.outputPath); -}; diff --git a/tools/broccoli/trees/node_tree.ts b/tools/broccoli/trees/node_tree.ts deleted file mode 100644 index 18be68bbe9..0000000000 --- a/tools/broccoli/trees/node_tree.ts +++ /dev/null @@ -1,255 +0,0 @@ -'use strict'; - -import destCopy from '../broccoli-dest-copy'; -import compileWithTypescript, {INTERNAL_TYPINGS_PATH} from '../broccoli-typescript'; -var Funnel = require('broccoli-funnel'); -import mergeTrees from '../broccoli-merge-trees'; -var path = require('path'); -import renderLodashTemplate from '../broccoli-lodash'; -import replace from '../broccoli-replace'; -import generateForTest from '../broccoli-generate-for-test'; -var stew = require('broccoli-stew'); -var writeFile = require('broccoli-file-creator'); - -var projectRootDir = path.normalize(path.join(__dirname, '..', '..', '..', '..')); - -module.exports = function makeNodeTree(projects: string[], destinationPath: string) { - // list of npm packages that this build will create - var outputPackages = ['angular2', 'benchpress']; - - let srcTree = new Funnel('modules', { - include: ['angular2/**'], - exclude: [ - '**/e2e_test/**', - 'angular2/test/**', - 'angular2/examples/**', - - 'angular2/src/testing/**', - 'angular2/testing.ts', - 'angular2/testing_internal.ts', - 'angular2/src/upgrade/**', - 'angular2/upgrade.ts', - 'angular2/platform/testing/**', - 'angular2/manual_typings/**', - 'angular2/typings/**', - ] - }); - - let externalTypings = [ - 'angular2/typings/hammerjs/hammerjs.d.ts', - 'angular2/typings/node/node.d.ts', - 'angular2/manual_typings/globals.d.ts', - 'angular2/typings/es6-collections/es6-collections.d.ts', - 'angular2/typings/es6-promise/es6-promise.d.ts', - ]; - - let externalTypingsTree = new Funnel('modules', {files: externalTypings}); - - let packageTypings = - new Funnel('node_modules', {include: ['rxjs/**/*.d.ts', 'zone.js/**/*.d.ts']}); - - let compileSrcContext = mergeTrees([srcTree, externalTypingsTree, packageTypings]); - - // Compile the sources and generate the @internal .d.ts - let compiledSrcTreeWithInternals = compileTree(compileSrcContext, true, []); - - var testTree = new Funnel('modules', { - include: [ - 'angular2/manual_typings/**', - 'angular2/typings/**', - - 'angular2/test/**', - 'benchpress/**', - '**/e2e_test/**', - 'angular2/examples/**/*_spec.ts', - - 'angular2/src/testing/**', - 'angular2/testing.ts', - 'angular2/testing_internal.ts', - 'angular2/src/upgrade/**', - 'angular2/upgrade.ts', - 'angular2/platform/testing/**', - ], - exclude: [ - // the following code and tests are not compatible with CJS/node environment - 'angular2/test/animate/**', - 'angular2/test/core/zone/**', - 'angular2/test/testing/fake_async_spec.ts', - 'angular2/test/testing/testing_public_browser_spec.ts', - 'angular2/test/platform/xhr_impl_spec.ts', - 'angular2/test/platform/browser/**/*.ts', - 'angular2/test/common/forms/**', - 'angular2/manual_typings/**', - 'angular2/typings/**', - - // we call browser's bootstrap - 'angular2/test/router/route_config/route_config_spec.ts', - 'angular2/test/router/integration/bootstrap_spec.ts', - - // we check the public api by importing angular2/angular2 - 'angular2/test/symbol_inspector/**/*.ts', - 'angular2/test/public_api_spec.ts', - - 'angular2/test/web_workers/worker/renderer_integration_spec.ts', - - 'angular2/test/upgrade/**/*.ts', - 'angular1_router/**', - 'payload_tests/**', - ] - }); - - // Compile the tests against the src @internal .d.ts - let srcPrivateDeclarations = - new Funnel(compiledSrcTreeWithInternals, {srcDir: INTERNAL_TYPINGS_PATH}); - - let testAmbients = [ - 'angular2/typings/jasmine/jasmine.d.ts', - 'angular2/typings/angular-protractor/angular-protractor.d.ts', - 'angular2/typings/selenium-webdriver/selenium-webdriver.d.ts' - ]; - let testAmbientsTree = new Funnel('modules', {files: testAmbients}); - - testTree = mergeTrees( - [testTree, srcPrivateDeclarations, testAmbientsTree, externalTypingsTree, packageTypings]); - - let compiledTestTree = compileTree(testTree, false, []); - - // Merge the compiled sources and tests - let compiledSrcTree = - new Funnel(compiledSrcTreeWithInternals, {exclude: [`${INTERNAL_TYPINGS_PATH}/**`]}); - - let compiledTree = mergeTrees([compiledSrcTree, compiledTestTree]); - - // Generate test files - let generatedJsTestFiles = - generateForTest(compiledTree, {files: ['*/test/**/*_codegen_untyped.js']}); - let generatedTsTestFiles = stew.rename( - generateForTest(compiledTree, {files: ['*/test/**/*_codegen_typed.js']}), /.js$/, '.ts'); - - // Compile generated test files against the src @internal .d.ts and the test files - compiledTree = mergeTrees( - [ - compiledTree, generatedJsTestFiles, - compileTree( - new Funnel( - mergeTrees([ - packageTypings, - new Funnel( - 'modules', {include: ['angular2/manual_typings/**', 'angular2/typings/**']}), - generatedTsTestFiles, srcPrivateDeclarations, compiledTestTree - ]), - {include: ['angular2/**', 'rxjs/**', 'zone.js/**']}), - false, []) - ], - {overwrite: true}); - - // Down-level .d.ts files to be TS 1.8 compatible - // TODO(alexeagle): this can be removed once we drop support for using Angular 2 with TS 1.8 - compiledTree = replace(compiledTree, { - files: ['**/*.d.ts'], - patterns: [ - // all readonly keywords - {match: /^(\s*(static\s+|private\s+)*)readonly\s+/mg, replacement: '$1'}, - // abstract properties (but not methods or classes) - {match: /^(\s+)abstract\s+([^\(\n]*$)/mg, replacement: '$1$2'}, - ] - }); - - // Now we add the LICENSE file into all the folders that will become npm packages - outputPackages.forEach(function(destDir) { - var license = new Funnel('.', {files: ['LICENSE'], destDir: destDir}); - // merge the test tree - compiledTree = mergeTrees([compiledTree, license]); - }); - - // Get all docs and related assets and prepare them for js build - var srcDocs = extractDocs(srcTree); - var testDocs = extractDocs(testTree); - - var BASE_PACKAGE_JSON = require(path.join(projectRootDir, 'package.json')); - var srcPkgJsons = extractPkgJsons(srcTree, BASE_PACKAGE_JSON); - var testPkgJsons = extractPkgJsons(testTree, BASE_PACKAGE_JSON); - - // Copy es6 typings so quickstart doesn't require typings install - let typingsTree = mergeTrees([ - new Funnel('modules', { - include: [ - 'angular2/typings/es6-collections/es6-collections.d.ts', - 'angular2/typings/es6-promise/es6-promise.d.ts', - ] - }), - writeFile( - 'angular2/typings/browser.d.ts', '// Typings needed for compilation with --target=es5\n' + - '///\n' + - '///\n') - ]); - - var nodeTree = - mergeTrees([compiledTree, srcDocs, testDocs, srcPkgJsons, testPkgJsons, typingsTree]); - - // Transform all tests to make them runnable in node - nodeTree = replace(nodeTree, { - files: ['**/test/**/*_spec.js'], - patterns: [ - { - match: /^/, - replacement: - () => - `var parse5Adapter = require('angular2/src/platform/server/parse5_adapter');\r\n` + - `parse5Adapter.Parse5DomAdapter.makeCurrent();` - }, - {match: /$/, replacement: (_: any, relativePath: string) => '\r\n main(); \r\n'} - ] - }); - - // Prepend 'use strict' directive to all JS files. - // See https://github.com/Microsoft/TypeScript/issues/3576 - nodeTree = replace( - nodeTree, {files: ['**/*.js'], patterns: [{match: /^/, replacement: () => `'use strict';`}]}); - - return destCopy(nodeTree, destinationPath); -}; - -function compileTree( - tree: BroccoliTree, genInternalTypings: boolean, rootFilePaths: string[] = []) { - return compileWithTypescript(tree, { - // build pipeline options - 'rootFilePaths': rootFilePaths, - 'internalTypings': genInternalTypings, - // tsc options - 'emitDecoratorMetadata': true, - 'experimentalDecorators': true, - 'declaration': true, - 'stripInternal': true, - 'module': 'commonjs', - 'moduleResolution': 'classic', - 'noEmitOnError': true, - 'rootDir': '.', - 'inlineSourceMap': true, - 'inlineSources': true, - 'target': 'es5' - }); -} - -function extractDocs(tree: BroccoliTree) { - var docs = new Funnel(tree, {include: ['**/*.md', '**/*.png'], exclude: ['**/*.dart.md']}); - return stew.rename(docs, 'README.js.md', 'README.md'); -} - -function extractPkgJsons(tree: BroccoliTree, BASE_PACKAGE_JSON: any) { - // Generate shared package.json info - var COMMON_PACKAGE_JSON = { - version: BASE_PACKAGE_JSON.version, - homepage: BASE_PACKAGE_JSON.homepage, - bugs: BASE_PACKAGE_JSON.bugs, - license: BASE_PACKAGE_JSON.license, - repository: BASE_PACKAGE_JSON.repository, - contributors: BASE_PACKAGE_JSON.contributors, - dependencies: BASE_PACKAGE_JSON.dependencies, - devDependencies: BASE_PACKAGE_JSON.devDependencies, - defaultDevDependencies: {} - }; - - var packageJsons = new Funnel(tree, {include: ['**/package.json']}); - return renderLodashTemplate(packageJsons, {context: {'packageJson': COMMON_PACKAGE_JSON}}); -}