1602 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1602 lines
		
	
	
		
			55 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var gulp = require('gulp');
 | |
| var gutil = require('gulp-util');
 | |
| var taskListing = require('gulp-task-listing');
 | |
| var path = require('canonical-path');
 | |
| var del = require('del');
 | |
| var _ = require('lodash');
 | |
| var argv = require('yargs').argv;
 | |
| var env = require('gulp-env');
 | |
| var Q = require("q");
 | |
| var Minimatch = require("minimatch").Minimatch;
 | |
| var Dgeni = require('dgeni');
 | |
| var Package = require('dgeni').Package;
 | |
| var fsExtra = require('fs-extra');
 | |
| var fs = fsExtra;
 | |
| var exec = require('child_process').exec;
 | |
| var execPromise = Q.denodeify(exec);
 | |
| var execSync = require('child_process').execSync;
 | |
| // cross platform version of spawn that also works on windows.
 | |
| var xSpawn = require('cross-spawn');
 | |
| var prompt = require('prompt');
 | |
| var globby = require("globby");
 | |
| // Ugh... replacement needed to kill processes on any OS
 | |
| // - because childProcess.kill does not work properly on windows
 | |
| var treeKill = require("tree-kill");
 | |
| var blc = require("broken-link-checker");
 | |
| var less = require('gulp-less');
 | |
| var tslint = require('gulp-tslint');
 | |
| 
 | |
| // TODO:
 | |
| //  1. Think about using runSequence
 | |
| //  2. Think about using spawn instead of exec in case of long error messages.
 | |
| 
 | |
| var TOOLS_PATH = './tools';
 | |
| var ANGULAR_IO_PROJECT_PATH = path.resolve('.');
 | |
| var ANGULAR_PROJECT_PATH = '../angular';
 | |
| var PUBLIC_PATH = './public';
 | |
| var TEMP_PATH = './_temp';
 | |
| var DOCS_PATH = path.join(PUBLIC_PATH, 'docs');
 | |
| 
 | |
| var EXAMPLES_PATH = path.join(DOCS_PATH, '_examples');
 | |
| var BOILERPLATE_PATH = path.join(EXAMPLES_PATH, '_boilerplate');
 | |
| var EXAMPLES_TESTING_PATH = path.join(EXAMPLES_PATH, 'testing/ts');
 | |
| var NOT_API_DOCS_GLOB = path.join(PUBLIC_PATH, './{docs/*/latest/!(api),!(docs)}/**/*.*');
 | |
| var RESOURCES_PATH = path.join(PUBLIC_PATH, 'resources');
 | |
| var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples');
 | |
| var STYLES_SOURCE_PATH = path.join(TOOLS_PATH, 'styles-builder/less');
 | |
| 
 | |
| var docShredder = require(path.resolve(TOOLS_PATH, 'doc-shredder/doc-shredder'));
 | |
| var exampleZipper = require(path.resolve(TOOLS_PATH, '_example-zipper/exampleZipper'));
 | |
| var regularPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/regularPlunker'));
 | |
| var embeddedPlunker = require(path.resolve(TOOLS_PATH, 'plunker-builder/embeddedPlunker'));
 | |
| var fsUtils = require(path.resolve(TOOLS_PATH, 'fs-utils/fsUtils'));
 | |
| 
 | |
| const WWW = argv.page ? 'www-pages' : 'www'
 | |
| 
 | |
| const isSilent = !!argv.silent;
 | |
| if (isSilent) gutil.log = gutil.noop;
 | |
| const _dgeniLogLevel = argv.dgeniLog || (isSilent ? 'error' : 'info');
 | |
| 
 | |
| var _devguideShredOptions =  {
 | |
|   examplesDir: path.join(DOCS_PATH, '_examples'),
 | |
|   fragmentsDir: path.join(DOCS_PATH, '_fragments'),
 | |
|   zipDir: path.join(RESOURCES_PATH, 'zips'),
 | |
|   logLevel: _dgeniLogLevel
 | |
| };
 | |
| 
 | |
| var _devguideShredJadeOptions =  {
 | |
|   jadeDir: DOCS_PATH,
 | |
|   logLevel: _dgeniLogLevel
 | |
| };
 | |
| 
 | |
| var _apiShredOptions =  {
 | |
|   lang: 'ts',
 | |
|   examplesDir: path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/examples'),
 | |
|   fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
 | |
|   zipDir: path.join(RESOURCES_PATH, 'zips/api'),
 | |
|   logLevel: _dgeniLogLevel
 | |
| };
 | |
| 
 | |
| const relDartDocApiDir = path.join('doc', 'api');
 | |
| var _apiShredOptionsForDart =  {
 | |
|   lang: 'dart',
 | |
|   examplesDir: path.resolve(ANGULAR_PROJECT_PATH + '2_api_examples'),
 | |
|   fragmentsDir: path.join(DOCS_PATH, '_fragments/_api'),
 | |
|   zipDir: path.join(RESOURCES_PATH, 'zips/api'),
 | |
|   logLevel: _dgeniLogLevel
 | |
| };
 | |
| 
 | |
| var _excludePatterns = ['**/node_modules/**', '**/packages/**'];
 | |
| 
 | |
| var _excludeMatchers = _excludePatterns.map(function(excludePattern){
 | |
|   return new Minimatch(excludePattern)
 | |
| });
 | |
| 
 | |
| var _exampleBoilerplateFiles = [
 | |
|   'a2docs.css',
 | |
|   'package.json',
 | |
|   'styles.css',
 | |
|   'systemjs.config.js',
 | |
|   'tsconfig.json',
 | |
|   'tslint.json'
 | |
| ];
 | |
| 
 | |
| var _exampleDartWebBoilerPlateFiles = ['a2docs.css', 'styles.css'];
 | |
| 
 | |
| var _exampleUnitTestingBoilerplateFiles = [
 | |
|   'karma-test-shim.js',
 | |
|   'karma.conf.js'
 | |
| ];
 | |
| 
 | |
| var _exampleConfigFilename = 'example-config.json';
 | |
| 
 | |
| var _styleLessName = 'a2docs.less';
 | |
| 
 | |
| // Gulp flags:
 | |
| //
 | |
| //   --lang=[all | ts | js | dart | 'ts|js' | 'ts|js|dart' | ...]
 | |
| //
 | |
| //    This affects which language API docs and E2E tests are run. Can be 'all',
 | |
| //    or a regex pattern to match any one of 'ts', 'js', or 'dart'.
 | |
| //    Default: 'ts|js' except for the "full site build" tasks (see below),
 | |
| //    for which it is 'all'.
 | |
| 
 | |
| // langs and skipLangs partition ['ts', 'js', 'dart'].
 | |
| var lang, langs, skipLangs, buildDartApiDocs = false;
 | |
| function configLangs(langOption) {
 | |
|   const fullSiteBuildTasks = ['build-compile', 'check-deploy', 'harp-compile'];
 | |
|   const buildAllDocs = argv['_'] &&
 | |
|     fullSiteBuildTasks.some((task) => argv['_'].indexOf(task) >= 0);
 | |
|   const langDefault = buildAllDocs ? 'all' : 'ts|js';
 | |
|   if (langOption === '') {
 | |
|     lang = '';
 | |
|     langs = [];
 | |
|   } else {
 | |
|     lang = (langOption || langDefault).toLowerCase();
 | |
|     if (lang === 'all') lang = 'ts|js|dart';
 | |
|     langs = lang.match(/\w+/g); // the languages in `lang` as an array
 | |
|   }
 | |
|   gutil.log(`Building docs for: [${langs}]`);
 | |
|   if (langs.indexOf('dart') >= 0) {
 | |
|     buildDartApiDocs = true;
 | |
|     // For Dart, be proactive about checking for the repo
 | |
|     checkAngularProjectPath(ngPathFor('dart'));
 | |
|   } else {
 | |
|     argv.pub = false;
 | |
|   }
 | |
|   skipLangs = [];
 | |
|   ['ts', 'js', 'dart'].forEach(lang => {
 | |
|     if (langs.indexOf(lang) < 0) skipLangs.push(lang);
 | |
|   });
 | |
|   gutil.log(`Skipped languages: [${skipLangs}]`);
 | |
| }
 | |
| configLangs(argv.lang);
 | |
| 
 | |
| function isDartPath(path) {
 | |
|   // Testing via indexOf() for now. If we need to match only paths with folders
 | |
|   // named 'dart' vs 'dart*' then try: path.match('/dart(/|$)') != null;
 | |
|   return path.indexOf('/dart') > -1;
 | |
| }
 | |
| 
 | |
| function excludeDartPaths(paths) {
 | |
|   return paths.filter(function (p) { return !isDartPath(p); });
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Run Protractor End-to-End Specs for Doc Samples
 | |
|  * Alias for 'run-e2e-tests'
 | |
|  */
 | |
| gulp.task('e2e', runE2e);
 | |
| 
 | |
| gulp.task('run-e2e-tests', runE2e);
 | |
| 
 | |
| /**
 | |
|  * Run Protractor End-to-End Tests for Doc Samples
 | |
|  *
 | |
|  * Flags
 | |
|  *   --filter to filter/select _example app subdir names
 | |
|  *    e.g. gulp e2e --filter=foo  // all example apps with 'foo' in their folder names.
 | |
|  *
 | |
|  *    --fast by-passes the npm install and webdriver update
 | |
|  *    Use it for repeated test runs (but not the FIRST run)
 | |
|  *    e.g. gulp e2e --fast
 | |
|  *
 | |
|  *   --lang to filter by code language (see above for details)
 | |
|  *     e.g. gulp e2e --lang=ts  // only TypeScript apps
 | |
|  */
 | |
| function runE2e() {
 | |
|   var promise;
 | |
|   if (argv.fast) {
 | |
|     // fast; skip all setup
 | |
|     promise = Promise.resolve(true);
 | |
|   } else  {
 | |
|     /*
 | |
|        // Not 'fast'; do full setup
 | |
|     var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
 | |
|     promise = spawnInfo.promise.then(function() {
 | |
|       copyExampleBoilerplate();
 | |
|       spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PATH});
 | |
|       return spawnInfo.promise;
 | |
|     });
 | |
|     */
 | |
|     // Not 'fast'; do full setup
 | |
|     gutil.log('runE2e: install _examples stuff');
 | |
|     var spawnInfo = spawnExt('npm', ['install'], { cwd: EXAMPLES_PATH});
 | |
|     promise = spawnInfo.promise
 | |
|       .then(function() {
 | |
|         buildStyles(copyExampleBoilerplate, _.noop);
 | |
|         gutil.log('runE2e: update webdriver');
 | |
|         spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PATH});
 | |
|         return spawnInfo.promise;
 | |
|       });
 | |
|   };
 | |
