feat(nx): add presets to simplify the creation of workspaces with angular and fullstack apps

This commit is contained in:
Victor Savkin 2019-02-24 17:11:26 -05:00
parent a6fd16d00e
commit 7f2c16f3a3
35 changed files with 519 additions and 174 deletions

View File

@ -6,3 +6,4 @@ node_modules
packages/schematics/src/collection/**/files/*.json
/.vscode
/.idea
/.github

View File

@ -19,7 +19,7 @@ import {
import { toClassName } from '@nrwl/schematics/src/utils/name-utils';
describe('Nrwl Workspace', () => {
fit('should work', async () => {
it('should work', async () => {
ensureProject();
const myapp = uniq('myapp');
const mylib = uniq('mylib');

View File

@ -14,7 +14,7 @@ import * as http from 'http';
import * as path from 'path';
import * as treeKill from 'tree-kill';
function getData() {
function getData(): Promise<any> {
return new Promise(resolve => {
http.get('http://localhost:3333/api', res => {
expect(res.statusCode).toEqual(200);
@ -23,7 +23,7 @@ function getData() {
data += chunk;
});
res.once('end', () => {
resolve(data);
resolve(JSON.parse(data));
});
});
});
@ -77,7 +77,7 @@ describe('Node Applications', () => {
expect(data.toString()).toContain('Listening at http://localhost:3333');
const result = await getData();
expect(result).toEqual(`Welcome to ${nodeapp}!`);
expect(result.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
resolve();
@ -118,7 +118,7 @@ describe('Node Applications', () => {
}
const result = await getData();
expect(result).toEqual(`Welcome to ${nodeapp}!`);
expect(result.message).toEqual(`Welcome to ${nodeapp}!`);
treeKill(process.pid, 'SIGTERM', err => {
expect(collectedOutput.startsWith('DONE')).toBeTruthy();
expect(err).toBeFalsy();
@ -164,7 +164,7 @@ describe('Node Applications', () => {
if (message.includes('Listening at http://localhost:3333')) {
const result = await getData();
expect(result).toEqual(`Welcome to ${nestapp}!`);
expect(result.message).toEqual(`Welcome to ${nestapp}!`);
treeKill(server.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
resolve();
@ -186,7 +186,7 @@ describe('Node Applications', () => {
return;
}
const result = await getData();
expect(result).toEqual(`Welcome to ${nestapp}!`);
expect(result.message).toEqual(`Welcome to ${nestapp}!`);
treeKill(process.pid, 'SIGTERM', err => {
expect(err).toBeFalsy();
done();

View File

@ -51,6 +51,7 @@
"tmp": "0.0.33",
"viz.js": "^1.8.1",
"yargs-parser": "10.0.0",
"yargs": "^11.0.0"
"yargs": "^11.0.0",
"prettier": "1.15.3"
}
}

View File

@ -13,7 +13,14 @@
"ng-new": {
"factory": "./collection/ng-new",
"schema": "./collection/ng-new/schema.json",
"description": "Create an empty workspace"
"description": "Create a workspace"
},
"workspace": {
"factory": "./collection/workspace",
"schema": "./collection/workspace/schema.json",
"description": "Create an empty workspace",
"hidden": true
},
"application": {

View File

@ -1,73 +1,217 @@
import {
apply,
branchAndMerge,
chain,
mergeWith,
move,
noop,
Rule,
schematic,
SchematicContext,
template,
Tree,
url
Tree
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import { strings } from '@angular-devkit/core';
import { addImportToModule, insert } from '../../utils/ast-utils';
import * as ts from 'typescript';
import { insertImport } from '@schematics/angular/utility/ast-utils';
import {
NodePackageInstallTask,
RepositoryInitializerTask
} from '@angular-devkit/schematics/tasks';
import { libVersions } from '../../lib-versions';
import { DEFAULT_NRWL_PRETTIER_CONFIG } from '../../utils/common';
export default function(options: Schema): Rule {
if (!options.name) {
throw new Error(`Invalid options, "name" is required.`);
}
if (!options.directory) {
options.directory = options.name;
}
const workspaceOpts = { ...options, preset: undefined };
return (host: Tree, context: SchematicContext) => {
addTasks(options, context);
const npmScope = options.npmScope ? options.npmScope : options.name;
const templateSource = apply(url('./files'), [
template({
utils: strings,
dot: '.',
tmpl: '',
...libVersions,
...(options as object),
npmScope,
defaultNrwlPrettierConfig: JSON.stringify(
DEFAULT_NRWL_PRETTIER_CONFIG,
null,
2
)
})
return chain([
schematic('workspace', workspaceOpts),
createPreset(options),
move('/', options.directory),
addTasks(options)
])(Tree.empty(), context);
};
}
function createPreset(options: Schema): Rule {
if (options.preset === 'empty') {
return noop();
} else if (options.preset === 'angular') {
return chain([
schematic(
'application',
{ name: options.name, style: options.style },
{ interactive: false }
)
]);
return chain([branchAndMerge(chain([mergeWith(templateSource)]))])(
host,
context
} else {
return chain([
schematic(
'application',
{ name: options.name, style: options.style },
{ interactive: false }
),
schematic(
'node-application',
{
name: 'api',
frontendProject: options.name
},
{ interactive: false }
),
schematic(
'library',
{ name: 'api-interface', framework: 'none' },
{ interactive: false }
),
connectFrontendAndApi(options)
]);
}
}
function connectFrontendAndApi(options: Schema) {
return (host: Tree) => {
host.create(
'libs/api-interface/src/lib/interfaces.ts',
`export interface Message { message: string }`
);
host.overwrite(
'libs/api-interface/src/index.ts',
`export * from './lib/interfaces';`
);
const modulePath = `apps/${options.name}/src/app/app.module.ts`;
const moduleFile = ts.createSourceFile(
modulePath,
host.read(modulePath).toString(),
ts.ScriptTarget.Latest,
true
);
insert(host, modulePath, [
insertImport(
moduleFile,
modulePath,
'HttpClientModule',
`@angular/common/http`
),
...addImportToModule(
moduleFile,
`@angular/common/http`,
`HttpClientModule`
)
]);
const scope = options.npmScope ? options.npmScope : options.name;
const style = options.style ? options.style : 'css';
host.overwrite(
`apps/${options.name}/src/app/app.component.ts`,
`import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Message } from '@${scope}/api-interface';
@Component({
selector: '${scope}-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.${style}']
})
export class AppComponent {
hello$ = this.http.get<Message>('/api/hello')
constructor(private http: HttpClient) {}
}
`
);
host.overwrite(
`apps/${options.name}/src/app/app.component.spec.ts`,
`import { Component } from '@angular/core';
import { TestBed, async } from '@angular/core/testing';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
imports: [HttpClientModule]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
`
);
host.overwrite(
`apps/${options.name}/src/app/app.component.html`,
`<div style="text-align:center">
<h1>Welcome to ${options.name}!</h1>
<img
width="300"
src="https://raw.githubusercontent.com/nrwl/nx/master/nx-logo.png"
/>
</div>
<div>Message: {{ (hello$|async)|json }}</div>
`
);
host.overwrite(
`apps/api/src/app/app.controller.ts`,
`import { Controller, Get } from '@nestjs/common';
import { Message } from '@${scope}/api-interface';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get('hello')
getData(): Message {
return this.appService.getData();
}
}
`
);
host.overwrite(
`apps/api/src/app/app.service.ts`,
`import { Injectable } from '@nestjs/common';
import { Message } from '@${scope}/api-interface';
@Injectable()
export class AppService {
getData(): Message {
return { message: 'Welcome to api!' };
}
}
`
);
};
}
function addTasks(options: Schema, context: SchematicContext) {
let packageTask;
if (!options.skipInstall) {
packageTask = context.addTask(
new NodePackageInstallTask(options.directory)
);
}
if (!options.skipGit) {
const commit =
typeof options.commit == 'object'
? options.commit
: !!options.commit
? {}
: false;
context.addTask(
new RepositoryInitializerTask(options.directory, commit),
packageTask ? [packageTask] : []
);
}
function addTasks(options: Schema) {
return (host: Tree, context: SchematicContext) => {
let packageTask;
if (!options.skipInstall) {
packageTask = context.addTask(
new NodePackageInstallTask(options.directory)
);
}
if (!options.skipGit) {
const commit =
typeof options.commit == 'object'
? options.commit
: !!options.commit
? {}
: false;
context.addTask(
new RepositoryInitializerTask(options.directory, commit),
packageTask ? [packageTask] : []
);
}
};
}

View File

@ -1,10 +1,8 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { Tree } from '@angular-devkit/schematics';
import { readJsonInTree } from '../../utils/ast-utils';
import { NxJson } from '../../command-line/shared';
describe('app', () => {
describe('ng-new', () => {
const schematicRunner = new SchematicTestRunner(
'@nrwl/schematics',
path.join(__dirname, '../../collection.json')
@ -16,80 +14,29 @@ describe('app', () => {
projectTree = Tree.empty();
});
it('should update angular.json', () => {
const tree = schematicRunner.runSchematic(
'ng-new',
{ name: 'proj' },
projectTree
);
it('should create files (preset = angular)', async () => {
const tree = await schematicRunner
.runSchematicAsync(
'ng-new',
{ name: 'proj', preset: 'angular' },
projectTree
)
.toPromise();
expect(tree.exists('/proj/apps/proj/src/app/app.component.ts')).toBe(true);
});
it('should create files', () => {
const tree = schematicRunner.runSchematic(
'ng-new',
{ name: 'proj' },
projectTree
it('should create files (preset = fullstack)', async () => {
const tree = await schematicRunner
.runSchematicAsync(
'ng-new',
{ name: 'proj', preset: 'fullstack' },
projectTree
)
.toPromise();
expect(tree.exists('/proj/apps/proj/src/app/app.component.ts')).toBe(true);
expect(tree.exists('/proj/apps/api/src/app/app.controller.ts')).toBe(true);
expect(tree.exists('/proj/libs/api-interface/src/lib/interfaces.ts')).toBe(
true
);
expect(tree.exists('/proj/nx.json')).toBe(true);
expect(tree.exists('/proj/angular.json')).toBe(true);
expect(tree.exists('/proj/.prettierrc')).toBe(true);
expect(tree.exists('/proj/.prettierignore')).toBe(true);
});
it('should create nx.json', () => {
const tree = schematicRunner.runSchematic(
'ng-new',
{ name: 'proj' },
projectTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/proj/nx.json');
expect(nxJson).toEqual({
npmScope: 'proj',
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tsconfig.json': '*',
'tslint.json': '*',
'nx.json': '*'
},
projects: {}
});
});
it('should recommend vscode extensions', () => {
const tree = schematicRunner.runSchematic(
'ng-new',
{ name: 'proj' },
projectTree
);
const recommendations = readJsonInTree<{ recommendations: string[] }>(
tree,
'/proj/.vscode/extensions.json'
).recommendations;
expect(recommendations).toEqual([
'nrwl.angular-console',
'angular.ng-template',
'ms-vscode.vscode-typescript-tslint-plugin',
'esbenp.prettier-vscode'
]);
});
it('should configure the project to use style argument', () => {
const tree = schematicRunner.runSchematic(
'ng-new',
{ name: 'proj', style: 'scss' },
projectTree
);
expect(
JSON.parse(tree.readContent('/proj/angular.json')).schematics
).toEqual({
'@nrwl/schematics:application': {
style: 'scss'
},
'@nrwl/schematics:library': {
style: 'scss'
}
});
});
});

View File

@ -1,5 +1,3 @@
import { UnitTestRunner } from '../../utils/test-runners';
export interface Schema {
directory: string;
name: string;
@ -7,5 +5,6 @@ export interface Schema {
skipInstall?: boolean;
skipGit?: boolean;
style?: string;
preset: 'empty' | 'angular' | 'fullstack';
commit?: { name: string; email: string; message?: string };
}

View File

@ -74,6 +74,29 @@
}
],
"default": true
},
"preset": {
"description": "What to create in the new workspace",
"enum": ["empty", "angular", "fullstack"],
"default": "empty",
"x-prompt": {
"message": "What to create in the new workspace (You can create other applications and libraries at any point using 'ng g')",
"type": "list",
"items": [
{
"value": "empty",
"label": "empty [an empty workspace]"
},
{
"value": "angular",
"label": "angular [a workspace with a single Angular application]"
},
{
"value": "fullstack",
"label": "full-stack [a workspace with a full stack application (NestJS + Angular)]"
}
]
}
}
}
}

View File

@ -8,7 +8,7 @@ import * as express from 'express';
const app = express();
app.get('/api', (req, res) => {
res.send(`Welcome to <%= name %>!`);
res.send({message: `Welcome to <%= name %>!`});
});
const port = process.env.port || 3333;

View File

@ -16,7 +16,7 @@ describe('AppController', () => {
describe('getData', () => {
it('should return "Welcome to <%= name %>!"', () => {
const appController = app.get<AppController>(AppController);
expect(appController.getData()).toBe('Welcome to <%= name %>!');
expect(appController.getData()).toEqual({message: 'Welcome to <%= name %>!'});
});
});
});

View File

@ -7,7 +7,7 @@ export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getData(): string {
getData() {
return this.appService.getData();
}
}

View File

@ -15,7 +15,7 @@ describe('AppService', () => {
describe('getData', () => {
it('should return "Welcome to <%= name %>!"', () => {
expect(service.getData()).toBe('Welcome to <%= name %>!');
expect(service.getData()).toEqual({message: 'Welcome to <%= name %>!'});
});
});
});

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getData(): string {
return 'Welcome to <%= name %>!';
getData(): { message: string } {
return ({ message: 'Welcome to <%= name %>!' });
}
}

View File

@ -94,7 +94,7 @@ describe('node-app', () => {
'const app = express();'
);
expect(tree.readContent('apps/my-node-app/src/main.ts')).toContain(
'res.send(`Welcome to my-node-app!`);'
'res.send({message: `Welcome to my-node-app!`});'
);
const tsconfig = readJsonInTree(tree, 'apps/my-node-app/tsconfig.json');

View File

@ -5,11 +5,7 @@
"rootDir": ".",
"module": "commonjs",
"target": "es5",
"types": [
"node"
]
"types": ["node"]
},
"include": [
"**/*.ts"
]
"include": ["**/*.ts"]
}

