angular-docs-cn/gulpfile.js
Filipe Silva eb60bfe20e Squashed commit of the following:
commit 17c7e154672dbf5feeeb971ed3b594d318703748
Author: Filipe Silva <filipematossilva@gmail.com>
Date:   Mon Nov 14 23:11:37 2016 +0000

    chore: update to 2.2.0 (#2797)

commit 1e5facfb980b85313d51559b3272950f662ce20c
Merge: 5c4cc9a db0fac9
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 12:44:51 2016 -0800

    Merge pull request #2799 from IdeaBlade/docs-changelog-2.2.0

    chore(changelog): update docs changelog for Ng v.2.2.0

commit 5c4cc9a3c8b2718b71ab7c3d1b5dd02bfdd5f1fc
Merge: 43457e9 1afe5dc
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 12:43:09 2016 -0800

    docs(router): Updated usage of observables in router tutorial and developer guide (#2696)

    Moved route configuration into separate variable for consistency
    Added async pipe to handle subscriptions for list items

commit 43457e9765c63b3889ab449959fe295327390315
Merge: a423a5a 2649647
Author: Jesús Rodríguez <Foxandxss@gmail.com>
Date:   Mon Nov 14 21:38:49 2016 +0100

    chore: add upgrade/static to API reference (#2755)

commit 1afe5dc97d457e045adb6c0e03d27023aa97eea5
Author: Brandon Roberts <robertsbt@gmail.com>
Date:   Sat Oct 29 16:08:54 2016 -0500

    docs(router): Updated usage of observables in router tutorial and developer guide

    Moved route configuration into separate variable for consistency
    Added async pipe to handle subscriptions for list items

commit db0fac94c980c99d9575a90d4b6bb15f572f9161
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 10:48:09 2016 -0800

    chore(changelog): update docs changelog for Ng v.2.2.0

commit a423a5abc704280794b6dea69cf1c9e76bbc9da1
Author: ikilled <ikilled@users.noreply.github.com>
Date:   Mon Nov 14 18:39:46 2016 +0000

    clicking on Books & Training sends user to Education (#2701)

    When user clicks in Books & Training item in footer the website should take them to Education section (anchor) in the middle of the page, not to the top where Development section is.

commit d63b1ccea36f4cf52333c846184b8e6876284a0a
Merge: f627706 8508140
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 10:35:48 2016 -0800

    docs(router): fixed verbiage about router-outlet (#2746)

commit f627706779c418ca8357aacd52aba5b5b7d05cb0
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 10:26:13 2016 -0800

    docs(cookbook-aot-compiler): improve Ahead-of-Time compilation cookbook (#2798)

    closes #2790

commit 75464d585cac944e3cffb4401663e4c1185b7cb5
Merge: 78e2584 02f5559
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 09:34:36 2016 -0800

    Merge pull request #2794 from IdeaBlade/chalin-guide-misc-fix-1113

    docs: guide/index misc Jade fixes

commit 78e25840b229b0fe345feacf786f34e62b2cb963
Merge: 182493f 85062c4
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 09:33:55 2016 -0800

    Merge pull request #2795 from IdeaBlade/chalin-util-js-getExampleName-1114

    chore(util.js): getExampleName - support optional .html suffix

commit 182493f8779fa37422dd942813fc5681dbdac55f
Merge: 9e9666b 53f5538
Author: Ward Bell <wardbell@hotmail.com>
Date:   Mon Nov 14 09:28:40 2016 -0800

    feat(cb-ts-to-js): add es6 examples (#2606)

    * feat(cb-ts-to-js): add es6 examples
    update docshredder to shred .es6
    optimized focus gulp task
    convert imports and metadate sections
    add DI section
    add host and query metadata section
    add intro
    fix capitalization and grammar

    * docs(ts-to-js): ward's edits (incomplete)

    * docs(ts-to-js): add separate template files for some components

    * docs(cb-ts-to-js): refactor sample code

commit 53f5538859c4d243552d288872e8c8fc8156f446
Author: Ward Bell <wardbell@hotmail.com>
Date:   Sun Nov 13 14:09:28 2016 -0800

    docs(cb-ts-to-js): refactor sample code

commit 9e9666b2cc3ce57cac172894e935f0c6b9f7d6f4
Author: Patrice Chalin <chalin@users.noreply.github.com>
Date:   Mon Nov 14 08:34:10 2016 -0800

    docs(template-syntax/dart): updates to match TS (#2751)

    * docs(template-syntax): refresh _cache

    * docs(template-syntax/dart): updates to match TS

    - Propagates TS-side changes:
      - update #2639 - new two-way binding section, and
      - fix #2687 - invalid attr syntax
    - Fixes
      - #1898 - currency symbols
      - #2748 - Dart template-syntax e2e is failing
      - #2749 - deprecated `[className]`

    * updated _cache file following Kathy's post-review edits

    * Post Ward's review w/ cache updated

    - Keep `my-` and `my` prefixes on selectors (for components and
    directives, respectively).
    - Drop `my-` from file names.
    - Drop `My` as component class prefix.

commit 5dcffd69dc09b48d99af4acd0dd06b029416e103
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Sun Nov 13 19:37:13 2016 -0800

    docs: dart glossary - fix misnamed Jade block

commit 6680acc513a0178bb522f5bc99d66f366542d33c
Merge: 14db838 3b03573
Author: Kathy Walrath <kathyw@google.com>
Date:   Mon Nov 14 08:31:11 2016 -0800

    docs(toh): avoid dup header title (#2796)

    * remove redundant headings

    * update _cache

    * misc: make block comment a Jade comment

    (This prevents the text from appearing in the generated HTML as an HTML
    comment.)

commit 3b03573f340cc7fc43e2642bdf52fb1bae61aff0
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Mon Nov 14 05:30:05 2016 -0800

    misc: make block comment a Jade comment

    (This prevents the text from appearing in the generated HTML as an HTML
    comment.)

commit 470426d5e03829442449cb59c0528811c6011c37
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Mon Nov 14 05:25:36 2016 -0800

    update _cache

commit c12d75a477a1e33ea37886c0bd5942ddb9c955b3
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Mon Nov 14 05:23:35 2016 -0800

    remove redundant headings

commit 85062c47cac44d63b06b97ae86b2a4f596889ad6
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Mon Nov 14 04:56:12 2016 -0800

    chore(util.js): getExampleName - support optional .html suffix

commit 02f55592b232618407a3a8fce70845589ee78978
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Mon Nov 14 04:29:17 2016 -0800

    docs: guide/index misc Jade fixes

    - Eliminate use of deprecated `clear=“all”` in `<br>`.
    - No need for local `langName`; use global `_Lang` var instead.
    - Remove duplicate id `learning-path`.

commit 14db838f8b76309597140d9857acb92c76c864f6
Author: Naomi Black <naomitraveller@gmail.com>
Date:   Sun Nov 13 21:48:52 2016 -0500

    news(nov): Some news and a blog post update

commit eff32ecbdd5a52d2240afd5ad4e14bdab21c9a8f
Author: Naomi Black <naomitraveller@gmail.com>
Date:   Sun Nov 13 21:48:37 2016 -0500

    chore(bios): update some bios for leads

commit 3ee36fbba211d115414d322b17a67626ed90d450
Author: koyner <markburrett@gmail.com>
Date:   Sun Nov 13 22:59:59 2016 +0100

    docs(forms): grammar fix (#2764)

commit b11438f211ed7009335bc18f5cbf2950fc4014fa
Author: Ward Bell <wardbell@hotmail.com>
Date:   Fri Nov 11 19:44:00 2016 -0800

    docs(ts-to-js): add separate template files for some components

commit 33b61977b2cd0bc35b257ce526d5dd2fd460cd10
Author: Ward Bell <wardbell@hotmail.com>
Date:   Thu Nov 3 01:37:55 2016 -0700

    docs(ts-to-js): ward's edits (incomplete)

commit 12eb19fa3c34bb48b28da8bd33d94d7cc0526cf5
Author: Filipe Silva <filipematossilva@gmail.com>
Date:   Thu Oct 13 17:59:00 2016 +0100

    feat(cb-ts-to-js): add es6 examples
    update docshredder to shred .es6
    optimized focus gulp task
    convert imports and metadate sections
    add DI section
    add host and query metadata section
    add intro
    fix capitalization and grammar

commit 64a8754386c0aa727e61d140d86bd31b6891ea99
Author: Patrice Chalin <chalin@users.noreply.github.com>
Date:   Thu Nov 10 20:01:36 2016 -0800

    example(template-syntax): follow style-guide and other updates (#2750)

commit 7619cdf4a4ac592d74c25a372234c2d3964355dc
Author: Jesús Rodríguez <Foxandxss@gmail.com>
Date:   Thu Nov 10 23:47:30 2016 +0100

    chore: ability to open a plunker on a specific file (#2778)

commit 0161d9db39b08be6a29e5a00b362c1eff12bf6b9
Author: Filipe Silva <filipematossilva@gmail.com>
Date:   Thu Nov 10 22:45:22 2016 +0000

    chore: ignore debug.log file (#2785)

    This file is generated when running `gulp e2e` and often enough committed by mistake.

    /cc @foxandxss

commit f92983cc6f7a895b751a6023791a283bb2e41073
Author: Jesús Rodríguez <Foxandxss@gmail.com>
Date:   Thu Nov 10 23:44:51 2016 +0100

    docs(ngmodule): fix plunkers (#2786)

commit 03db4bbc4873d8b68d08ef32bfa1b2b43cf46224
Author: Martin Eckardt <m.eckardt@outlook.com>
Date:   Wed Nov 9 17:43:40 2016 +0100

    docs(a1-a2): fix link to Filter/Pipes (#2770)

commit 60565a5cf1dfa7e43cb18b448c7d61c5f765f0de
Author: Pavol Pitonak <pavol@pitonak.com>
Date:   Wed Nov 9 17:42:57 2016 +0100

    docs(testing): configureTestModule -> configureTestingModule (#2767)

commit ec471974a776dc208b2c56115cf1105fa39c8f88
Author: Catalin Zalog <xxxxxcata@yahoo.com>
Date:   Wed Nov 9 18:41:56 2016 +0200

    docs(style-guide): fix missing *.ts (#2763)

commit 234e468d5d6a11782e306901f80546d8989f17b3
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Tue Nov 8 08:21:02 2016 -0800

    docs: intra-site links should be relative

    Contributes to #2772.

commit 6b37da78e4901a57b6ebf77720ec4796f2bb5f5f
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Tue Nov 8 09:27:10 2016 -0800

    docs(forms/dart): remove mention of FORM_DIRECTIVES

    Fixes #2752

commit c24dd074a6acbceb87f8e973969c9bd94f769d8f
Author: Patrice Chalin <chalin@users.noreply.github.com>
Date:   Tue Nov 8 14:48:03 2016 -0800

    docs(toh-5/dart): use routerLink in dashboard (#2744)

    * docs(toh-5/dart): use routerLink in dashboard

    * minor edits to TS jade

    * remove dart/toh-pt5 from bad-code-excerpt-skip-patterns

commit 2808878c36d7429611e4f0584323e99c9b83219e
Author: Patrice Chalin <pchalin@gmail.com>
Date:   Tue Nov 8 07:41:27 2016 -0800

    chore(deploy): don't name project in firebase deploy

    Naming the project would sometimes cause gulp to report `An unexpected
    error has occurred` with exit code 2.

commit 2649647ecb14a28932405d6b0bc22afd1f931089
Author: Jesus Rodriguez <Foxandxss@gmail.com>
Date:   Sat Nov 5 00:37:47 2016 +0100

    chore: add upgrade/static to API reference

commit 850814097f9908781ce954d13878d20eed779c87
Merge: 37f93bc b1c2c27
Author: Adrian Irwin <mr.irwin@gmail.com>
Date:   Thu Nov 3 17:27:43 2016 -0700

    Merge branch 'router' of https://github.com/adrianirwin/angular.io into router

commit 37f93bc0cbc2a766846b7715b1d8ddeeec43de39
Author: Adrian Irwin <mr.irwin@gmail.com>
Date:   Thu Nov 3 17:25:55 2016 -0700

    docs(router): fixed verbiage and example of how routed views are related to the router outlet

commit b1c2c27d365186d4231a470945a0975482df58e9
Author: Adrian Irwin <adrianirwin@kpmg.com>
Date:   Thu Nov 3 16:57:56 2016 -0700

    docs(router): fixed verbiage and example of how routed views are related to the router outlet
2016-11-15 00:44:35 +00:00

1596 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/*'
];
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'});
// 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}`);
}
}