| 
 | |
|   var outputFile = path.join(process.cwd(), 'protractor-results.txt');
 | |
| 
 | |
|   promise.then(function() {
 | |
|     return findAndRunE2eTests(argv.filter, outputFile);
 | |
|   }).then(function(status) {
 | |
|     reportStatus(status, outputFile);
 | |
|     if (status.failed.length > 0){
 | |
|       return Promise.reject('Some test suites failed');
 | |
|     }
 | |
|   }).catch(function(e) {
 | |
|     gutil.log(e);
 | |
|     process.exitCode = 1;
 | |
|   });
 | |
|   return promise;
 | |
| }
 | |
| 
 | |
| // finds all of the *e2e-spec.tests under the _examples folder along
 | |
| // with the corresponding apps that they should run under. Then run
 | |
| // each app/spec collection sequentially.
 | |
| function findAndRunE2eTests(filter, outputFile) {
 | |
|   // create an output file with header.
 | |
|   var startTime = new Date().getTime();
 | |
|   var header = `Doc Sample Protractor Results for ${lang} on ${new Date().toLocaleString()}\n`;
 | |
|   header += argv.fast ?
 | |
|     '  Fast Mode (--fast): no npm install, webdriver update, or boilerplate copy\n' :
 | |
|     '  Slow Mode: npm install, webdriver update, and boilerplate copy\n';
 | |
|   header += `  Filter: ${filter ? filter : 'All tests'}\n\n`;
 | |
|   fs.writeFileSync(outputFile, header);
 | |
| 
 | |
|   // create an array of combos where each
 | |
|   // combo consists of { examplePath: ... }
 | |
|   var examplePaths = [];
 | |
|   var e2eSpecPaths = getE2eSpecPaths(EXAMPLES_PATH);
 | |
|    e2eSpecPaths.forEach(function(specPath) {
 | |
|     // get all of the examples under each dir where a pcFilename is found
 | |
|     localExamplePaths = getExamplePaths(specPath, true);
 | |
|     // Filter by example name
 | |
|     if (filter) {
 | |
|       localExamplePaths = localExamplePaths.filter(function (fn) {
 | |
|         return fn.match(filter) != null;
 | |
|       })
 | |
|     }
 | |
|     // Filter by language, also supports variations like js-es6
 | |
|     localExamplePaths = localExamplePaths.filter(function (fn) {
 | |
|       return fn.match('/'+lang+'(?:-[^/]*)?$') != null;
 | |
|     });
 | |
|     localExamplePaths.forEach(function(examplePath) {
 | |
|       examplePaths.push(examplePath);
 | |
|     })
 | |
|   });
 | |
| 
 | |
|   // run the tests sequentially
 | |
|   var status = { passed: [], failed: [] };
 | |
|   return examplePaths.reduce(function (promise, examplePath) {
 | |
|     return promise.then(function () {
 | |
|       var runTests = isDartPath(examplePath) ? runE2eDartTests : runE2eTsTests;
 | |
|       return runTests(examplePath, outputFile).then(function(ok) {
 | |
|         var arr = ok ? status.passed : status.failed;
 | |
|         arr.push(examplePath);
 | |
|       })
 | |
|     });
 | |
|   }, Q.resolve()).then(function() {
 | |
|     var stopTime = new Date().getTime();
 | |
|     status.elapsedTime = (stopTime - startTime)/1000;
 | |
|     return status;
 | |
|   });
 | |
| }
 | |
| 
 | |
| // start the example in appDir; then run protractor with the specified
 | |
| // fileName; then shut down the example.  All protractor output is appended
 | |
| // to the outputFile.
 | |
| function runE2eTsTests(appDir, outputFile) {
 | |
|   // Grab protractor configuration or defaults to systemjs config.
 | |
|   try {
 | |
|     var exampleConfig = fs.readJsonSync(`${appDir}/${_exampleConfigFilename}`);
 | |
|   } catch (e) {
 | |
|     exampleConfig = {};
 | |
|   }
 | |
| 
 | |
|   var config = {
 | |
|     build: exampleConfig.build || 'tsc',
 | |
|     run: exampleConfig.run || 'http-server:e2e'
 | |
|   };
 | |
| 
 | |
|   var appBuildSpawnInfo = spawnExt('npm', ['run', config.build], { cwd: appDir });
 | |
|   var appRunSpawnInfo = spawnExt('npm', ['run', config.run, '--', '-s'], { cwd: appDir });
 | |
| 
 | |
|   var run = runProtractor(appBuildSpawnInfo.promise, appDir, appRunSpawnInfo, outputFile);
 | |
| 
 | |
|   if (fs.existsSync(appDir + '/aot/index.html')) {
 | |
|     run = run.then(() => runProtractorAoT(appDir, outputFile));
 | |
|   }
 | |
|   return run;
 | |
| }
 | |
| 
 | |
| function runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile) {
 | |
|   var specFilename = path.resolve(`${appDir}/../e2e-spec.ts`);
 | |
|   return prepPromise
 | |
|     .catch(function(){
 | |
|       var emsg = `Application at ${appDir} failed to transpile.\n\n`;
 | |
|       gutil.log(emsg);
 | |
|       fs.appendFileSync(outputFile, emsg);
 | |
|       return Promise.reject(emsg);
 | |
|     })
 | |
|     .then(function (data) {
 | |
|       var transpileError = false;
 | |
| 
 | |
|       // start protractor
 | |
| 
 | |
|       var spawnInfo = spawnExt('npm', [ 'run', 'protractor', '--', 'protractor.config.js',
 | |
|         `--specs=${specFilename}`, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: EXAMPLES_PATH });
 | |
| 
 | |
|       spawnInfo.proc.stderr.on('data', function (data) {
 | |
|         transpileError = transpileError || /npm ERR! Exit status 100/.test(data.toString());
 | |
|       });
 | |
|       return spawnInfo.promise.catch(function(err) {
 | |
|         if (transpileError) {
 | |
|         var emsg = `${specFilename} failed to transpile.\n\n`;
 | |
|         gutil.log(emsg);
 | |
|         fs.appendFileSync(outputFile, emsg);
 | |
|         }
 | |
|         return Promise.reject(emsg);
 | |
|       });
 | |
|     })
 | |
|     .then(
 | |
|        function() { return finish(true);},
 | |
|        function() { return finish(false);}
 | |
|     )
 | |
| 
 | |
|     function finish(ok){
 | |
|       // Ugh... proc.kill does not work properly on windows with child processes.
 | |
|       // appRun.proc.kill();
 | |
|       treeKill(appRunSpawnInfo.proc.pid);
 | |
|       return ok;
 | |
|     }
 | |
| }
 | |
| 
 | |
| function runProtractorAoT(appDir, outputFile) {
 | |
|   fs.appendFileSync(outputFile, '++ AoT version ++\n');
 | |
|   var aotBuildSpawnInfo = spawnExt('npm', ['run', 'build:aot'], { cwd: appDir });
 | |
|   var promise = aotBuildSpawnInfo.promise;
 | |
| 
 | |
|   var copyFileCmd = 'copy-dist-files.js';
 | |
|   if (fs.existsSync(appDir + '/' + copyFileCmd)) {
 | |
|     promise = promise.then(() =>
 | |
|      spawnExt('node', [copyFileCmd], { cwd: appDir }).promise );
 | |
|   }
 | |
|   var aotRunSpawnInfo = spawnExt('npm', ['run', 'http-server:e2e', 'aot', '--', '-s'], { cwd: appDir });
 | |
|   return runProtractor(promise, appDir, aotRunSpawnInfo, outputFile);
 | |
| }
 | |
| 
 | |
| // start the server in appDir/build/web; then run protractor with the specified
 | |
| // fileName; then shut down the example.  All protractor output is appended
 | |
| // to the outputFile.
 | |
| function runE2eDartTests(appDir, outputFile) {
 | |
|   // Launch http server out of ts directory because all the config files are there.
 | |
|   var httpLaunchDir = path.resolve(appDir, '../ts');
 | |
|   var deployDir = path.resolve(appDir, 'build/web');
 | |
|   gutil.log('AppDir for Dart e2e: ' + appDir);
 | |
|   gutil.log('Deploying from: ' + deployDir);
 | |
| 
 | |
|   var appRunSpawnInfo = spawnExt('npm', ['run', 'http-server:e2e', '--', deployDir, '-s'], { cwd: httpLaunchDir });
 | |
|   if (!appRunSpawnInfo.proc.pid) {
 | |
|     gutil.log('http-server failed to launch over ' + deployDir);
 | |
|     return false;
 | |
|   }
 | |
|   if (argv.pub === false) {
 | |
|     var prepPromise = Promise.resolve(true);
 | |
|     gutil.log('Skipping pub upgrade and pub build (--no-pub flag present)');
 | |
|   } else {
 | |
|     var pubUpgradeSpawnInfo = spawnExt('pub', ['upgrade'], { cwd: appDir });
 | |
|     var prepPromise = pubUpgradeSpawnInfo.promise.then(function (data) {
 | |
|       return spawnExt('pub', ['build'], { cwd: appDir }).promise;
 | |
|     });
 | |
|   }
 | |
|   return runProtractor(prepPromise, appDir, appRunSpawnInfo, outputFile);
 | |
| }
 | |
| 
 | |