View File

@ -0,0 +1,44 @@
import {
apply,
branchAndMerge,
chain,
mergeWith,
Rule,
SchematicContext,
template,
Tree,
url
} from '@angular-devkit/schematics';
import { Schema } from './schema';
import { strings } from '@angular-devkit/core';
import { libVersions } from '../../lib-versions';
import { DEFAULT_NRWL_PRETTIER_CONFIG } from '../../utils/common';
export default function(options: Schema): Rule {
if (!options.name) {
throw new Error(`Invalid options, "name" is required.`);
}
return (host: Tree, context: SchematicContext) => {
const npmScope = options.npmScope ? options.npmScope : options.name;
const templateSource = apply(url('./files'), [
template({
utils: strings,
dot: '.',
tmpl: '',
...libVersions,
...(options as object),
npmScope,
defaultNrwlPrettierConfig: JSON.stringify(
DEFAULT_NRWL_PRETTIER_CONFIG,
null,
2
)
})
]);
return chain([branchAndMerge(chain([mergeWith(templateSource)]))])(
host,
context
);
};
}

View File

@ -0,0 +1,11 @@
import { UnitTestRunner } from '../../utils/test-runners';
export interface Schema {
directory: string;
name: string;
npmScope?: string;
skipInstall?: boolean;
skipGit?: boolean;
style?: string;
commit?: { name: string; email: string; message?: string };
}

