build: remove dashboard (#14023)

* Since we going to move from Travis to CircleCI we don't want to use the dashboard anymore. Since nobody has been
looking at the dashboard frequently, it just makes things more complicated and additionally we will be using
the Github robot in order to keep track of size changes.
This commit is contained in:
Paul Gschwendtner
2018-11-07 18:04:16 +00:00
committed by vivian-hu
parent d50b9f28c1
commit a02e6c64f1
55 changed files with 34 additions and 8188 deletions
+1 -6
View File
@@ -33,7 +33,6 @@ node_modules
.DS_Store
/.sass-cache
/connect.lock
/coverage/*
/libpeerconnection.log
npm-debug.log
yarn-debug.log
@@ -41,8 +40,4 @@ yarn-error.log
testem.log
/.chrome
/.git
/.firebase
# schematics
/src/lib/schematics/**/*.js
/src/lib/schematics/**/*.map
/.firebase
+2 -8
View File
@@ -8,10 +8,6 @@ node_js:
- '8.12.0'
addons:
jwt:
# SAUCE_ACCESS_KEY<=secret for FIREBASE_ACCESS_TOKEN to work around travis-ci/travis-ci#7223, unencrypted value in valentine as FIREBASE_ACCESS_TOKEN>
# we alias FIREBASE_ACCESS_TOKEN to $SAUCE_ACCESS_KEY in env.sh and set the SAUCE_ACCESS_KEY there
- secure: "PKts/IbxuJRWWOEeiGbl8Z9zds0M+hIdCH/g/E4WbQ9yzSvSbdwzfmRfFccQFjxjsrY7+SJMVjsURZy+xUyBpzqgWYHUItnSVqjZb8DlyAU2IXyg8TM9BVLkGGe6k5k4PIFVmfMMMzQwWMM0X0W9w3oYmfHL5egxwSHvf9HIqLolLNXg8sqamIdS5d5KoCXf1c+oRjN/IMBktzNBR6N4OFOZQXVoepXNiIvTWAcTtOPBvFWdKP2n7RVioHKdm4a85aCUpDJp+LYGaLqiQZoRzmzfVTnAhTAPdd4ao5w/+jojrfZIHV55bqYF9rLnQMTneKsiyVNVYJzOLuxmARa/EEKfZld+J3rX4/o4cogrU38YSZF+T7J9g/7CTsnIZ3F6W6m+8iJbIBh55nGOQi5PVe458Q/nGb3fgQd2Z4+6lK9k479H4Ssh/Y7hbVQbepqEVIXzZKqWX6/ZE4iWoR/Q2dm0hySFmmB/R2etixX5JxhnHvgobTYIQ+1liJVp/3YFW1ru64Yg6yz/V291Bhh9g31znmTROCJ/usAmZZaLUqW1TDKnLIMP+M74MF9XERqcWKywXRFwxP4E5uDnx/vAyN49gL+SDfrBUxUtXrTkKZAlglwo9SgA7cOYEPWrionvKcGm87gCBYHFUmXZNQVzh212fpuJYXb/vy0sPDj8La4="
chrome: stable
firefox: latest
@@ -25,23 +21,21 @@ branches:
jobs:
include:
- env: "MODE=payload"
- env: "MODE=saucelabs_required"
- env: "DEPLOY_MODE=dashboard"
if: type = cron
env:
global:
# Keep Yarn version in sync with Yarn version within Bazel WORKSPACE.
- YARN_VERSION=1.9.4
- LOGS_DIR=/tmp/angular-material2-build/logs
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=angularteam1
- BROWSER_STACK_ACCESS_KEY=CaXMeMHD9pr5PHg8N7Jq
- BROWSER_PROVIDER_READY_FILE=/tmp/angular-material2-build/readyfile
- BROWSER_PROVIDER_ERROR_FILE=/tmp/angular-material2-build/errorfile
before_install:
- source ./scripts/ci/travis-env.sh
- source ./scripts/ci/install-yarn.sh
install:
-1
View File
@@ -111,7 +111,6 @@
"karma": "^3.0.0",
"karma-browserstack-launcher": "^1.3.0",
"karma-chrome-launcher": "^2.2.0",
"karma-coverage": "^1.1.1",
"karma-firefox-launcher": "^1.0.1",
"karma-jasmine": "^1.1.2",
"karma-sauce-launcher": "^1.2.0",
-4
View File
@@ -13,10 +13,6 @@ is_aot() {
[[ "${MODE}" = aot ]]
}
is_payload() {
[[ "${MODE}" = payload ]]
}
is_unit() {
[[ "${MODE}" =~ ^.*_(optional|required)$ ]]
}
+1 -3
View File
@@ -19,9 +19,7 @@ echo ""
echo "Starting the deployment script. Running mode: ${DEPLOY_MODE}"
echo ""
if [[ "${DEPLOY_MODE}" == "dashboard" ]]; then
retryCall ${DEPLOY_RETRIES} ./scripts/deploy/deploy-dashboard.sh
elif [[ "${DEPLOY_MODE}" == "build-artifacts" ]]; then
if [[ "${DEPLOY_MODE}" == "build-artifacts" ]]; then
retryCall ${DEPLOY_RETRIES} ./scripts/deploy/publish-build-artifacts.sh
elif [[ "${DEPLOY_MODE}" == "docs-content" ]]; then
retryCall ${DEPLOY_RETRIES} ./scripts/deploy/publish-docs-content.sh
-11
View File
@@ -1,11 +0,0 @@
#!/bin/bash
# If FIREBASE_ACCESS_TOKEN not set yet, export the FIREBASE_ACCESS_TOKEN using the JWT token that
# Travis generated and exported for SAUCE_ACCESS_KEY.
# This is a workaround for travis-ci/travis-ci#7223
# WARNING: FIREBASE_ACCESS_TOKEN should NOT be printed
export FIREBASE_ACCESS_TOKEN=${FIREBASE_ACCESS_TOKEN:-$SAUCE_ACCESS_KEY}
# - we overwrite the value set by Travis JWT addon here to work around travis-ci/travis-ci#7223
# for FIREBASE_ACCESS_TOKEN
export SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
-7
View File
@@ -39,17 +39,10 @@ elif is_e2e; then
xvfb-run -a --server-args='-screen 0, 1024x768x16' $(npm bin)/gulp ci:e2e
elif is_aot; then
$(npm bin)/gulp ci:aot
elif is_payload; then
$(npm bin)/gulp ci:payload
elif is_unit; then
$(npm bin)/gulp ci:test
elif is_prerender; then
$(npm bin)/gulp ci:prerender
fi
# Upload coverage results if those are present.
if [ -f dist/coverage/coverage-summary.json ]; then
$(npm bin)/gulp ci:coverage
fi
teardown_tunnel
-41
View File
@@ -1,41 +0,0 @@
#!/bin/bash
# The script should immediately exit if any command in the script fails.
set -e
# This script deploys the Dashboard App and their Cloud Functions to Firebase.
# Before deploying, the script installs all dependencies and builds the dashboard app in production.
# Go to the project root directory
cd $(dirname ${0})/../..
if [ -z ${MATERIAL2_BOARD_FIREBASE_DEPLOY_KEY} ]; then
echo "Error: No access token for firebase specified." \
"Please set the environment variable 'MATERIAL2_DASHBOARD_ACCESS_TOKEN'."
exit 1
fi
# Paths to the dashboard and functions directories.
dashboardFolder=tools/dashboard
# Path to the firebase binary of the root package.json
firebaseBin=$(npm bin)/firebase
# Go to the dashboard folder because otherwise the Firebase CLI tries to deploy the wrong project.
cd ${dashboardFolder}
# Install node_modules for the dashboard and afterwards build the dashboard app in production.
(npm install; npm run build-production) &
# Install node modules for dashboard functions. Firebase CLI needs to execute the functions
# to collect all function names before it can deploy them.
(cd functions; npm install) &
# The dashboard and function dependencies are installed concurrently. Also the dashboard app is
# build in production afterwards. Wait for all async tasks to finish before proceeding.
wait
# Deploy the dashboard to Firebase. Based on the current configuration hosting and functions
# will be deployed.
${firebaseBin} deploy --token ${MATERIAL2_BOARD_FIREBASE_DEPLOY_KEY} \
--non-interactive --project material2-board
+1 -1
View File
@@ -89,7 +89,7 @@
"no-eol-whitespace": true,
"max-line-length": 100,
"linebreaks": "unix",
"selector-class-pattern": ["^_?(mat-|cdk-|example-|demo-|dashboard-|ng-)", {
"selector-class-pattern": ["^_?(mat-|cdk-|example-|demo-|ng-)", {
"resolveNestedSelectors": true
}]
}
-14
View File
@@ -13,7 +13,6 @@ module.exports = (config) => {
require('karma-chrome-launcher'),
require('karma-firefox-launcher'),
require('karma-sourcemap-loader'),
require('karma-coverage'),
],
files: [
{pattern: 'node_modules/core-js/client/core.min.js', included: true, watched: false},
@@ -52,12 +51,6 @@ module.exports = (config) => {
reporters: ['dots'],
autoWatch: false,
coverageReporter: {
type : 'json-summary',
dir : 'dist/coverage/',
subdir: '.'
},
sauceLabs: {
testName: 'Angular Material Unit Tests',
startConnect: false,
@@ -124,13 +117,6 @@ module.exports = (config) => {
if (process.env['TRAVIS']) {
const buildId = `TRAVIS #${process.env.TRAVIS_BUILD_NUMBER} (${process.env.TRAVIS_BUILD_ID})`;
if (process.env['TRAVIS_PULL_REQUEST'] === 'false' &&
process.env['MODE'] === "travis_required") {
config.preprocessors['dist/packages/**/!(*+(.|-)spec).js'] = ['coverage'];
config.reporters.push('coverage');
}
// The MODE variable is the indicator of what row in the test matrix we're running.
// It will look like <platform>_<target>, where platform is one of 'saucelabs', 'browserstack'
// or 'travis'. The target is a reference to different collections of browsers that can run
-3
View File
@@ -1,3 +0,0 @@
# Sub Gitignore file for the Dashboard project.
dist/
node_modules/
-101
View File
@@ -1,101 +0,0 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"material2-board": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"prefix": "app",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.json",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
"src/theme.scss"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "material2-board:build"
},
"configurations": {
"production": {
"browserTarget": "material2-board:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "material2-board:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
"src/styles.css"
],
"scripts": [],
"assets": [
"src/favicon.ico",
"src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "material2-board"
}
-11
View File
@@ -1,11 +0,0 @@
{
"hosting": {
"public": "dist",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
-1
View File
@@ -1 +0,0 @@
export {payloadGithubStatus} from './payload-github-status';
@@ -1,11 +0,0 @@
import {config} from 'firebase-functions';
import * as GithubApi from '@octokit/rest';
/** API token for the Github repository. Required to set the github status on commits and PRs. */
const githubAccessToken = config().secret.github;
/** Export the GitHub V3 API instance that is authenticated. */
export const githubApiV3 = new GithubApi();
// Authenticate the Github API package with the user token.
githubApiV3.authenticate({type: 'token', token: githubAccessToken});
@@ -1,24 +0,0 @@
import {githubApiV3} from './github-api';
/** Data that must be specified to set a Github PR status. */
export type GithubStatusData = {
result: boolean;
name: string;
description: string;
url?: string;
};
/** Function that sets a Github commit status */
export function setGithubStatus(commitSHA: string, data: GithubStatusData) {
const state = data.result ? 'success' : 'failure';
return githubApiV3.repos.createStatus({
owner: 'angular',
repo: 'material2',
sha: commitSHA,
state: state,
target_url: data.url,
description: data.description,
context: data.name,
});
}
-16
View File
@@ -1,16 +0,0 @@
/**
* Entry point for the Firebase functions of the dashboard app. Firebase functions only support
* JavaScript files and therefore the TypeScript files needs to be transpiled.
*/
'use strict';
const path = require('path');
// Enable TypeScript compilation at runtime using ts-node.
require('ts-node').register({
project: path.join(__dirname, 'tsconfig.json')
});
// Export all functions from the TypeScript source.
Object.assign(exports, require('./functions'));
@@ -1,24 +0,0 @@
import {verify} from 'jsonwebtoken';
import {config} from 'firebase-functions';
/** The JWT secret. This is used to validate JWT. */
const jwtSecret = config().secret.jwt;
/** The repo slug. This is used to validate the JWT is sent from correct repo. */
const repoSlug = config().repo.slug;
export function verifyToken(token: string): boolean {
try {
// The returned value of the verify method can be either a string or a object. Reading
// properties without explicitly treating the result as `any` will lead to a TypeScript error.
const tokenPayload = verify(token, jwtSecret, {issuer: 'Travis CI, GmbH'}) as any;
if (tokenPayload.slug !== repoSlug) {
console.log(`JWT slugs are not matching. Expected ${repoSlug}`);
}
return true;
} catch {
return false;
}
}
-16
View File
@@ -1,16 +0,0 @@
{
"name": "material2-board-functions",
"version": "0.0.0",
"main": "index.js",
"dependencies": {
"@firebase/app-types": "^0.3.2",
"@octokit/rest": "^15.9.4",
"@types/jsonwebtoken": "^7.2.8",
"firebase-admin": "^5.13.0",
"firebase-functions": "^1.1.0",
"jsonwebtoken": "^8.3.0",
"request": "^2.87.0",
"ts-node": "^7.0.0",
"typescript": "^2.9.2"
}
}
@@ -1,58 +0,0 @@
import {https} from 'firebase-functions';
import {verifyToken} from './jwt/verify-token';
import {setGithubStatus} from './github/github-status';
export const payloadGithubStatus = https.onRequest(async (request, response) => {
const authToken = request.header('auth-token');
const commitSha = request.header('commit-sha');
const packageName = request.header('package-name');
const packageSize = parseFloat(request.header('package-full-size') || '');
const packageDiff = parseFloat(request.header('package-size-diff') || '');
if (!authToken || !verifyToken(authToken)) {
return response.status(403).json({message: 'Auth token is not valid'});
}
if (!commitSha) {
return response.status(404).json({message: 'No commit has been specified'});
}
if (isNaN(packageDiff)) {
return response.status(400).json({message: 'No valid package difference has been specified.'});
}
if (isNaN(packageSize)) {
return response.status(400).json({message: 'No full size of the package has been specified.'});
}
if (!packageName) {
return response.status(400).json({message: 'No package name has been specified.'});
}
if (packageDiff === 0) {
return response.status(400).json({message: `The difference equals zero. Status won't be set.`});
}
// Better message about the diff that shows whether the payload increased or decreased.
const diffMessage = packageDiff < 0 ? 'decrease' : 'increase';
const diffFormatted = Math.abs(packageDiff).toFixed(2);
const packageSizeFormatted = packageSize.toFixed(2);
try {
await setGithubStatus(commitSha, {
result: true,
name: `${capitalizeFirstLetter(packageName)} Payload Size`,
description: `${packageSizeFormatted}kb / ${diffFormatted}kb ${diffMessage} (ES2015 bundle)`
});
response.json({message: 'Payload Github status successfully set.'});
} catch (e) {
response.json({message: 'An error occurred while setting the GitHub status.'});
console.error('An error occurred while setting Github status.', e);
}
});
/** Capitalizes the first letter of a string. */
function capitalizeFirstLetter(word: string) {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}
-20
View File
@@ -1,20 +0,0 @@
{
"compilerOptions": {
"lib": ["es2015", "es2016", "dom"],
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"sourceMap": true,
"target": "es5",
"baseUrl": "",
"outDir": "../../../dist/dashboard-functions/",
"types": ["jsonwebtoken"]
},
"files": [
"./functions.ts"
]
}
-39
View File
@@ -1,39 +0,0 @@
{
"name": "material2-board",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build-production": "ng build --aot --prod"
},
"private": true,
"dependencies": {
"@angular/animations": "^7.0.0",
"@angular/cdk": "^7.0.0",
"@angular/common": "^7.0.0",
"@angular/compiler": "^7.0.0",
"@angular/core": "^7.0.0",
"@angular/fire": "^5.0.2",
"@angular/forms": "^7.0.0",
"@angular/material": "^7.0.0",
"@angular/platform-browser": "^7.0.0",
"@angular/platform-browser-dynamic": "^7.0.0",
"@angular/router": "^7.0.0",
"@swimlane/ngx-charts": "^9.0.0",
"core-js": "^2.5.7",
"d3": "^5.7.0",
"firebase": "^5.4.2",
"rxjs": "^6.3.1",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.10.2",
"@angular/cli": "^7.0.0",
"@angular/compiler-cli": "^7.0.0",
"@angular/language-service": "^7.0.0",
"@types/node": "^10.9.4",
"typescript": "^3.1.1"
}
}
@@ -1,21 +0,0 @@
<ngx-charts-line-chart
*ngIf="chartData.length"
[scheme]="colorScheme"
[results]="chartData"
[gradient]="false"
[xAxis]="true"
[yAxis]="true"
[legend]="true"
[showXAxisLabel]="true"
[showYAxisLabel]="true"
[showGridLines]="true"
[timeline]="true"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
[autoScale]="true">
</ngx-charts-line-chart>
<mat-spinner *ngIf="!chartData.length" class="dashboard-panel-loading-indicator"
[diameter]="50"
[strokeWidth]="5">
</mat-spinner>
@@ -1,96 +0,0 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {CoverageEntries, CoverageResult} from '../data-definitions';
import {NgxChartResult} from '../ngx-definitions';
@Component({
selector: 'coverage-chart',
templateUrl: './coverage-chart.html',
styleUrls: ['./coverage-chart.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'class': 'dashboard-panel'
}
})
export class CoverageChart {
/** Color scheme for the coverage graph. */
colorScheme = {
domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
};
/** X-Axis label of the Chart. */
xAxisLabel = 'Date';
/** Y-Axis label of the Chart. */
yAxisLabel = 'Coverage Percentage (%)';
/** Chart data that is taken by NGX-Charts. */
chartData: NgxChartResult[];
/** Coverage data that will be rendered in the chart. */
@Input()
set data(value: CoverageResult[]) {
this.chartData = this.createChartResults(value);
}
/** Remove duplicate coverage results for similar days. */
private filterDuplicateDays(data: CoverageResult[]) {
const filteredData = new Map<string, CoverageResult>();
data.forEach(result => {
// Parse the timestamp from the coverage result as a date.
const date = new Date(result.timestamp);
// Ignore hours, minutes, seconds and milliseconds from the date to allow comparisons
// only of the day.
date.setHours(0, 0, 0, 0);
// Store the ISO string of the date in a Map to overwrite the previous coverage result for
// the same day.
filteredData.set(date.toISOString(), result);
});
return Array.from(filteredData.values());
}
/** Creates a list of ngx-chart results of the coverage results. */
private createChartResults(data: CoverageResult[]) {
if (!data) {
return [];
}
// In the early stages of the coverage reports that will be uploaded to Firebase, the timestamp
// was not added to the reports. All old results need to be filtered out for the chart.
data = data.filter(result => result.timestamp);
// Data snapshot from Firebase is not ordered by timestamp. Before rendering the graph
// manually sort the results by their timestamp.
data = data.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1);
// It can happen that there will be multiple coverage reports for the same day. This happens
// if multiple PRs are merged at the same day. For the charts we only want to
// have the last coverage report of a day (for performance and value correctness)
data = this.filterDuplicateDays(data);
return [
{ name: 'Lines', series: this.createCoverageChartItems(data, 'lines') },
{ name: 'Branches', series: this.createCoverageChartItems(data, 'branches') },
{ name: 'Functions', series: this.createCoverageChartItems(data, 'functions') },
{ name: 'Statements', series: this.createCoverageChartItems(data, 'statements') }
];
}
/** Creates a list of items for NGX-Charts that represent a coverage entry type. */
private createCoverageChartItems(data: CoverageResult[], entryType: CoverageEntries) {
return data.map(result => {
const entryResult = result[entryType];
// Convert the timestamp of the coverage result into a date because NGX-Charts can group
// dates in the x-axis.
const date = new Date(result.timestamp);
return { name: date, value: entryResult.pct };
});
}
}
@@ -1,21 +0,0 @@
<mat-toolbar color="primary">
<span>Angular Material - Board</span>
</mat-toolbar>
<div class="dashboard-content">
<mat-card class="dashboard-panel-card">
<mat-card-title>Payload Size Chart</mat-card-title>
<mat-card-content>
<payload-chart [data]="payloads | async"></payload-chart>
</mat-card-content>
</mat-card>
<mat-card class="dashboard-panel-card">
<mat-card-title>Coverage Reports</mat-card-title>
<mat-card-content>
<coverage-chart [data]="coverage | async"></coverage-chart>
</mat-card-content>
</mat-card>
</div>
@@ -1,11 +0,0 @@
.dashboard-content {
display: flex;
flex-flow: wrap;
padding: 16px;
}
.dashboard-panel-card {
width: 100%;
max-width: 700px;
margin: 12px;
}
-24
View File
@@ -1,24 +0,0 @@
import {Component} from '@angular/core';
import {AngularFireDatabase} from '@angular/fire/database';
import {Observable} from 'rxjs';
import {CoverageResult, PayloadResult} from './data-definitions';
@Component({
selector: 'dashboard-app',
templateUrl: './dashboard-app.html',
styleUrls: ['./dashboard-app.scss']
})
export class DashboardApp {
/** Observable that emits all payload results from Firebase. */
payloads: Observable<PayloadResult[]>;
/** Observable that emits all coverage reports from Firebase. */
coverage: Observable<CoverageResult[]>;
constructor(database: AngularFireDatabase) {
this.payloads = database.list<PayloadResult>('payloads').valueChanges();
this.coverage = database.list<CoverageResult>('coverage-reports').valueChanges();
}
}
@@ -1,39 +0,0 @@
import {NgModule} from '@angular/core';
import {MatCardModule, MatProgressSpinnerModule, MatToolbarModule} from '@angular/material';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NgxChartsModule} from '@swimlane/ngx-charts';
import {AngularFireModule} from '@angular/fire';
import {AngularFireDatabaseModule} from '@angular/fire/database';
import {environment} from '../environments/environment';
import {CoverageChart} from './coverage-chart/coverage-chart';
import {DashboardApp} from './dashboard-app';
import {PayloadChart} from './payload-chart/payload-chart';
@NgModule({
exports: [
MatCardModule,
MatToolbarModule,
MatProgressSpinnerModule
]
})
export class DashboardMaterialModule {}
@NgModule({
declarations: [
DashboardApp,
PayloadChart,
CoverageChart
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFireDatabaseModule,
DashboardMaterialModule,
NgxChartsModule
],
providers: [],
bootstrap: [DashboardApp]
})
export class DashboardModule {}
@@ -1,34 +0,0 @@
/** Interface that describes the payload results from the Firebase database. */
export interface PayloadResult {
timestamp: number;
// Material bundles
material_umd: number;
material_umd_minified_uglify: number;
material_fesm_2015: number;
material_fesm_2014: number;
// CDK bundles
cdk_umd: number;
cdk_umd_minified_uglify: number;
cdk_fesm_2015: number;
cdk_fesm_2014: number;
}
/** Type that specifies the available coverage entries. */
export type CoverageEntries = 'branches' | 'functions' | 'lines' | 'statements';
/** Interface that describes the coverage reports in Firebase. */
export interface CoverageResult {
branches: CoverageDataEntry;
functions: CoverageDataEntry;
lines: CoverageDataEntry;
statements: CoverageDataEntry;
timestamp: number;
}
/** Interface that describes data entries for different coverage types. */
export interface CoverageDataEntry {
covered: number;
pct: number;
skipped: number;
total: number;
}
@@ -1,9 +0,0 @@
export interface NgxChartResult {
name: string;
series: NgxChartItem[];
}
export interface NgxChartItem {
name: Date;
value: number|string;
}
@@ -1,21 +0,0 @@
<ngx-charts-line-chart
*ngIf="chartData.length"
[scheme]="colorScheme"
[results]="chartData"
[gradient]="false"
[xAxis]="true"
[yAxis]="true"
[legend]="true"
[showXAxisLabel]="true"
[showYAxisLabel]="true"
[showGridLines]="true"
[timeline]="true"
[xAxisLabel]="xAxisLabel"
[yAxisLabel]="yAxisLabel"
[autoScale]="true">
</ngx-charts-line-chart>
<mat-spinner *ngIf="!chartData.length" class="dashboard-panel-loading-indicator"
[diameter]="50"
[strokeWidth]="5">
</mat-spinner>
@@ -1,96 +0,0 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {PayloadResult} from '../data-definitions';
import {NgxChartItem, NgxChartResult} from '../ngx-definitions';
@Component({
selector: 'payload-chart',
templateUrl: './payload-chart.html',
styleUrls: ['./payload-chart.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'class': 'dashboard-panel'
}
})
export class PayloadChart {
/** Color scheme for the payload graph. */
colorScheme = {
domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
};
/** X-Axis label of the Chart. */
xAxisLabel = 'Date';
/** Y-Axis label of the Chart. */
yAxisLabel = 'Payload Size (kb)';
/** Chart data that is taken by NGX-Charts. */
chartData: NgxChartResult[];
/** Payload data that will be rendered in the chart. */
@Input()
set data(value: PayloadResult[]) {
this.chartData = this.createChartResults(value);
}
/** Remove duplicate payload results for similar days. */
private filterDuplicateDays(data: PayloadResult[]) {
const filteredData = new Map<string, PayloadResult>();
data.forEach(result => {
// Parse the timestamp from the payload results as a date.
const date = new Date(result.timestamp);
// Ignore hours, minutes, seconds and milliseconds from the date to allow comparisons
// only of the day.
date.setHours(0, 0, 0, 0);
// Store the ISO string of the date in a Map to overwrite the previous payload result for
// the same day.
filteredData.set(date.toISOString(), result);
});
return Array.from(filteredData.values());
}
/** Creates a list of ngx-chart results of the Payload results. */
private createChartResults(data: PayloadResult[]) {
if (!data) {
return [];
}
// Data snapshot from Firebase is not ordered by timestamp. Before rendering the graph
// manually sort the results by their timestamp.
data = data.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1);
// It can happen that there will be multiple payload results for the same day. This happens
// when multiple Pull Requests are merged in the same day. For the charts we only want to
// have the last payload result of a day (for performance and value correctness)
data = this.filterDuplicateDays(data);
const materialChartItems: NgxChartItem[] = [];
const cdkChartItems: NgxChartItem[] = [];
// Walk through every result entry and create a NgxChart item that can be rendered inside of
// the linear chart.
data.forEach(result => {
if (!result.material_fesm_2015 || !result.cdk_fesm_2015) {
return;
}
// Convert the timestamp of the payload result into a date because NGX-Charts can group
// dates in the x-axis.
const date = new Date(result.timestamp);
materialChartItems.push({ name: date, value: result.material_fesm_2015 });
cdkChartItems.push({ name: date, value: result.cdk_fesm_2015 });
});
return [
{ name: 'Material', series: materialChartItems },
{ name: 'CDK', series: cdkChartItems }
];
}
}
View File
@@ -1,6 +0,0 @@
import {firebaseConfig} from './firebase';
export const environment = {
production: true,
firebase: firebaseConfig
};
@@ -1,6 +0,0 @@
import {firebaseConfig} from './firebase';
export const environment = {
production: false,
firebase: firebaseConfig
};
@@ -1,7 +0,0 @@
/** Shared Firebase configuration that will be used in every environment. */
export const firebaseConfig = {
apiKey: 'AIzaSyCbDnV4udMgMciQ3Sva88Ji1udOIPltDAA',
authDomain: 'material2-board.firebaseapp.com',
databaseURL: 'https://material2-board.firebaseio.com',
projectId: 'material2-board'
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

-14
View File
@@ -1,14 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular Material - Board</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<dashboard-app></dashboard-app>
</body>
</html>
-11
View File
@@ -1,11 +0,0 @@
import {enableProdMode} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {environment} from './environments/environment';
import {DashboardModule} from './app/dashboard-module';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(DashboardModule);
-3
View File
@@ -1,3 +0,0 @@
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
-26
View File
@@ -1,26 +0,0 @@
$panel-loading-indicator-diameter: 50px;
body, html {
margin: 0;
font-family: Roboto, Arial, sans-serif;
min-height: 100vh;
}
.dashboard-panel-loading-indicator {
position: absolute;
top: calc(50% - #{$panel-loading-indicator-diameter});
left: calc(50% - #{$panel-loading-indicator-diameter});
height: $panel-loading-indicator-diameter;
width: $panel-loading-indicator-diameter;
}
.dashboard-panel {
display: block;
position: relative;
height: 400px;
width: 100%;
max-width: 700px;
}
-18
View File
@@ -1,18 +0,0 @@
@import '~@angular/material/_theming';
$dashboard-primary: mat-palette($mat-blue);
$dashboard-accent: mat-palette($mat-grey);
$dashboard-light-theme: mat-light-theme($dashboard-primary, $dashboard-accent);
@include mat-core();
// Only include the theme of the components that are used.
@include mat-toolbar-theme($dashboard-light-theme);
@include mat-progress-spinner-theme($dashboard-light-theme);
@include mat-card-theme($dashboard-light-theme);
body {
// As a custom background for the dashboard we want to use hue 200 of the grey palette.
// Setting the body style manually is easier than overwriting the whole $background variables.
background-color: map_get($mat-grey, 200);
}
-17
View File
@@ -1,17 +0,0 @@
{
"compilerOptions": {
"outDir": "../dist",
"baseUrl": ".",
"lib": ["es2016", "dom"],
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"target": "es5",
"module": "es2015",
"typeRoots": ["node_modules/@types"],
"types": []
}
}
-5
View File
@@ -1,5 +0,0 @@
/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
id: string;
}
File diff suppressed because it is too large Load Diff
-2
View File
@@ -20,7 +20,6 @@ import './tasks/breaking-changes';
import './tasks/changelog';
import './tasks/ci';
import './tasks/clean';
import './tasks/coverage';
import './tasks/default';
import './tasks/development';
import './tasks/docs';
@@ -28,7 +27,6 @@ import './tasks/e2e';
import './tasks/example-module';
import './tasks/lint';
import './tasks/material-release';
import './tasks/payload';
import './tasks/unit-test';
import './tasks/universal';
-6
View File
@@ -15,12 +15,6 @@ task('ci:e2e', ['e2e']);
*/
task('ci:aot', ['build-aot:no-release-build']);
/** Task which reports the size of the library and stores it in a database. */
task('ci:payload', ['payload']);
/** Task that uploads the coverage results to a firebase database. */
task('ci:coverage', ['coverage:upload']);
/** Task that verifies if all Material components are working with platform-server. */
task('ci:prerender', ['prerender']);
-42
View File
@@ -1,42 +0,0 @@
import {task} from 'gulp';
import {join} from 'path';
import {existsSync} from 'fs-extra';
import {spawnSync} from 'child_process';
import {isTravisMasterBuild} from '../util/travis-ci';
import {openFirebaseDashboardApp} from '../util/firebase';
import {buildConfig} from 'material2-build-tools';
/** Path to the file that includes all coverage information form Karma. */
const coverageResultFile = join(buildConfig.outputDir, 'coverage/coverage-summary.json');
task('coverage:upload', () => {
if (!existsSync(coverageResultFile)) {
throw new Error('No coverage file has been found!');
}
if (!isTravisMasterBuild()) {
throw new Error('Coverage results will be only uploaded inside of Travis Push builds.');
}
const results = require(coverageResultFile)['total'];
// Add a timestamp to the coverage result that will be uploaded to Firebase.
// This is necessary for visual representation which should be displayed chronologically.
results.timestamp = Date.now();
// To reduce database payload, the covered lines won't be pushed to the Firebase database.
delete results['linesCovered'];
return uploadResults(results);
});
/** Uploads the coverage results to the firebase database. */
function uploadResults(results: any): Promise<void> {
const latestSha = spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trim();
const dashboardApp = openFirebaseDashboardApp();
const database = dashboardApp.database();
return database.ref('coverage-reports').child(latestSha).set(results)
.catch((err: any) => console.error(err))
.then(() => dashboardApp.delete());
}
+3 -6
View File
@@ -5,11 +5,8 @@ import {IMinimatch, Minimatch} from 'minimatch';
import {join} from 'path';
import {execNodeTask} from '../util/task_helpers';
/** Globs that matchall SCSS or CSS files that should be linted. */
const styleGlobs = [
'src/**/*.+(css|scss)',
'tools/dashboard/src/*.+(css|scss)',
];
/** Glob that matches all SCSS or CSS files that should be linted. */
const styleGlob = 'src/**/*.+(css|scss)';
/** List of flags that will passed to the different TSLint tasks. */
const tsLintBaseFlags = ['-c', 'tslint.json', '--project', './tsconfig.json'];
@@ -24,7 +21,7 @@ task('lint', ['tslint', 'stylelint', 'ownerslint']);
/** Task to lint Angular Material's scss stylesheets. */
task('stylelint', execNodeTask(
'stylelint', [...styleGlobs, '--config', 'stylelint-config.json', '--syntax', 'scss']
'stylelint', [styleGlob, '--config', 'stylelint-config.json', '--syntax', 'scss']
));
/** Task to run TSLint against the e2e/ and src/ directories. */
-181
View File
@@ -1,181 +0,0 @@
import {task} from 'gulp';
import {join} from 'path';
import {statSync} from 'fs';
import {sync as glob} from 'glob';
import {isTravisBuild, isTravisMasterBuild} from '../util/travis-ci';
import {buildConfig} from 'material2-build-tools';
import {openFirebaseDashboardApp, openFirebaseDashboardAppAsGuest} from '../util/firebase';
import {spawnSync} from 'child_process';
import * as firebaseAdmin from 'firebase-admin';
// These imports lack of type definitions.
const request = require('request');
/** Path to the directory where all bundles are living. */
const bundlesDir = join(buildConfig.outputDir, 'bundles');
/** Task which runs test against the size of material. */
task('payload', ['material:clean-build'], async () => {
const results = {
timestamp: Date.now(),
// Material bundles
material_umd: getBundleSize('material.umd.js'),
material_umd_minified_uglify: getBundleSize('material.umd.min.js'),
material_fesm_2015: getBundleSize('material.js') + getBundleSize('material/!(*.es5).js'),
material_fesm_2014: getBundleSize('material.es5.js') + getBundleSize('material/*.es5.js'),
// CDK bundles
cdk_umd: getBundleSize('cdk*.umd.js'),
cdk_umd_minified_uglify: getBundleSize('cdk*.umd.min.js'),
cdk_fesm_2015: getBundleSize('cdk.js') + getBundleSize('cdk/!(*.es5).js'),
cdk_fesm_2014: getBundleSize('cdk.es5.js') + getBundleSize('cdk/*.es5.js'),
};
// Print the results to the console, so we can read it from the CI.
console.log('Payload Results:', JSON.stringify(results, null, 2));
if (isTravisBuild()) {
// Open a connection to Firebase. For PRs the connection will be established as a guest.
const firebaseApp = isTravisMasterBuild() ?
openFirebaseDashboardApp() :
openFirebaseDashboardAppAsGuest();
const database = firebaseApp.database();
const currentSha = process.env['TRAVIS_PULL_REQUEST_SHA']! || process.env['TRAVIS_COMMIT']!;
// Upload the payload results and calculate the payload diff in parallel. Otherwise the
// payload task will take much more time inside of Travis builds.
await Promise.all([
uploadPayloadResults(database, currentSha, results),
calculatePayloadDiff(database, currentSha, results)
]);
// Disconnect database connection because Firebase otherwise prevents Gulp from exiting.
firebaseApp.delete();
}
});
/** Returns the size of the given library bundle. */
function getBundleSize(bundleName: string) {
return glob(bundleName, {cwd: bundlesDir})
.reduce((sum, fileName) => sum + getFilesize(join(bundlesDir, fileName)), 0);
}
/** Returns the size of a file in kilobytes. */
function getFilesize(filePath: string) {
return statSync(filePath).size / 1000;
}
/**
* Calculates the difference between the last and current library payload.
* The results will be published as a commit status on Github.
*/
async function calculatePayloadDiff(database: firebaseAdmin.database.Database, currentSha: string,
currentPayload: any) {
const authToken = process.env['FIREBASE_ACCESS_TOKEN']!;
if (!authToken) {
console.error('Cannot calculate Payload diff because there is no "FIREBASE_ACCESS_TOKEN" ' +
'environment variable set.');
return;
}
const previousSha = getCommitFromPreviousPayloadUpload();
const previousPayload = await getPayloadResults(database, previousSha);
if (!previousPayload) {
console.warn('There are no previous payload results uploaded. Cannot calculate payload ' +
'difference for this job');
return;
}
console.log(`Comparing payload against payload results from SHA ${previousSha}`);
// Calculate the payload diffs by subtracting the previous size of the FESM ES2015 bundles.
const cdkFullSize = currentPayload.cdk_fesm_2015;
const cdkDiff = roundFileSize(cdkFullSize - previousPayload.cdk_fesm_2015);
const materialFullSize = currentPayload.material_fesm_2015;
const materialDiff = roundFileSize(materialFullSize - previousPayload.material_fesm_2015);
// Set the Github statuses for the packages by sending a HTTP request to the dashboard functions.
await Promise.all([
updateGithubStatus(currentSha, 'material', materialDiff, materialFullSize, authToken),
updateGithubStatus(currentSha, 'cdk', cdkDiff, cdkFullSize, authToken)
]);
}
/**
* Updates the Github status of a given commit by sending a request to a Firebase function of
* the dashboard. The function has access to the Github repository and can set status for PRs too.
*/
async function updateGithubStatus(commitSha: string, packageName: string, packageDiff: number,
packageFullSize: number, authToken: string) {
const options = {
url: 'https://us-central1-material2-board.cloudfunctions.net/payloadGithubStatus',
headers: {
'User-Agent': 'Material2/PayloadTask',
'auth-token': authToken,
'commit-sha': commitSha,
'package-name': packageName,
'package-full-size': packageFullSize,
'package-size-diff': packageDiff
}
};
return new Promise((resolve, reject) => {
request(options, (err: any, response: any, body: string) => {
if (err) {
reject(`Dashboard Error ${err.toString()}`);
} else {
console.info(`Dashboard Response (${response.statusCode}): ${body}`);
resolve(response.statusCode);
}
});
});
}
/** Uploads the current payload results to the Dashboard database. */
async function uploadPayloadResults(database: firebaseAdmin.database.Database, currentSha: string,
currentPayload: any) {
if (isTravisMasterBuild()) {
await database.ref('payloads').child(currentSha).set(currentPayload);
}
}
/** Gets payload results of the specified commit sha. */
async function getPayloadResults(database: firebaseAdmin.database.Database, commitSha: string) {
const snapshot = await database.ref('payloads').child(commitSha).once('value');
if (snapshot.exists()) {
return snapshot.val();
} else {
console.error(`There is no payload data uploaded for SHA ${commitSha}`);
}
}
/** Gets the SHA of the commit where the payload was uploaded before this Travis Job started. */
function getCommitFromPreviousPayloadUpload(): string {
if (isTravisMasterBuild()) {
const commitRange = process.env['TRAVIS_COMMIT_RANGE']!;
// In some situations, Travis will include multiple commits in a single Travis Job. This means
// that we can't just resolve the previous commit by using the parent commit of HEAD.
// By resolving the amount of commits inside of the current Travis Job, we can figure out
// how many commits before HEAD the last Travis Job ran.
const commitCount = spawnSync('git', ['rev-list', '--count', commitRange]).stdout
.toString().trim();
// With the amount of commits inside of the current Travis Job, we can query Git to print
// the SHA of the commit that ran before this Travis Job was created.
return spawnSync('git', ['rev-parse', `HEAD~${commitCount}`]).stdout.toString().trim();
} else {
// Travis applies the changes of Pull Requests in new branches. This means that resolving
// the commit that previously ran on the target branch (mostly "master") can be done
// by just loading the SHA of the most recent commit in the target branch.
return spawnSync('git', ['rev-parse', process.env['TRAVIS_BRANCH']!]).stdout.toString().trim();
}
}
/** Rounds the specified file size to two decimal places. */
function roundFileSize(fileSize: number) {
return Math.round(fileSize * 100) / 100;
}
-36
View File
@@ -1,36 +0,0 @@
import {initializeApp} from 'firebase';
// This import lacks type definitions.
const firebaseAdmin = require('firebase-admin');
/** Database URL of the dashboard firebase project. */
const dashboardDatabaseUrl = 'https://material2-board.firebaseio.com';
/** Opens a connection to the Firebase dashboard app using a service account. */
export function openFirebaseDashboardApp() {
// Initialize the Firebase application with firebaseAdmin credentials.
// Credentials need to be for a Service Account, which can be created in the Firebase console.
return firebaseAdmin.initializeApp({
databaseURL: dashboardDatabaseUrl,
credential: firebaseAdmin.credential.cert({
project_id: 'material2-board',
client_email: 'material2-board@appspot.gserviceaccount.com',
// In Travis CI the private key will be incorrect because the line-breaks are escaped.
// The line-breaks need to persist in the service account private key.
private_key: decode(process.env['MATERIAL2_BOARD_FIREBASE_SERVICE_KEY']!)
}),
});
}
/** Opens a connection to the Firebase dashboard app with no authentication. */
export function openFirebaseDashboardAppAsGuest() {
return initializeApp({ databaseURL: dashboardDatabaseUrl });
}
/** Decodes a Travis CI variable that is public in favor for PRs. */
export function decode(str: string): string {
// In Travis CI the private key will be incorrect because the line-breaks are escaped.
// The line-breaks need to persist in the service account private key.
return (str || '').replace(/\\n/g, '\n');
}
+1 -2
View File
@@ -29,7 +29,6 @@
// IDEs should not type-check the different node_modules directories of the different packages.
// This would cause the IDEs to be slower and also linters would check the node_modules.
"node_modules/",
"tools/dashboard/node_modules/"
"node_modules/"
]
}
+25 -106
View File
@@ -1021,11 +1021,6 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
accepts@~1.3.4, accepts@~1.3.5:
version "1.3.5"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
@@ -1473,11 +1468,6 @@ async-limiter@~1.0.0:
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==
async@1.x, async@^1.3.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
async@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.0.1.tgz#b709cc0280a9c36f09f4536be823c838a9049e25"
@@ -1492,6 +1482,11 @@ async@2.6.0:
dependencies:
lodash "^4.14.0"
async@^1.3.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
async@^2.0.0, async@^2.0.1, async@^2.1.2, async@^2.3.0, async@^2.4.0, async@^2.5.0, async@~2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
@@ -3082,7 +3077,7 @@ date-format@^1.2.0:
resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8"
integrity sha1-YV6CjiM90aubua4JUODOzPpuytg=
dateformat@^1.0.11, dateformat@^1.0.6, dateformat@^1.0.7-1.2.3:
dateformat@^1.0.11, dateformat@^1.0.7-1.2.3:
version "1.0.12"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9"
integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=
@@ -3727,18 +3722,6 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escodegen@1.8.x:
version "1.8.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018"
integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=
dependencies:
esprima "^2.7.1"
estraverse "^1.9.1"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.2.0"
escodegen@1.x.x, escodegen@^1.6.1:
version "1.11.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
@@ -3756,11 +3739,6 @@ espree@^2.2.3:
resolved "https://registry.yarnpkg.com/espree/-/espree-2.2.5.tgz#df691b9310889402aeb29cc066708c56690b854b"
integrity sha1-32kbkxCIlAKuspzAZnCMVmkLhUs=
esprima@2.7.x, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
esprima@3.x.x, esprima@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@@ -3778,11 +3756,6 @@ espurify@^1.6.0:
dependencies:
core-js "^2.0.0"
estraverse@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=
estraverse@^4.1.0, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
@@ -4761,17 +4734,6 @@ glob@^4.3.1:
minimatch "^2.0.1"
once "^1.3.0"
glob@^5.0.15:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=
dependencies:
inflight "^1.0.4"
inherits "2"
minimatch "2 || 3"
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
@@ -5363,7 +5325,7 @@ hammerjs@^2.0.8:
resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=
handlebars@^4.0.1, handlebars@^4.0.2:
handlebars@^4.0.2:
version "4.0.12"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==
@@ -6396,26 +6358,6 @@ isstream@0.1.x, isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
istanbul@^0.4.0:
version "0.4.5"
resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b"
integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=
dependencies:
abbrev "1.0.x"
async "1.x"
escodegen "1.8.x"
esprima "2.7.x"
glob "^5.0.15"
handlebars "^4.0.1"
js-yaml "3.x"
mkdirp "0.5.x"
nopt "3.x"
once "1.x"
resolve "1.1.x"
supports-color "^3.1.0"
which "^1.1.1"
wordwrap "^1.0.0"
isurl@^1.0.0-alpha5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
@@ -6484,7 +6426,7 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@3.x, js-yaml@^3.7.0, js-yaml@^3.9.0:
js-yaml@^3.7.0, js-yaml@^3.9.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==
@@ -6697,17 +6639,6 @@ karma-chrome-launcher@2.2.0, karma-chrome-launcher@^2.2.0:
fs-access "^1.0.0"
which "^1.2.1"
karma-coverage@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.2.tgz#cc09dceb589a83101aca5fe70c287645ef387689"
integrity sha512-eQawj4Cl3z/CjxslYy9ariU4uDh7cCNFZHNWXWRpl0pNeblY/4wHR7M7boTYXWrn9bY0z2pZmr11eKje/S/hIw==
dependencies:
dateformat "^1.0.6"
istanbul "^0.4.0"
lodash "^4.17.0"
minimatch "^3.0.0"
source-map "^0.5.1"
karma-firefox-launcher@1.1.0, karma-firefox-launcher@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz#2c47030452f04531eb7d13d4fc7669630bb93339"
@@ -7300,7 +7231,7 @@ lodash@^3.10.1:
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=
lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.8.0, lodash@~4.17.10:
lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.8.0, lodash@~4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
@@ -7741,13 +7672,6 @@ mimic-response@^1.0.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimatch@^2.0.1:
version "2.0.10"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7"
@@ -7755,6 +7679,13 @@ minimatch@^2.0.1:
dependencies:
brace-expansion "^1.0.0"
minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimatch@~0.2.11:
version "0.2.14"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a"
@@ -8136,7 +8067,7 @@ nodemailer@^2.5.0:
nodemailer-smtp-transport "2.7.2"
socks "1.1.9"
"nopt@2 || 3", nopt@3.x:
"nopt@2 || 3":
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
@@ -8350,7 +8281,7 @@ on-headers@^1.0.0, on-headers@~1.0.1:
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=
once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0:
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
@@ -9892,11 +9823,6 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2, resolve@^1.4.0:
version "1.8.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
@@ -10616,13 +10542,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
source-map@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d"
integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50=
dependencies:
amdefine ">=0.0.4"
sourcemap-codec@^1.3.0, sourcemap-codec@^1.4.1:
version "1.4.3"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.3.tgz#0ba615b73ec35112f63c2f2d9e7c3f87282b0e33"
@@ -11069,7 +10988,7 @@ supports-color@^2.0.0:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
supports-color@^3.1.0, supports-color@^3.2.3:
supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
@@ -12187,7 +12106,7 @@ which-module@^2.0.0:
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
which@1, which@^1.1.1, which@^1.2.1, which@^1.2.14, which@^1.2.9:
which@1, which@^1.2.1, which@^1.2.14, which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -12285,16 +12204,16 @@ wordwrap@0.0.2:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=
wordwrap@^1.0.0, wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"