| function reportStatus(status, outputFile) {
 | |
|   var log = [''];
 | |
|   log.push('Suites passed:');
 | |
|   status.passed.forEach(function(val) {
 | |
|     log.push('  ' + val);
 | |
|   });
 | |
| 
 | |
|   if (status.failed.length == 0) {
 | |
|     log.push('All tests passed');
 | |
|   } else {
 | |
|     log.push('Suites failed:');
 | |
|     status.failed.forEach(function (val) {
 | |
|       log.push('  ' + val);
 | |
|     });
 | |
|   }
 | |
|   log.push('\nElapsed time: ' +  status.elapsedTime + ' seconds');
 | |
|   var log = log.join('\n');
 | |
|   gutil.log(log);
 | |
|   fs.appendFileSync(outputFile, log);
 | |
| }
 | |
| 
 | |
| // returns both a promise and the spawned process so that it can be killed if needed.
 | |
| function spawnExt(command, args, options) {
 | |
|   var deferred = Q.defer();
 | |
|   var descr = command + " " + args.join(' ');
 | |
|   var proc;
 | |
|   gutil.log('running: ' + descr);
 | |
|   try {
 | |
|     proc = xSpawn.spawn(command, args, options);
 | |
|   } catch(e) {
 | |
|     gutil.log(e);
 | |
|     deferred.reject(e);
 | |
|     return { proc: null, promise: deferred.promise };
 | |
|   }
 | |
|   proc.stdout.on('data', function (data) {
 | |
|     gutil.log(data.toString());
 | |
|   });
 | |
|   proc.stderr.on('data', function (data) {
 | |
|     gutil.log(data.toString());
 | |
|   });
 | |
|   proc.on('close', function (returnCode) {
 | |
|     gutil.log('completed: ' + descr);
 | |
|     // Many tasks (e.g., tsc) complete but are actually errors;
 | |
|     // Confirm return code is zero.
 | |
|     returnCode === 0 ? deferred.resolve(0) : deferred.reject(returnCode);
 | |
|   });
 | |
|   proc.on('error', function (data) {
 | |
|     gutil.log('completed with error:' + descr);
 | |
|     gutil.log(data.toString());
 | |
|     deferred.reject(data);
 | |
|   });
 | |
|   return { proc: proc, promise: deferred.promise };
 | |
| }
 | |
| 
 | |
| // Public tasks
 | |
| 
 | |
| gulp.task('default', ['help']);
 | |
| 
 | |
| gulp.task('help', taskListing.withFilters(function(taskName) {
 | |
|   var isSubTask = taskName.substr(0,1) == "_";
 | |
|   return isSubTask;
 | |
| }, function(taskName) {
 | |
|   var shouldRemove = taskName === 'default';
 | |
|   return shouldRemove;
 | |
| }));
 | |
| 
 | |
| // requires admin access because it adds symlinks
 | |
| gulp.task('add-example-boilerplate', function(done) {
 | |
|   var realPath = path.join(EXAMPLES_PATH, '/node_modules');
 | |
|   var nodeModulesPaths = excludeDartPaths(getNodeModulesPaths(EXAMPLES_PATH));
 | |
| 
 | |
|   nodeModulesPaths.forEach(function(linkPath) {
 | |
|     gutil.log("symlinking " + linkPath + ' -> ' + realPath)
 | |
|     fsUtils.addSymlink(realPath, linkPath);
 | |
|   });
 | |
| 
 | |
|   return buildStyles(copyExampleBoilerplate, done);
 | |
| });
 | |
| 
 | |
| 
 | |
| // copies boilerplate files to locations
 | |
| // where an example app is found
 | |
| gulp.task('_copy-example-boilerplate', function (done) {
 | |
|   return argv.fast ? done() : buildStyles(copyExampleBoilerplate, done);
 | |
| });
 | |
| 
 | |
| //Builds Angular Docs CSS file from Bootstrap npm LESS source
 | |
| //and copies the result to the _examples folder to be included as
 | |
| //part of the example boilerplate.
 | |
| function buildStyles(cb, done){
 | |
|   gulp.src(path.join(STYLES_SOURCE_PATH, _styleLessName))
 | |
|     .pipe(less())
 | |
|     .pipe(gulp.dest(BOILERPLATE_PATH)).on('end', function(){
 | |
|       cb().then(function() { done(); });
 | |
|     });
 | |
| }
 | |
| 
 | |
| // copies boilerplate files to locations
 | |
| // where an example app is found
 | |
| // also copies certain web files (e.g., styles.css) to ~/_examples/**/dart/**/web
 | |
| function copyExampleBoilerplate() {
 | |
|   gutil.log('Copying example boilerplate files');
 | |
|   var sourceFiles = _exampleBoilerplateFiles.map(function(fn) {
 | |
|     return path.join(BOILERPLATE_PATH, fn);
 | |
|   });
 | |
|   var examplePaths = excludeDartPaths(getExamplePaths(EXAMPLES_PATH));
 | |
| 
 | |
|   var dartWebSourceFiles = _exampleDartWebBoilerPlateFiles.map(function(fn){
 | |
|     return path.join(BOILERPLATE_PATH, fn);
 | |
|   });
 | |
|   var dartExampleWebPaths = getDartExampleWebPaths(EXAMPLES_PATH);
 | |
| 
 | |
|   // Make boilerplate files read-only to avoid that they be edited by mistake.
 | |
|   var destFileMode = '444';
 | |
|   return copyFiles(sourceFiles, examplePaths, destFileMode)
 | |
|     .then(function() {
 | |
|       return copyFiles(dartWebSourceFiles, dartExampleWebPaths, destFileMode);
 | |
|     })
 | |
|     // copy the unit test boilerplate
 | |
|     .then(function() {
 | |
|       var unittestSourceFiles =
 | |
|         _exampleUnitTestingBoilerplateFiles
 | |
|           .map(function(name) { return path.join(EXAMPLES_TESTING_PATH, name); });
 | |
|       var unittestPaths = getUnitTestingPaths(EXAMPLES_PATH);
 | |
|       return copyFiles(unittestSourceFiles, unittestPaths, destFileMode);
 | |
|     })
 | |
|     .catch(function(err) {
 | |
|       gutil.log(err);
 | |
|       throw err;
 | |
|     });
 | |
| }
 | |
| 
 | |
| gulp.task('remove-example-boilerplate', function() {
 | |
|   var nodeModulesPaths = getNodeModulesPaths(EXAMPLES_PATH);
 | |
|   nodeModulesPaths.forEach(function(linkPath) {
 | |
|     fsUtils.removeSymlink(linkPath);
 | |
|   });
 | |
| 
 | |
|   deleteExampleBoilerPlate();
 | |
| });
 | |
| 
 | |
| // Npm install Angular libraries into examples/node_modules,
 | |
| // either release or current build packages
 | |
| // Examples:
 | |
| //   gulp install-example-angular --build  // use current build packages
 | |
| //   gulp install-example-angular --build=2.0.0-b43f954  // use tagged packages
 | |
| //   gulp install-example-angular          // restore release packages
 | |
| //
 | |
| // Find the tags here: https://github.com/angular/core-builds/releases
 | |
| gulp.task('install-example-angular', installExampleAngular);
 | |
| 
 | |
| function installExampleAngular() {
 | |
|   var sources;
 | |
|   var template;
 | |
|   var libs = [
 | |
|     'core', 'common', 'compiler', 'compiler-cli',
 | |
|     'platform-browser', 'platform-browser-dynamic',
 | |
|     'forms', 'http', 'router', 'upgrade'];
 | |
| 
 | |
|   var build = argv.build;
 | |
|   if (build) {
 | |
|     if (typeof build === 'string') {
 | |
|       build = (build[0]==='#' ? '' : '#') + build;
 | |
|     } else {
 | |
|       build = '';
 | |
|     }
 | |
|   } else{
 | |
|     build = 'npm';
 | |
|   }
 | |
|   // Like: "angular/core-builds" or "@angular/core"
 | |
|   sources = libs.map( lib => {
 | |
|     return build === 'npm'
 | |
|       ? `@angular/${lib}`
 | |
|       : `git+https://github.com/angular/${lib}-builds${build}`;
 | |
|   });
 | |
| 
 | |
|   if (argv.build) { sources.push('@angular/tsc-wrapped');} // tsc-wrapped needed for builds
 | |
| 
 | |
|   sources.push('@angular/router-deprecated');
 | |
| 
 | |
|   gutil.log(`Installing Angular packages from ${build === 'npm' ? 'NPM' : 'BUILD ' + build}`);
 | |
| 
 | |
|   var spawnInfo = spawnExt('rm', ['-rf', 'node_modules/@angular'], { cwd: EXAMPLES_PATH});
 | |
|   return spawnInfo.promise
 | |
|     .then(() =>  {
 | |
|       spawnInfo = spawnExt('npm', ['install', ...sources], {cwd: EXAMPLES_PATH});
 | |
|       return spawnInfo.promise
 | |
|     });
 | |
| }
 | |
| 
 | |
| // deletes boilerplate files that were added by copyExampleBoilerplate
 | |
| // from locations where an example app is found
 | |
| gulp.task('_delete-example-boilerplate', deleteExampleBoilerPlate);
 | |
| 
 | |
| function deleteExampleBoilerPlate() {
 | |
|   gutil.log('Deleting example boilerplate files');
 | |
|   var examplePaths = getExamplePaths(EXAMPLES_PATH);
 | |
|   var dartExampleWebPaths = getDartExampleWebPaths(EXAMPLES_PATH);
 | |
| 
 | |
|   return deleteFiles(_exampleBoilerplateFiles, examplePaths)
 | |
|     .then(function() {
 | |
|       return deleteFiles(_exampleDartWebBoilerPlateFiles, dartExampleWebPaths);
 | |
|     });
 | |
| }
 | |
| 
 | |
| gulp.task('serve-and-sync', ['build-docs'], function (cb) {
 | |
|   // watchAndSync({devGuide: true, apiDocs: true, apiExamples: true, localFiles: true}, cb);
 | |
|   watchAndSync({devGuide: true, devGuideJade: true, apiDocs: true, apiExamples: true, localFiles: true}, cb);
 | |
| });
 | |
| 
 | |
| gulp.task('serve-and-sync-api', ['build-docs'], function (cb) {
 | |
|   watchAndSync({apiDocs: true, apiExamples: true}, cb);
 | |
| });
 | |