View File

@ -0,0 +1,79 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsNxNgNew",
"title": "Create an empty workspace",
"type": "object",
"properties": {
"name": {
"description": "The name of the workspace.",
"type": "string",
"format": "html-selector",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the workspace?"
},
"style": {
"description": "The file extension to be used for style files.",
"type": "string",
"default": "css",
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{ "value": "scss", "label": "SCSS [ http://sass-lang.com ]" },
{ "value": "sass", "label": "SASS [ http://sass-lang.com ]" },
{ "value": "less", "label": "LESS [ http://lesscss.org ]" },
{ "value": "styl", "label": "Stylus [ http://stylus-lang.com ]" }
]
}
},
"directory": {
"type": "string",
"format": "path",
"description": "The directory name to create the workspace in.",
"default": ""
},
"npmScope": {
"type": "string",
"description": "Npm scope for importing libs.",
"x-prompt": "What is the npm scope you would like to use for your Nx Workspace?"
},
"skipInstall": {
"description": "Skip installing dependency packages.",
"type": "boolean",
"default": false
},
"skipGit": {
"description": "Skip initializing a git repository.",
"type": "boolean",
"default": false,
"alias": "g"
},
"commit": {
"description": "Initial repository commit information.",
"oneOf": [
{ "type": "boolean" },
{
"type": "object",
"properties": {
"name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"message": {
"type": "string"
}
},
"required": ["name", "email"]
}
],
"default": true
}
}
}

