feat: add cypress schematic and builders for angular (#16449)

* fix: lint fixes. add .eslintrc

* Update eslint config

* fix: remove jasmine. add mocha/chai. remove builder test for now. update tsconfig. update package json with main folder. remove package yarn.lock in favor of monorepo

* fix: restore filenames

* fix: add eslint ignore for compiled files

* fix: move eslintignore to root. add circle commands for schematic.

* fix: upate file pattern for ignore

* fix: add angular/cli to build step

* fix: update working dir

* fix: add eslint

* fix: update polocy version to avoid eslint issue
This commit is contained in:
Adam Murray
2021-05-11 20:04:12 -05:00
committed by GitHub
parent 811c7e3607
commit cac3685cac
61 changed files with 12032 additions and 15 deletions
+2
View File
@@ -32,6 +32,8 @@ packages/server/test/support/fixtures/projects/e2e/cypress/integration/typescrip
npm/webpack-preprocessor/cypress/tests/e2e/compile-error.js
npm/webpack-preprocessor/examples/use-babelrc/cypress/integration/spec.js
npm/cypress-schematic/src/**/*.js
**/.projects
**/*.d.ts
**/package-lock.json
+25
View File
@@ -1375,6 +1375,27 @@ jobs:
name: Run tests
command: yarn workspace @cypress/eslint-plugin-dev test
npm-cypress-schematic:
<<: *defaults
steps:
- restore_cached_workspace
- run:
name: Build
command: yarn workspace @cypress/schematic build:all
- run:
name: Install @angular/cli
command: yarn policies set-version 1.19.0 && yarn add --dev @angular/cli
working_directory: npm/cypress-schematic
- run:
name: Launch
command: yarn launch:test
working_directory: npm/cypress-schematic
- run:
name: Test Schematics
command: yarn test:schematics
working_directory: npm/cypress-schematic
- store-npm-logs
npm-release:
<<: *defaults
steps:
@@ -1960,6 +1981,9 @@ linux-workflow: &linux-workflow
- npm-eslint-plugin-dev:
requires:
- build
- npm-cypress-schematic:
requires:
- build
# This release definition must be updated with any new jobs
# Any attempts to automate this are welcome
# If CircleCI provided an "after all" hook, then this wouldn't be necessary
@@ -1976,6 +2000,7 @@ linux-workflow: &linux-workflow
- npm-webpack-preprocessor
# - npm-vite-dev-server
- npm-webpack-dev-server
- npm-cypress-schematic
- ui-components-integration-tests
- reporter-integration-tests
- Linux lint
+16
View File
@@ -0,0 +1,16 @@
{
"plugins": [
"cypress",
"@cypress/dev"
],
"extends": [
"plugin:@cypress/dev/general"
],
"parser": "@typescript-eslint/parser",
"env": {
"cypress/globals": true
},
"rules": {
"no-console": "off"
}
}
+21
View File
@@ -0,0 +1,21 @@
# Outputs
src/**/*.js
src/**/*.js.map
src/**/*.d.ts
# IDEs
.idea/
jsconfig.json
.vscode/
# Misc
node_modules/
npm-debug.log*
yarn-error.log*
# Mac OSX Finder files.
**/.DS_Store
.DS_Store
# Exclude Cypress Plugin JS files
!src/schematics/cypress/files/cypress/plugins/*.js
+9
View File
@@ -0,0 +1,9 @@
# Ignores TypeScript files, but keeps definitions.
*.ts
!*.d.ts
*.js.map
!src/**/__files__/**/*.js
!src/**/files/**/*.ts
*.vscode
sandbox
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
...require('../../.releaserc.base'),
branches: [
{ name: 'next/npm/cypress-schematic', channel: 'next', prerelease: 'alpha' },
],
}
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2021 Cypress.io
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.
+88
View File
@@ -0,0 +1,88 @@
# Cypress Angular Schematic
<p align="center">
<a href="https://cypress.io">
<img width="140" alt="Cypress Logo" src="./src/svgs/built-by-cypress.svg" />
</a>
</p>
Add [Cypress](https://cypress.io) to an Angular CLI project
This schematic will:
- install Cypress, its dependencies, and new scripts
- add necessary files for Cypress to work with Angular & Typescript
- prompt for removal of Protractor files and configuration
## Usage 🚀
Run as one command in an Angular CLI app directory. Note this will add the schematic as a dependency to your project.
```shell
ng add @cypress/schematic
```
With the custom builder installed, you can run cypress with the following commands:
```shell script
ng e2e
```
```shell script
ng run {your-project-name}:cypress-open
```
These two commands do the same thing. They will launch the (Electron) Cypress Test Runner in watch mode.
```shell script
ng run {your-project-name}:cypress-run
```
This command will open the (Electron) Cypress Test Runner and run your tests one time, with output to your terminal.
## Issues
Issues with this schematic can be filed [here](https://github.com/cypress-io/cypress-schematics/issues/new/choose).
## Getting started
### Prerequisites
The only requirement for this project is [Node.js](https://nodejs.org/en/).
TypeScript will be added as a local dependency to the project, so no need to install it.
### Installation
⬇ **Install** the dependencies for the schematic and the sandbox application
```shell
yarn install && cd sandbox && yarn install && cd ..
```
🖇 **Link** the schematic in the sandbox to run locally
```shell
yarn link:sandbox
```
🏃 **Run** the schematic
```shell
yarn build:clean:launch
```
### Testing
To test locally, use the `schematics` command line tool that is included locally. That tool acts the same as the `generate` command of the Angular CLI, but also has a debug mode.
Check the documentation with
```bash
schematics --help
```
### Unit & Integration Testing
`yarn test:schematics` will run the schematic unit tests, using Jasmine as a runner and test framework.
`yarn test:builders` will run the builder integration tests, linking the schematic, and installing Cypress into the sandbox to run the included Cypress spec.
+56
View File
@@ -0,0 +1,56 @@
{
"name": "@cypress/schematic",
"version": "0.0.0-development",
"description": "Official Cypress schematic for the Angular CLI",
"private": false,
"main": "./src",
"scripts": {
"build": "tsc -p tsconfig.json",
"build:test": "tsc -p tsconfig.spec.json",
"build:all": "tsc -p tsconfig.json && tsc -p tsconfig.spec.json",
"build:watch": "tsc -p tsconfig.json --watch",
"clean": "git checkout HEAD -- sandbox && git clean -f -d sandbox",
"launch": "yarn link:sandbox && cd sandbox && ng add @cypress/schematic && cd ..",
"launch:test": "yarn link:sandbox && cd sandbox && ng add @cypress/schematic --removeProtractor=false && cd ..",
"link:sandbox": "yarn link && cd sandbox && yarn link @cypress/schematic",
"test:all": "yarn build:all && yarn test:schematics && yarn launch:test && yarn test:builders",
"test:schematics": "mocha src/schematics/cypress/*_spec.js",
"test:e2e:sandbox": "cd sandbox && ng run sandbox:cypress-run",
"unlink:sandbox": "cd sandbox && yarn unlink @cypress/schematic && cd .. && yarn unlink"
},
"dependencies": {
"@angular-devkit/architect": "^0.1001.0",
"@angular-devkit/core": "^10.1.0",
"@angular-devkit/schematics": "^10.1.0",
"@schematics/angular": "^10.1.0",
"jsonc-parser": "^3.0.0",
"rxjs": "6.6.2"
},
"devDependencies": {
"@angular-devkit/schematics-cli": "^0.1102.12",
"@types/chai-enzyme": "0.6.7",
"@types/mocha": "8.0.3",
"@types/node": "^12.11.1",
"chai": "4.2.0",
"cypress": "0.0.0-development",
"mocha": "3.5.3",
"semantic-release": "^17.4.2",
"typescript": "~4.2.4"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/cypress-io/cypress.git"
},
"homepage": "https://github.com/cypress-io/cypress/tree/master/npm/cypress-schematic#readme",
"author": "Cypress DX Team",
"bugs": "https://github.com/cypress-io/cypress/issues/new?assignees=&labels=npm%3A%20%40cypress%2Fcypress-schematics&template=1-bug-report.md&title=",
"keywords": [
"schematics",
"cypress",
"e2e",
"testing"
],
"builders": "./src/builders/builders.json",
"schematics": "./src/schematics/collection.json"
}
@@ -0,0 +1,17 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# For the full list of supported browsers by the Angular framework, please see:
# https://angular.io/guide/browser-support
# You can see what browsers were selected by your queries by running:
# npx browserslist
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major versions
last 2 iOS major versions
Firefox ESR
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
[*.md]
max_line_length = off
trim_trailing_whitespace = false
+46
View File
@@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
+27
View File
@@ -0,0 +1,27 @@
# Sandbox
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 11.2.11.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
+124
View File
@@ -0,0 +1,124 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sandbox": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/sandbox",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sandbox:build"
},
"configurations": {
"production": {
"browserTarget": "sandbox:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sandbox:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "sandbox:serve"
},
"configurations": {
"production": {
"devServerTarget": "sandbox:serve:production"
}
}
}
}
}
},
"defaultProject": "sandbox"
}
@@ -0,0 +1,39 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter')
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts',
],
capabilities: {
browserName: 'chrome',
},
directConnect: true,
SELENIUM_PROMISE_MANAGER: false,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print () {},
},
onPrepare () {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json'),
})
// eslint-disable-next-line no-undef
jasmine.getEnv().addReporter(new SpecReporter({
spec: {
displayStacktrace: StacktraceOption.PRETTY,
},
}))
},
}
@@ -0,0 +1,24 @@
import { browser, logging } from 'protractor'
import { AppPage } from './app.po'
describe('workspace-project App', () => {
let page: AppPage
beforeEach(() => {
page = new AppPage()
})
it('should display welcome message', async () => {
await page.navigateTo()
expect(await page.getTitleText()).toEqual('sandbox app is running!')
})
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER)
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry))
})
})
@@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor'
export class AppPage {
async navigateTo (): Promise<unknown> {
return browser.get(browser.baseUrl)
}
async getTitleText (): Promise<string> {
return element(by.css('app-root .content span')).getText()
}
}
@@ -0,0 +1,13 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es2018",
"types": [
"jasmine",
"node"
]
}
}
@@ -0,0 +1,44 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma'),
],
client: {
jasmine: {
// you can add configuration options for Jasmine here
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true, // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/sandbox'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' },
],
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true,
})
}
@@ -0,0 +1,45 @@
{
"name": "sandbox",
"version": "0.0.0",
"private": true,
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"dependencies": {
"@angular/animations": "~11.2.12",
"@angular/common": "~11.2.12",
"@angular/compiler": "~11.2.12",
"@angular/core": "~11.2.12",
"@angular/forms": "~11.2.12",
"@angular/platform-browser": "~11.2.12",
"@angular/platform-browser-dynamic": "~11.2.12",
"@angular/router": "~11.2.12",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.11.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.1102.11",
"@angular/cli": "~11.2.11",
"@angular/compiler-cli": "~11.2.12",
"@types/jasmine": "~3.6.0",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~6.1.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.0.3",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.1.5"
}
}
@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
const routes: Routes = []
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule { }
@@ -0,0 +1,520 @@
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content below * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * Delete the template below * * * * * * * * * * -->
<!-- * * * * * * * to get started with your project! * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<style>
:host {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 14px;
color: #333;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 8px 0;
}
p {
margin: 0;
}
.spacer {
flex: 1;
}
.toolbar {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 60px;
display: flex;
align-items: center;
background-color: #1976d2;
color: white;
font-weight: 600;
}
.toolbar img {
margin: 0 16px;
}
.toolbar #twitter-logo {
height: 40px;
margin: 0 8px;
}
.toolbar #youtube-logo {
height: 40px;
margin: 0 16px;
}
.toolbar #twitter-logo:hover,
.toolbar #youtube-logo:hover {
opacity: 0.8;
}
.content {
display: flex;
margin: 82px auto 32px;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;
}
svg.material-icons {
height: 24px;
width: auto;
}
svg.material-icons:not(:last-child) {
margin-right: 8px;
}
.card svg.material-icons path {
fill: #888;
}
.card-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin-top: 16px;
}
.card {
border-radius: 4px;
border: 1px solid #eee;
background-color: #fafafa;
height: 40px;
width: 200px;
margin: 0 8px 16px;
padding: 16px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
transition: all 0.2s ease-in-out;
line-height: 24px;
}
.card-container .card:not(:last-child) {
margin-right: 0;
}
.card.card-small {
height: 16px;
width: 168px;
}
.card-container .card:not(.highlight-card) {
cursor: pointer;
}
.card-container .card:not(.highlight-card):hover {
transform: translateY(-3px);
box-shadow: 0 4px 17px rgba(0, 0, 0, 0.35);
}
.card-container .card:not(.highlight-card):hover .material-icons path {
fill: rgb(105, 103, 103);
}
.card.highlight-card {
background-color: #1976d2;
color: white;
font-weight: 600;
border: none;
width: auto;
min-width: 30%;
position: relative;
}
.card.card.highlight-card span {
margin-left: 60px;
}
svg#rocket {
width: 80px;
position: absolute;
left: -10px;
top: -24px;
}
svg#rocket-smoke {
height: calc(100vh - 95px);
position: absolute;
top: 10px;
right: 180px;
z-index: -10;
}
a,
a:visited,
a:hover {
color: #1976d2;
text-decoration: none;
}
a:hover {
color: #125699;
}
.terminal {
position: relative;
width: 80%;
max-width: 600px;
border-radius: 6px;
padding-top: 45px;
margin-top: 8px;
overflow: hidden;
background-color: rgb(15, 15, 16);
}
.terminal::before {
content: "\2022 \2022 \2022";
position: absolute;
top: 0;
left: 0;
height: 4px;
background: rgb(58, 58, 58);
color: #c2c3c4;
width: 100%;
font-size: 2rem;
line-height: 0;
padding: 14px 0;
text-indent: 4px;
}
.terminal pre {
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
color: white;
padding: 0 1rem 1rem;
margin: 0;
}
.circle-link {
height: 40px;
width: 40px;
border-radius: 40px;
margin: 8px;
background-color: white;
border: 1px solid #eeeeee;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
transition: 1s ease-out;
}
.circle-link:hover {
transform: translateY(-0.25rem);
box-shadow: 0px 3px 15px rgba(0, 0, 0, 0.2);
}
footer {
margin-top: 8px;
display: flex;
align-items: center;
line-height: 20px;
}
footer a {
display: flex;
align-items: center;
}
.github-star-badge {
color: #24292e;
display: flex;
align-items: center;
font-size: 12px;
padding: 3px 10px;
border: 1px solid rgba(27,31,35,.2);
border-radius: 3px;
background-image: linear-gradient(-180deg,#fafbfc,#eff3f6 90%);
margin-left: 4px;
font-weight: 600;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol;
}
.github-star-badge:hover {
background-image: linear-gradient(-180deg,#f0f3f6,#e6ebf1 90%);
border-color: rgba(27,31,35,.35);
background-position: -.5em;
}
.github-star-badge .material-icons {
height: 16px;
width: 16px;
margin-right: 4px;
}
svg#clouds {
position: fixed;
bottom: -160px;
left: -230px;
z-index: -10;
width: 1920px;
}
/* Responsive Styles */
@media screen and (max-width: 767px) {
.card-container > *:not(.circle-link) ,
.terminal {
width: 100%;
}
.card:not(.highlight-card) {
height: 16px;
margin: 8px 0;
}
.card.highlight-card span {
margin-left: 72px;
}
svg#rocket-smoke {
right: 120px;
transform: rotate(-5deg);
}
}
@media screen and (max-width: 575px) {
svg#rocket-smoke {
display: none;
visibility: hidden;
}
}
</style>
<!-- Toolbar -->
<div class="toolbar" role="banner">
<img
width="40"
alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg=="
/>
<span>Welcome</span>
<div class="spacer"></div>
<a aria-label="Angular on twitter" target="_blank" rel="noopener" href="https://twitter.com/angular" title="Twitter">
<svg id="twitter-logo" height="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
<rect width="400" height="400" fill="none"/>
<path d="M153.62,301.59c94.34,0,145.94-78.16,145.94-145.94,0-2.22,0-4.43-.15-6.63A104.36,104.36,0,0,0,325,122.47a102.38,102.38,0,0,1-29.46,8.07,51.47,51.47,0,0,0,22.55-28.37,102.79,102.79,0,0,1-32.57,12.45,51.34,51.34,0,0,0-87.41,46.78A145.62,145.62,0,0,1,92.4,107.81a51.33,51.33,0,0,0,15.88,68.47A50.91,50.91,0,0,1,85,169.86c0,.21,0,.43,0,.65a51.31,51.31,0,0,0,41.15,50.28,51.21,51.21,0,0,1-23.16.88,51.35,51.35,0,0,0,47.92,35.62,102.92,102.92,0,0,1-63.7,22A104.41,104.41,0,0,1,75,278.55a145.21,145.21,0,0,0,78.62,23" fill="#fff"/>
</svg>
</a>
<a aria-label="Angular on YouTube" target="_blank" rel="noopener" href="https://youtube.com/angular" title="YouTube">
<svg id="youtube-logo" height="24" width="24" data-name="Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff">
<path d="M0 0h24v24H0V0z" fill="none"/>
<path d="M21.58 7.19c-.23-.86-.91-1.54-1.77-1.77C18.25 5 12 5 12 5s-6.25 0-7.81.42c-.86.23-1.54.91-1.77 1.77C2 8.75 2 12 2 12s0 3.25.42 4.81c.23.86.91 1.54 1.77 1.77C5.75 19 12 19 12 19s6.25 0 7.81-.42c.86-.23 1.54-.91 1.77-1.77C22 15.25 22 12 22 12s0-3.25-.42-4.81zM10 15V9l5.2 3-5.2 3z"/>
</svg>
</a>
</div>
<div class="content" role="main">
<!-- Highlight Card -->
<div class="card highlight-card card-small">
<svg id="rocket" xmlns="http://www.w3.org/2000/svg" width="101.678" height="101.678" viewBox="0 0 101.678 101.678">
<title>Rocket Ship</title>
<g id="Group_83" data-name="Group 83" transform="translate(-141 -696)">
<circle id="Ellipse_8" data-name="Ellipse 8" cx="50.839" cy="50.839" r="50.839" transform="translate(141 696)" fill="#dd0031"/>
<g id="Group_47" data-name="Group 47" transform="translate(165.185 720.185)">
<path id="Path_33" data-name="Path 33" d="M3.4,42.615a3.084,3.084,0,0,0,3.553,3.553,21.419,21.419,0,0,0,12.215-6.107L9.511,30.4A21.419,21.419,0,0,0,3.4,42.615Z" transform="translate(0.371 3.363)" fill="#fff"/>
<path id="Path_34" data-name="Path 34" d="M53.3,3.221A3.09,3.09,0,0,0,50.081,0,48.227,48.227,0,0,0,18.322,13.437c-6-1.666-14.991-1.221-18.322,7.218A33.892,33.892,0,0,1,9.439,25.1l-.333.666a3.013,3.013,0,0,0,.555,3.553L23.985,43.641a2.9,2.9,0,0,0,3.553.555l.666-.333A33.892,33.892,0,0,1,32.647,53.3c8.55-3.664,8.884-12.326,7.218-18.322A48.227,48.227,0,0,0,53.3,3.221ZM34.424,9.772a6.439,6.439,0,1,1,9.106,9.106,6.368,6.368,0,0,1-9.106,0A6.467,6.467,0,0,1,34.424,9.772Z" transform="translate(0 0.005)" fill="#fff"/>
</g>
</g>
</svg>
<span>{{ title }} app is running!</span>
<svg id="rocket-smoke" xmlns="http://www.w3.org/2000/svg" width="516.119" height="1083.632" viewBox="0 0 516.119 1083.632">
<title>Rocket Ship Smoke</title>
<path id="Path_40" data-name="Path 40" d="M644.6,141S143.02,215.537,147.049,870.207s342.774,201.755,342.774,201.755S404.659,847.213,388.815,762.2c-27.116-145.51-11.551-384.124,271.9-609.1C671.15,139.365,644.6,141,644.6,141Z" transform="translate(-147.025 -140.939)" fill="#f5f5f5"/>
</svg>
</div>
<!-- Resources -->
<h2>Resources</h2>
<p>Here are some links to help you get started:</p>
<div class="card-container">
<a class="card" target="_blank" rel="noopener" href="https://angular.io/tutorial">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M5 13.18v4L12 21l7-3.82v-4L12 17l-7-3.82zM12 3L1 9l11 6 9-4.91V17h2V9L12 3z"/></svg>
<span>Learn Angular</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a>
<a class="card" target="_blank" rel="noopener" href="https://angular.io/cli">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
<span>CLI Documentation</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</a>
<a class="card" target="_blank" rel="noopener" href="https://blog.angular.io/">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13.5.67s.74 2.65.74 4.8c0 2.06-1.35 3.73-3.41 3.73-2.07 0-3.63-1.67-3.63-3.73l.03-.36C5.21 7.51 4 10.62 4 14c0 4.42 3.58 8 8 8s8-3.58 8-8C20 8.61 17.41 3.8 13.5.67zM11.71 19c-1.78 0-3.22-1.4-3.22-3.14 0-1.62 1.05-2.76 2.81-3.12 1.77-.36 3.6-1.21 4.62-2.58.39 1.29.59 2.65.59 4.04 0 2.65-2.15 4.8-4.8 4.8z"/></svg>
<span>Angular Blog</span>
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg>
</a>
</div>
<!-- Next Steps -->
<h2>Next Steps</h2>
<p>What do you want to do next with your app?</p>
<input type="hidden" #selection>
<div class="card-container">
<div class="card card-small" (click)="selection.value = 'component'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>New Component</span>
</div>
<div class="card card-small" (click)="selection.value = 'material'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Angular Material</span>
</div>
<div class="card card-small" (click)="selection.value = 'pwa'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Add PWA Support</span>
</div>
<div class="card card-small" (click)="selection.value = 'dependency'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Add Dependency</span>
</div>
<div class="card card-small" (click)="selection.value = 'test'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Run and Watch Tests</span>
</div>
<div class="card card-small" (click)="selection.value = 'build'" tabindex="0">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
<span>Build for Production</span>
</div>
</div>
<!-- Terminal -->
<div class="terminal" [ngSwitch]="selection.value">
<pre *ngSwitchDefault>ng generate component xyz</pre>
<pre *ngSwitchCase="'material'">ng add @angular/material</pre>
<pre *ngSwitchCase="'pwa'">ng add @angular/pwa</pre>
<pre *ngSwitchCase="'dependency'">ng add _____</pre>
<pre *ngSwitchCase="'test'">ng test</pre>
<pre *ngSwitchCase="'build'">ng build --prod</pre>
</div>
<!-- Links -->
<div class="card-container">
<a class="circle-link" title="Animations" href="https://angular.io/guide/animations" target="_blank" rel="noopener">
<svg id="Group_20" data-name="Group 20" xmlns="http://www.w3.org/2000/svg" width="21.813" height="23.453" viewBox="0 0 21.813 23.453">
<path id="Path_15" data-name="Path 15" d="M4099.584,972.736h0l-10.882,3.9,1.637,14.4,9.245,5.153,9.245-5.153,1.686-14.4Z" transform="translate(-4088.702 -972.736)" fill="#ffa726"/>
<path id="Path_16" data-name="Path 16" d="M4181.516,972.736v23.453l9.245-5.153,1.686-14.4Z" transform="translate(-4170.633 -972.736)" fill="#fb8c00"/>
<path id="Path_17" data-name="Path 17" d="M4137.529,1076.127l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1058.315)" fill="#ffe0b2"/>
<path id="Path_18" data-name="Path 18" d="M4137.529,1051.705l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1036.757)" fill="#fff3e0"/>
<path id="Path_19" data-name="Path 19" d="M4137.529,1027.283l-7.7-3.723,4.417-2.721,7.753,3.723Z" transform="translate(-4125.003 -1015.199)" fill="#fff"/>
</svg>
</a>
<a class="circle-link" title="CLI" href="https://cli.angular.io/" target="_blank" rel="noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="21.762" height="23.447" viewBox="0 0 21.762 23.447">
<title>Angular CLI Logo</title>
<g id="Group_21" data-name="Group 21" transform="translate(0)">
<path id="Path_20" data-name="Path 20" d="M2660.313,313.618h0l-10.833,3.9,1.637,14.4,9.2,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-2649.48 -313.618)" fill="#37474f"/>
<path id="Path_21" data-name="Path 21" d="M2741.883,313.618v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-2731.05 -313.618)" fill="#263238"/>
<path id="Path_22" data-name="Path 22" d="M2692.293,379.169h11.724V368.618h-11.724Zm11.159-.6h-10.608v-9.345h10.621v9.345Z" transform="translate(-2687.274 -362.17)" fill="#fff"/>
<path id="Path_23" data-name="Path 23" d="M2709.331,393.688l.4.416,2.265-2.28-2.294-2.294-.4.4,1.893,1.893Z" transform="translate(-2702.289 -380.631)" fill="#fff"/>
<rect id="Rectangle_12" data-name="Rectangle 12" width="3.517" height="0.469" transform="translate(9.709 13.744)" fill="#fff"/>
</g>
</svg>
</a>
<a class="circle-link" title="Protractor" href="https://www.protractortest.org/" target="_blank" rel="noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="21.81" height="23.447" viewBox="0 0 21.81 23.447">
<title>Angular Protractor Logo</title>
<g id="Group_26" data-name="Group 26" transform="translate(0)">
<path id="Path_28" data-name="Path 28" d="M4620.155,311.417h0l-10.881,3.9,1.637,14.4,9.244,5.152,9.244-5.152,1.685-14.4Z" transform="translate(-4609.274 -311.417)" fill="#e13439"/>
<path id="Path_29" data-name="Path 29" d="M4702.088,311.417v23.447l9.244-5.152,1.685-14.4Z" transform="translate(-4691.207 -311.417)" fill="#b52f32"/>
<path id="Path_30" data-name="Path 30" d="M4651.044,369.58v-.421h1.483a7.6,7.6,0,0,0-2.106-5.052l-1.123,1.123-.3-.3,1.122-1.121a7.588,7.588,0,0,0-4.946-2.055v1.482h-.421v-1.485a7.589,7.589,0,0,0-5.051,2.058l1.122,1.121-.3.3-1.123-1.123a7.591,7.591,0,0,0-2.106,5.052h1.482v.421h-1.489v1.734h15.241V369.58Zm-10.966-.263a4.835,4.835,0,0,1,9.67,0Z" transform="translate(-4634.008 -355.852)" fill="#fff"/>
</g>
</svg>
</a>
<a class="circle-link" title="Find a Local Meetup" href="https://www.meetup.com/find/?keywords=angular" target="_blank" rel="noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="24.607" height="23.447" viewBox="0 0 24.607 23.447">
<title>Meetup Logo</title>
<path id="logo--mSwarm" d="M21.221,14.95A4.393,4.393,0,0,1,17.6,19.281a4.452,4.452,0,0,1-.8.069c-.09,0-.125.035-.154.117a2.939,2.939,0,0,1-2.506,2.091,2.868,2.868,0,0,1-2.248-.624.168.168,0,0,0-.245-.005,3.926,3.926,0,0,1-2.589.741,4.015,4.015,0,0,1-3.7-3.347,2.7,2.7,0,0,1-.043-.38c0-.106-.042-.146-.143-.166a3.524,3.524,0,0,1-1.516-.69A3.623,3.623,0,0,1,2.23,14.557a3.66,3.66,0,0,1,1.077-3.085.138.138,0,0,0,.026-.2,3.348,3.348,0,0,1-.451-1.821,3.46,3.46,0,0,1,2.749-3.28.44.44,0,0,0,.355-.281,5.072,5.072,0,0,1,3.863-3,5.028,5.028,0,0,1,3.555.666.31.31,0,0,0,.271.03A4.5,4.5,0,0,1,18.3,4.7a4.4,4.4,0,0,1,1.334,2.751,3.658,3.658,0,0,1,.022.706.131.131,0,0,0,.1.157,2.432,2.432,0,0,1,1.574,1.645,2.464,2.464,0,0,1-.7,2.616c-.065.064-.051.1-.014.166A4.321,4.321,0,0,1,21.221,14.95ZM13.4,14.607a2.09,2.09,0,0,0,1.409,1.982,4.7,4.7,0,0,0,1.275.221,1.807,1.807,0,0,0,.9-.151.542.542,0,0,0,.321-.545.558.558,0,0,0-.359-.534,1.2,1.2,0,0,0-.254-.078c-.262-.047-.526-.086-.787-.138a.674.674,0,0,1-.617-.75,3.394,3.394,0,0,1,.218-1.109c.217-.658.509-1.286.79-1.918a15.609,15.609,0,0,0,.745-1.86,1.95,1.95,0,0,0,.06-1.073,1.286,1.286,0,0,0-1.051-1.033,1.977,1.977,0,0,0-1.521.2.339.339,0,0,1-.446-.042c-.1-.092-.2-.189-.307-.284a1.214,1.214,0,0,0-1.643-.061,7.563,7.563,0,0,1-.614.512A.588.588,0,0,1,10.883,8c-.215-.115-.437-.215-.659-.316a2.153,2.153,0,0,0-.695-.248A2.091,2.091,0,0,0,7.541,8.562a9.915,9.915,0,0,0-.405.986c-.559,1.545-1.015,3.123-1.487,4.7a1.528,1.528,0,0,0,.634,1.777,1.755,1.755,0,0,0,1.5.211,1.35,1.35,0,0,0,.824-.858c.543-1.281,1.032-2.584,1.55-3.875.142-.355.28-.712.432-1.064a.548.548,0,0,1,.851-.24.622.622,0,0,1,.185.539,2.161,2.161,0,0,1-.181.621c-.337.852-.68,1.7-1.018,2.552a2.564,2.564,0,0,0-.173.528.624.624,0,0,0,.333.71,1.073,1.073,0,0,0,.814.034,1.22,1.22,0,0,0,.657-.655q.758-1.488,1.511-2.978.35-.687.709-1.37a1.073,1.073,0,0,1,.357-.434.43.43,0,0,1,.463-.016.373.373,0,0,1,.153.387.7.7,0,0,1-.057.236c-.065.157-.127.316-.2.469-.42.883-.846,1.763-1.262,2.648A2.463,2.463,0,0,0,13.4,14.607Zm5.888,6.508a1.09,1.09,0,0,0-2.179.006,1.09,1.09,0,0,0,2.179-.006ZM1.028,12.139a1.038,1.038,0,1,0,.01-2.075,1.038,1.038,0,0,0-.01,2.075ZM13.782.528a1.027,1.027,0,1,0-.011,2.055A1.027,1.027,0,0,0,13.782.528ZM22.21,6.95a.882.882,0,0,0-1.763.011A.882.882,0,0,0,22.21,6.95ZM4.153,4.439a.785.785,0,1,0,.787-.78A.766.766,0,0,0,4.153,4.439Zm8.221,18.22a.676.676,0,1,0-.677.666A.671.671,0,0,0,12.374,22.658ZM22.872,12.2a.674.674,0,0,0-.665.665.656.656,0,0,0,.655.643.634.634,0,0,0,.655-.644A.654.654,0,0,0,22.872,12.2ZM7.171-.123A.546.546,0,0,0,6.613.43a.553.553,0,1,0,1.106,0A.539.539,0,0,0,7.171-.123ZM24.119,9.234a.507.507,0,0,0-.493.488.494.494,0,0,0,.494.494.48.48,0,0,0,.487-.483A.491.491,0,0,0,24.119,9.234Zm-19.454,9.7a.5.5,0,0,0-.488-.488.491.491,0,0,0-.487.5.483.483,0,0,0,.491.479A.49.49,0,0,0,4.665,18.936Z" transform="translate(0 0.123)" fill="#f64060"/>
</svg>
</a>
<a class="circle-link" title="Join the Conversation on Discord" href="https://discord.gg/angular" target="_blank" rel="noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 245 240">
<title>Discord Logo</title>
<path d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"/>
<path d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"/>
</svg>
</a>
</div>
<!-- Footer -->
<footer>
Love Angular?&nbsp;
<a href="https://github.com/angular/angular" target="_blank" rel="noopener"> Give our repo a star.
<div class="github-star-badge">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>
Star
</div>
</a>
<a href="https://github.com/angular/angular" target="_blank" rel="noopener">
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" fill="#1976d2"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</a>
</footer>
<svg id="clouds" xmlns="http://www.w3.org/2000/svg" width="2611.084" height="485.677" viewBox="0 0 2611.084 485.677">
<title>Gray Clouds Background</title>
<path id="Path_39" data-name="Path 39" d="M2379.709,863.793c10-93-77-171-168-149-52-114-225-105-264,15-75,3-140,59-152,133-30,2.83-66.725,9.829-93.5,26.25-26.771-16.421-63.5-23.42-93.5-26.25-12-74-77-130-152-133-39-120-212-129-264-15-54.084-13.075-106.753,9.173-138.488,48.9-31.734-39.726-84.4-61.974-138.487-48.9-52-114-225-105-264,15a162.027,162.027,0,0,0-103.147,43.044c-30.633-45.365-87.1-72.091-145.206-58.044-52-114-225-105-264,15-75,3-140,59-152,133-53,5-127,23-130,83-2,42,35,72,70,86,49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33,61.112,8.015,113.854-5.72,150.492-29.764a165.62,165.62,0,0,0,110.861-3.236c47,94,178,113,251,33,31.385,4.116,60.563,2.495,86.487-3.311,25.924,5.806,55.1,7.427,86.488,3.311,73,80,204,61,251-33a165.625,165.625,0,0,0,120,0c51,13,108,15,157-5a147.188,147.188,0,0,0,33.5-18.694,147.217,147.217,0,0,0,33.5,18.694c49,20,106,18,157,5a165.625,165.625,0,0,0,120,0c47,94,178,113,251,33C2446.709,1093.793,2554.709,922.793,2379.709,863.793Z" transform="translate(142.69 -634.312)" fill="#eee"/>
</svg>
</div>
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * * The content above * * * * * * * * * * * -->
<!-- * * * * * * * * * * is only a placeholder * * * * * * * * * * -->
<!-- * * * * * * * * * * and can be replaced. * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<!-- * * * * * * * * * * End of Placeholder * * * * * * * * * * * -->
<!-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -->
<router-outlet></router-outlet>
@@ -0,0 +1,39 @@
import { TestBed } from '@angular/core/testing'
import { RouterTestingModule } from '@angular/router/testing'
import { AppComponent } from './app.component'
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
RouterTestingModule,
],
declarations: [
AppComponent,
],
}).compileComponents()
})
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance
expect(app).toBeTruthy()
})
it(`should have as title 'sandbox'`, () => {
const fixture = TestBed.createComponent(AppComponent)
const app = fixture.componentInstance
expect(app.title).toEqual('sandbox')
})
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent)
fixture.detectChanges()
const compiled = fixture.nativeElement
expect(compiled.querySelector('.content span').textContent).toContain('sandbox app is running!')
})
})
@@ -0,0 +1,10 @@
import { Component } from '@angular/core'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'sandbox';
}
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule { }
@@ -0,0 +1,3 @@
export const environment = {
production: true,
}
@@ -0,0 +1,16 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
}
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sandbox</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>
<app-root></app-root>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
import { enableProdMode } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
import { AppModule } from './app/app.module'
import { environment } from './environments/environment'
if (environment.production) {
enableProdMode()
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch((err) => console.error(err)) // eslint-disable-line no-console
@@ -0,0 +1,64 @@
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/**
* IE11 requires the following for NgClass support on SVG elements
*/
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone' // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
@@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */
+27
View File
@@ -0,0 +1,27 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing'
import { getTestBed } from '@angular/core/testing'
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing'
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
keys(): string[]
<T>(id: string): T
}
}
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
)
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/)
// And load the modules.
context.keys().map(context)
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
}
@@ -0,0 +1,23 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"module": "es2020",
"lib": [
"es2018",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false
}
}
@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
+152
View File
@@ -0,0 +1,152 @@
{
"extends": "tslint:recommended",
"rulesDirectory": [
"codelyzer"
],
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-return-shorthand": true,
"curly": true,
"deprecation": {
"severity": "warning"
},
"eofline": true,
"import-blacklist": [
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"quotemark": [
true,
"single"
],
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"typedef": [
true,
"call-signature"
],
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
]
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,10 @@
{
"$schema": "../../node_modules/@angular-devkit/architect/src/builders-schema.json",
"builders": {
"cypress": {
"implementation": "./cypress",
"schema": "./cypress/schema.json",
"description": "Run Cypress tests"
}
}
}
@@ -0,0 +1,19 @@
import { JsonObject } from '@angular-devkit/core'
export interface CypressBuilderOptions extends JsonObject {
baseUrl: string
configFile: string | false
browser: 'electron' | 'chrome' | 'chromium' | 'canary' | 'firefox' | string
devServerTarget: string
env: Record<string, string>
quiet: boolean
exit: boolean
headless: boolean
key: string
parallel: boolean
projectPath: string
record: boolean
spec: string
tsConfig: string
watch: boolean
}
@@ -0,0 +1,113 @@
import {
BuilderContext,
BuilderOutput,
createBuilder,
scheduleTargetAndForget,
targetFromTargetString,
} from '@angular-devkit/architect'
import { asWindowsPath, experimental, normalize } from '@angular-devkit/core'
import { NodeJsSyncHost } from '@angular-devkit/core/node'
import * as os from 'os'
import { dirname, join } from 'path'
import { open, run } from 'cypress'
import { from, noop, Observable, of } from 'rxjs'
import { catchError, concatMap, first, map, switchMap, tap } from 'rxjs/operators'
import { CypressBuilderOptions } from './cypress-builder-options'
export default createBuilder<CypressBuilderOptions>(runCypress)
type CypressOptions = Partial<CypressCommandLine.CypressRunOptions> &
Partial<CypressCommandLine.CypressOpenOptions>;
function runCypress (
options: CypressBuilderOptions,
context: BuilderContext,
): Observable<BuilderOutput> {
options.env = options.env || {}
if (options.tsConfig) {
options.env.tsConfig = join(context.workspaceRoot, options.tsConfig)
}
const workspace = new experimental.workspace.Workspace(
normalize(context.workspaceRoot),
new NodeJsSyncHost(),
)
return workspace.loadWorkspaceFromHost(normalize('angular.json')).pipe(
map(() => os.platform() === 'win32'),
map((isWin) => (!isWin ? workspace.root : asWindowsPath(workspace.root))),
map((workspaceRoot) => {
return {
...options,
projectPath: `${workspaceRoot}/cypress`,
}
}),
switchMap((options: CypressBuilderOptions) => {
return (options.devServerTarget
? startDevServer(options.devServerTarget, options.watch, context)
: of(options.baseUrl)
).pipe(
concatMap((baseUrl: string) => initCypress({ ...options, baseUrl })),
options.watch ? tap(noop) : first(),
catchError((error) => {
return of({ success: false }).pipe(
tap(() => context.reportStatus(`Error: ${error.message}`)),
tap(() => context.logger.error(error.message)),
)
}),
)
}),
)
}
function initCypress (userOptions: CypressBuilderOptions): Observable<BuilderOutput> {
const projectFolderPath = dirname(userOptions.projectPath)
const defaultOptions: CypressOptions = {
project: projectFolderPath,
browser: 'electron',
headless: true,
record: false,
spec: '',
}
const options: CypressOptions = {
...defaultOptions,
...userOptions,
}
if (userOptions.configFile === undefined) {
options.config = {}
}
if (userOptions.baseUrl) {
options.config = { ...options.config, baseUrl: userOptions.baseUrl }
}
const { watch, headless } = userOptions
return from(watch === false || headless ? run(options) : open(options)).pipe(
map((result: any) => ({ success: !result.totalFailed && !result.failures })),
)
}
export function startDevServer (
devServerTarget: string,
watch: boolean,
context: any,
): Observable<string> {
const overrides = {
watch,
}
return scheduleTargetAndForget(context, targetFromTargetString(devServerTarget), overrides).pipe(
map((output: any) => {
if (!output.success && !watch) {
throw new Error('Could not compile application files')
}
return output.baseUrl as string
}),
)
}
@@ -0,0 +1,80 @@
{
"title": "Cypress Target",
"description": "Cypress target option for Build Facade",
"type": "object",
"properties": {
"baseUrl": {
"type": "string",
"description": "Use this to directly pass the address of your distant server address with the port running your application"
},
"configFile": {
"type": [
"string",
"boolean"
],
"description": "Specify a path to a JSON file where configuration values are set or pass false to disable the use of a configuration file entirely."
},
"quiet": {
"type": "boolean",
"description": "If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print.",
"default": false
},
"browser": {
"type": "string",
"description": "The browser to run tests in.",
"enum": [
"electron",
"chrome",
"chromium",
"canary",
"firefox"
]
},
"devServerTarget": {
"type": "string",
"description": "Dev server target to run tests against."
},
"env": {
"type": "object",
"description": "A key-value pair of environment variables to pass to Cypress runner"
},
"exit": {
"type": "boolean",
"description": "Whether or not the Cypress Test Runner will stay open after running tests in a spec file",
"default": true
},
"headless": {
"type": "boolean",
"description": "Whether or not to open the Cypress application to run the tests. If set to 'true', will run in headless mode",
"default": false
},
"key": {
"type": "string",
"description": "The key Cypress should use to run tests in parallel/record the run (CI only)"
},
"parallel": {
"type": "boolean",
"description": "Whether or not Cypress should run its tests in parallel (CI only)",
"default": false
},
"record": {
"type": "boolean",
"description": "Whether or not Cypress should record the results of the tests",
"default": false
},
"spec": {
"type": "string",
"description": "A comma delimited glob string that is provided to the Cypress runner to specify which spec files to run. i.e. '**examples/**,**actions.spec**"
},
"tsConfig": {
"type": "string",
"description": "The path of the Cypress tsconfig configuration json file."
},
"watch": {
"type": "boolean",
"description": "Recompile and run tests when files change.",
"default": false
}
},
"additionalProperties": true
}
@@ -0,0 +1,10 @@
{
"$schema": "../../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Adds Cypress to an Angular project.",
"factory": "./cypress/index",
"schema": "./cypress/schema.json"
}
}
}
@@ -0,0 +1,10 @@
{
"integrationFolder": "<%= root%>cypress/integration",
"supportFile": "<%= root%>cypress/support/index.ts",
"videosFolder": "<%= root%>cypress/videos",
"screenshotsFolder": "<%= root%>cypress/screenshots",
"pluginsFile": "<%= root%>cypress/plugins/index.js",
"fixturesFolder": "<%= root%>cypress/fixtures",
"baseUrl": "<%= baseUrl%>",
"projectId": "d6gihf"
}
@@ -0,0 +1,7 @@
describe('My First Test', () => {
it('Visits the initial project page', () => {
cy.visit('/')
cy.contains('Welcome')
cy.contains('sandbox app is running!')
})
})
@@ -0,0 +1 @@
module.exports = (on, config) => {}
@@ -0,0 +1,43 @@
// ***********************************************
// This example namespace declaration will help
// with Intellisense and code completion in your
// IDE or Text Editor.
// ***********************************************
// declare namespace Cypress {
// interface Chainable<Subject = any> {
// customCommand(param: any): typeof customCommand;
// }
// }
//
// function customCommand(param: any): void {
// console.warn(param);
// }
//
// NOTE: You can use it like so:
// Cypress.Commands.add('customCommand', customCommand);
//
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
@@ -0,0 +1,17 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// When a command from ./commands is ready to use, import with `import './commands'` syntax
// import './commands';
@@ -0,0 +1,8 @@
{
"extends": "<%= relativeToWorkspace %>/tsconfig.json",
"include": ["**/*.ts"],
"compilerOptions": {
"sourceMap": false,
"types": ["cypress"]
}
}
@@ -0,0 +1,327 @@
import { getSystemPath, JsonObject, JsonValue, normalize, strings } from '@angular-devkit/core'
import {
apply,
chain,
mergeWith,
move,
noop,
Rule,
SchematicContext,
SchematicsException,
template,
Tree,
url,
} from '@angular-devkit/schematics'
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'
import { concat, Observable, of } from 'rxjs'
import { concatMap, map } from 'rxjs/operators'
import { addPackageJsonDependency, NodeDependencyType, removePackageJsonDependency } from '../utility/dependencies'
import {
getAngularVersion,
getLatestNodeVersion,
NodePackage,
} from '../utility'
import { relative, resolve } from 'path'
import { JSONFile, JSONPath } from '../utility/json-file'
export default function (_options: any): Rule {
return (tree: Tree, _context: SchematicContext) => {
_options = { ..._options, __version__: getAngularVersion(tree) }
return chain([
updateDependencies(_options),
_options.removeProtractor ? removeFiles() : noop(),
addCypressFiles(),
addCypressTestScriptsToPackageJson(),
modifyAngularJson(_options),
])(tree, _context)
}
}
function addPropertyToPackageJson (tree: Tree, path: JSONPath, value: JsonValue) {
const json = new JSONFile(tree, '/package.json')
json.modify(path, value)
}
function updateDependencies (options: any): Rule {
let removeDependencies: Observable<Tree>
return (tree: Tree, context: SchematicContext): any => {
context.logger.debug('Updating dependencies...')
context.addTask(new NodePackageInstallTask())
if (options.removeProtractor) {
removeDependencies = of('protractor').pipe(
map((packageName: string) => {
context.logger.debug(`Removing ${packageName} dependency`)
removePackageJsonDependency(tree, packageName)
return tree
}),
)
}
const addDependencies = of('cypress').pipe(
concatMap((packageName: string) => getLatestNodeVersion(packageName)),
map((packageFromRegistry: NodePackage) => {
const { name, version } = packageFromRegistry
context.logger.debug(`Adding ${name}:${version} to ${NodeDependencyType.Dev}`)
addPackageJsonDependency(tree, {
type: NodeDependencyType.Dev,
name,
version,
})
return tree
}),
)
if (options.removeProtractor) {
return concat(removeDependencies, addDependencies)
}
return concat(addDependencies)
}
}
function addCypressTestScriptsToPackageJson (): Rule {
return (tree: Tree) => {
addPropertyToPackageJson(tree, ['scripts'], {
'cy:open': 'cypress open',
'cy:run': 'cypress run',
})
}
}
function deleteDirectory (tree: Tree, path: string): void {
try {
tree.delete(path)
} catch (e) {
console.log(e) // eslint-disable-line no-console
}
}
function removeFiles (): Rule {
return (tree: Tree, context: SchematicContext) => {
if (tree.exists('./angular.json')) {
const angularJsonValue = getAngularJsonValue(tree)
const { projects } = angularJsonValue
// clean up projects generated by cli with versions <= 7
Object.keys(projects)
.filter((name) => name.endsWith('-e2e'))
.forEach((projectName) => {
const projectRoot = projects[projectName].root
deleteDirectory(tree, projectRoot)
context.logger.debug(`Removing ${projectName} from angular.json projects`)
delete angularJsonValue.projects[projectName]
})
// clean up projects generated by cli with versions > 7
Object.keys(projects)
.filter((name) => !name.endsWith('-e2e'))
.forEach((projectName) => {
const projectRoot = projects[projectName].root
deleteDirectory(tree, `${projectRoot}/e2e`)
})
return tree.overwrite('./angular.json', JSON.stringify(angularJsonValue, null, 2))
}
return tree
}
}
function addCypressFiles (): Rule {
return (tree: Tree, context: SchematicContext) => {
context.logger.debug('Adding cypress files')
const angularJsonValue = getAngularJsonValue(tree)
const { projects } = angularJsonValue
return chain(
Object.keys(projects).map((name) => {
const project = projects[name]
const projectPath = resolve(getSystemPath(normalize(project.root)))
const workspacePath = resolve(getSystemPath(normalize('')))
const relativeToWorkspace = relative(`${projectPath}/cypress`, workspacePath)
const baseUrl = getBaseUrl(project)
return mergeWith(
apply(url('./files'), [
move(project.root),
template({
...strings,
root: project.root ? `${project.root}/` : project.root,
baseUrl,
relativeToWorkspace,
}),
]),
)
}),
)(tree, context)
}
}
function getBaseUrl (project: { architect: { serve: { options: any } } }): string {
let options = { protocol: 'http', port: 4200, host: 'localhost' }
if (project.architect?.serve?.options) {
const projectOptions = project.architect?.serve?.options
options = { ...options, ...projectOptions }
options.protocol = projectOptions.ssl ? 'https' : 'http'
}
return `${options.protocol}://${options.host}:${options.port}`
}
function addNewCypressCommands (
tree: Tree,
angularJsonVal: any,
project: string,
runJson: JsonObject,
openJson: JsonObject,
removeProtractor: boolean,
) {
const projectArchitectJson = angularJsonVal['projects'][project]['architect']
projectArchitectJson['cypress-run'] = runJson
projectArchitectJson['cypress-open'] = openJson
if (removeProtractor || !projectArchitectJson['e2e']) {
projectArchitectJson['e2e'] = openJson
}
return tree.overwrite('./angular.json', JSON.stringify(angularJsonVal, null, 2))
}
function getAngularJsonValue (tree: Tree) {
const angularJson = new JSONFile(tree, './angular.json')
return angularJson.get([]) as any
}
function modifyAngularJson (options: any): Rule {
return (tree: Tree, context: SchematicContext) => {
if (tree.exists('./angular.json')) {
const angularJsonVal = getAngularJsonValue(tree)
const { projects } = angularJsonVal
if (!projects) {
throw new SchematicsException('projects in angular.json is not defined')
}
Object.keys(projects).forEach((project) => {
const cypressRunJson = {
builder: '@cypress/schematic:cypress',
options: {
devServerTarget: `${project}:serve`,
},
configurations: {
production: {
devServerTarget: `${project}:serve:production`,
},
},
}
const cypressOpenJson = {
builder: '@cypress/schematic:cypress',
options: {
watch: true,
headless: false,
},
configurations: {
production: {
devServerTarget: `${project}:serve:production`,
},
},
}
const configFile = projects[project].root
? `${projects[project].root}/cypress.json`
: null
if (configFile) {
Object.assign(cypressRunJson.options, { configFile })
Object.assign(cypressOpenJson.options, { configFile })
}
if (options.removeProtractor) {
context.logger.debug(`Replacing e2e command with cypress-run in angular.json`)
removeE2ELinting(tree, angularJsonVal, project)
}
context.logger.debug(`Adding cypress/tsconfig.json to angular.json-tslint config`)
addCypressTsConfig(tree, angularJsonVal, project)
context.logger.debug(`Adding cypress-run and cypress-open commands in angular.json`)
addNewCypressCommands(
tree,
angularJsonVal,
project,
cypressRunJson,
cypressOpenJson,
options.removeProtractor,
)
})
} else {
throw new SchematicsException('angular.json not found')
}
return tree
}
}
export const addCypressTsConfig = (tree: Tree, angularJsonVal: any, projectName: string) => {
const project = angularJsonVal.projects[projectName]
let tsConfig = project?.architect?.lint?.options?.tsConfig
if (tsConfig) {
let prefix = ''
if (project.root) {
prefix = `${project.root}/`
}
if (!Array.isArray(tsConfig)) {
project.architect.lint.options.tsConfig = tsConfig = [tsConfig]
}
tsConfig.push(`${prefix}cypress/tsconfig.json`)
}
return tree.overwrite('./angular.json', JSON.stringify(angularJsonVal, null, 2))
}
export const removeE2ELinting = (tree: Tree, angularJsonVal: any, project: string) => {
const projectLintOptionsJson = angularJsonVal.projects[project]?.architect?.lint?.options
if (projectLintOptionsJson) {
let filteredTsConfigPaths
if (Array.isArray(projectLintOptionsJson['tsConfig'])) {
filteredTsConfigPaths = projectLintOptionsJson?.tsConfig?.filter((path: string) => {
const pathIncludesE2e = path.includes('e2e')
return !pathIncludesE2e && path
})
} else {
filteredTsConfigPaths = !projectLintOptionsJson?.tsConfig?.includes('e2e')
? projectLintOptionsJson?.tsConfig
: ''
}
projectLintOptionsJson['tsConfig'] = filteredTsConfigPaths
}
return tree.overwrite('./angular.json', JSON.stringify(angularJsonVal, null, 2))
}
@@ -0,0 +1,47 @@
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
import { join } from 'path'
import { expect } from 'chai'
// const NUMBER_OF_SCAFFOLDED_FILES = 36;
describe('@cypress/schematic: ng-add', () => {
const schematicRunner = new SchematicTestRunner(
'schematics',
join(__dirname, './../collection.json'),
)
let appTree: UnitTestTree
const workspaceOptions = {
name: 'workspace',
newProjectRoot: 'projects',
version: '6.0.0',
defaultProject: 'sandbox',
}
const appOptions = {
name: 'sandbox',
inlineTemplate: false,
routing: false,
skipTests: false,
skipPackageJson: false,
}
beforeEach(async () => {
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions).toPromise()
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise()
})
it('should create cypress files', async () => {
const files = ['integration/spec.ts', 'plugins/index.js', 'support/commands.ts', 'support/index.ts', 'tsconfig.json', 'cypress.json']
const homePath = '/projects/sandbox/cypress/'
schematicRunner.runSchematicAsync('ng-add', {}, appTree).toPromise().then((tree) => {
files.forEach((f) => {
const path = `${homePath}${f}`
expect(tree.exists(path)).equal(true)
})
})
})
})
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "CypressIoSchematics",
"title": "Cypress Install Schema",
"type": "object",
"properties": {
"removeProtractor": {
"description": "When true, the protractor dependency and e2e directory will be removed from the project",
"type": "boolean",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "Would you like to remove Protractor from the project? If yes, this will remove Protractor files and the e2e folder, and Cypress will be the default for `ng e2e`. Otherwise, Cypress will run alongside Protractor with its own commands."
}
},
"required": []
}
@@ -0,0 +1,83 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
* https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utility/dependencies.ts
*/
import { Tree } from '@angular-devkit/schematics'
import { JSONFile } from './json-file'
const PKG_JSON_PATH = '/package.json'
export enum NodeDependencyType {
Default = 'dependencies',
Dev = 'devDependencies',
Peer = 'peerDependencies',
Optional = 'optionalDependencies',
}
export interface NodeDependency {
type: NodeDependencyType
name: string
version: string
overwrite?: boolean
}
const ALL_DEPENDENCY_TYPE = [
NodeDependencyType.Default,
NodeDependencyType.Dev,
NodeDependencyType.Optional,
NodeDependencyType.Peer,
]
export function addPackageJsonDependency (
tree: Tree,
dependency: NodeDependency,
pkgJsonPath = PKG_JSON_PATH,
): void {
const json = new JSONFile(tree, pkgJsonPath)
const { overwrite, type, name, version } = dependency
const path = [type, name]
if (overwrite || !json.get(path)) {
json.modify(path, version)
}
}
export function removePackageJsonDependency (
tree: Tree,
name: string,
pkgJsonPath = PKG_JSON_PATH,
): void {
const json = new JSONFile(tree, pkgJsonPath)
for (const depType of ALL_DEPENDENCY_TYPE) {
json.remove([depType, name])
}
}
export function getPackageJsonDependency (
tree: Tree,
name: string,
pkgJsonPath = PKG_JSON_PATH,
): NodeDependency | null {
const json = new JSONFile(tree, pkgJsonPath)
for (const depType of ALL_DEPENDENCY_TYPE) {
const version = json.get([depType, name])
if (typeof version === 'string') {
return {
type: depType,
name,
version,
}
}
}
return null
}
@@ -0,0 +1,61 @@
import { Tree } from '@angular-devkit/schematics'
import { get } from 'http'
import { getPackageJsonDependency } from './dependencies'
export interface NodePackage {
name: string
version: string
}
export enum Paths {
AngularJson = './angular.json',
}
export enum Configs {
JsonIndentLevel = 4,
}
export interface CypressOptions {
project?: string
__version__: number
}
export function getAngularVersion (tree: Tree): number {
const packageNode = getPackageJsonDependency(tree, '@angular/core')
const version = packageNode && packageNode.version.split('').find((char) => !!parseInt(char, 10))
return version ? +version : 0
}
/**
* Attempt to retrieve the latest package version from NPM
* Return an optional "latest" version in case of error
* @param packageName
*/
export function getLatestNodeVersion (packageName: string): Promise<NodePackage> {
const DEFAULT_VERSION = 'latest'
return new Promise((resolve) => {
return get(`http://registry.npmjs.org/${packageName}`, (res) => {
let rawData = ''
res.on('data', (chunk) => (rawData += chunk))
res.on('end', () => {
try {
const response = JSON.parse(rawData)
const version = (response && response['dist-tags']) || {}
resolve(buildPackage(packageName, version.latest))
} catch (e) {
resolve(buildPackage(packageName))
}
})
}).on('error', () => resolve(buildPackage(packageName)))
})
function buildPackage (name: string, version: string = DEFAULT_VERSION): NodePackage {
return { name, version }
}
}
@@ -0,0 +1,108 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { JsonValue } from '@angular-devkit/core'
import { Tree } from '@angular-devkit/schematics'
import {
Node,
ParseError,
applyEdits,
findNodeAtLocation,
getNodeValue,
modify,
parseTree,
printParseErrorCode,
} from 'jsonc-parser'
export type InsertionIndex = (properties: string[]) => number;
export type JSONPath = (string | number)[];
/** @internal */
export class JSONFile {
content: string;
constructor (private readonly host: Tree, private readonly path: string) {
const buffer = this.host.read(this.path)
if (buffer) {
this.content = buffer.toString()
} else {
throw new Error(`Could not read '${path}'.`)
}
}
private _jsonAst: Node | undefined;
private get JsonAst (): Node | undefined {
if (this._jsonAst) {
return this._jsonAst
}
const errors: ParseError[] = []
this._jsonAst = parseTree(this.content, errors, { allowTrailingComma: true })
if (errors.length) {
const { error, offset } = errors[0]
throw new Error(
`Failed to parse "${this.path}" as JSON AST Object. ${printParseErrorCode(
error,
)} at location: ${offset}.`,
)
}
return this._jsonAst
}
get (jsonPath: JSONPath): unknown {
const jsonAstNode = this.JsonAst
if (!jsonAstNode) {
return undefined
}
if (jsonPath.length === 0) {
return getNodeValue(jsonAstNode)
}
const node = findNodeAtLocation(jsonAstNode, jsonPath)
return node === undefined ? undefined : getNodeValue(node)
}
modify (
jsonPath: JSONPath,
value: JsonValue | undefined,
): void {
let updatedValue = value
if (jsonPath.includes('scripts')) {
const currentValue = this.get(jsonPath) as object
const newValue = value as object
updatedValue = { ...currentValue, ...newValue }
}
const edits = modify(this.content, jsonPath, updatedValue, {
formattingOptions: {
insertSpaces: true,
tabSize: 2,
},
})
this.content = applyEdits(this.content, edits)
this.host.overwrite(this.path, this.content)
this._jsonAst = undefined
}
remove (jsonPath: JSONPath): void {
if (this.get(jsonPath) !== undefined) {
this.modify(jsonPath, undefined)
}
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

+31
View File
@@ -0,0 +1,31 @@
{
"compilerOptions": {
"baseUrl": "tsconfig",
"lib": [
"es2018",
"dom"
],
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"noUnusedLocals": true,
"rootDir": "src",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"target": "es6",
"types": [
"node"
]
},
"include": [
"src/**/*"
],
"exclude": ["src/**/files/**/*", "src/**/*_spec.ts"]
}
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"types": [
"mocha"
]
},
"include": [
"src/**/*_spec.ts"
],
"exclude": [
"src/**/files/**/*"
]
}
+230 -15
View File
@@ -7,6 +7,78 @@
resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.0.3.tgz#bc5b5532ecafd923a61f2fb097e3b108c0106a3f"
integrity sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==
"@angular-devkit/architect@^0.1001.0":
version "0.1001.7"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1001.7.tgz#b3d75cddf5c5a2677cebba1d7b7eaffe73748d37"
integrity sha512-uFYIvMdewU44GbIyRfsUHNMLkx+C0kokpnj7eH5NbJfbyFpCfd3ijBHh+voPdPsDRWs9lLgjbxfHpswSPj4D8w==
dependencies:
"@angular-devkit/core" "10.1.7"
rxjs "6.6.2"
"@angular-devkit/core@10.1.7":
version "10.1.7"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.1.7.tgz#c4c4332d738075bf1346aa040c78756e3144ba4b"
integrity sha512-RRyDkN2FByA+nlnRx/MzUMK1FXwj7+SsrzJcvZfWx4yA5rfKmJiJryXQEzL44GL1aoaXSuvOYu3H72wxZADN8Q==
dependencies:
ajv "6.12.4"
fast-json-stable-stringify "2.1.0"
magic-string "0.25.7"
rxjs "6.6.2"
source-map "0.7.3"
"@angular-devkit/core@10.2.3", "@angular-devkit/core@^10.1.0":
version "10.2.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-10.2.3.tgz#499978929e58532f6f0caab8bd860c882d926eea"
integrity sha512-pMM1v9Xjqx6YLOQxQYs0D+03H6XPDZLS8cyEtoQX2iYdh8qlKHZVbJa2WsfzwMoIPtgcXfQAXn113VEgrQPLFA==
dependencies:
ajv "6.12.4"
fast-json-stable-stringify "2.1.0"
magic-string "0.25.7"
rxjs "6.6.2"
source-map "0.7.3"
"@angular-devkit/core@11.2.12":
version "11.2.12"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.2.12.tgz#b80843d2ff9ac934ae4f31ed22dbc1d4b803bd95"
integrity sha512-VMRMmRj6ZX32cWpuA6vD4KSmji17yC4EtbXsiqrHZ8zAho4ifu8xImCC5PugTQnHa+RlIadOXwXX89aujUEwRw==
dependencies:
ajv "6.12.6"
fast-json-stable-stringify "2.1.0"
magic-string "0.25.7"
rxjs "6.6.3"
source-map "0.7.3"
"@angular-devkit/schematics-cli@^0.1102.12":
version "0.1102.12"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics-cli/-/schematics-cli-0.1102.12.tgz#6ff2ee30c95b165214d294e123d7f86c473e6daa"
integrity sha512-0OjWe/9SAWBOIL3cruwQWSDz/wMuOsO4fHmaUcKjz7e31N0qkT8EcusyGThYGsAHs7S8V9ie7p38r/AYceKbKQ==
dependencies:
"@angular-devkit/core" "11.2.12"
"@angular-devkit/schematics" "11.2.12"
"@schematics/schematics" "0.1102.12"
ansi-colors "4.1.1"
inquirer "7.3.3"
minimist "1.2.5"
symbol-observable "3.0.0"
"@angular-devkit/schematics@10.2.3", "@angular-devkit/schematics@^10.1.0":
version "10.2.3"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-10.2.3.tgz#1f384eb9db6f02e3e867e442e3569628e0eb6de5"
integrity sha512-uCNeq5qH4QEiftgOud+EhTVvdriYQVBrYmX4f4BjVHkjnFhm73h30nfAgs6YuStIp8oxSI8jUGE9DAy331xvmA==
dependencies:
"@angular-devkit/core" "10.2.3"
ora "5.0.0"
rxjs "6.6.2"
"@angular-devkit/schematics@11.2.12":
version "11.2.12"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.2.12.tgz#a2b7b02f6677a2fa9f4f6716f0aaa87347d41509"
integrity sha512-d1fxxkLPtP87iwAFZ0iUNWMlqULm05bbnRc5g5vrFxdtpwwybC15+NS64d3gzX0vAvnCa+cDQkjeD92bJJNjLw==
dependencies:
"@angular-devkit/core" "11.2.12"
ora "5.3.0"
rxjs "6.6.3"
"@ant-design/css-animation@^1.7.2":
version "1.7.3"
resolved "https://registry.yarnpkg.com/@ant-design/css-animation/-/css-animation-1.7.3.tgz#60a1c970014e86b28f940510d69e503e428f1136"
@@ -5574,6 +5646,23 @@
dependencies:
any-observable "^0.3.0"
"@schematics/angular@^10.1.0":
version "10.2.3"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-10.2.3.tgz#a5ea7e9bc17fbfb9b1c5f760f44bc4a7b0b18834"
integrity sha512-xcnfH5XMmGcs33VHm2cu0+4g3rkfSD+qpiKFjfg7KGC4lLoOKSED4ZnjzIYwcQ6QN4gTpAvlZKxI8zO7NkKv0A==
dependencies:
"@angular-devkit/core" "10.2.3"
"@angular-devkit/schematics" "10.2.3"
jsonc-parser "2.3.0"
"@schematics/schematics@0.1102.12":
version "0.1102.12"
resolved "https://registry.yarnpkg.com/@schematics/schematics/-/schematics-0.1102.12.tgz#0190bd6430bb22dbe83ba16a6e400ed429e3f286"
integrity sha512-o4SJfwqzHWOzk/NUDokCdiGqoUF3yccQjjaOmB3YdolzhI8/9K9kHHtW8aCZAwjGn95+HTa5fioDjUGiMqAqMw==
dependencies:
"@angular-devkit/core" "11.2.12"
"@angular-devkit/schematics" "11.2.12"
"@semantic-release/changelog@5.0.1":
version "5.0.1"
resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-5.0.1.tgz#50a84b63e5d391b7debfe021421589fa2bcdafe4"
@@ -7212,6 +7301,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.6.2.tgz#264b44c5a28dfa80198fc2f7b6d3c8a054b9491f"
integrity sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==
"@types/node@^12.11.1":
version "12.20.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.12.tgz#fd9c1c2cfab536a2383ed1ef70f94adea743a226"
integrity sha512-KQZ1al2hKOONAs2MFv+yTQP1LkDWMrRJ9YCVRalXltOfXsBmH5IownLxQaiq0lnAHwAViLnh2aTYqrPcRGEbgg==
"@types/node@^12.12.29":
version "12.20.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.4.tgz#73687043dd00fcb6962c60fbf499553a24d6bdf2"
@@ -8769,6 +8863,26 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
ajv@6.12.4:
version "6.12.4"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
integrity sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@6.12.6, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9"
@@ -8779,16 +8893,6 @@ ajv@6.5.3:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@^7.0.2:
version "7.1.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84"
@@ -12654,6 +12758,11 @@ cli-spinners@^2.0.0, cli-spinners@^2.5.0:
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047"
integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==
cli-spinners@^2.4.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939"
integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==
cli-table3@0.5.1, cli-table3@^0.5.0, cli-table3@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202"
@@ -17452,7 +17561,7 @@ fast-json-parse@^1.0.3:
resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d"
integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw==
fast-json-stable-stringify@^2.0.0:
fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
@@ -17910,6 +18019,13 @@ find-versions@^3.0.0:
dependencies:
semver-regex "^2.0.0"
find-versions@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-4.0.0.tgz#3c57e573bf97769b8cb8df16934b627915da4965"
integrity sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==
dependencies:
semver-regex "^3.1.2"
find-webpack@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/find-webpack/-/find-webpack-1.5.0.tgz#2c3e0a50205ddf29ac5e79b15f9289875fd12a37"
@@ -20028,6 +20144,13 @@ hosted-git-info@^3.0.0, hosted-git-info@^3.0.5, hosted-git-info@^3.0.6:
dependencies:
lru-cache "^6.0.0"
hosted-git-info@^4.0.0:
version "4.0.2"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961"
integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==
dependencies:
lru-cache "^6.0.0"
hotkeys-js@3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.2.tgz#177c3e7dc19279c881e69983177849bed76e4747"
@@ -23040,6 +23163,16 @@ json5@^2.1.0, json5@^2.1.1, json5@^2.1.2, json5@^2.1.3:
dependencies:
minimist "^1.2.5"
jsonc-parser@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.0.tgz#7c7fc988ee1486d35734faaaa866fadb00fa91ee"
integrity sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==
jsonc-parser@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22"
integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==
jsonfile@^2.1.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
@@ -24472,7 +24605,7 @@ macos-release@^2.2.0:
resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.4.1.tgz#64033d0ec6a5e6375155a74b1a1eba8e509820ac"
integrity sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==
magic-string@^0.25.7:
magic-string@0.25.7, magic-string@^0.25.7:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
@@ -24635,7 +24768,7 @@ markdown-to-jsx@^7.1.0:
resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.2.tgz#19d3da4cd8864045cdd13a0d179147fbd6a088d4"
integrity sha512-O8DMCl32V34RrD+ZHxcAPc2+kYytuDIoQYjY36RVdsLK7uHjgNVvFec4yv0X6LgB4YEZgSvK5QtFi5YVqEpoMA==
marked-terminal@^4.0.0:
marked-terminal@^4.0.0, marked-terminal@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-4.1.1.tgz#34a6f063cd6cfe26bffaf5bac3724e24242168a9"
integrity sha512-t7Mdf6T3PvOEyN01c3tYxDzhyKZ8xnkp8Rs6Fohno63L/0pFTJ5Qtwto2AQVuDtbQiWzD+4E5AAu1Z2iLc8miQ==
@@ -24657,6 +24790,11 @@ marked@^1.0.0:
resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.9.tgz#53786f8b05d4c01a2a5a76b7d1ec9943d29d72dc"
integrity sha512-H8lIX2SvyitGX+TRdtS06m1jHMijKN/XjfH6Ooii9fvxMlh8QdqBfBDkGUpMWH2kQNrtixjzYUa3SH8ROTgRRw==
marked@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.3.tgz#3551c4958c4da36897bda2a16812ef1399c8d6b0"
integrity sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==
marker-clusterer-plus@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/marker-clusterer-plus/-/marker-clusterer-plus-2.1.4.tgz#f8eff74d599dab3b7d0e3fed5264ea0e704f5d67"
@@ -27189,7 +27327,7 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
ora@*, ora@^5.1.0:
ora@*, ora@5.3.0, ora@^5.1.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-5.3.0.tgz#fb832899d3a1372fe71c8b2c534bbfe74961bb6f"
integrity sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==
@@ -27203,6 +27341,20 @@ ora@*, ora@^5.1.0:
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
ora@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-5.0.0.tgz#4f0b34f2994877b49b452a707245ab1e9f6afccb"
integrity sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw==
dependencies:
chalk "^4.1.0"
cli-cursor "^3.1.0"
cli-spinners "^2.4.0"
is-interactive "^1.0.0"
log-symbols "^4.0.0"
mute-stream "0.0.8"
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
ora@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
@@ -31978,6 +32130,20 @@ rx-lite@*, rx-lite@^4.0.8:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
rxjs@6.6.2:
version "6.6.2"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2"
integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==
dependencies:
tslib "^1.9.0"
rxjs@6.6.3:
version "6.6.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==
dependencies:
tslib "^1.9.0"
rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.4, rxjs@^6.6.0:
version "6.6.6"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70"
@@ -32317,6 +32483,40 @@ semantic-release@17.2.3:
signale "^1.2.1"
yargs "^15.0.1"
semantic-release@^17.4.2:
version "17.4.2"
resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-17.4.2.tgz#b5147b5a629c227b7074ad4cc89920a14cb08c96"
integrity sha512-TPLWuoe2L2DmgnQEh+OLWW5V1T+ZAa1xWuHXsuPAWEko0BqSdLPl+5+BlQ+D5Bp27S5gDJ1//Y1tgbmvUhnOCw==
dependencies:
"@semantic-release/commit-analyzer" "^8.0.0"
"@semantic-release/error" "^2.2.0"
"@semantic-release/github" "^7.0.0"
"@semantic-release/npm" "^7.0.0"
"@semantic-release/release-notes-generator" "^9.0.0"
aggregate-error "^3.0.0"
cosmiconfig "^7.0.0"
debug "^4.0.0"
env-ci "^5.0.0"
execa "^5.0.0"
figures "^3.0.0"
find-versions "^4.0.0"
get-stream "^6.0.0"
git-log-parser "^1.2.0"
hook-std "^2.0.0"
hosted-git-info "^4.0.0"
lodash "^4.17.15"
marked "^2.0.0"
marked-terminal "^4.1.1"
micromatch "^4.0.2"
p-each-series "^2.1.0"
p-reduce "^2.0.0"
read-pkg-up "^7.0.0"
resolve-from "^5.0.0"
semver "^7.3.2"
semver-diff "^3.1.1"
signale "^1.2.1"
yargs "^16.2.0"
semaphore@1.1.0, semaphore@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa"
@@ -32353,6 +32553,11 @@ semver-regex@^2.0.0:
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338"
integrity sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==
semver-regex@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807"
integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA==
"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -34429,6 +34634,11 @@ symbol-observable@1.2.0, symbol-observable@^1.0.4, symbol-observable@^1.1.0:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
symbol-observable@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-3.0.0.tgz#eea8f6478c651018e059044268375c408c15c533"
integrity sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -35607,6 +35817,11 @@ typescript@^4.2.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
typescript@~4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
ua-parser-js@^0.7.18:
version "0.7.24"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
@@ -38465,7 +38680,7 @@ yargs@13.3.2, yargs@^13.3.0, yargs@^13.3.2:
y18n "^4.0.0"
yargs-parser "^13.1.2"
yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3:
yargs@16.2.0, yargs@^16.0.0, yargs@^16.0.3, yargs@^16.2.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==