| 
 | |
| gulp.task('serve-and-sync-devguide', ['build-devguide-docs', 'build-plunkers' ], function (cb) {
 | |
|   watchAndSync({devGuide: true, devGuideJade: true, localFiles: true}, cb);
 | |
| });
 | |
| 
 | |
| gulp.task('_serve-and-sync-jade', function (cb) {
 | |
|   watchAndSync({devGuideJade: true, localFiles: true}, cb);
 | |
| });
 | |
| 
 | |
| gulp.task('build-and-serve', ['build-docs'], function (cb) {
 | |
|   watchAndSync({localFiles: true}, cb);
 | |
| });
 | |
| 
 | |
| gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers']);
 | |
| // Stop zipping examples Feb 28, 2016
 | |
| //gulp.task('build-docs', ['build-devguide-docs', 'build-api-docs', 'build-plunkers', '_zip-examples']);
 | |
| 
 | |
| gulp.task('build-api-docs', ['build-js-api-docs', 'build-ts-api-docs']
 | |
|     .concat(buildDartApiDocs ? ['build-dart-api-docs', 'build-dart-cheatsheet'] : []));
 | |
| 
 | |
| gulp.task('build-devguide-docs', ['_shred-devguide-examples', '_shred-devguide-shared-jade'], function() {
 | |
|   return buildShredMaps(true);
 | |
| });
 | |
| 
 | |
| gulp.task('build-ts-api-docs', ['_shred-api-examples'], function() {
 | |
|   return buildApiDocs('ts');
 | |
| });
 | |
| 
 | |
| gulp.task('build-js-api-docs', ['_shred-api-examples'], function() {
 | |
|   return buildApiDocs('js');
 | |
| });
 | |
| 
 | |
| gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function() {
 | |
|   return buildApiDocsForDart();
 | |
| });
 | |
| 
 | |
| // Using the --build flag will use systemjs.config.web.build.js (for preview builds)
 | |
| gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() {
 | |
|   regularPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build });
 | |
|   return embeddedPlunker.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build, targetSelf: argv.targetSelf });
 | |
| });
 | |
| 
 | |
| gulp.task('build-dart-cheatsheet', [], function() {
 | |
|   return buildDartCheatsheet();
 | |
| });
 | |
| 
 | |
| gulp.task('dartdoc', ['pub upgrade'], function() {
 | |
|   const ngRepoPath = ngPathFor('dart');
 | |
|   if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, relDartDocApiDir))) {
 | |
|     gutil.log(`Skipping dartdoc: --fast flag enabled and api dir exists (${relDartDocApiDir})`);
 | |
|     return true;
 | |
|   }
 | |
|   checkAngularProjectPath(ngRepoPath);
 | |
|   const topLevelLibFilePath = path.resolve(ngRepoPath, 'lib', 'angular2.dart');
 | |
|   const tmpPath = topLevelLibFilePath + '.disabled';
 | |
|   renameIfExistsSync(topLevelLibFilePath, tmpPath);
 | |
|   gutil.log(`Hiding top-level angular2 library: ${topLevelLibFilePath}`);
 | |
|   // Remove dartdoc '--add-crossdart' flag while we are fixing links to API pages.
 | |
|   const dartdoc = spawnExt('dartdoc', ['--output', relDartDocApiDir], { cwd: ngRepoPath});
 | |
|   return dartdoc.promise.finally(() => {
 | |
|       gutil.log(`Restoring top-level angular2 library: ${topLevelLibFilePath}`);
 | |
|       renameIfExistsSync(tmpPath, topLevelLibFilePath);
 | |
|   })
 | |
| });
 | |
| 
 | |
| gulp.task('pub upgrade', [], function() {
 | |
|   const ngRepoPath = ngPathFor('dart');
 | |
|   if (argv.fast && fs.existsSync(path.resolve(ngRepoPath, 'packages'))) {
 | |
|     gutil.log('Skipping pub upgrade: --fast flag enabled and "packages" dir exists');
 | |
|     return true;
 | |
|   }
 | |
|   checkAngularProjectPath(ngRepoPath);
 | |
|   const pubUpgrade = spawnExt('pub', ['upgrade'], { cwd: ngRepoPath});
 | |
|   return pubUpgrade.promise;
 | |
| });
 | |
| 
 | |
| gulp.task('git-changed-examples', ['_shred-devguide-examples'], function(){
 | |
|   var after, sha, messageSuffix;
 | |
|   if (argv.after) {
 | |
|     try {
 | |
|       after = new Date(argv.after);
 | |
|       messageSuffix = ' after: ' + argv.after;
 | |
|     } catch (e) {
 | |
|       throw argv.after + " is not a valid date.";
 | |
|     }
 | |
|   } else if (argv.sha) {
 | |
|     sha = argv.sha;
 | |
|     messageSuffix = ' on commit: ' + (argv.sha.length ? argv.sha : '[last commit]');
 | |
|   } else {
 | |
|     gutil.log('git-changed-examples may be called with either an "--sha" argument like this:');
 | |
|     gutil.log('   gulp git-changed-examples --sha=4d2ac96fa247306ddd2d4c4e0c8dee2223502eb2');
 | |
|     gutil.log('or with an "--after" argument like this')
 | |
|     gutil.log('   gulp git-changed-examples --after="August 1, 2015"');
 | |
|     return;
 | |
|   }
 | |
|   var jadeShredMap;
 | |
|   return buildShredMaps(false).then(function(docs) {
 | |
|     jadeShredMap = docs[0];
 | |
|     if (after) {
 | |
|       return getChangedExamplesAfter(after);
 | |
|     } else if (sha) {
 | |
|       return getChangedExamples(sha);
 | |
|     } else {
 | |
|       gutil.log('git-changed-examples may be called with either an "--sha" argument like this:');
 | |
|       gutil.log('   gulp git-changed-examples --sha=4d2ac96fa247306ddd2d4c4e0c8dee2223502eb2');
 | |
|       gutil.log('or with an "--after" argument like this')
 | |
|       gutil.log('   gulp git-changed-examples --after="August 1, 2015"');
 | |
|     }
 | |
|   }).then(function(examplePaths) {
 | |
|     examplePaths = filterOutExcludedPatterns(examplePaths, _excludeMatchers);
 | |
|     gutil.log('\nExamples changed ' + messageSuffix);
 | |
|     gutil.log(examplePaths)
 | |
|     gutil.log("\nJade files affected by changed example files " + messageSuffix);
 | |
|     var jadeExampleMap = jadeShredMapToJadeExampleMap(jadeShredMap, examplePaths);
 | |
|     gutil.log(JSON.stringify(jadeExampleMap, null, "  "));
 | |
|     gutil.log("-----");
 | |
|   }).catch(function(err) {
 | |
|     gutil.log(err);
 | |
|     throw err;
 | |
|   });
 | |
| });
 | |
| 
 | |
| gulp.task('harp-compile', () => {
 | |
|   return harpCompile()
 | |
| });
 | |
| 
 | |
| gulp.task('harp-serve', () => {
 | |
|   // Harp will watch and serve workspace files.
 | |
|   const cmd = 'npm run harp -- server .';
 | |
|   gutil.log('Launching harp server (over project files)');
 | |
|   gutil.log(`  > ${cmd}`);
 | |
|   gutil.log('Note: issuing this command directly from the command line will show harp comiple warnings.');
 | |
|   return execPromise(cmd);
 | |
| });
 | |
| 
 | |
| gulp.task('serve-www', () => {
 | |
|   // Serve generated site.
 | |
|   return execPromise(`npm run live-server ${WWW}`);
 | |
| });
 | |
| 
 | |
| gulp.task('build-compile', ['build-docs'], function() {
 | |
|   return harpCompile();
 | |
| });
 | |
| 
 | |
| gulp.task('check-deploy', ['firebase-use-proj-check', 'build-docs'], () => {
 | |
|   return harpCompile().then(function() {
 | |
|     gutil.log('compile ok');
 | |
|     gutil.log('running live server ...');
 | |
|     execPromise(`npm run live-server ${WWW}`);
 | |
|     return askDeploy();
 | |
|   }).then(function(shouldDeploy) {
 | |
|     if (shouldDeploy) {
 | |
|       gutil.log('deploying...');
 | |
|       return execPromise('firebase deploy');
 | |
|     } else {
 | |
|       return ['Not deploying'];
 | |
|     }
 | |
|   }).then(function(s) {
 | |
|     gutil.log(s.join(''));
 | |
|   }).catch(function(e) {
 | |
|     gutil.log(e);
 | |
|   });
 | |
| });
 | |
| 
 | |
| gulp.task('firebase-use-proj-check', cb => {
 | |
|   try {
 | |
|     execSync('firebase use');
 | |
|   } catch (e) {
 | |
|     // Rerun command so user gets project + alias info
 | |
|     execSync('firebase use', {stdio:[0,1,2]});
 | |
|     throw `\nAborting: no firebase project selected. Run:\n\n  firebase use <project-or-alias-name>\n\n`;
 | |
|   }
 | |
|   return cb();
 | |
| });
 | |
| 
 | |
| gulp.task('test-api-builder', function (cb) {
 | |
|   execCommands(['npm run test-api-builder'], {}, cb);
 | |
| });
 | |
| 
 | |
| // Usage:
 | |
| //   angular.io:  gulp link-checker
 | |
| //   local site:  gulp link-checker --url=http://localhost:3000
 | |
| gulp.task('link-checker', function(done) {
 | |
|   var method = 'get'; // the default 'head' fails for some sites
 | |
|   var exclude = [
 | |
|     // Dart API docs aren't working yet; ignore them
 | |
|     '*/dart/latest/api/*',
 | |
|     // Somehow the link checker sees ng1 {{...}} in the resource page; ignore it
 | |
|     'resources/%7B%7Bresource.url%7D%7D',
 | |
|     // API docs have links directly into GitHub repo sources; these can
 | |
|     // quickly become invalid, so ignore them for now:
 | |
|     '*/angular/tree/*',
 | |
|     // harp.json "bios" for "Ryan Schmukler", URL isn't valid:
 | |
|     'http://slingingcode.com'
 | |
|   ];
 | |
|   var blcOptions = { requestMethod: method, excludedKeywords: exclude};
 | |
|   return linkChecker({ blcOptions: blcOptions });
 | |
| });
 | |