View File

@ -0,0 +1,93 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { Tree } from '@angular-devkit/schematics';
import { readJsonInTree } from '../../utils/ast-utils';
import { NxJson } from '../../command-line/shared';
describe('workspace', () => {
const schematicRunner = new SchematicTestRunner(
'@nrwl/schematics',
path.join(__dirname, '../../collection.json')
);
let projectTree: Tree;
beforeEach(() => {
projectTree = Tree.empty();
});
it('should update angular.json', () => {
const tree = schematicRunner.runSchematic(
'workspace',
{ name: 'proj' },
projectTree
);
});
it('should create files', () => {
const tree = schematicRunner.runSchematic(
'workspace',
{ name: 'proj' },
projectTree
);
expect(tree.exists('/nx.json')).toBe(true);
expect(tree.exists('/angular.json')).toBe(true);
expect(tree.exists('/.prettierrc')).toBe(true);
expect(tree.exists('/.prettierignore')).toBe(true);
});
it('should create nx.json', () => {
const tree = schematicRunner.runSchematic(
'workspace',
{ name: 'proj' },
projectTree
);
const nxJson = readJsonInTree<NxJson>(tree, '/nx.json');
expect(nxJson).toEqual({
npmScope: 'proj',
implicitDependencies: {
'angular.json': '*',
'package.json': '*',
'tsconfig.json': '*',
'tslint.json': '*',
'nx.json': '*'
},
projects: {}
});
});
it('should recommend vscode extensions', () => {
const tree = schematicRunner.runSchematic(
'workspace',
{ name: 'proj' },
projectTree
);
const recommendations = readJsonInTree<{ recommendations: string[] }>(
tree,
'/.vscode/extensions.json'
).recommendations;
expect(recommendations).toEqual([
'nrwl.angular-console',
'angular.ng-template',
'ms-vscode.vscode-typescript-tslint-plugin',
'esbenp.prettier-vscode'
]);
});
it('should configure the project to use style argument', () => {
const tree = schematicRunner.runSchematic(
'workspace',
{ name: 'proj', style: 'scss' },
projectTree
);
expect(JSON.parse(tree.readContent('/angular.json')).schematics).toEqual({
'@nrwl/schematics:application': {
style: 'scss'
},
'@nrwl/schematics:library': {
style: 'scss'
}
});
});
});

