feat(schematics): use @ngrx/schematics

* ngrx collection now extends @ngrx/schematics
* modify the nrwl `ngrx` schematic to use `@ngrx/schematics` to generate the initial source for actions, reducers, and effects
* modify the generated source to conform to Nx standards
* generate init and interface files
* remove template files for action, effect, and reducer scaffolding
* modify collection to extend `@ngrx/schematics` so the ngrx commands are also easily accessible
* inject dataloaded enums into action class
* configure nx workspace devDep on @ngrx/schematics version
* override ngrx effects.spec.ts with nx templated version

fixes #174.
This commit is contained in:
Thomas Burleson 2018-03-27 11:38:20 -05:00 committed by Victor Savkin
parent 2acf28c50d
commit 4edc8e6c8b
34 changed files with 1662 additions and 530 deletions

View File

@ -1,4 +1,4 @@
import { newApp, newProject, runCLI, updateFile } from '../utils';
import { newApp, newProject, runCLI } from '../utils';
describe('ngrx', () => {
it(
@ -9,47 +9,11 @@ describe('ngrx', () => {
runCLI(
'generate ngrx app --module=apps/myapp/src/app/app.module.ts --root --collection=@nrwl/schematics'
);
updateFile(
'apps/myapp/src/app/+state/app.interfaces.ts',
`
export interface App {
rootCount: number;
}
export interface AppState {
readonly app: App;
}
`
);
updateFile(
'apps/myapp/src/app/+state/app.init.ts',
`
import { App } from './app.interfaces';
export const appInitialState: App = {
rootCount: 0
};
`
);
console.log('build');
console.log(runCLI('build'));
updateFile(
'apps/myapp/src/app/+state/app.reducer.spec.ts',
`
import { appReducer } from './app.reducer';
import { App } from './app.interfaces';
import { DataLoaded } from './app.actions';
describe('appReducer', () => {
it('should work', () => {
const state: App = {rootCount: 0};
const action: DataLoaded = {type: 'DATA_LOADED', payload: {}};
const actual = appReducer(state, action);
expect(actual).toEqual({rootCount: 0});
});
});
`
);
runCLI('build');
console.log('test');
runCLI('test --single-run');
},
1000000

View File

@ -5,8 +5,8 @@ import {
runCLI,
runNgNew,
updateFile,
readJson,
readFile
readFile,
readJson
} from '../utils';
import { angularCliSchema } from '../../packages/schematics/src/lib-versions';

View File

@ -53,7 +53,8 @@ export function copyMissingPackages(): void {
'jasmine-marbles',
'@nrwl',
'angular',
'@angular/upgrade'
'@angular/upgrade',
'npm-run-all'
];
modulesToCopy.forEach(m => copyNodeModule(projectName, m));
}

View File

@ -34,6 +34,7 @@
"@angular/upgrade": "5.2.7",
"@ngrx/effects": "5.2.0",
"@ngrx/router-store": "5.2.0",
"@ngrx/schematics": "^5.2.0",
"@ngrx/store": "5.2.0",
"@ngrx/store-devtools": "5.2.0",
"@types/jasmine": "~2.8.3",
@ -67,6 +68,9 @@
"author": "Victor Savkin",
"license": "MIT",
"jest": {
"modulePathIgnorePatterns": ["tmp", "files"]
"modulePathIgnorePatterns": [
"tmp",
"files"
]
}
}

View File

@ -2,21 +2,42 @@
"name": "@nrwl/schematics",
"version": "0.0.1",
"description": "Nrwl Extensions for Angular: Schematics",
"repository": {
"type": "git",
"url": "git+https://github.com/nrwl/nx.git"
},
"keywords": [
"RxJS",
"Angular",
"Workspace",
"NgRx",
"Schematics",
"Angular CLI"
],
"bin": {
"create-nx-workspace": "./bin/create-nx-workspace.js",
"nx": "./src/command-line/nx.js"
},
"main": "index.js",
"types": "index.d.js",
"peerDependencies": {},
"peerDependencies": { },
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://github.com/nrwl/nx#readme",
"schematics": "./src/collection.json",
"dependencies": {
"@ngrx/schematics": "5.2.0",
"@schematics/angular": "0.4.6",
"app-root-path": "^2.0.1",
"npm-run-all": "4.1.2",
"semver": "5.4.1",
"tmp": "0.0.33",
"yargs-parser": "9.0.2"
},
"devDependencies": {
"@angular-devkit/schematics": "0.4.6"
}
}

View File

@ -0,0 +1,6 @@
@nrwl/schematics
=======
The sources for this package are in the main [nrwl/nx](https://github.com/nrwl/nx) repo. Please file issues and pull requests against that repo.
License: MIT

View File

@ -1,7 +1,7 @@
{
"name": "nx",
"version": "0.1",
"extends": "@schematics/angular",
"extends": "@ngrx/schematics",
"schematics": {
"workspace": {
"factory": "./collection/workspace",

View File

@ -47,6 +47,7 @@
"devDependencies": {
"@angular/cli": "<%= angularCliVersion %>",
"@angular/compiler-cli": "<%= angularVersion %>",
"@ngrx/schematics": "<%= ngrxSchematicsVersion %>",
"@nrwl/schematics": "<%= schematicsVersion %>",
"@angular/language-service": "<%= angularVersion %>",<% if (!minimal) { %>
"@types/jasmine": "~2.8.3",

View File

@ -1,12 +0,0 @@
export interface LoadData {
type: 'LOAD_DATA';
payload: {};
}
export interface DataLoaded {
type: 'DATA_LOADED';
payload: {};
}
export type <%= className %>Action = LoadData | DataLoaded;

View File

@ -1,13 +1,17 @@
import {TestBed, async} from '@angular/core/testing';
import {TestBed} from '@angular/core/testing';
import {StoreModule} from '@ngrx/store';
import {provideMockActions} from '@ngrx/effects/testing';
import {DataPersistence} from '@nrwl/nx';
import {hot} from '@nrwl/nx/testing';
import {<%= className %>Effects} from './<%= fileName %>.effects';
import {Load<%= className %>, <%= className %>Loaded } from './<%= fileName %>.actions';
import { Observable } from 'rxjs/Observable';
describe('<%= className %>Effects', () => {
let actions;
let effects: <%= className %>Effects;
let actions$: Observable<any>;
let effects$: <%= className %>Effects;
beforeEach(() => {
TestBed.configureTestingModule({
@ -17,18 +21,18 @@ describe('<%= className %>Effects', () => {
providers: [
<%= className %>Effects,
DataPersistence,
provideMockActions(() => actions)
provideMockActions(() => actions$)
],
});
effects = TestBed.get(<%= className %>Effects);
effects$ = TestBed.get(<%= className %>Effects);
});
describe('someEffect', () => {
it('should work', () => {
actions = hot('-a-|', {a: {type: 'LOAD_DATA'}});
expect(effects.loadData).toBeObservable(
hot('-a-|', {a: {type: 'DATA_LOADED', payload: {}}})
actions$ = hot('-a-|', {a: new Load<%= className %>({})});
expect(effects$.load<%= className %>$).toBeObservable(
hot('-a-|', {a: new <%= className %>Loaded({})})
);
});
});

View File

@ -1,25 +0,0 @@
import {Injectable} from '@angular/core';
import {Effect, Actions} from '@ngrx/effects';
import {DataPersistence} from '@nrwl/nx';
import {of} from 'rxjs/observable/of';
import 'rxjs/add/operator/switchMap';
import {<%= className %>State} from './<%= fileName %>.interfaces';
import {LoadData, DataLoaded} from './<%= fileName %>.actions';
@Injectable()
export class <%= className %>Effects {
@Effect() loadData = this.dataPersistence.fetch('LOAD_DATA', {
run: (action: LoadData, state: <%= className %>State) => {
return {
type: 'DATA_LOADED',
payload: {}
};
},
onError: (action: LoadData, error) => {
console.error('Error', error);
}
});
constructor(private actions: Actions, private dataPersistence: DataPersistence<<%= className %>State>) {}
}

View File

@ -1,5 +0,0 @@
import {<%= className %>} from './<%= fileName %>.interfaces';
export const <%= propertyName %>InitialState: <%= className %> = {
// fill it initial state here
};

View File

@ -1,7 +0,0 @@
export interface <%= className %> {
// define state here
}
export interface <%= className %>State {
readonly <%= propertyName %>: <%= className %>;
}

View File

@ -1,13 +1,10 @@
import { <%= propertyName %>Reducer } from './<%= fileName %>.reducer';
import { <%= propertyName %>InitialState } from './<%= fileName %>.init';
import { <%= className %> } from './<%= fileName %>.interfaces';
import { DataLoaded } from './<%= fileName %>.actions';
import { <%= className %>Loaded } from './<%= fileName %>.actions';
import { <%= propertyName %>Reducer, initialState } from './<%= fileName %>.reducer';
describe('<%= propertyName %>Reducer', () => {
it('should work', () => {
const state: <%= className %> = {};
const action: DataLoaded = {type: 'DATA_LOADED', payload: {}};
const actual = <%= propertyName %>Reducer(state, action);
const action: <%= className %>Loaded = new <%= className %>Loaded({});
const actual = <%= fileName %>Reducer(initialState, action);
expect(actual).toEqual({});
});
});

View File

@ -1,13 +0,0 @@
import {<%= className %>} from './<%= fileName %>.interfaces';
import {<%= className %>Action} from './<%= fileName %>.actions';
export function <%= propertyName %>Reducer(state: <%= className %>, action: <%= className %>Action): <%= className %> {
switch (action.type) {
case 'DATA_LOADED': {
return {...state, ...action.payload};
}
default: {
return state;
}
}
}

View File

@ -2,238 +2,116 @@ import {
apply,
branchAndMerge,
chain,
externalSchematic,
mergeWith,
move,
noop,
Rule,
template,
Tree,
url
} from '@angular-devkit/schematics';
import { Tree } from '@angular-devkit/schematics';
import {
names,
toClassName,
toFileName,
toPropertyName
} from '../../utils/name-utils';
import * as path from 'path';
import * as ts from 'typescript';
import {
addImportToModule,
addProviderToModule,
insert,
updateJson
} from '../../utils/ast-utils';
import { insertImport } from '@schematics/angular/utility/route-utils';
import { Schema } from './schema';
import {
ngrxVersion,
routerStoreVersion,
ngrxStoreFreezeVersion
} from '../../lib-versions';
import * as path from 'path';
import { names, toFileName } from '../../utils/name-utils';
import { wrapIntoFormat } from '../../utils/tasks';
function addImportsToModule(name: string, options: Schema): Rule {
return (host: Tree) => {
if (options.onlyAddFiles) {
return host;
}
import {
RequestContext,
updateNgrxReducers,
updateNgrxActions,
updateNgrxEffects,
addImportsToModule,
addNgRxToPackageJson
} from './rules';
import { deleteFile } from '../../utils/rules/deleteFile';
if (!host.exists(options.module)) {
throw new Error('Specified module does not exist');
}
const modulePath = options.module;
const sourceText = host.read(modulePath)!.toString('utf-8');
const source = ts.createSourceFile(
modulePath,
sourceText,
ts.ScriptTarget.Latest,
true
);
if (options.onlyEmptyRoot) {
insert(host, modulePath, [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'),
insertImport(
source,
modulePath,
'StoreDevtoolsModule',
'@ngrx/store-devtools'
),
insertImport(
source,
modulePath,
'environment',
'../environments/environment'
),
insertImport(
source,
modulePath,
'StoreRouterConnectingModule',
'@ngrx/router-store'
),
insertImport(source, modulePath, 'storeFreeze', 'ngrx-store-freeze'),
...addImportToModule(
source,
modulePath,
`StoreModule.forRoot({},{metaReducers: !environment.production ? [storeFreeze] : []})`
),
...addImportToModule(source, modulePath, `EffectsModule.forRoot([])`),
...addImportToModule(
source,
modulePath,
`!environment.production ? StoreDevtoolsModule.instrument() : []`
),
...addImportToModule(source, modulePath, `StoreRouterConnectingModule`)
]);
return host;
} else {
const reducerPath = `./${toFileName(options.directory)}/${toFileName(
name
)}.reducer`;
const effectsPath = `./${toFileName(options.directory)}/${toFileName(
name
)}.effects`;
const initPath = `./${toFileName(options.directory)}/${toFileName(
name
)}.init`;
const reducerName = `${toPropertyName(name)}Reducer`;
const effectsName = `${toClassName(name)}Effects`;
const initName = `${toPropertyName(name)}InitialState`;
const common = [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'),
insertImport(source, modulePath, reducerName, reducerPath),
insertImport(source, modulePath, initName, initPath),
insertImport(source, modulePath, effectsName, effectsPath),
...addProviderToModule(source, modulePath, effectsName)
];
if (options.root) {
insert(host, modulePath, [
...common,
insertImport(
source,
modulePath,
'StoreDevtoolsModule',
'@ngrx/store-devtools'
),
insertImport(
source,
modulePath,
'environment',
'../environments/environment'
),
insertImport(
source,
modulePath,
'StoreRouterConnectingModule',
'@ngrx/router-store'
),
insertImport(source, modulePath, 'storeFreeze', 'ngrx-store-freeze'),
...addImportToModule(
source,
modulePath,
`StoreModule.forRoot({${toPropertyName(name)}: ${reducerName}}, {
initialState: {${toPropertyName(name)}: ${initName}},
metaReducers: !environment.production ? [storeFreeze] : []
})`
),
...addImportToModule(
source,
modulePath,
`EffectsModule.forRoot([${effectsName}])`
),
...addImportToModule(
source,
modulePath,
`!environment.production ? StoreDevtoolsModule.instrument() : []`
),
...addImportToModule(
source,
modulePath,
`StoreRouterConnectingModule`
)
]);
} else {
insert(host, modulePath, [
...common,
...addImportToModule(
source,
modulePath,
`StoreModule.forFeature('${toPropertyName(
name
)}', ${reducerName}, {initialState: ${initName}})`
),
...addImportToModule(
source,
modulePath,
`EffectsModule.forFeature([${effectsName}])`
)
]);
}
return host;
}
/**
* Rule to generate the Nx 'ngrx' Collection
*/
export default function generateNgrxCollection(_options: Schema): Rule {
return wrapIntoFormat((host: Tree) => {
const options = normalizeOptions(_options);
const context: RequestContext = {
featureName: options.name,
moduleDir: path.dirname(options.module),
options,
host
};
}
function addNgRxToPackageJson(): Rule {
return updateJson('package.json', packageJson => {
if (!packageJson['dependencies']) {
packageJson['dependencies'] = {};
}
return chain([
branchAndMerge(generateNgrxFiles(context)),
branchAndMerge(generateNxFiles(context)),
if (!packageJson['dependencies']['@ngrx/store']) {
packageJson['dependencies']['@ngrx/store'] = ngrxVersion;
}
if (!packageJson['dependencies']['@ngrx/router-store']) {
packageJson['dependencies']['@ngrx/router-store'] = routerStoreVersion;
}
if (!packageJson['dependencies']['@ngrx/effects']) {
packageJson['dependencies']['@ngrx/effects'] = ngrxVersion;
}
if (!packageJson['dependencies']['@ngrx/store-devtools']) {
packageJson['dependencies']['@ngrx/store-devtools'] = ngrxVersion;
}
if (!packageJson['dependencies']['ngrx-store-freeze']) {
packageJson['dependencies']['ngrx-store-freeze'] = ngrxStoreFreezeVersion;
}
return packageJson;
addImportsToModule(context),
updateNgrxActions(context),
updateNgrxReducers(context),
updateNgrxEffects(context),
options.skipPackageJson ? noop() : addNgRxToPackageJson()
]);
});
}
export default function(schema: Schema): Rule {
return wrapIntoFormat(() => {
const options = normalizeOptions(schema);
const name = options.name;
const moduleDir = path.dirname(options.module);
// ********************************************************
// Internal Function
// ********************************************************
if (options.onlyEmptyRoot) {
return chain([
addImportsToModule(name, options),
options.skipPackageJson ? noop() : addNgRxToPackageJson()
]);
} else {
/**
* Generate the Nx files that are NOT created by the @ngrx/schematic(s)
*/
function generateNxFiles(context: RequestContext) {
const templateSource = apply(url('./files'), [
template({ ...options, tmpl: '', ...names(name) }),
move(moduleDir)
template({ ...context.options, tmpl: '', ...names(context.featureName) }),
move(context.moduleDir)
]);
return chain([
branchAndMerge(chain([mergeWith(templateSource)])),
addImportsToModule(name, options),
options.skipPackageJson ? noop() : addNgRxToPackageJson()
]);
}
});
return chain([mergeWith(templateSource)]);
}
/**
* Using @ngrx/schematics, generate scaffolding for 'feature': action, reducer, effect files
*/
function generateNgrxFiles(context: RequestContext) {
const filePrefix = `app/${context.featureName}/${context.featureName}`;
return chain([
externalSchematic('@ngrx/schematics', 'feature', {
name: context.featureName,
sourceDir: './',
flat: false
}),
deleteFile(`/${filePrefix}.effects.spec.ts`),
deleteFile(`/${filePrefix}.reducer.spec.ts`),
moveToNxMonoTree(
context.featureName,
context.moduleDir,
context.options.directory
)
]);
}
/**
* @ngrx/schematics generates files in:
* `/apps/<ngrxFeatureName>/`
*
* For Nx monorepo, however, we need to move the files to either
* a) apps/<appName>/src/app/<directory>, or
* b) libs/<libName>/src/<directory>
*/
function moveToNxMonoTree(
ngrxFeatureName: string,
nxDir: string,
directory: string
): Rule {
return move(`app/${ngrxFeatureName}`, path.join(nxDir, directory));
}
/**
* Extract the parent 'directory' for the specified
*/
function normalizeOptions(options: Schema): Schema {
return { ...options, directory: toFileName(options.directory) };
}

View File

@ -0,0 +1,203 @@
# ngrx
--------
## Overview
Generates a ngrx feature set containing an `init`, `interfaces`, `actions`, `reducer` and `effects` files.
You use this schematic to build out a new ngrx feature area that provides a new piece of state.
## Command
```sh
ng generate ngrx FeatureName [options]
```
##### OR
```sh
ng generate f FeatureName [options]
```
### Options
Specifies the name of the ngrx feature (e.g., Products, User, etc.)
- `name`
- Type: `string`
- Required: true
Path to Angular Module. Also used to determine the parent directory for the new **+state**
directory; unless the `--directory` option is used to override the dir name.
> e.g. --module=apps/myapp/src/app/app.module.ts
- `--module`
- Type: `string`
- Required: true
Specifies the directory name used to nest the **ngrx** files within a folder.
- `--directory`
- Type: `string`
- Default: `+state`
#### Examples
Generate a `User` feature set and register it within an `Angular Module`.
```sh
ng generate ngrx User -m apps/myapp/src/app/app.module.ts
ng g ngrx Producrts -m libs/mylib/src/mylib.module.ts
```
Generate a `User` feature set within a `user` folder and register it with the `user.module.ts` file in the same `user` folder.
```sh
ng g ngrx User -m apps/myapp/src/app/app.module.ts -directory user
```
## Generated Files
The files generated are shown below and include placeholders for the *feature* name specified.
> The &lt;Feature&gt; notation used be below indicates a placeholder for the actual *feature* name.
* [&lt;feature&gt;.actions.ts](#featureactionsts)
* [&lt;feature&gt;.reducer.ts](#featurereducerts)
* [&lt;feature&gt;.effects.ts](#featureeffectsts)
* [&lt;feature&gt;.selectors.ts](#featureselectorsts)
* [&lt;feature&gt;.facade.ts](#featurefacadests)
* [../app.module.ts](#appmodulets)
#### &lt;feature&gt;.actions.ts
```ts
import {Action} from "@ngrx/store";
export enum <Feature>ActionTypes {
<Feature> = "[<Feature>] Action",
Load<Feature> = "[<Feature>] Load Data",
<Feature>Loaded = "[<Feature>] Data Loaded"
}
export class <Feature> implements Action {
readonly type = <Feature>ActionTypes.<Feature>;
}
export class Load<Feature> implements Action {
readonly type = <Feature>ActionTypes.Load<Feature>;
constructor(public payload: any) { }
}
export class DataLoaded implements Action {
readonly type = <Feature>ActionTypes.<Feature>Loaded;
constructor(public payload: any) { }
}
export type <Feature>Actions = <Feature> | Load<Feature> | <Feature>Loaded;
```
#### &lt;feature&gt;.reducer.ts
```ts
import { <Feature> } from './<feature>.interfaces';
import { <Feature>Action, <Feature>ActionTypes } from './<feature>.actions';
/**
* Interface for the '<Feature>' data used in
* - <Feature>State, and
* - <feature>Reducer
*/
export interface <Feature>Data {
}
/**
* Interface to the part of the Store containing <Feature>State
* and other information related to <Feature>Data.
*/
export interface <Feature>State {
readonly <feature>: <Feature>Data;
}
export const initialState: <Feature>Data = { };
export function <feature>Reducer(state: <Feature>Data = initialState, action: <Feature>Actions): <Feature>Data {
switch (action.type) {
case <Feature>ActionTypes.<Feature>Loaded: {
return { ...state, ...action.payload };
}
default: {
return state;
}
}
}
```
#### &lt;feature&gt;.effects.ts
```ts
import { Injectable } from '@angular/core';
import { Effect, Actions } from '@ngrx/effects';
import { DataPersistence } from '@nrwl/nx';
import { <Feature> } from './<feature>.interfaces';
import { Load<Feature>, <Feature>Loaded, <Feature>ActionTypes } from './<feature>.actions';
@Injectable()
export class <Feature>Effects {
@Effect() load<Feature>$ = this.dataPersistence.fetch(<Feature>ActionTypes.Load<Feature>, {
run: (action: Load<Feature>, state: <Feature>) => {
return new <Feature>Loaded({});
},
onError: (action: Load<Feature>, error) => {
console.error('Error', error);
}
});
constructor(
private actions: Actions,
private dataPersistence: DataPersistence<Feature>) { }
}
```
#### ../app.module.ts
```ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import {
<feature>Reducer,
<Feature>State,
<Feature>Data,
initialState as <feature>InitialState
} from './+state/<Feature>.reducer';
import { <Feature>Effects } from './+state/<Feature>.effects';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import { StoreRouterConnectingModule } from '@ngrx/router-store';
import { storeFreeze } from 'ngrx-store-freeze';
@NgModule({
imports: [BrowserModule, RouterModule.forRoot([]),
StoreModule.forRoot({ <feature>: <feature>Reducer }, {
initialState: { <feature>: <feature>InitialState },
metaReducers: !environment.production ? [storeFreeze] : []
}),
EffectsModule.forRoot([<Feature>Effects]),
!environment.production ? StoreDevtoolsModule.instrument() : [],
StoreRouterConnectingModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [<Feature>Effects]
})
export class AppModule {
}
```

View File

@ -1,10 +1,20 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as path from 'path';
import {
SchematicTestRunner,
UnitTestTree
} from '@angular-devkit/schematics/testing';
import { Tree, VirtualTree } from '@angular-devkit/schematics';
import { createApp, createEmptyWorkspace } from '../../utils/testing-utils';
import { getFileContent } from '@schematics/angular/utility/test';
import { readJson } from '../../utils/ast-utils';
import * as path from 'path';
import { findModuleParent } from '../../utils/name-utils';
import {
createApp,
createEmptyWorkspace,
AppConfig,
getAppConfig
} from '../../utils/testing-utils';
describe('ngrx', () => {
const schematicRunner = new SchematicTestRunner(
'@nrwl/schematics',
@ -29,21 +39,23 @@ describe('ngrx', () => {
},
appTree
);
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
expect(appModule).toContain(
'StoreModule.forRoot({},{metaReducers: !environment.production ? [storeFreeze] : []})'
);
expect(appModule).toContain('EffectsModule.forRoot');
expect(tree.exists('apps/myapp/src/app/+state')).toBeFalsy();
[
'StoreModule.forRoot({},{ metaReducers : !environment.production ? [storeFreeze] : [] })',
'EffectsModule.forRoot'
].forEach(text => {
expect(appModule).toContain(text);
});
});
it('should add root', () => {
const tree = schematicRunner.runSchematic(
'ngrx',
{
name: 'state',
name: 'app',
module: 'apps/myapp/src/app/app.module.ts',
root: true
},
@ -51,31 +63,26 @@ describe('ngrx', () => {
);
const appModule = getFileContent(tree, '/apps/myapp/src/app/app.module.ts');
console.log(appModule);
expect(appModule).toContain('StoreModule.forRoot');
expect(appModule).toContain('EffectsModule.forRoot');
expect(appModule).toContain('!environment.production ? [storeFreeze] : []');
expect(
tree.exists(`/apps/myapp/src/app/+state/state.actions.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.effects.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.effects.spec.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.init.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.interfaces.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.reducer.ts`)
).toBeTruthy();
expect(
tree.exists(`/apps/myapp/src/app/+state/state.reducer.spec.ts`)
).toBeTruthy();
expect(appModule).toContain('appReducer, initialState');
expect(appModule).not.toContain('AppData');
expect(appModule).not.toContain('AppState');
[
'/apps/myapp/src/app/+state/app.actions.ts',
'/apps/myapp/src/app/+state/app.effects.ts',
'/apps/myapp/src/app/+state/app.effects.spec.ts',
'/apps/myapp/src/app/+state/app.reducer.ts',
'/apps/myapp/src/app/+state/app.reducer.spec.ts'
].forEach(fileName => {
expect(tree.exists(fileName)).toBeTruthy();
});
});
it('should add feature', () => {
@ -173,4 +180,102 @@ describe('ngrx', () => {
)
).toThrow("should have required property 'module'");
});
it('should create the ngrx files', () => {
const appConfig = getAppConfig();
const hasFile = file => expect(tree.exists(file)).toBeTruthy();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
hasFile(`${statePath}/user.actions.ts`);
hasFile(`${statePath}/user.effects.ts`);
hasFile(`${statePath}/user.effects.spec.ts`);
hasFile(`${statePath}/user.reducer.ts`);
hasFile(`${statePath}/user.reducer.spec.ts`);
});
it('should create ngrx action enums', () => {
const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.actions.ts`);
expect(content).toContain('UserActionTypes');
expect(content).toContain("LoadUser = '[User] Load Data'");
expect(content).toContain("UserLoaded = '[User] Data Loaded'");
});
it('should create ngrx action classes', () => {
const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.actions.ts`);
expect(content).toContain('class LoadUser implements Action');
expect(content).toContain('class UserLoaded implements Action');
});
it('should enhance the ngrx action type', () => {
const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.actions.ts`);
expect(content).toContain(
'type UserActions = User | LoadUser | UserLoaded'
);
});
it('should enhance the ngrx reducer', () => {
const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.reducer.ts`);
expect(content).not.toContain('function reducer');
[
`import { UserActions, UserActionTypes } from \'./user.actions\'`,
`export interface UserData`,
`export interface UserState`,
`readonly user: UserData`,
`const initialState: UserData`,
'function userReducer(state = initialState, action: UserActions): UserData',
'case UserActionTypes.UserLoaded'
].forEach(text => {
expect(content).toContain(text);
});
});
it('should enhance the ngrx effects', () => {
const appConfig = getAppConfig();
const tree = buildNgrxTree(appConfig);
const statePath = `${findModuleParent(appConfig.appModule)}/+state`;
const content = getFileContent(tree, `${statePath}/user.effects.ts`);
[
`import { DataPersistence } from \'@nrwl/nx\'`,
`import { UserActions, UserActionTypes, LoadUser, UserLoaded } from \'./user.actions\'`,
`loadUser$`,
`run: (action: LoadUser, state: UserState)`,
`return new UserLoaded(state)`,
'constructor(private actions$: Actions, private dataPersistence: DataPersistence<UserState>)'
].forEach(text => {
expect(content).toContain(text);
});
});
function buildNgrxTree(appConfig: AppConfig): UnitTestTree {
return schematicRunner.runSchematic(
'ngrx',
{
name: 'user',
module: appConfig.appModule
},
appTree
);
}
});

View File

@ -0,0 +1,119 @@
import { Rule, Tree } from '@angular-devkit/schematics';
import { Change } from '@ngrx/schematics/src/utility/change';
import { insertImport } from '@schematics/angular/utility/route-utils';
import * as ts from 'typescript';
import {
toClassName,
toFileName,
toPropertyName
} from '../../../utils/name-utils';
import {
insert,
addImportToModule,
addProviderToModule
} from '../../../utils/ast-utils';
import { RequestContext } from './request-context';
export function addImportsToModule(context: RequestContext): Rule {
return (host: Tree) => {
if (context.options.onlyAddFiles) {
return host;
}
if (!host.exists(context.options.module)) {
throw new Error('Specified module does not exist');
}
const modulePath = context.options.module;
const sourceText = host.read(modulePath)!.toString('utf-8');
const source = ts.createSourceFile(
modulePath,
sourceText,
ts.ScriptTarget.Latest,
true
);
const addImport = (symbolName: string, fileName: string): Change => {
return insertImport(source, modulePath, symbolName, fileName);
};
const dir = `./${toFileName(context.options.directory)}`;
const pathPrefix = `${dir}/${toFileName(context.featureName)}`;
const reducerPath = `${pathPrefix}.reducer`;
const effectsPath = `${pathPrefix}.effects`;
const featureName = `${toPropertyName(context.featureName)}`;
const reducerName = `${toPropertyName(context.featureName)}Reducer`;
const effectsName = `${toClassName(context.featureName)}Effects`;
const reducerImports = `${reducerName}, initialState as ${featureName}InitialState`;
const storeReducers = `{ ${featureName}: ${reducerName} }`;
const storeInitState = `initialState : { ${featureName} : ${featureName}InitialState }`;
const storeMetaReducers = `metaReducers : !environment.production ? [storeFreeze] : []`;
const storeForRoot = `StoreModule.forRoot(
${storeReducers},
{
${storeInitState},
${storeMetaReducers}
}
)`;
const storeForEmptyRoot = `StoreModule.forRoot({},{ ${storeMetaReducers} })`;
const effectsForRoot = `EffectsModule.forRoot([${effectsName}])`;
const effectsForEmptyRoot = `EffectsModule.forRoot([])`;
const storeForFeature = `StoreModule.forFeature('${featureName}', ${reducerName}, { ${storeInitState} })`;
const effectsForFeature = `EffectsModule.forFeature([${effectsName}])`;
const devTools = `!environment.production ? StoreDevtoolsModule.instrument() : []`;
const storeRouterModule = 'StoreRouterConnectingModule';
// InsertImport [symbol,source] value pairs
const storeModule = ['StoreModule', '@ngrx/store'];
const effectsModule = ['EffectsModule', '@ngrx/effects'];
const storeDevTools = ['StoreDevtoolsModule', '@ngrx/store-devtools'];
const environment = ['environment', '../environments/environment'];
const storeRouter = ['StoreRouterConnectingModule', '@ngrx/router-store'];
const storeFreeze = ['storeFreeze', 'ngrx-store-freeze'];
if (context.options.onlyEmptyRoot) {
insert(host, modulePath, [
addImport.apply(this, storeModule),
addImport.apply(this, effectsModule),
addImport.apply(this, storeDevTools),
addImport.apply(this, environment),
addImport.apply(this, storeRouter),
addImport.apply(this, storeFreeze),
...addImportToModule(source, modulePath, storeForEmptyRoot),
...addImportToModule(source, modulePath, effectsForEmptyRoot),
...addImportToModule(source, modulePath, devTools),
...addImportToModule(source, modulePath, storeRouterModule)
]);
} else {
const common = [
addImport.apply(this, storeModule),
addImport.apply(this, effectsModule),
addImport(reducerImports, reducerPath),
addImport(effectsName, effectsPath),
...addProviderToModule(source, modulePath, effectsName)
];
if (context.options.root) {
insert(host, modulePath, [
...common,
addImport.apply(this, storeDevTools),
addImport.apply(this, environment),
addImport.apply(this, storeRouter),
addImport.apply(this, storeFreeze),
...addImportToModule(source, modulePath, storeForRoot),
...addImportToModule(source, modulePath, effectsForRoot),
...addImportToModule(source, modulePath, devTools),
...addImportToModule(source, modulePath, storeRouterModule)
]);
} else {
insert(host, modulePath, [
...common,
...addImportToModule(source, modulePath, storeForFeature),
...addImportToModule(source, modulePath, effectsForFeature)
]);
}
}
return host;
};
}

View File

@ -0,0 +1,41 @@
import { Rule, Tree } from '@angular-devkit/schematics';
import {
ngrxVersion,
routerStoreVersion,
ngrxStoreFreezeVersion
} from '../../../lib-versions';
import { serializeJson } from '../../../utils/fileutils';
export function addNgRxToPackageJson(): Rule {
return (host: Tree) => {
if (!host.exists('package.json')) return host;
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);
if (!json['dependencies']) {
json['dependencies'] = {};
}
if (!json['dependencies']['@ngrx/store']) {
json['dependencies']['@ngrx/store'] = ngrxVersion;
}
if (!json['dependencies']['@ngrx/effects']) {
json['dependencies']['@ngrx/effects'] = ngrxVersion;
}
if (!json['dependencies']['@ngrx/entity']) {
json['dependencies']['@ngrx/entity'] = ngrxVersion;
}
if (!json['dependencies']['@ngrx/store-devtools']) {
json['dependencies']['@ngrx/store-devtools'] = ngrxVersion;
}
if (!json['dependencies']['@ngrx/router-store']) {
json['dependencies']['@ngrx/router-store'] = routerStoreVersion;
}
if (!json['dependencies']['ngrx-store-freeze']) {
json['dependencies']['ngrx-store-freeze'] = ngrxStoreFreezeVersion;
}
host.overwrite('package.json', serializeJson(json));
return host;
};
}

View File

@ -0,0 +1,6 @@
export { RequestContext } from './request-context';
export { updateNgrxReducers } from './update-reducers';
export { updateNgrxActions } from './update-actions';
export { updateNgrxEffects } from './update-effects';
export { addImportsToModule } from './add-imports-to-module';
export { addNgRxToPackageJson } from './add-ngrx-to-package-json';

View File

@ -0,0 +1,23 @@
import * as path from 'path';
import { Tree } from '@angular-devkit/schematics';
import { Schema } from '../schema';
import * as stringUtils from '../../../utils/strings';
/**
* Schematic request context
*/
export interface RequestContext {
featureName: string;
moduleDir: string;
options?: Schema;
host?: Tree;
}
export function buildNameToNgrxFile(context: RequestContext, suffice: string) {
return path.join(
context.moduleDir,
context.options.directory,
`${stringUtils.dasherize(context.featureName)}.${suffice}`
);
}

View File

@ -0,0 +1,103 @@
import * as ts from 'typescript';
import { SchematicsException, Rule, Tree } from '@angular-devkit/schematics';
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
import { toClassName } from '../../../utils/name-utils';
import {
insert,
addClass,
addEnumeratorValues,
addUnionTypes
} from '../../../utils/ast-utils';
import { RequestContext, buildNameToNgrxFile } from './request-context';
/**
* Add custom actions to <featureName>.actions.ts
* See Ngrx Enhancement doc: https://bit.ly/2I5QwxQ
*
* Desired output:
*
* ```
* import {Action} from "@ngrx/store";
*
* export enum <Feature>ActionTypes {
* <Feature> = "[<Feature>] Action",
* Load<Feature> = "[<Feature>] Load Data",
* <Feature>Loaded = "[<Feature>] Data Loaded"
* }
*
* export class <Feature> implements Action {
* readonly type = <Feature>ActionTypes.<Feature>;
* }
*
* export class Load<Feature> implements Action {
* readonly type = <Feature>ActionTypes.Load<Feature>;
* constructor(public payload: any) { }
* }
*
* export class <Feature>LOADED implements Action {
* readonly type = <Feature>ActionTypes.<Feature>LOADED;
* constructor(public payload: any) { }
* }
*
* export type <FeatureActions> = <Feature> | Load<Feature> | <Feature>Loaded
*
* ```
*
*/
export function updateNgrxActions(context: RequestContext): Rule {
return (host: Tree) => {
const clazzName = toClassName(context.featureName);
const componentPath = buildNameToNgrxFile(context, 'actions.ts');
const text = host.read(componentPath);
if (text === null) {
throw new SchematicsException(`File ${componentPath} does not exist.`);
}
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(
componentPath,
sourceText,
ts.ScriptTarget.Latest,
true
);
insert(host, componentPath, [
...addEnumeratorValues(source, componentPath, `${clazzName}ActionTypes`, [
{
name: `Load${clazzName}`,
value: `[${clazzName}] Load Data`
},
{
name: `${clazzName}Loaded`,
value: `[${clazzName}] Data Loaded`
}
]),
addClass(
source,
componentPath,
`Load${clazzName}`,
stripIndents`
export class Load${clazzName} implements Action {
readonly type = ${clazzName}ActionTypes.Load${clazzName};
constructor(public payload: any) { }
}`
),
addClass(
source,
componentPath,
`${clazzName}Loaded`,
stripIndents`
export class ${clazzName}Loaded implements Action {
readonly type = ${clazzName}ActionTypes.${clazzName}Loaded;
constructor(public payload: any) { }
}`
),
addUnionTypes(source, componentPath, `${clazzName}Actions`, [
`Load${clazzName}`,
`${clazzName}Loaded`
])
]);
};
}

View File

@ -0,0 +1,101 @@
import * as ts from 'typescript';
import { SchematicsException, Rule, Tree } from '@angular-devkit/schematics';
import { InsertChange } from '@schematics/angular/utility/change';
import { findNodes } from '@schematics/angular/utility/ast-utils';
import { insertImport } from '@schematics/angular/utility/route-utils';
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
import { toClassName } from '../../../utils/name-utils';
import { insert } from '../../../utils/ast-utils';
import { RequestContext, buildNameToNgrxFile } from './request-context';
/**
*
* Desired output:
*
* ```
* import { Injectable } from '@angular/core';
* import { Effect, Actions } from '@ngrx/effects';
* import { DataPersistence } from '@nrwl/nx';
*
* import { <Feature>, <Feature>State } from './<feature>.reducer';
* import { Load<Feature>, <Feature>Loaded, <Feature>ActionTypes } from './<feature>.actions';
*
* @Injectable()
* export class <Feature>Effects {
* @Effect() load<Feature>$ = this.dataPersistence.fetch(<Feature>ActionTypes.Load<Feature>, {
* run: (action: Load<Feature>, state: <Feature>State) => {
* return new <Feature>Loaded({});
* },
*
* onError: (action: Load<Feature>, error) => {
* console.error('Error', error);
* }
* });
*
* constructor(
* private actions: Actions,
* private dataPersistence: DataPersistence<<Feature>State>) { }
* }
*
*/
export function updateNgrxEffects(context: RequestContext): Rule {
return (host: Tree) => {
const clazzName = toClassName(context.featureName);
const componentPath = buildNameToNgrxFile(context, 'effects.ts');
const featureReducer = `./${context.featureName}.reducer`;
const text = host.read(componentPath);
if (text === null) {
throw new SchematicsException(`File ${componentPath} does not exist.`);
}
const modulePath = context.options.module;
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(
componentPath,
sourceText,
ts.ScriptTarget.Latest,
true
);
const updateConstructor = () => {
const astConstructor = findNodes(source, ts.SyntaxKind.Constructor)[0];
const lastParameter = findNodes(
astConstructor,
ts.SyntaxKind.Parameter
).pop();
return new InsertChange(
componentPath,
lastParameter.end,
stripIndents`, private dataPersistence: DataPersistence<${clazzName}State>`
);
};
const addEffect$ = () => {
const toInsert = `\n
@Effect()
load${clazzName}$ = this.dataPersistence.fetch(${clazzName}ActionTypes.Load${clazzName}, {
run: (action: Load${clazzName}, state: ${clazzName}State) => {
return new ${clazzName}Loaded(state);
},
onError: (action: Load${clazzName}, error) => {
console.error('Error', error);
}
});`;
const astConstructor = findNodes(source, ts.SyntaxKind.Constructor)[0];
return new InsertChange(componentPath, astConstructor.pos, toInsert);
};
const actionsFile = `./${context.featureName}.actions`;
const actionImports = `Load${clazzName}, ${clazzName}Loaded`;
insert(host, componentPath, [
insertImport(source, modulePath, actionImports, actionsFile),
insertImport(source, modulePath, `${clazzName}State`, featureReducer),
insertImport(source, modulePath, 'DataPersistence', `@nrwl/nx`),
updateConstructor(),
addEffect$()
]);
};
}

View File

@ -0,0 +1,186 @@
import * as ts from 'typescript';
import { SchematicsException, Rule, Tree } from '@angular-devkit/schematics';
import {
Change,
ReplaceChange,
InsertChange
} from '@schematics/angular/utility/change';
import {
findNodes,
insertAfterLastOccurrence
} from '@schematics/angular/utility/ast-utils';
import { insertImport } from '@schematics/angular/utility/route-utils';
import { toClassName, toPropertyName } from '../../../utils/name-utils';
import { insert, findNodesOfType } from '../../../utils/ast-utils';
import { RequestContext, buildNameToNgrxFile } from './request-context';
/**
* Update ngrx-generated Reducer to confirm to DataLoaded action to <featureName>.reducer.ts
*
* Desired output:
*
* ```
* import { <Feature>Actions, <Feature>ActionTypes } from './<feature>.actions';
*
* export interface <Feature>State {
* }
*
* export const initialState: <Feature>State = {
* };
*
* export function <feature>Reducer(
* state : <Feature>State = initialState,
* action: <Feature>Actions ) : <Feature>State
* {
* switch (action.type) {
* case <Feature>ActionTypes.<Feature>Loaded: {
* return { ...state, ...action.payload };
* }
* default: {
* return state;
* }
* }
* }
* ```
*
*
*/
export function updateNgrxReducers(context: RequestContext): Rule {
return (host: Tree) => {
const clazzName = toClassName(context.featureName);
const propertyName = toPropertyName(context.featureName);
const componentPath = buildNameToNgrxFile(context, 'reducer.ts');
const text = host.read(componentPath);
if (text === null) {
throw new SchematicsException(`File ${componentPath} does not exist.`);
}
const modulePath = context.options.module;
const sourceText = text.toString('utf-8');
const source = ts.createSourceFile(
componentPath,
sourceText,
ts.ScriptTarget.Latest,
true
);
const renameStateInterface = () => {
const name = findNodesOfType(
source,
ts.SyntaxKind.InterfaceDeclaration,
(it: ts.InterfaceDeclaration) => it.name.getText() === 'State',
(it: ts.InterfaceDeclaration) => it.name,
true
);
return new ReplaceChange(
componentPath,
name.pos,
'State',
`${clazzName}Data`
);
};
const addInterfaceComments = () => {
const node = findNodes(source, ts.SyntaxKind.InterfaceDeclaration, 1)[0];
const toAdd = `
/**
* Interface for the '${clazzName}' data used in
* - ${clazzName}State, and
* - ${propertyName}Reducer
*/`;
return new InsertChange(componentPath, node.pos + 1, `\n ${toAdd}`);
};
const addFeatureState = () => {
const node = findNodes(source, ts.SyntaxKind.VariableStatement, 1)[0];
const toAdd = `
/**
* Interface to the part of the Store containing ${clazzName}State
* and other information related to ${clazzName}Data.
*/
export interface ${clazzName}State {
readonly ${propertyName}: ${clazzName}Data;
}`;
return new InsertChange(componentPath, node.pos, `\n${toAdd}`);
};
const renameInitialState = () => {
const getIdentifier = node => node.typeName;
const target = findNodes(source, ts.SyntaxKind.VariableStatement, 1);
const name = findNodesOfType(
target[0],
ts.SyntaxKind.TypeReference,
it => {
return getIdentifier(it).getText() === 'State';
},
it => getIdentifier(it),
true
);
return new ReplaceChange(
componentPath,
name.pos,
'State',
`${clazzName}Data`
);
};
const updateReducerFn = () => {
let actions: Change[] = [];
findNodes(source, ts.SyntaxKind.FunctionDeclaration)
.filter((it: ts.FunctionDeclaration) => it.name.getText() === 'reducer')
.map((it: ts.FunctionDeclaration) => {
const fnName: ts.Identifier = it.name;
const typeName = findNodes(it, ts.SyntaxKind.Identifier).reduce(
(result: ts.Identifier, it: ts.Identifier): ts.Identifier => {
return !!result
? result
: it.getText() === 'State' ? it : undefined;
},
undefined
);
actions = [
new ReplaceChange(
componentPath,
fnName.pos,
fnName.getText(),
`${propertyName}Reducer`
),
new ReplaceChange(
componentPath,
typeName.pos,
typeName.getText(),
`${clazzName}Data`
)
];
});
return actions;
};
const updateSwitchStatement = () => {
const toInsert = `
case ${clazzName}ActionTypes.${clazzName}Loaded: {
return { ...state, ...action.payload };
}`;
return insertAfterLastOccurrence(
findNodes(source, ts.SyntaxKind.SwitchStatement),
toInsert,
componentPath,
0,
ts.SyntaxKind.CaseClause
);
};
insert(host, componentPath, [
addInterfaceComments(),
addFeatureState(),
renameStateInterface(),
renameInitialState(),
insertImport(
source,
modulePath,
`${clazzName}Actions`,
`./${context.featureName}.actions`
),
...updateReducerFn(),
updateSwitchStatement()
]);
};
}

View File

@ -6,17 +6,22 @@
"properties": {
"name": {
"type": "string",
"description": "Name of the directory (e.g., state)."
"description": "Name of the ngrx feature (e.g., Products, User, etc.)."
},
"module": {
"type": "string",
"description": "Path to an Angular module (e.g., src/app/app.module.ts)."
"description": "Path to ngModule; host directory will contain the new '+state' directory (e.g., src/libs/mylib/mylib.module.ts)."
},
"onlyAddFiles": {
"type": "boolean",
"default": false,
"description": "Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)."
},
"directory": {
"type": "string",
"default": "+state",
"description": "The directory name for the ngrx files: contains actions, effects, reducers. (e.g., +state)"
},
"root": {
"type": "boolean",
"default": false,
@ -31,11 +36,6 @@
"type": "boolean",
"default": false,
"description": "Do not add ngrx dependencies to package.json (e.g., --skipPackageJson)"
},
"directory": {
"type": "string",
"default": "+state",
"description": "The store directory name (e.g., +state)"
}
},
"required": [

0
packages/schematics/src/collection/workspace/index.ts Normal file → Executable file
View File

View File

@ -1,7 +1,7 @@
export const angularCliVersion = '1.7.1';
export const angularVersion = '5.2.6';
export const angularJsVersion = '1.6.6';
export const ngrxVersion = '5.1.0';
export const ngrxVersion = '5.2.0';
export const ngrxStoreFreezeVersion = '^0.2.1';
export const routerStoreVersion = '5.0.1';
export const nxVersion = '*';
@ -14,6 +14,7 @@ export const typescriptVersion = '2.6.2';
export const rxjsVersion = '^5.5.6';
export const devKitCoreVersion = '^0.0.29';
export const devKitSchematicsVersion = '0.0.52';
export const ngrxSchematicsVersion = '5.2.0';
export const schematicsAngularVersion = '0.1.17';
export const libVersions = {
@ -32,5 +33,6 @@ export const libVersions = {
devKitCoreVersion,
devKitSchematicsVersion,
schematicsAngularVersion,
ngrxSchematicsVersion,
routerStoreVersion
};

218
packages/schematics/src/utils/ast-utils.ts Normal file → Executable file
View File

@ -2,24 +2,27 @@
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* 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 { Tree, Rule } from '@angular-devkit/schematics';
import {
findNodes,
getDecoratorMetadata,
getSourceNodes
getSourceNodes,
insertAfterLastOccurrence
} from '@schematics/angular/utility/ast-utils';
import {
Change,
InsertChange,
NoopChange,
RemoveChange
RemoveChange,
ReplaceChange
} from '@schematics/angular/utility/change';
import * as ts from 'typescript';
import { toFileName } from './name-utils';
import * as path from 'path';
import { toFileName } from './name-utils';
import { serializeJson } from './fileutils';
// This should be moved to @schematics/angular once it allows to pass custom expressions as providers
@ -226,9 +229,10 @@ export function removeFromNgModule(
}
}
function findClass(
export function findClass(
source: ts.SourceFile,
className: string
className: string,
silent: boolean = false
): ts.ClassDeclaration {
const nodes = getSourceNodes(source);
@ -238,7 +242,7 @@ function findClass(
(<any>n).name.text === className
)[0];
if (!clazz) {
if (!clazz && !silent) {
throw new Error(`Cannot find class '${className}'`);
}
@ -531,6 +535,10 @@ export function insert(host: Tree, modulePath: string, changes: Change[]) {
recorder.remove((<any>change).pos - 1, (<any>change).toRemove.length + 1);
} else if (change instanceof NoopChange) {
// do nothing
} else if (change instanceof ReplaceChange) {
const action = <any>change;
recorder.remove(action.pos + 1, action.oldText.length);
recorder.insertLeft(action.pos + 1, action.newText);
} else {
throw new Error(`Unexpected Change '${change}'`);
}
@ -658,3 +666,199 @@ export function readBootstrapInfo(
bootstrapComponentFileName
};
}
export function addClass(
source: ts.SourceFile,
modulePath: string,
clazzName: string,
clazzSrc: string
): Change {
if (!findClass(source, clazzName, true)) {
const nodes = findNodes(source, ts.SyntaxKind.ClassDeclaration);
return insertAfterLastOccurrence(
nodes,
offset(clazzSrc, 0, true),
modulePath,
0,
ts.SyntaxKind.ClassDeclaration
);
}
return new NoopChange();
}
export function addUnionTypes(
source: ts.SourceFile,
modulePath: string,
typeName: string,
typeValues: string[]
) {
const target: ts.TypeAliasDeclaration = findNodesOfType(
source,
ts.SyntaxKind.TypeAliasDeclaration,
it => it.name.getText() === typeName
);
if (!target) {
throw new Error(`Cannot find union type '${typeName}'`);
}
const node = target.type as ts.TypeReferenceNode;
// Append new types to create a union type...
return new InsertChange(
modulePath,
node.end,
['', ...typeValues].join(' | ')
);
}
/**
* Add 1..n enumerators using name + (optional) value pairs
*/
export function addEnumeratorValues(
source: ts.SourceFile,
modulePath: string,
enumName: string,
pairs: NameValue[] = []
): Change[] {
const target = findNodesOfType(
source,
ts.SyntaxKind.EnumDeclaration,
it => it.name.getText() === enumName
);
const list = target ? target.members : undefined;
if (!target) {
throw new Error(`Cannot find enum '${enumName}'`);
}
const addComma = !(list.hasTrailingComma || list.length === 0);
return pairs.reduce((buffer, it) => {
const member = it.value ? `${it.name} = '${it.value}'` : it.name;
const memberExists = () => {
return list.filter(m => m.name.getText() === it.name).length;
};
if (memberExists()) {
throw new Error(`Enum '${enumName}.${it.name}' already exists`);
}
return [
...buffer,
new InsertChange(modulePath, list.end, (addComma ? ', ' : '') + member)
];
}, []);
}
/**
* Find Enum declaration in source based on name
* e.g.
* export enum ProductsActionTypes {
* ProductsAction = '[Products] Action'
* }
*/
const IDENTITY = a => a;
export function findNodesOfType(
source: ts.Node,
kind: ts.SyntaxKind,
predicate: (a: any) => boolean,
extract: (a: any) => any = IDENTITY,
firstOnly: boolean = true
): any {
const nodes = findNodes(source, kind);
const matching = nodes.filter((i: any) => predicate(i)).map(extract);
return matching.length ? (firstOnly ? matching[0] : matching) : undefined;
}
export interface NameValue {
name: string;
value?: string;
}
export function insertImport(
source: ts.SourceFile,
fileToEdit: string,
symbolName: string,
fileName: string,
isDefault = false
): Change {
const rootNode = source;
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
// get nodes that map to import statements from the file fileName
const relevantImports = allImports.filter(node => {
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
const importFiles = node
.getChildren()
.filter(child => child.kind === ts.SyntaxKind.StringLiteral)
.map(n => (n as ts.StringLiteral).text);
return importFiles.filter(file => file === fileName).length === 1;
});
if (relevantImports.length > 0) {
let importsAsterisk = false;
// imports from import file
const imports: ts.Node[] = [];
relevantImports.forEach(n => {
Array.prototype.push.apply(
imports,
findNodes(n, ts.SyntaxKind.Identifier)
);
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
importsAsterisk = true;
}
});
// if imports * from fileName, don't add symbolName
if (importsAsterisk) {
return new NoopChange();
}
const importTextNodes = imports.filter(
n => (n as ts.Identifier).text === symbolName
);
// insert import if it's not there
if (importTextNodes.length === 0) {
const fallbackPos =
findNodes(
relevantImports[0],
ts.SyntaxKind.CloseBraceToken
)[0].getStart() ||
findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
return insertAfterLastOccurrence(
imports,
`, ${symbolName}`,
fileToEdit,
fallbackPos
);
}
return new NoopChange();
}
// no such import declaration exists
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(
(n: ts.StringLiteral) => n.text === 'use strict'
);
let fallbackPos = 0;
if (useStrict.length > 0) {
fallbackPos = useStrict[0].end;
}
const open = isDefault ? '' : '{ ';
const close = isDefault ? '' : ' }';
// if there are no imports or 'use strict' statement, insert import at beginning of file
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
const separator = insertAtBeginning ? '' : ';\n';
const toInsert =
`${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral
);
}

View File

@ -1,3 +1,5 @@
import * as path from 'path';
export function names(name: string): any {
return {
name,
@ -30,3 +32,11 @@ export function toFileName(s: string): string {
function toCapitalCase(s: string): string {
return s.charAt(0).toUpperCase() + s.substr(1);
}
/**
* Determine the parent directory for the ngModule specified
* in the full-path option 'module'
*/
export function findModuleParent(modulePath) {
return path.dirname(modulePath);
}

View File

@ -0,0 +1,10 @@
import { forEach, FileEntry, Rule } from '@angular-devkit/schematics';
/**
* Remove a file from the Virtual Schematic Tree
*/
export function deleteFile(from: string): Rule {
return forEach((entry: FileEntry): FileEntry | null => {
return entry.path === from ? null : entry;
});
}

View File

@ -0,0 +1,152 @@
/**
* @license
* Copyright Google Inc. 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
*/
const STRING_DASHERIZE_REGEXP = /[ _]/g;
const STRING_DECAMELIZE_REGEXP = /([a-z\d])([A-Z])/g;
const STRING_CAMELIZE_REGEXP = /(-|_|\.|\s)+(.)?/g;
const STRING_UNDERSCORE_REGEXP_1 = /([a-z\d])([A-Z]+)/g;
const STRING_UNDERSCORE_REGEXP_2 = /-|\s+/g;
/**
* Converts a camelized string into all lower case separated by underscores.
*
```javascript
decamelize('innerHTML'); // 'inner_html'
decamelize('action_name'); // 'action_name'
decamelize('css-class-name'); // 'css-class-name'
decamelize('my favorite items'); // 'my favorite items'
```
@method decamelize
@param {String} str The string to decamelize.
@return {String} the decamelized string.
*/
export function decamelize(str: string): string {
return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase();
}
/**
Replaces underscores, spaces, or camelCase with dashes.
```javascript
dasherize('innerHTML'); // 'inner-html'
dasherize('action_name'); // 'action-name'
dasherize('css-class-name'); // 'css-class-name'
dasherize('my favorite items'); // 'my-favorite-items'
```
@method dasherize
@param {String} str The string to dasherize.
@return {String} the dasherized string.
*/
export function dasherize(str?: string): string {
return decamelize(str || '').replace(STRING_DASHERIZE_REGEXP, '-');
}
/**
Returns the lowerCamelCase form of a string.
```javascript
camelize('innerHTML'); // 'innerHTML'
camelize('action_name'); // 'actionName'
camelize('css-class-name'); // 'cssClassName'
camelize('my favorite items'); // 'myFavoriteItems'
camelize('My Favorite Items'); // 'myFavoriteItems'
```
@method camelize
@param {String} str The string to camelize.
@return {String} the camelized string.
*/
export function camelize(str: string): string {
return str
.replace(
STRING_CAMELIZE_REGEXP,
(_match: string, _separator: string, chr: string) => {
return chr ? chr.toUpperCase() : '';
}
)
.replace(/^([A-Z])/, (match: string) => match.toLowerCase());
}
/**
Returns the UpperCamelCase form of a string.
```javascript
'innerHTML'.classify(); // 'InnerHTML'
'action_name'.classify(); // 'ActionName'
'css-class-name'.classify(); // 'CssClassName'
'my favorite items'.classify(); // 'MyFavoriteItems'
```
@method classify
@param {String} str the string to classify
@return {String} the classified string
*/
export function classify(str: string): string {
return str
.split('.')
.map(part => capitalize(camelize(part)))
.join('.');
}
/**
More general than decamelize. Returns the lower\_case\_and\_underscored
form of a string.
```javascript
'innerHTML'.underscore(); // 'inner_html'
'action_name'.underscore(); // 'action_name'
'css-class-name'.underscore(); // 'css_class_name'
'my favorite items'.underscore(); // 'my_favorite_items'
```
@method underscore
@param {String} str The string to underscore.
@return {String} the underscored string.
*/
export function underscore(str: string): string {
return str
.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2')
.replace(STRING_UNDERSCORE_REGEXP_2, '_')
.toLowerCase();
}
/**
Returns the Capitalized form of a string
```javascript
'innerHTML'.capitalize() // 'InnerHTML'
'action_name'.capitalize() // 'Action_name'
'css-class-name'.capitalize() // 'Css-class-name'
'my favorite items'.capitalize() // 'My favorite items'
```
@method capitalize
@param {String} str The string to capitalize.
@return {String} The capitalized string.
*/
export function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.substr(1);
}
export function group(name: string, group: string | undefined) {
return group ? `${group}/${name}` : name;
}
export function featurePath(
group: boolean | undefined,
flat: boolean | undefined,
path: string,
name: string
) {
if (group && !flat) {
return `../../${path}/${name}/`;
}
return group ? `../${path}/` : './';
}

View File

@ -1,5 +1,16 @@
import { Tree } from '@angular-devkit/schematics';
export interface AppConfig {
appName: string; // name of app or lib
appModule: string; // app/app.module.ts in the above sourceDir
}
var appConfig: AppConfig; // configure built in createApp()
export function getAppConfig(): AppConfig {
return appConfig;
}
export function createEmptyWorkspace(tree: Tree): Tree {
tree.create('/.angular-cli.json', JSON.stringify({}));
tree.create('/package.json', JSON.stringify({}));
@ -22,8 +33,14 @@ export function createEmptyWorkspace(tree: Tree): Tree {
}
export function createApp(tree: Tree, appName: string): Tree {
// save for getAppDir() lookup by external *.spec.ts tests
appConfig = {
appName,
appModule: `/apps/${appName}/src/app/app.module.ts`
};
tree.create(
`/apps/${appName}/src/app/app.module.ts`,
appConfig.appModule,
`
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

336
yarn.lock
View File

@ -151,6 +151,10 @@
version "5.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-5.2.0.tgz#bf4b174ce19a36eba8211fc1ddeaf1e35ae74368"
"@ngrx/schematics@^5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/schematics/-/schematics-5.2.0.tgz#ab7180d79f0ec68bd4ac3ef4ec83b9348d18d26c"
"@ngrx/store-devtools@5.2.0":
version "5.2.0"
resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-5.2.0.tgz#2fff916a9aa349375826772b359dbb64b9e5d622"
@ -205,8 +209,8 @@
"@types/jasmine" "*"
"@types/node@~6.0.60":
version "6.0.102"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.102.tgz#a6cf3b9843286b63eb362a8522bc382d96fe68d1"
version "6.0.103"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.103.tgz#4fddb6c254756e98004039da4e4f4230d1e397ca"
"@types/prettier@^1.10.0":
version "1.10.0"
@ -297,12 +301,13 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@~5.5.1:
json-schema-traverse "^0.3.0"
ajv@^6.1.0, ajv@^6.1.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.1.tgz#28a6abc493a2abe0fb4c8507acaedb43fa550671"
version "6.4.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.4.0.tgz#d3aff78e9277549771daf0164cff48482b754fc6"
dependencies:
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
uri-js "^3.0.2"
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
@ -567,8 +572,8 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
atob@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
version "2.1.0"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.0.tgz#ab2b150e51d7b122b9efc8d7340c06b6c41076bc"
autoprefixer@^7.1.1, autoprefixer@^7.2.3:
version "7.2.6"
@ -928,8 +933,8 @@ brorand@^1.0.1:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
browser-pack@^6.0.1:
version "6.0.4"
resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f"
version "6.1.0"
resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.1.0.tgz#c34ba10d0b9ce162b5af227c7131c92c2ecd5774"
dependencies:
JSONStream "^1.0.3"
combine-source-map "~0.8.0"
@ -1071,6 +1076,10 @@ buffer-crc32@^0.2.5:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
buffer-from@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
buffer-indexof@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
@ -1210,8 +1219,8 @@ camelcase@^4.0.0, camelcase@^4.1.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805:
version "1.0.30000813"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000813.tgz#7b25e27fdfb8d133f3c932b01f77452140fcc6c9"
version "1.0.30000821"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000821.tgz#0f3223f1e048ed96451c56ca6cf197058c42cb93"
capture-stack-trace@^1.0.0:
version "1.0.0"
@ -1242,7 +1251,7 @@ chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1:
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65"
dependencies:
@ -1274,8 +1283,8 @@ chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.6.0, chokidar@^1.7.0:
fsevents "^1.0.0"
chokidar@^2.0.0, chokidar@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
version "2.0.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176"
dependencies:
anymatch "^2.0.0"
async-each "^1.0.0"
@ -1289,7 +1298,7 @@ chokidar@^2.0.0, chokidar@^2.0.2:
readdirp "^2.0.0"
upath "^1.0.0"
optionalDependencies:
fsevents "^1.0.0"
fsevents "^1.1.2"
chownr@^1.0.1:
version "1.0.1"
@ -1369,8 +1378,8 @@ clone-deep@^2.0.1:
shallow-clone "^1.0.0"
clone@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
co@^4.6.0:
version "4.6.0"
@ -1415,16 +1424,7 @@ combine-lists@^1.0.0:
dependencies:
lodash "^4.5.0"
combine-source-map@~0.7.1:
version "0.7.2"
resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
dependencies:
convert-source-map "~1.1.0"
inline-source-map "~0.6.0"
lodash.memoize "~3.0.3"
source-map "~0.5.3"
combine-source-map@~0.8.0:
combine-source-map@^0.8.0, combine-source-map@~0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b"
dependencies:
@ -1439,13 +1439,9 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
commander@2.14.x, commander@~2.14.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
commander@^2.12.0, commander@^2.12.1, commander@^2.9.0:
version "2.15.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.0.tgz#ad2a23a1c3b036e392469b8012cec6b33b4c1322"
commander@2.15.x, commander@^2.12.0, commander@^2.12.1, commander@^2.9.0, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
commander@~2.13.0:
version "2.13.0"
@ -1489,7 +1485,7 @@ compressible@~2.0.13:
compression@^1.5.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69"
resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69"
dependencies:
accepts "~1.3.4"
bytes "3.0.0"
@ -1503,10 +1499,11 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
concat-stream@^1.5.0:
version "1.6.1"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26"
concat-stream@^1.5.0, concat-stream@^1.6.1:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
@ -1520,8 +1517,8 @@ concat-stream@~1.5.0, concat-stream@~1.5.1:
typedarray "~0.0.5"
configstore@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
version "3.1.2"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f"
dependencies:
dot-prop "^4.1.0"
graceful-fs "^4.1.2"
@ -1614,8 +1611,8 @@ copy-webpack-plugin@~4.4.1:
serialize-javascript "^1.4.0"
core-js@^2.2.0, core-js@^2.4.0, core-js@^2.5.0:
version "2.5.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
version "2.5.4"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.4.tgz#f2c8bf181f2a80b92f360121429ce63a2f0aeae0"
core-object@^3.1.0:
version "3.1.5"
@ -2112,12 +2109,12 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
ejs@^2.5.7:
version "2.5.7"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
version "2.5.8"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.8.tgz#2ab6954619f225e6193b7ac5f7c39c48fefe4380"
electron-to-chromium@^1.3.30:
version "1.3.37"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.37.tgz#4a92734e0044c8cf0b1553be57eae21a4c6e5fab"
version "1.3.41"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.41.tgz#7e33643e00cd85edfd17e04194f6d00e73737235"
elliptic@^6.0.0:
version "6.4.0"
@ -2218,8 +2215,8 @@ error-ex@^1.2.0, error-ex@^1.3.1:
is-arrayish "^0.2.1"
es-abstract@^1.4.3, es-abstract@^1.7.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
version "1.11.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681"
dependencies:
es-to-primitive "^1.1.1"
function-bind "^1.1.1"
@ -2236,11 +2233,12 @@ es-to-primitive@^1.1.1:
is-symbol "^1.0.1"
es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
version "0.10.40"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.40.tgz#ab3d2179b943008c5e9ef241beb25ef41424c774"
version "0.10.42"
resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.42.tgz#8c07dd33af04d5dcd1310b5cef13bea63a89ba8d"
dependencies:
es6-iterator "~2.0.3"
es6-symbol "~3.1.1"
next-tick "1"
es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3:
version "2.0.3"
@ -2681,8 +2679,8 @@ find-up@^2.0.0, find-up@^2.1.0:
locate-path "^2.0.0"
flush-write-stream@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
version "1.0.3"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd"
dependencies:
inherits "^2.0.1"
readable-stream "^2.0.4"
@ -2805,7 +2803,7 @@ fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
fsevents@^1.0.0:
fsevents@^1.0.0, fsevents@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8"
dependencies:
@ -3229,12 +3227,12 @@ html-entities@^1.2.0:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
html-minifier@^3.2.3:
version "3.5.10"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.10.tgz#8522c772c388db81aa5c26f62033302d906ea1c7"
version "3.5.12"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.12.tgz#6bfad4d0327f5b8d2b62f5854654ac3703b9b031"
dependencies:
camel-case "3.0.x"
clean-css "4.1.x"
commander "2.14.x"
commander "2.15.x"
he "1.1.x"
ncname "1.0.x"
param-case "2.1.x"
@ -3269,7 +3267,7 @@ http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
http-errors@1.6.2, http-errors@~1.6.2:
http-errors@1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
dependencies:
@ -3278,6 +3276,15 @@ http-errors@1.6.2, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-errors@~1.6.2:
version "1.6.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
dependencies:
depd "~1.1.2"
inherits "2.0.3"
setprototypeof "1.1.0"
statuses ">= 1.4.0 < 2"
http-parser-js@>=0.4.0:
version "0.4.11"
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529"
@ -3362,8 +3369,8 @@ iconv-lite@0.4.19:
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
iferr@^0.1.5:
version "0.1.5"
@ -3444,12 +3451,12 @@ inline-source-map@~0.6.0:
source-map "~0.5.3"
insert-module-globals@^7.0.0:
version "7.0.2"
resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.2.tgz#012c56baa7d3307a8b417d4ec5270cf9741c18f4"
version "7.0.5"
resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.5.tgz#6d0a6f28d4a7e0eae171ad305e0f47bdfe0c258e"
dependencies:
JSONStream "^1.0.3"
combine-source-map "~0.7.1"
concat-stream "~1.5.1"
combine-source-map "^0.8.0"
concat-stream "^1.6.1"
is-buffer "^1.1.0"
lexical-scope "^1.2.0"
process "~0.11.0"
@ -3467,8 +3474,8 @@ interpret@^1.0.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
invariant@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688"
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies:
loose-envify "^1.0.0"
@ -3692,8 +3699,8 @@ is-path-cwd@^1.0.0:
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
is-path-in-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc"
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52"
dependencies:
is-path-inside "^1.0.0"
@ -3759,7 +3766,7 @@ is-wsl@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
isarray@0.0.1, isarray@~0.0.1:
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@ -3771,6 +3778,10 @@ isarray@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
isarray@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.4.tgz#38e7bcbb0f3ba1b7933c86ba1894ddfc3781bbb7"
isbinaryfile@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
@ -3811,8 +3822,8 @@ istanbul-api@^1.1.1:
once "^1.4.0"
istanbul-instrumenter-loader@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2"
version "3.0.1"
resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz#9957bd59252b373fae5c52b7b5188e6fde2a0949"
dependencies:
convert-source-map "^1.5.0"
istanbul-lib-instrument "^1.7.3"
@ -4155,8 +4166,8 @@ json-loader@^0.5.4:
resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d"
json-parse-better-errors@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a"
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
json-schema-traverse@^0.3.0:
version "0.3.1"
@ -4302,11 +4313,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
labeled-stream-splicer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
version "2.0.1"
resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.1.tgz#9cffa32fd99e1612fd1d86a8db962416d5292926"
dependencies:
inherits "^2.0.1"
isarray "~0.0.1"
isarray "^2.0.4"
stream-splicer "^2.0.0"
latest-version@^3.0.0:
@ -4380,8 +4391,8 @@ libqp@1.1.0:
resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8"
license-webpack-plugin@^1.0.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.3.0.tgz#b2c547f1a16d80426eecef38560d1312438d988e"
version "1.3.1"
resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-1.3.1.tgz#688b76472188ef597918b7cae3eec7dc2fa5a0e8"
dependencies:
ejs "^2.5.7"
@ -4528,8 +4539,8 @@ lower-case@^1.1.1:
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
version "1.0.1"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1:
version "4.1.2"
@ -4542,12 +4553,18 @@ lru-cache@~2.6.5:
version "2.6.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
magic-string@0.22.4, magic-string@^0.22.3, magic-string@^0.22.4:
magic-string@0.22.4:
version "0.22.4"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.4.tgz#31039b4e40366395618c1d6cf8193c53917475ff"
dependencies:
vlq "^0.2.1"
magic-string@^0.22.3, magic-string@^0.22.4:
version "0.22.5"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
dependencies:
vlq "^0.2.2"
mailcomposer@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4"
@ -4673,8 +4690,8 @@ micromatch@^2.1.5, micromatch@^2.3.11:
regex-cache "^0.4.2"
micromatch@^3.1.4, micromatch@^3.1.8:
version "3.1.9"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89"
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
dependencies:
arr-diff "^4.0.0"
array-unique "^0.3.2"
@ -4688,7 +4705,7 @@ micromatch@^3.1.4, micromatch@^3.1.8:
object.pick "^1.3.0"
regex-not "^1.0.0"
snapdragon "^0.8.1"
to-regex "^3.0.1"
to-regex "^3.0.2"
miller-rabin@^4.0.0:
version "4.0.1"
@ -4838,9 +4855,9 @@ multicast-dns@^6.0.1:
dns-packet "^1.3.1"
thunky "^1.0.2"
nan@^2.3.0, nan@^2.3.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866"
nan@^2.10.0, nan@^2.3.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
nanomatch@^1.2.9:
version "1.2.9"
@ -4881,6 +4898,10 @@ netmask@~1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
next-tick@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
ng-packagr@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ng-packagr/-/ng-packagr-2.2.0.tgz#a66b7d822c40e8aa0dee34e06cca05151c522a3b"
@ -5004,14 +5025,14 @@ node-pre-gyp@^0.6.39:
tar-pack "^3.4.0"
node-sass-tilde-importer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.1.tgz#3eab5247a3bf53354766bb1e6fd2214ce17e74ee"
version "1.0.2"
resolved "https://registry.yarnpkg.com/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz#1a15105c153f648323b4347693fdb0f331bad1ce"
dependencies:
find-parent-dir "^0.3.0"
node-sass@^4.5.3, node-sass@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e"
version "4.8.3"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.8.3.tgz#d077cc20a08ac06f661ca44fb6f19cd2ed41debb"
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@ -5025,7 +5046,7 @@ node-sass@^4.5.3, node-sass@^4.7.2:
lodash.mergewith "^4.6.0"
meow "^3.7.0"
mkdirp "^0.5.1"
nan "^2.3.2"
nan "^2.10.0"
node-gyp "^3.3.1"
npmlog "^4.0.0"
request "~2.79.0"
@ -5170,8 +5191,8 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
"nwmatcher@>= 1.3.9 < 2.0.0":
version "1.4.3"
resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c"
version "1.4.4"
resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e"
oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
@ -5243,8 +5264,8 @@ onetime@^2.0.0:
mimic-fn "^1.0.0"
opn@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
version "5.3.0"
resolved "https://registry.yarnpkg.com/opn/-/opn-5.3.0.tgz#64871565c863875f052cfdf53d3e3cb5adb53b1c"
dependencies:
is-wsl "^1.1.0"
@ -5625,8 +5646,8 @@ postcss-load-plugins@^2.3.0:
object-assign "^4.1.0"
postcss-loader@^2.0.10:
version "2.1.1"
resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.1.tgz#208935af3b1d65e1abb1a870a912dd12e7b36895"
version "2.1.3"
resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.3.tgz#eb210da734e475a244f76ccd61f9860f5bb3ee09"
dependencies:
loader-utils "^1.1.0"
postcss "^6.0.0"
@ -5648,12 +5669,12 @@ postcss-value-parser@^3.2.3:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.16, postcss@^6.0.17, postcss@^6.0.2, postcss@^6.x:
version "6.0.19"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555"
version "6.0.21"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.21.tgz#8265662694eddf9e9a5960db6da33c39e4cd069d"
dependencies:
chalk "^2.3.1"
chalk "^2.3.2"
source-map "^0.6.1"
supports-color "^5.2.0"
supports-color "^5.3.0"
precise-commits@1.0.0:
version "1.0.0"
@ -5790,6 +5811,10 @@ punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
punycode@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
q@~1.4.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
@ -5868,8 +5893,8 @@ raw-loader@^0.5.1:
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
version "1.2.5"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd"
version "1.2.6"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.6.tgz#eb18989c6d4f4f162c399f79ddd29f3835568092"
dependencies:
deep-extend "~0.4.0"
ini "~1.3.0"
@ -6229,8 +6254,8 @@ resolve@1.1.7:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2, resolve@^1.4.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
version "1.6.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.6.0.tgz#0fbd21278b27b4004481c395349e7aba60a9ff5c"
dependencies:
path-parse "^1.0.5"
@ -6293,8 +6318,8 @@ rollup-plugin-license@^0.6.0:
moment "2.21.0"
rollup-plugin-node-resolve@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.2.0.tgz#31534952f3ab21f9473c1d092be7ed43937ea4d4"
version "3.3.0"
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz#c26d110a36812cbefa7ce117cadcd3439aa1c713"
dependencies:
builtin-modules "^2.0.0"
is-module "^1.0.0"
@ -6318,8 +6343,8 @@ run-queue@^1.0.0, run-queue@^1.0.3:
aproba "^1.1.1"
rxjs@^5.5.0, rxjs@^5.5.6:
version "5.5.6"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.6.tgz#e31fb96d6fd2ff1fd84bcea8ae9c02d007179c02"
version "5.5.8"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.8.tgz#b2b0809a57614ad6254c03d7446dea0d83ca3791"
dependencies:
symbol-observable "1.0.1"
@ -6521,8 +6546,8 @@ setprototypeof@1.1.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
version "2.4.10"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b"
version "2.4.11"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@ -6750,8 +6775,8 @@ source-map-support@^0.4.1, source-map-support@^0.4.15:
source-map "^0.5.6"
source-map-support@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.3.tgz#2b3d5fff298cfa4d1afd7d4352d569e9a0158e76"
version "0.5.4"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.4.tgz#54456efa89caa9270af7cd624cc2f123e51fbae8"
dependencies:
source-map "^0.6.0"
@ -6780,10 +6805,8 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
sourcemap-codec@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.0.tgz#905439c4c65a4db421a2f2d06065bd8b55846a8e"
dependencies:
vlq "^1.0.0"
version "1.4.1"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2"
spdx-correct@^3.0.0:
version "3.0.0"
@ -6808,8 +6831,8 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87"
spdy-transport@^2.0.18:
version "2.0.20"
resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d"
version "2.1.0"
resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.1.0.tgz#4bbb15aaffed0beefdd56ad61dbdc8ba3e2cb7a1"
dependencies:
debug "^2.6.8"
detect-node "^2.0.3"
@ -6847,8 +6870,8 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
sshpk@^1.7.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3"
version "1.14.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
@ -6861,8 +6884,8 @@ sshpk@^1.7.0:
tweetnacl "~0.14.0"
ssri@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52"
version "5.3.0"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06"
dependencies:
safe-buffer "^5.1.1"
@ -6873,14 +6896,18 @@ static-extend@^0.1.1:
define-property "^0.2.5"
object-copy "^0.1.0"
"statuses@>= 1.3.1 < 2", statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
"statuses@>= 1.3.1 < 2", "statuses@>= 1.4.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
statuses@~1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
stdout-stream@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
@ -6915,8 +6942,8 @@ stream-each@^1.1.0:
stream-shift "^1.0.0"
stream-http@^2.0.0, stream-http@^2.7.2:
version "2.8.0"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10"
version "2.8.1"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.1.tgz#d0441be1a457a73a733a8a7b53570bebd9ef66a4"
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
@ -6973,9 +7000,9 @@ string.prototype.padend@^3.0.0:
es-abstract "^1.4.3"
function-bind "^1.0.2"
string_decoder@^1.0.0, string_decoder@~1.0.0, string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
string_decoder@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
dependencies:
safe-buffer "~5.1.0"
@ -6983,6 +7010,12 @@ string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
string_decoder@~1.0.0, string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
safe-buffer "~5.1.0"
stringstream@~0.0.4, stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
@ -7075,7 +7108,7 @@ supports-color@^4.0.0, supports-color@^4.2.1:
dependencies:
has-flag "^2.0.0"
supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0:
supports-color@^5.1.0, supports-color@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
dependencies:
@ -7218,7 +7251,7 @@ to-regex-range@^2.1.0:
is-number "^3.0.0"
repeat-string "^1.6.1"
to-regex@^3.0.1:
to-regex@^3.0.1, to-regex@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
dependencies:
@ -7294,8 +7327,8 @@ tsscmp@~1.0.0:
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
tsutils@^2.12.1:
version "2.22.2"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.22.2.tgz#0b9f3d87aa3eb95bd32d26ce2b88aa329a657951"
version "2.25.0"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.25.0.tgz#14d616ef59224a3c9fb7eb483e1c182b6665ae5e"
dependencies:
tslib "^1.8.1"
@ -7350,10 +7383,10 @@ uglify-es@^3.3.4:
source-map "~0.6.1"
uglify-js@3.3.x, uglify-js@^3.0.7:
version "3.3.14"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.14.tgz#d3d84d18722ff342fa96029cca71c67367700079"
version "3.3.16"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.3.16.tgz#23ba13efa27aa00885be7417819e8a9787f94028"
dependencies:
commander "~2.14.1"
commander "~2.15.0"
source-map "~0.6.1"
uglify-js@^2.6, uglify-js@^2.8.29:
@ -7378,8 +7411,8 @@ uglifyjs-webpack-plugin@^0.4.6:
webpack-sources "^1.0.1"
uglifyjs-webpack-plugin@^1.1.8:
version "1.2.3"
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.3.tgz#bf23197b37a8fc953fecfbcbab66e506f9a0ae72"
version "1.2.4"
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.4.tgz#5eec941b2e9b8538be0a20fc6eda25b14c7c1043"
dependencies:
cacache "^10.0.4"
find-cache-dir "^1.0.0"
@ -7399,8 +7432,8 @@ ultron@~1.1.0:
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
umd@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.2.tgz#95bdbc6d3983050df600431f44e5faeb4b7b3d45"
version "3.0.3"
resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.3.tgz#aa9fe653c42b9097678489c01000acb69f0b26cf"
underscore@~1.7.0:
version "1.7.0"
@ -7457,13 +7490,14 @@ upath@^1.0.0:
resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d"
update-notifier@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451"
version "2.4.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.4.0.tgz#f9b4c700fbfd4ec12c811587258777d563d8c866"
dependencies:
boxen "^1.2.1"
chalk "^2.0.1"
configstore "^3.0.0"
import-lazy "^2.1.0"
is-ci "^1.0.10"
is-installed-globally "^0.1.0"
is-npm "^1.0.0"
latest-version "^3.0.0"
@ -7474,6 +7508,12 @@ upper-case@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
uri-js@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-3.0.2.tgz#f90b858507f81dea4dcfbb3c4c3dbfa2b557faaa"
dependencies:
punycode "^2.1.0"
urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
@ -7575,14 +7615,10 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vlq@^0.2.1:
vlq@^0.2.1, vlq@^0.2.2:
version "0.2.3"
resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26"
vlq@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.0.tgz#8101be90843422954c2b13eb27f2f3122bdcc806"
vm-browserify@0.0.4, vm-browserify@~0.0.1:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
@ -7961,5 +7997,5 @@ yeast@0.1.2:
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
zone.js@^0.8.19:
version "0.8.20"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.20.tgz#a218c48db09464b19ff6fc8f0d4bb5b1046e185d"
version "0.8.21"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.21.tgz#8c0e8e361bd326ee1474e4f96e17d5ef99bec4b2"