chore: add gulp link-checker task
also fix some of the broken links that it found
This commit is contained in:
parent
469612f50c
commit
1a154daa2e
|
@ -27,6 +27,4 @@ plnkr.html
|
||||||
*plnkr.no-link.html
|
*plnkr.no-link.html
|
||||||
public/docs/*/latest/guide/cheatsheet.json
|
public/docs/*/latest/guide/cheatsheet.json
|
||||||
protractor-results.txt
|
protractor-results.txt
|
||||||
|
link-checker-results.txt
|
||||||
|
|
||||||
|
|
||||||
|
|
82
gulpfile.js
82
gulpfile.js
|
@ -23,6 +23,7 @@ var globby = require("globby");
|
||||||
// Ugh... replacement needed to kill processes on any OS
|
// Ugh... replacement needed to kill processes on any OS
|
||||||
// - because childProcess.kill does not work properly on windows
|
// - because childProcess.kill does not work properly on windows
|
||||||
var treeKill = require("tree-kill");
|
var treeKill = require("tree-kill");
|
||||||
|
var blc = require("broken-link-checker");
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// 1. Think about using runSequence
|
// 1. Think about using runSequence
|
||||||
|
@ -424,6 +425,13 @@ gulp.task('test-api-builder', function (cb) {
|
||||||
execCommands(['npm run test-api-builder'], {}, 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) {
|
||||||
|
return linkChecker();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Internal tasks
|
// Internal tasks
|
||||||
gulp.task('set-prod-env', function () {
|
gulp.task('set-prod-env', function () {
|
||||||
|
@ -514,6 +522,80 @@ function harpCompile() {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function linkChecker(options) {
|
||||||
|
var deferred = Q.defer();
|
||||||
|
var options = options || {};
|
||||||
|
|
||||||
|
var blcOptions = options.blcOptions || {};
|
||||||
|
var customData = options.customData || {};
|
||||||
|
|
||||||
|
var excludeBad; // don't bother reporting bad links matching this RegExp
|
||||||
|
if (argv.excludeBad) {
|
||||||
|
excludeBad = new RegExp(argv.excludeBad);
|
||||||
|
} else {
|
||||||
|
excludeBad = options.excludeBad === undefined ? /docs\/dart\/latest\/api/ : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
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);docs/ts/latest/api/core/
|
||||||
|
},
|
||||||
|
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() +
|
||||||
|
'\nSkipping bad links matching 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 {
|
||||||
|
siteChecker.enqueue(siteUrl, customData);
|
||||||
|
} catch (err) {
|
||||||
|
deferred.reject(err);
|
||||||
|
}
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
// harp has issues with node_modules under the public dir
|
// harp has issues with node_modules under the public dir
|
||||||
// but we need them there for example testing and development
|
// but we need them there for example testing and development
|
||||||
// this method allows the node modules folder under '_examples'
|
// this method allows the node modules folder under '_examples'
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"archiver": "^0.16.0",
|
"archiver": "^0.16.0",
|
||||||
"assert-plus": "^0.1.5",
|
"assert-plus": "^0.1.5",
|
||||||
|
"broken-link-checker":"0.7.0",
|
||||||
"browser-sync": "^2.9.3",
|
"browser-sync": "^2.9.3",
|
||||||
"canonical-path": "0.0.2",
|
"canonical-path": "0.0.2",
|
||||||
"cross-spawn": "^2.1.0",
|
"cross-spawn": "^2.1.0",
|
||||||
|
|
|
@ -295,7 +295,7 @@ table(width="100%")
|
||||||
In this example, the `table` element is removed from the DOM unless the `movies` array has a length.
|
In this example, the `table` element is removed from the DOM unless the `movies` array has a length.
|
||||||
|
|
||||||
The (*) before `ngIf` is required in this example.
|
The (*) before `ngIf` is required in this example.
|
||||||
For more information see [Structural Directives](../guide/structural-directives).
|
For more information see [Structural Directives](../guide/structural-directives.html).
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
|
@ -342,7 +342,7 @@ table(width="100%")
|
||||||
the (#) identifies `movie` as a local variable;
|
the (#) identifies `movie` as a local variable;
|
||||||
the list preposition is `of`, not `in`.
|
the list preposition is `of`, not `in`.
|
||||||
|
|
||||||
For more information see [Structural Directives](../guide/structural-directives).
|
For more information see [Structural Directives](../guide/structural-directives.html).
|
||||||
tr(style=top)
|
tr(style=top)
|
||||||
td
|
td
|
||||||
:marked
|
:marked
|
||||||
|
|
|
@ -38,7 +38,7 @@ include ../_util-fns
|
||||||
## Pass data from parent to child with input binding
|
## Pass data from parent to child with input binding
|
||||||
|
|
||||||
`HeroChildComponent` has two ***input properties***,
|
`HeroChildComponent` has two ***input properties***,
|
||||||
typically adorned with [@Input decorations](docs/ts/latest/guide/template-syntax.html#inputs-outputs).
|
typically adorned with [@Input decorations](/docs/ts/latest/guide/template-syntax.html#inputs-outputs).
|
||||||
|
|
||||||
+makeExample('cb-component-communication/ts/app/hero-child.component.ts')
|
+makeExample('cb-component-communication/ts/app/hero-child.component.ts')
|
||||||
:marked
|
:marked
|
||||||
|
@ -142,7 +142,7 @@ figure.image-display
|
||||||
The parent binds to that event property and reacts to those events.
|
The parent binds to that event property and reacts to those events.
|
||||||
|
|
||||||
The child's `EventEmitter` property is an ***output property***,
|
The child's `EventEmitter` property is an ***output property***,
|
||||||
typically adorned with an [@Output decoration](docs/ts/latest/guide/template-syntax.html#inputs-outputs)
|
typically adorned with an [@Output decoration](/docs/ts/latest/guide/template-syntax.html#inputs-outputs)
|
||||||
as seen in this `VoterComponent`:
|
as seen in this `VoterComponent`:
|
||||||
|
|
||||||
+makeExample('cb-component-communication/ts/app/voter.component.ts')
|
+makeExample('cb-component-communication/ts/app/voter.component.ts')
|
||||||
|
|
|
@ -403,7 +403,7 @@ figure
|
||||||
Here's an example of a service class that logs to the browser console
|
Here's an example of a service class that logs to the browser console
|
||||||
+makeExample('architecture/ts/app/logger.service.ts', 'class', 'app/logger.service.ts (class only)')(format=".")
|
+makeExample('architecture/ts/app/logger.service.ts', 'class', 'app/logger.service.ts (class only)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
Here's a `HeroService` that fetches heroes and returns them in a resolved [promise](http://www.html5rocks.com/en/tutorials/es6/promises/).
|
Here's a `HeroService` that fetches heroes and returns them in a resolved [promise](http://www.2ality.com/2014/10/es6-promises-api.html).
|
||||||
The `HeroService` depends on the `LoggerService` and another `BackendService` that handles the server communication grunt work.
|
The `HeroService` depends on the `LoggerService` and another `BackendService` that handles the server communication grunt work.
|
||||||
+makeExample('architecture/ts/app/hero.service.ts', 'class', 'app/hero.service.ts (class only)')(format=".")
|
+makeExample('architecture/ts/app/hero.service.ts', 'class', 'app/hero.service.ts (class only)')(format=".")
|
||||||
:marked
|
:marked
|
||||||
|
|
|
@ -581,7 +581,7 @@ figure.image-display
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
### The NgForm directive
|
### The NgForm directive
|
||||||
What `NgForm` directive? We didn't add an [NgForm](../api/core/NgForm-class.html) directive!
|
What `NgForm` directive? We didn't add an [NgForm](../api/common/NgForm-directive.html) directive!
|
||||||
|
|
||||||
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
Angular did. Angular creates and attaches an `NgForm` directive to the `<form>` tag automatically.
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ figure.image-display
|
||||||
|
|
||||||
.l-sub-section
|
.l-sub-section
|
||||||
:marked
|
:marked
|
||||||
Learn more about the `DatePipes` format options in the [API Docs](../api/core/DatePipe-class.html).
|
Learn more about the `DatePipes` format options in the [API Docs](../api/common/DatePipe-class.html).
|
||||||
:marked
|
:marked
|
||||||
## Chaining pipes
|
## Chaining pipes
|
||||||
We can chain pipes together in potentially useful combinations.
|
We can chain pipes together in potentially useful combinations.
|
||||||
|
|
|
@ -895,7 +895,7 @@ code-example(format="").
|
||||||
<a id="lifecycle-hooks"></a>
|
<a id="lifecycle-hooks"></a>
|
||||||
### Router Lifecycle Hooks
|
### Router Lifecycle Hooks
|
||||||
Angular components have [lifecycle hooks](lifecycle-hooks.html). For example, Angular calls the hook methods of the
|
Angular components have [lifecycle hooks](lifecycle-hooks.html). For example, Angular calls the hook methods of the
|
||||||
[OnInit](../api/core/OnInit-interface.html) and [OnDestroy]((../api/core/OnDestroy-interface.html)
|
[OnInit](../api/core/OnInit-interface.html) and [OnDestroy](../api/core/OnDestroy-interface.html)
|
||||||
interfaces when it creates and destroys components.
|
interfaces when it creates and destroys components.
|
||||||
|
|
||||||
The router calls similar hook methods,
|
The router calls similar hook methods,
|
||||||
|
@ -954,7 +954,7 @@ code-example(format="").
|
||||||
:marked
|
:marked
|
||||||
The `DialogService` (injected in the `AppComponent` for app-wide use) does the asking.
|
The `DialogService` (injected in the `AppComponent` for app-wide use) does the asking.
|
||||||
|
|
||||||
It returns a [promise](http://www.html5rocks.com/en/tutorials/es6/promises/) that
|
It returns a [promise](http://www.2ality.com/2014/10/es6-promises-api.html) that
|
||||||
*resolves* when the user eventually decides what to do: either
|
*resolves* when the user eventually decides what to do: either
|
||||||
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
|
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-2016 Google, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
Loading…
Reference in New Issue