View File

@ -2,7 +2,7 @@
# yarn lockfile v1
"@angular-devkit/architect@0.13.1", "@angular-devkit/architect@~0.13.1":
"@angular-devkit/architect@0.13.1":
version "0.13.1"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.13.1.tgz#39597ce94f72d89bdd89ee567cb937cff4c13b98"
integrity sha512-QDmIbqde75ZZSEFbw6Q6kQWq4cY6C7D67yujXw6XTyubDNAs1tyXJyxTIB8vjSlEKwRizTTDd/B0ZXVcke3Mvw==
@ -10,7 +10,7 @@
"@angular-devkit/core" "7.3.1"
rxjs "6.3.3"
"@angular-devkit/build-angular@~0.13.1":
"@angular-devkit/build-angular@0.13.1":
version "0.13.1"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.13.1.tgz#369febda48dd40e47a4f0077064e792612a8e1c1"
integrity sha512-vkKwMVQ+NNCcVR3HFMffS+Mq4b2afXeUjI+02N38hBuFTppnC83uivUB6Uu2NUk5NTSQA4BnJlG5CbMs6N4QYg==
@ -62,7 +62,7 @@
optionalDependencies:
node-sass "4.11.0"
"@angular-devkit/build-ng-packagr@^0.13.1":
"@angular-devkit/build-ng-packagr@0.13.1":
version "0.13.1"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-ng-packagr/-/build-ng-packagr-0.13.1.tgz#3508523a039f71ccff1364db553a904e4db2c8ea"
integrity sha512-9qvdNvtlgJ3WDppbzwD9fOQzAsVogBlDeLE5zUH1ap+zcoyZEGjS1BKluiYSJ1u5Q5Nlfb3FSI/D1r9LuDQS/A==
@ -82,7 +82,7 @@
typescript "3.2.4"
webpack-sources "1.3.0"
"@angular-devkit/build-webpack@0.13.1", "@angular-devkit/build-webpack@~0.13.1":
"@angular-devkit/build-webpack@0.13.1":
version "0.13.1"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.13.1.tgz#98d666765705e9379c9b2e0a3b6dfcd0347a2a32"
integrity sha512-OGwC7bAl3u+w7Glw+OqIrN7OD1BkDXgrWbeQSpKAmsx6VdNPCnI4NPS+JldWNp70LVlE2nQlJUhtEqMVfBMnlg==
@ -102,10 +102,10 @@
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/core@7.2.4":
version "7.2.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.2.4.tgz#4464d536f4838e5c61ab4bc1ff6b7f221fd43056"
integrity sha512-XHF59tIHg2qEM1Wd415xhykBLjjfOK6yMB7CjNk1bToUMX2QDT87izJF4y1Vwa0lIw9G0jdgP/4/M/OqXcbYmA==
"@angular-devkit/core@7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.2.2.tgz#f0daf3e24f0ce8105341118f4505c1db4e284ab0"
integrity sha512-gDF8iXiPN870WuBMl7bCQ5+Qz5SjGL/qMcvP4hli5hkn+kMAhgG38ligUK1bbhPQUJ+Z/nSOEmyv8gLHO09SZg==
dependencies:
ajv "6.6.2"
chokidar "2.0.4"
@ -132,12 +132,12 @@
"@angular-devkit/core" "7.1.2"
rxjs "6.3.3"
"@angular-devkit/schematics@7.2.4":
version "7.2.4"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.2.4.tgz#d92b2b8b9114135806d593ea6a2b376c777354dc"
integrity sha512-ObIDnIxXRpts+Jzs0PQ7JVuK4d5vWEh9K+Ow8nMO5/LmYJQ8/2nMEQo/9lhdKPMiXmhbuvB7qZL5J+cxwwijhw==
"@angular-devkit/schematics@7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.2.2.tgz#d8d667684603e1debcc4598d88a254560e787f87"
integrity sha512-3qONTeqe+bUJ967PNDeITuD4F+3huTEs3u89zZLV+yvaxoK9XJlvaRoQXAkNAMUkij37BoFrGgBfGNijserd6A==
dependencies:
"@angular-devkit/core" "7.2.4"
"@angular-devkit/core" "7.2.2"
rxjs "6.3.3"
"@angular-devkit/schematics@7.3.1", "@angular-devkit/schematics@~7.3.1":
@ -148,7 +148,7 @@
"@angular-devkit/core" "7.3.1"
rxjs "6.3.3"
"@angular/cli@~7.3.1":
"@angular/cli@7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-7.3.1.tgz#a18acdec84deb03a1fae79cae415bbc8f9c87ffa"
integrity sha512-8EvXYRhTqTaTk5PKv7VZxIWJiyG51R9RC9gtpRFx4bbnurqBHdEUxGMmaRsGT8QDbfvVsWnuakE0eeW1CrfZAQ==
@ -461,6 +461,15 @@
universal-user-agent "^2.0.0"
url-template "^2.0.8"
"@schematics/angular@7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.2.2.tgz#5a466ebbbd7e1fbb13851f26446ec308b822d1dc"
integrity sha512-Yonddct1XBG1H5rTikagFTIT2/RhszJnNa2Iz+rvc26ffAl1mmYPB4sQb7gkOaZQSzK6SE7bT2QW32PVjYFoSQ==
dependencies:
"@angular-devkit/core" "7.2.2"
"@angular-devkit/schematics" "7.2.2"
typescript "3.2.2"
"@schematics/angular@7.3.1":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.3.1.tgz#6fcd7004210fa9305310c3109c084df5c5521776"
@ -470,15 +479,6 @@
"@angular-devkit/schematics" "7.3.1"
typescript "3.2.4"
"@schematics/angular@~7.2.2":
version "7.2.4"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-7.2.4.tgz#4c3c942e2fd0c32e177602a20f8fa16a88552fca"
integrity sha512-aflQwIX4E9tDhp6ZASuQCm8CzxLxdkuOe6qN1FbCxpxMUc9E+iK9jhOjw+Xnl3boJpWHAA+k9JO1sYe3wrh3Ng==
dependencies:
"@angular-devkit/core" "7.2.4"
"@angular-devkit/schematics" "7.2.4"
typescript "3.2.2"
"@schematics/update@0.13.1":
version "0.13.1"
resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.13.1.tgz#481475aee18b4a9472a06512b2e4d6429af68231"
@ -3650,7 +3650,7 @@ cyclist@~0.2.2:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=
cypress@^3.1.0:
cypress@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.0.tgz#b718ba64289b887c7ab7a7f09245d871a4a409ba"
integrity sha512-UqLbXgHvM8Y6Y+roHrepZMWcyMN5u4KcjpTbJTZi0d5O2Prvtqmnpoky7a4C65q4oRQXeSc6cBZUhxJkhU4pbQ==
@ -12090,7 +12090,7 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@3.2.2, typescript@~3.2.2:
typescript@3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==