| 
 | |
| 
 | |
| // Internal tasks
 | |
| gulp.task('set-prod-env', function () {
 | |
|   // Supposedly running in production makes harp faster
 | |
|   // and less likely to drown in node_modules.
 | |
|   env({
 | |
|     vars: { NODE_ENV: "production" }
 | |
|   });
 | |
|   gutil.log("NODE_ENV: " + process.env.NODE_ENV);
 | |
| });
 | |
| 
 | |
| // used to test just harpCompile without a build step
 | |
| gulp.task('_harp-compile', function() {
 | |
|   return harpCompile().then(function() {
 | |
|     gutil.log('compile ok');
 | |
|   }).catch(function(e) {
 | |
|     gutil.log('compile failed');
 | |
|   });
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-devguide-examples', ['_shred-clean-devguide', '_copy-example-boilerplate'], function() {
 | |
|   // Split big shredding task into partials 2016-06-14
 | |
|   var examplePaths = globby.sync(EXAMPLES_PATH+'/*/', {ignore: ['**/node_modules', '**/_boilerplate']});
 | |
|   var promise = Promise.resolve(true);
 | |
|   examplePaths.forEach(function (examplePath) {
 | |
|     promise = promise.then(() => docShredder.shredSingleExampleDir(_devguideShredOptions, examplePath));
 | |
|   });
 | |
|   return promise;
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-devguide-shared-jade', ['_shred-clean-devguide-shared-jade', '_copy-example-boilerplate'],  function() {
 | |
|   return docShredder.shred(_devguideShredJadeOptions);
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-clean-devguide-shared-jade', function(cb) {
 | |
|   // oldCleanPath is only needed to cleanup any jade fragments still sitting in the old location
 | |
|   var oldCleanPath = path.join(DOCS_PATH, '**/_.*.jade');
 | |
|   // jade fragments now all go into _fragments subdirs under their source.
 | |
|   var newCleanPath = path.join(DOCS_PATH, '**/_fragments/*.jade');
 | |
|   // Much slower 8-9x then using globby first ... ???
 | |
|   // return del([ newCleanPath, oldCleanPath]);
 | |
|   var files = globby.sync( [newCleanPath, oldCleanPath]);
 | |
|   return del(files);
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-clean-devguide', function(cb) {
 | |
|   var cleanPath = path.join(_devguideShredOptions.fragmentsDir, '**/*.*')
 | |
|   return del([ cleanPath, '!**/*.ovr.*', '!**/_api/**']);
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-api-examples', ['_shred-clean-api'], function() {
 | |
|   const promises = [];
 | |
|   gutil.log('Shredding API examples for languages: ' + langs.join(', '));
 | |
|   langs.forEach(lang => {
 | |
|     if (lang === 'js') return; // JS is handled via TS.
 | |
|     checkAngularProjectPath(ngPathFor(lang));
 | |
|     const options = lang == 'dart' ? _apiShredOptionsForDart : _apiShredOptions;
 | |
|     promises.push(docShredder.shred(options));
 | |
|   });
 | |
|   return Q.all(promises);
 | |
| });
 | |
| 
 | |
| gulp.task('_shred-clean-api', function(cb) {
 | |
|   var cleanPath = path.join(_apiShredOptions.fragmentsDir, '**/*.*')
 | |
|   return del([ cleanPath, '!**/*.ovr.*' ]);
 | |
| });
 | |
| 
 | |
| gulp.task('_zip-examples', function() {
 | |
|   exampleZipper.zipExamples(_devguideShredOptions.examplesDir, _devguideShredOptions.zipDir);
 | |
|   exampleZipper.zipExamples(_apiShredOptions.examplesDir, _apiShredOptions.zipDir);
 | |
| });
 | |
| 
 | |
| 
 | |
| // Linting
 | |
| 
 | |
| gulp.task('lint', function() {
 | |
|   return gulp.src([
 | |
|       './public/docs/_examples/**/*.ts',
 | |
|       '!./public/docs/_examples/**/ts-snippets/*.ts',
 | |
|       '!./public/docs/_examples/style-guide/ts/**/*.avoid.ts',
 | |
|       '!./public/docs/_examples/**/node_modules/**/*',
 | |
|       '!./public/docs/_examples/**/build/**/*',
 | |
|       // temporary until codelyzer is fixed mgechev/codelyzer#60
 | |
|       '!./public/docs/_examples/animations/ts/app/hero.service.ts'
 | |
|     ])
 | |
|     .pipe(tslint({
 | |
|       rulesDirectory: ['node_modules/codelyzer'],
 | |
|       configuration: require('./tslint.json')
 | |
|     }))
 | |
|     .pipe(tslint.report('prose', {
 | |
|       summarizeFailureOutput: true
 | |
|     }));
 | |
| });
 | |
| 
 | |
| 
 | |
| // Helper functions
 | |
| 
 | |
| function harpCompile() {
 | |
|   // Supposedly running in production makes harp faster
 | |
|   // and less likely to drown in node_modules.
 | |
|   env({ vars: { NODE_ENV: "production" } });
 | |
|   gutil.log("NODE_ENV: " + process.env.NODE_ENV);
 | |
| 
 | |
|   if(argv.page) harpJsonSetJade2NgTo(true);
 | |
| 
 | |
|   if(skipLangs && fs.existsSync(WWW) && backupApiHtmlFilesExist(WWW)) {
 | |
|     gutil.log(`Harp site recompile: skipping recompilation of API docs for [${skipLangs}]`);
 | |
|     gutil.log(`API docs will be copied from existing ${WWW} folder.`)
 | |
|     del.sync(`${WWW}-backup`); // remove existing backup if it exists
 | |
|     renameIfExistsSync(WWW, `${WWW}-backup`);
 | |
|   } else {
 | |
|     gutil.log(`Harp full site compile, including API docs for all languages.`);
 | |
|     if (skipLangs)
 | |
|       gutil.log(`Ignoring API docs skip set (${skipLangs}) because full ` +
 | |
|       `site has not been built yet or some API HTML files are missing.`);
 | |
|   }
 | |
| 
 | |
|   var deferred = Q.defer();
 | |
|   gutil.log('running harp compile...');
 | |
|   showHideExampleNodeModules('hide');
 | |
|   showHideApiDir('hide');
 | |
|   var spawnInfo = spawnExt('npm',['run','harp', '--', 'compile', '.', WWW ]);
 | |
|   spawnInfo.promise.then(function(x) {
 | |
|     gutil.log("NODE_ENV: " + process.env.NODE_ENV);
 | |
|     showHideExampleNodeModules('show');
 | |
|     showHideApiDir('show');
 | |
|     harpJsonSetJade2NgTo(false);
 | |
|     if (x !== 0) {
 | |
|       deferred.reject(x)
 | |
|     } else {
 | |
|       restoreApiHtml();
 | |
|       deferred.resolve(x);
 | |
|     }
 | |
|   }).catch(function(e) {
 | |
|     gutil.log("NODE_ENV: " + process.env.NODE_ENV);
 | |
|     showHideExampleNodeModules('show');
 | |
|     showHideApiDir('show');
 | |
|     harpJsonSetJade2NgTo(false);
 | |
|     deferred.reject(e);
 | |
|   });
 | |
|   return deferred.promise;
 | |
| }
 | |
| 
 | |
| function linkChecker(options) {
 | |
|   var deferred = Q.defer();
 | |
|   var options = options || {};
 | |
| 
 | |
|   var blcOptions = options.blcOptions || {};
 | |
|   var customData = options.customData || {};
 | |
| 
 | |
|   // don't bother reporting bad links matching this RegExp
 | |
|   var excludeBad = argv.excludeBad ? new RegExp(argv.excludeBad) : (options.excludeBad || '');
 | |
| 
 | |
|   var previousPage;
 | |
|   var siteUrl = argv.url || options.url || 'https://angular.io/';
 | |
| 
 | |
|   // See https://github.com/stevenvachon/broken-link-checker#blcsitecheckeroptions-handlers
 | |
|   var handlers = {
 | |
|     robots: function(robots, customData){},
 | |
|     html: function(tree, robots, response, pageUrl, customData){
 | |
|       // gutil.log('Scanning ' + pageUrl);
 | |
|     },
 | |
|     junk: function(result, customData){},
 | |
| 
 | |
|     // Analyze links
 | |
|     link: function(result, customData){
 | |
|       if (!result.broken) { return; }
 | |
|       if (excludeBad && excludeBad.test(result.url.resolved)) { return; }
 | |
| 
 | |
|       var currentPage = result.base.resolved
 | |
|       if (previousPage !== currentPage) {
 | |
|         previousPage = currentPage;
 | |
|         fs.appendFileSync(outputFile, '\n' + currentPage);
 | |
|         gutil.log('broken: ' + currentPage);
 | |
|       }
 | |
|       var msg = '\n  [' + result.html.location.line + ', ' + result.brokenReason + '] ' + result.url.resolved;
 | |
|       fs.appendFileSync(outputFile, msg);
 | |
|       // gutil.log(msg);
 | |
|       // gutil.log(result);
 | |
|     },
 | |
| 
 | |
|     page: function(error, pageUrl, customData){},
 | |
|     site: function(error, siteUrl, customData){},
 | |
| 
 | |
|     end: function(){
 | |
|       var stopTime = new Date().getTime();
 | |
|       var elapsed = 'Elapsed link-checking time: ' + ((stopTime - startTime)/1000) + ' seconds';
 | |
|       gutil.log(elapsed);
 | |
|       fs.appendFileSync(outputFile, '\n'+elapsed);
 | |
|       gutil.log('Output in file: ' + outputFile);
 | |
|       deferred.resolve(true);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   // create an output file with header.
 | |
|   var outputFile = path.join(process.cwd(), 'link-checker-results.txt');
 | |
|   var header = 'Link checker results for: ' + siteUrl +
 | |
|                '\nStarted: ' + (new Date()).toLocaleString() +
 | |
|                '\nExcluded links (blc file globs): ' + blcOptions.excludedKeywords +
 | |
|                '\nExcluded links (custom --exclude-bad regex): ' + excludeBad.toString() + '\n\n';
 | |
|   gutil.log(header);
 | |
|   fs.writeFileSync(outputFile, header);
 | |
| 
 | |
|   var siteChecker = new blc.SiteChecker(blcOptions, handlers);
 | |
|   var startTime = new Date().getTime();
 | |
| 
 | |
|   try {
 | |
|     gutil.log('link checker started');
 | |
|     siteChecker.enqueue(siteUrl, customData);
 | |
|   } catch (err) {
 | |
|     gutil.log('link checker died');
 | |
|     console.error('link checker died', err);
 | |
|     deferred.reject(err);
 | |
|   }
 | |
|   return deferred.promise;
 | |
| }
 | |
| 
 | |
| // harp has issues with node_modules under the public dir
 | |
| // but we need them there for example testing and development
 | |
| // this method allows the node modules folder under '_examples'
 | |
| // to be temporarily moved out from under 'public' while harp
 | |
| // compilation is occurring.
 | |
| function showHideExampleNodeModules(showOrHide) {
 | |
|   var nmPath = path.join(EXAMPLES_PATH, "/node_modules");
 | |
|   var nmHiddenPath = path.join(TEMP_PATH, "/node_modules");
 | |
|   if (showOrHide == 'hide' && fs.existsSync(nmPath)) {
 | |
|     if (!fs.existsSync(TEMP_PATH)) {
 | |
|       fs.mkdirSync(TEMP_PATH);
 | |
|     }
 | |
|     fs.renameSync(nmPath, nmHiddenPath);
 | |
|   } else if (showOrHide == 'show' && fs.existsSync(nmHiddenPath)) {
 | |
|     fs.renameSync(nmHiddenPath, nmPath);
 | |
|     fs.rmdirSync(TEMP_PATH);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Show/hide the API docs harp source folder for every lang in skipLangs.
 | |
| function showHideApiDir(showOrHide) {
 | |
|   skipLangs.forEach(lang => {
 | |
|     _showHideApiDir(lang, showOrHide);
 | |
|   });
 | |
| }
 | |
| 
 | |
| // Rename the API docs harp source folder for lang to/from 'api' to '_api-tmp-foo'.
 | |
| function _showHideApiDir(lang, showOrHide) {
 | |
|   const vers = 'latest';
 | |
|   const basePath = path.join(DOCS_PATH, lang, vers);
 | |
|   const apiDirPath = path.join(basePath, 'api');
 | |
|   const disabledApiDirPath = path.join(basePath, '_api-tmp-hide-from-jade');
 | |
|   const args = showOrHide == 'hide'
 | |
|     ? [apiDirPath, disabledApiDirPath]
 | |
|     : [disabledApiDirPath, apiDirPath];
 | |
|   renameIfExistsSync(...args);
 | |
| }
 | |
| 
 | |
| // For each lang in skipLangs, copy the API dir from ${WWW}-backup to WWW.
 | |
| function restoreApiHtml() {
 | |
|   const vers = 'latest';
 | |
|   skipLangs.forEach(lang => {
 | |
|     const relApiDir = path.join('docs', lang, vers, 'api');
 | |
|     const apiSubdir = path.join(WWW, relApiDir);
 | |
|     const backupApiSubdir = path.join(`${WWW}-backup`, relApiDir);
 | |
|     if (fs.existsSync(backupApiSubdir) || argv.forceSkipApi !== true) {
 | |
|       gutil.log(`cp ${backupApiSubdir} ${apiSubdir}`)
 | |
|       fs.copySync(backupApiSubdir, apiSubdir);
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| // For each lang in skipLangs, ensure API dir exists in folderName
 | |
| function backupApiHtmlFilesExist(folderName) {
 | |
|   const vers = 'latest';
 | |
|   var result = 1;
 | |
|   skipLangs.forEach(lang => {
 | |
|     const relApiDir = path.join('docs', lang, vers, 'api');
 | |
|     const backupApiSubdir = path.join(folderName, relApiDir);
 | |
|     if (!fs.existsSync(backupApiSubdir)) {
 | |
|       gutil.log(`WARNING: API docs HTML folder doesn't exist: ${backupApiSubdir}`);
 | |
|       result = 0;
 | |
|     }
 | |
|   });
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| function harpJsonSetJade2NgTo(v) {
 | |
|   const harpJsonPath = path.join(ANGULAR_IO_PROJECT_PATH, 'harp.json');
 | |
|   execSync(`perl -pi -e 's/("jade2ng": *)\\w+/$1${v}/' ${harpJsonPath}`);
 | |
|   const harpJson = require(harpJsonPath);
 | |
|   gutil.log(`jade2ng: ${harpJson.globals.jade2ng}`);
 | |
| }
 | |
| 
 | |
| // Copies fileNames into destPaths, setting the mode of the
 | |
| // files at the destination as optional_destFileMode if given.
 | |
| // returns a promise
 | |
| function copyFiles(fileNames, destPaths, optional_destFileMode) {
 | |
|   var copy = Q.denodeify(fsExtra.copy);
 | |
|   var chmod = Q.denodeify(fsExtra.chmod);
 | |
|   var copyPromises = [];
 | |
|   destPaths.forEach(function(destPath) {
 | |
|     fileNames.forEach(function(fileName) {
 | |
|       var baseName = path.basename(fileName);
 | |
|       var destName = path.join(destPath, baseName);
 | |
|       var p = copy(fileName, destName, { clobber: true});
 | |
|       if(optional_destFileMode !== undefined) {
 | |
|         p = p.then(function () {
 | |
|           return chmod(destName, optional_destFileMode);
 | |
|         });
 | |
|       }
 | |
|       copyPromises.push(p);
 | |
|     });
 | |
|   });
 | |
|   return Q.all(copyPromises);
 | |
| }
 | |
| 
 | |
| function deleteFiles(baseFileNames, destPaths) {
 | |
|   var remove = Q.denodeify(fsExtra.remove);
 | |
|   var delPromises = [];
 | |
|   destPaths.forEach(function(destPath) {
 | |
|     baseFileNames.forEach(function(baseFileName) {
 | |
|       var destFileName = path.join(destPath, baseFileName);
 | |
|       var p = remove(destFileName);
 | |
|       delPromises.push(p);
 | |
|     });
 | |
|   });
 | |
|   return Q.all(delPromises);
 | |
| }
 | |
| 
 | |
| // TODO: filter out all paths that are subdirs of another
 | |
| // path in the result.
 | |
| function getE2eSpecPaths(basePath) {
 | |
|   var paths = getPaths(basePath, '*e2e-spec.+(js|ts)', true);
 | |
|   return _.uniq(paths);
 | |
| }
 | |
| 
 | |
| function getNodeModulesPaths(basePath) {
 | |
|   var paths = getExamplePaths(basePath).map(function(examplePath) {
 | |
|     return path.join(examplePath, "/node_modules");
 | |
|   });
 | |
|   return paths;
 | |
| }
 | |
| 
 | |
| function getExamplePaths(basePath, includeBase) {
 | |
|   // includeBase defaults to false
 | |
|   return getPaths(basePath, _exampleConfigFilename, includeBase);
 | |
| }
 | |
| 
 | |
| function getDartExampleWebPaths(basePath) {
 | |
|   var paths = globby.sync([path.join(basePath,"**/dart/**/web")])
 | |
|   return paths;
 | |
| }
 | |
| 
 | |
| function getUnitTestingPaths(basePath) {
 | |
|   var examples = getPaths(basePath, _exampleConfigFilename, true);
 | |
|   return examples.filter((example) => {
 | |
|     var exampleConfig = fs.readJsonSync(`${example}/${_exampleConfigFilename}`, {throws: false});
 | |
|     return exampleConfig && !!exampleConfig.unittesting;
 | |
|   });
 | |
| }
 | |
| 
 | |
| function getPaths(basePath, filename, includeBase) {
 | |
|   var filenames = getFilenames(basePath, filename, includeBase);
 | |
|   var paths = filenames.map(function(fileName) {
 | |
|     return path.dirname(fileName);
 | |
|   });
 | |
|   return paths;
 | |
| }
 | |
| 
 | |
| function getFilenames(basePath, filename, includeBase) {
 | |
|   // includeBase defaults to false
 | |
|   var includePatterns = [path.join(basePath, "**/" + filename)];
 | |
|   if (!includeBase) {
 | |
|     // ignore (skip) the top level version.
 | |
|     includePatterns.push("!" + path.join(basePath, "/" + filename));
 | |
|   }
 | |
|   // ignore (skip) the files in BOILERPLATE_PATH.
 | |
|   includePatterns.push("!" + path.join(BOILERPLATE_PATH, "/" + filename));
 | |
|   var nmPattern = path.join(basePath, "**/node_modules/**");
 | |
|   var filenames = globby.sync(includePatterns, {ignore: [nmPattern]});
 | |
|   return filenames;
 | |
| }
 | |
| 
 | |
| function watchAndSync(options, cb) {
 | |
|   // Supposedly running in production makes harp faster
 | |
|   // and less likely to drown in node_modules.
 | |
|   env({
 | |
|     vars: { NODE_ENV: "production" }
 | |
|   });
 | |
| 
 | |
|   execCommands(['npm run harp -- server .'], {}, cb);
 | |
| 
 | |
|   var browserSync = require('browser-sync').create();
 | |
|   browserSync.init(
 | |
|     {
 | |
|       proxy: 'localhost:9000',
 | |
|       scrollRestoreTechnique: 'cookie'
 | |
|     });
 | |
| 
 | |
|   // When using the --focus=name flag, only **/name/**/*.* example files and
 | |
|   // **/name.jade files are watched. This is useful for performance reasons.
 | |
|   // Example: gulp serve-and-sync --focus=architecture
 | |
|   var focus = argv.focus;
 | |
| 
 | |
|   if (options.devGuide) {
 | |
|     devGuideExamplesWatch(_devguideShredOptions, browserSync.reload, focus);
 | |
|   }
 | |
|   if (options.devGuideJade) {
 | |
|     devGuideSharedJadeWatch( { jadeDir: DOCS_PATH}, browserSync.reload, focus);
 | |
|   }
 | |
|   if (options.apiDocs) {
 | |
|     apiSourceWatch(browserSync.reload);
 | |
|   }
 | |
|   if (options.apiExamples) {
 | |
|     apiExamplesWatch(browserSync.reload);
 | |
|   }
 | |
|   if (options.localFiles) {
 | |
|     gulp.watch(NOT_API_DOCS_GLOB, browserSync.reload);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // returns a promise;
 | |
| function askDeploy() {
 | |
|   // Show user what the currently active firebase project is:
 | |
|   execSync('firebase use', {stdio:[0,1,2]});
 | |
|   prompt.start();
 | |
|   var schema = {
 | |
|     name: 'shouldDeploy',
 | |
|     description: `Deploy ${WWW} to firebase? (y/n)`,
 | |
|     type: 'string',
 | |
|     pattern: /Y|N|y|n/,
 | |
|     message: "Respond with either a 'y' or 'n'",
 | |
|     required: true
 | |
|   }
 | |
|   var getPromise = Q.denodeify(prompt.get);
 | |
|   return getPromise([schema]).then(function(result) {
 | |
|     return result.shouldDeploy.toLowerCase() === 'y';
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| function filterOutExcludedPatterns(fileNames, excludeMatchers) {
 | |
|   return fileNames.filter(function(fileName) {
 | |
|     return !excludeMatchers.some(function(excludeMatcher) {
 | |
|       return excludeMatcher.match(fileName);
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| function apiSourceWatch(postBuildAction) {
 | |
|   var srcPattern = [path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/**/*.*')];
 | |
|   gulp.watch(srcPattern, {readDelay: 500}, function (event, done) {
 | |
|     gutil.log('API source changed');
 | |
|     gutil.log('Event type: ' + event.event); // added, changed, or deleted
 | |
|     gutil.log('Event path: ' + event.path); // The path of the modified file
 | |
| 
 | |
|     return Q.all([buildApiDocs('ts'), buildApiDocs('js')]).then(postBuildAction);
 | |
|   });
 | |
| }
 | |
| 
 | |
| function apiExamplesWatch(postShredAction) {
 | |
|   var examplesPath = path.join(ANGULAR_PROJECT_PATH, 'modules/@angular/examples/**');
 | |
|   var includePattern = path.join(examplesPath, '**/*.*');
 | |
|   var excludePattern = '!' + path.join(examplesPath, '**/node_modules/**/*.*');
 | |
|   var cleanPath = [path.join(_apiShredOptions.fragmentsDir, '**/*.*'), '!**/*.ovr.*'];
 | |
| 
 | |
|   gulp.watch([includePattern, excludePattern], {readDelay: 500}, function (event, done) {
 | |
|     gutil.log('API example changed');
 | |
|     gutil.log('Event type: ' + event.type); // added, changed, or deleted
 | |
|     gutil.log('Event path: ' + event.path); // The path of the modified file
 | |
| 
 | |
|     return del(cleanPath).then(function() {
 | |
|       return docShredder.shred(_apiShredOptions);
 | |
|     }).then(postShredAction);
 | |
|   });
 | |
| }
 | |
| 
 | |
| function devGuideExamplesWatch(shredOptions, postShredAction, focus) {
 | |
|   var watchPattern = focus ? '{' + focus + ',cb-' + focus+ '}/**/*.*' : '**/*.*';
 | |
|   var includePattern = path.join(shredOptions.examplesDir, watchPattern);
 | |
|   // removed this version because gulp.watch has the same glob issue that dgeni has.
 | |
|   // var excludePattern = '!' + path.join(shredOptions.examplesDir, '**/node_modules/**/*.*');
 | |
|   // gulp.watch([includePattern, excludePattern], {readDelay: 500}, function (event, done) {
 | |
|   var ignoreThese = [ '**/node_modules/**', '**/_fragments/**', '**/dist/**',
 | |
|                       '**/dart/.pub/**', '**/dart/build/**', '**/dart/packages/**'];
 | |
|   ignoreThese = ignoreThese.concat(_exampleBoilerplateFiles.map((file) => `public/docs/_examples/*/*/${file}`));
 | |
|   var files = globby.sync( [includePattern], { ignore: ignoreThese });
 | |
|   gulp.watch([files], {readDelay: 500}, function (event, done) {
 | |
|     gutil.log('Dev Guide example changed')
 | |
|     gutil.log('Event type: ' + event.type); // added, changed, or deleted
 | |
|     gutil.log('Event path: ' + event.path); // The path of the modified file
 | |
|     return docShredder.shredSingleDir(shredOptions, event.path).then(postShredAction);
 | |
|   });
 | |
| }
 | |
| 
 | |
| function devGuideSharedJadeWatch(shredOptions, postShredAction, focus) {
 | |
|   var watchPattern = focus ? '**/' + focus + '.jade' : '**/*.jade';
 | |
|   var includePattern = path.join(DOCS_PATH, watchPattern);
 | |
|   // removed this version because gulp.watch has the same glob issue that dgeni has.
 | |
|   // var excludePattern = '!' + path.join(shredOptions.jadeDir, '**/node_modules/**/*.*');
 | |
|   // gulp.watch([includePattern, excludePattern], {readDelay: 500}, function (event, done) {
 | |
|   var ignoreThese = [ '**/node_modules/**', '**/_examples/**', '**/_fragments/**', '**/latest/api/**' ];
 | |
|   var files = globby.sync( [includePattern], { ignore: ignoreThese});
 | |
|   gulp.watch([files], {readDelay: 500}, function (event, done) {
 | |
|     gutil.log('Dev Guide jade file changed')
 | |
|     gutil.log('Event type: ' + event.type); // added, changed, or deleted
 | |
|     gutil.log('Event path: ' + event.path); // The path of the modified file
 | |
|     return docShredder.shredSingleJadeDir(shredOptions, event.path).then(postShredAction);
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| // Generate the API docs for the specified language, if not specified then it defaults to ts
 | |
| function buildApiDocs(targetLanguage) {
 | |
|   var ALLOWED_LANGUAGES = ['ts', 'js', 'dart'];
 | |
|   var GENERATE_API_LANGUAGES = ['ts', 'js'];
 | |
|   checkAngularProjectPath();
 | |
|   try {
 | |
|     // Build a specialized package to generate different versions of the API docs
 | |
|     var package = new Package('apiDocs', [require(path.resolve(TOOLS_PATH, 'api-builder/angular.io-package'))]);
 | |
|     package.config(function(log, targetEnvironments, writeFilesProcessor, readTypeScriptModules, linkDocsInlineTagDef) {
 | |
|       log.level = _dgeniLogLevel;
 | |
|       ALLOWED_LANGUAGES.forEach(function(target) { targetEnvironments.addAllowed(target); });
 | |
|       if (targetLanguage) {
 | |
|         targetEnvironments.activate(targetLanguage);
 | |
| 
 | |
|         if (GENERATE_API_LANGUAGES.indexOf(targetLanguage) === -1) {
 | |
|           // Don't read TypeScript modules if we are not generating API docs - Dart I am looking at you!
 | |
|           readTypeScriptModules.$enabled = false;
 | |
|         }
 | |
|         linkDocsInlineTagDef.lang = targetLanguage;
 | |
|         linkDocsInlineTagDef.vers = 'latest';
 | |
|         writeFilesProcessor.outputFolder  = path.join(targetLanguage, linkDocsInlineTagDef.vers, 'api');
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     var dgeni = new Dgeni([package]);
 | |
|     return dgeni.generate();
 | |
|   } catch(err) {
 | |
|     console.error(err);
 | |
|     console.error(err.stack);
 | |
|     throw err;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function buildDartCheatsheet() {
 | |
|   'use strict';
 | |
|   const ALLOWED_LANGUAGES = ['ts', 'js', 'dart'];
 | |
|   const lang = 'dart';
 | |
|   const vers = 'latest';
 | |
|   checkAngularProjectPath(ngPathFor(lang));
 | |
|   try {
 | |
|     const pkg = new Package('dartApiDocs', [require(path.resolve(TOOLS_PATH, 'dart-api-builder'))]);
 | |
|     pkg.config(function(log, targetEnvironments, writeFilesProcessor) {
 | |
|       log.level = _dgeniLogLevel;
 | |
|       ALLOWED_LANGUAGES.forEach(function(target) { targetEnvironments.addAllowed(target); });
 | |
|       targetEnvironments.activate(lang);
 | |
|       const outputPath = path.join(lang, vers, 'can-be-any-name-read-comment-below');
 | |
|       // Note: cheatsheet data gets written to: outputPath + '/../guide';
 | |
|       writeFilesProcessor.outputFolder  = outputPath;
 | |
|     });
 | |
|     var dgeni = new Dgeni([pkg]);
 | |
|     return dgeni.generate();
 | |
|   } catch(err) {
 | |
|     console.error(err);
 | |
|     console.error(err.stack);
 | |
|     throw err;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function buildApiDocsForDart() {
 | |
|   const vers = 'latest';
 | |
|   const dab = require('./tools/dart-api-builder/dab')(ANGULAR_IO_PROJECT_PATH);
 | |
|   const log = dab.log;
 | |
| 
 | |
|   log.level = _dgeniLogLevel;
 | |
|   const dabInfo = dab.dartPkgConfigInfo;
 | |
|   dabInfo.ngIoDartApiDocPath = path.join(DOCS_PATH, 'dart', vers, 'api');
 | |
|   dabInfo.ngDartDocPath = path.join(ngPathFor('dart'), relDartDocApiDir);
 | |
|   // Exclude API entries for developer/internal libraries. Also exclude entries for
 | |
|   // the top-level catch all "angular2" library (otherwise every entry appears twice).
 | |
|   dabInfo.excludeLibRegExp = new RegExp(/^(?!angular2)|testing|_|codegen|^angular2$/);
 | |
| 
 | |
|   try {
 | |
|     checkAngularProjectPath(ngPathFor('dart'));
 | |
|     var destPath = dabInfo.ngIoDartApiDocPath;
 | |
|     var sourceDirs = fs.readdirSync(dabInfo.ngDartDocPath)
 | |
|       .filter(name => !name.match(/^index|^(?!angular2)|testing|codegen/))
 | |
|       .map(name => path.join(dabInfo.ngDartDocPath, name));
 | |
|     log.info(`Building Dart API pages for ${sourceDirs.length} libraries`);
 | |
| 
 | |
|     return copyFiles(sourceDirs, [destPath]).then(() => {
 | |
|       log.debug('Finished copying', sourceDirs.length, 'directories from', dabInfo.ngDartDocPath, 'to', destPath);
 | |
| 
 | |
|       const apiEntries = dab.loadApiDataAndSaveToApiListFile();
 | |
|       const tmpDocsPath = path.resolve(path.join(process.env.HOME, 'tmp/docs.json'));
 | |
|       if (argv.dumpDocsJson) fs.writeFileSync(tmpDocsPath, JSON.stringify(apiEntries, null, 2));
 | |
|       dab.createApiDataAndJadeFiles(apiEntries);
 | |
|     }).catch((err) => {
 | |
|       console.error(err);
 | |
|     });
 | |
| 
 | |
|   } catch(err) {
 | |
|     console.error(err);
 | |
|     console.error(err.stack);
 | |
|     throw err;
 | |
|   }
 | |
| }
 | |
| 
 | |
| function buildShredMaps(shouldWrite) {
 | |
|   var options = {
 | |
|     devguideExamplesDir: _devguideShredOptions.examplesDir,
 | |
|     apiExamplesDir: _apiShredOptions.examplesDir,
 | |
|     fragmentsDir: _devguideShredOptions.fragmentsDir,
 | |
|     jadeDir: './public/docs',
 | |
|     outputDir: './public/docs',
 | |
|     writeFilesEnabled: shouldWrite,
 | |
|     logLevel: _dgeniLogLevel
 | |
|   };
 | |
|   return docShredder.buildShredMap(options).then(function(docs) {
 | |
|     return docs;
 | |
|   });
 | |
| }
 | |
| 
 | |
| // returns a promise containing filePaths with any changed or added examples;
 | |
| function getChangedExamples(sha) {
 | |
|   var Git = require("nodegit");
 | |
|   var examplesPath = _devguideShredOptions.examplesDir;
 | |
|   var relativePath = path.relative(process.cwd(), examplesPath);
 | |
|   return Git.Repository.open(".").then(function(repo) {
 | |
|     if (sha.length) {
 | |
|       return repo.getCommit(sha);
 | |
|     } else {
 | |
|       return repo.getHeadCommit();
 | |
|     }
 | |
|   }).then(function(commit) {
 | |
|     return getChangedExamplesForCommit(commit, relativePath);
 | |
|   }).catch(function(err) {
 | |
| 
 | |
|   });
 | |
| }
 | |
| 
 | |
| function getChangedExamplesAfter(date, relativePath) {
 | |
|   var Git = require("nodegit");
 | |
|   var examplesPath = _devguideShredOptions.examplesDir;
 | |
|   var relativePath = path.relative(process.cwd(), examplesPath);
 | |
|   return Git.Repository.open(".").then(function(repo) {
 | |
|     return repo.getHeadCommit();
 | |
|   }).then(function(commit) {
 | |
|     var repo = commit.owner();
 | |
|     var revWalker = repo.createRevWalk();
 | |
|     revWalker.sorting(Git.Revwalk.SORT.TIME);
 | |
|     revWalker.push(commit.id());
 | |
|     return revWalker.getCommitsUntil(function (commit) {
 | |
|       return commit.date().getTime() > date.getTime();
 | |
|     });
 | |
|   }).then(function(commits) {
 | |
|     return Q.all(commits.map(function(commit) {
 | |
|       return getChangedExamplesForCommit(commit, relativePath);
 | |
|     }));
 | |
|   }).then(function(arrayOfPaths) {
 | |
|     var pathMap = {};
 | |
|     arrayOfPaths.forEach(function(paths) {
 | |
|       paths.forEach(function(path) {
 | |
|         pathMap[path] = true;
 | |
|       });
 | |
|     });
 | |
|     var uniqPaths = _.keys(pathMap);
 | |
|     return uniqPaths;
 | |
|   }).catch(function(err) {
 | |
|     var x = err;
 | |
|   });
 | |
| 
 | |
| }
 | |
| 
 | |
| function getChangedExamplesForCommit(commit, relativePath) {
 | |
|   return commit.getDiff().then(function(diffList) {
 | |
|     var filePaths = [];
 | |
|     diffList.forEach(function (diff) {
 | |
|       diff.patches().then(function (patch) {
 | |
|         if (patch.isAdded() || patch.isModified) {
 | |
|           var filePath = path.normalize(patch.newFile().path());
 | |
|           var isExample = filePath.indexOf(relativePath) >= 0;
 | |
|           // gutil.log(filePath + " isExample: " + isExample);
 | |
|           if (isExample) {
 | |
|             filePaths.push(filePath);
 | |
|           }
 | |
|         }
 | |
|       });
 | |
|     });
 | |
|     return filePaths;
 | |
|   });
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| function jadeShredMapToJadeExampleMap(jadeShredMap, examplePaths) {
 | |
|   // remove dups in examplePaths
 | |
|   var exampleSet = {};
 | |
|   examplePaths.forEach(function(examplePath) {
 | |
|     exampleSet[examplePath] = examplePath;
 | |
|   });
 | |
|   var basePath = path.resolve(".");
 | |
|   var jadeToFragMap = jadeShredMap.jadeToFragMap;
 | |
|   var jadeExampleMap = {};
 | |
|   for (var jadePath in jadeToFragMap) {
 | |
|     var relativeJadePath = path.relative(basePath, jadePath);
 | |
|     var vals = jadeToFragMap[jadePath];
 | |
|     vals.forEach(function(val) {
 | |
|       var relativeExamplePath = path.relative(basePath, val.examplePath);
 | |
|       if (exampleSet[relativeExamplePath] != null) {
 | |
|         addKeyValue(jadeExampleMap, relativeJadePath, relativeExamplePath);
 | |
|       }
 | |
|     });
 | |
|   }
 | |
|   return jadeExampleMap;
 | |
| }
 | |
| 
 | |
| function jadeShredMapToExampleJadeMap(jadeShredMap) {
 | |
| 
 | |
|   var jadeToFragMap = jadeShredMap.jadeToFragMap;
 | |
|   var exampleJadeMap = {};
 | |
|   for (var jadePath in jadeToFragMap) {
 | |
|     var vals = jadeToFragMap[jadePath];
 | |
|     vals.forEach(function(val) {
 | |
|       var examplePath = val.examplePath;
 | |
|       addKeyValue(exampleJadeMap, examplePath, jadePath);
 | |
|     });
 | |
|   }
 | |
|   return exampleJadeMap;
 | |
| }
 | |
| 
 | |
| function addKeyValue(map, key, value) {
 | |
|   var vals = map[key];
 | |
|   if (vals) {
 | |
|     if (vals.indexOf(value) == -1) {
 | |
|       vals.push(value);
 | |
|     }
 | |
|   } else {
 | |
|     map[key] = [value];
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| // Synchronously execute a chain of commands.
 | |
| // cmds: an array of commands
 | |
| // options: { shouldLog: true,  shouldThrow: true }
 | |
| // cb: function(err, stdout, stderr)
 | |
| function execCommands(cmds, options, cb) {
 | |
|   options = options || {};
 | |
|   options.shouldThrow = options.shouldThrow == null ? true : options.shouldThrow;
 | |
|   options.shouldLog = options.shouldLog == null ? true : options.shouldLog;
 | |
|   if (!cmds || cmds.length == 0) cb(null, null, null);
 | |
|   var exec = require('child_process').exec;  // just to make it more portable.
 | |
|   gutil.log("NODE_ENV: " + process.env.NODE_ENV);
 | |
| 
 | |
|   exec(cmds[0], options, function(err, stdout, stderr) {
 | |
|     if (err == null) {
 | |
|       if (options.shouldLog) {
 | |
|         gutil.log('cmd: ' + cmds[0]);
 | |
|         gutil.log('stdout: ' + stdout);
 | |
|       }
 | |
|       if (cmds.length == 1) {
 | |
|         cb(err, stdout, stderr);
 | |
|       } else {
 | |
|         execCommands(cmds.slice(1), options, cb);
 | |
|       }
 | |
|     } else {
 | |
|       if (options.shouldLog) {
 | |
|         gutil.log('exec error on cmd: ' + cmds[0]);
 | |
|         gutil.log('exec error: ' + err);
 | |
|         if (stdout) gutil.log('stdout: ' + stdout);
 | |
|         if (stderr) gutil.log('stderr: ' + stderr);
 | |
|       }
 | |
|       if (err && options.shouldThrow) throw err;
 | |
|       cb(err, stdout, stderr);
 | |
|     }
 | |
|   });
 | |
| }
 | |
| 
 | |
| function ngPathFor(lang) {
 | |
|   return ANGULAR_PROJECT_PATH + (lang === 'dart' ? '-dart' : '');
 | |
| }
 | |
| 
 | |
| function checkAngularProjectPath(_ngPath) {
 | |
|   var ngPath = path.resolve(_ngPath || ngPathFor('ts'));
 | |
|   if (fs.existsSync(ngPath)) return;
 | |
|   throw new Error('API related tasks require the angular2 repo to be at ' + ngPath);
 | |
| }
 | |
| 
 | |
| function renameIfExistsSync(oldPath, newPath) {
 | |
|   if (fs.existsSync(oldPath)) {
 | |
|     gutil.log(`Rename: mv ${oldPath} ${newPath}`);
 | |
|     fs.renameSync(oldPath, newPath);
 | |
|   } else {
 | |
|     gutil.log(`renameIfExistsSync cannot rename, path not found: ${oldPath}`);
 | |
|   }
 | |
| }
 |