Refactor the project to have three separate packages

This commit is contained in:
vsavkin 2017-08-29 17:52:36 -04:00
parent f852f1f2c2
commit 7377de404d
93 changed files with 420 additions and 239 deletions

196
README.md
View File

@ -1,171 +1,35 @@
# Nrwl Extensions for Angular # Angular Extensions
NX (Nrwl Extensions) is a set of libraries and schematics for the Angular framework. * See packages/bazel/README.md
* See packages/nx/README.md
* See packages/schematics/README.md
## Development Guide
### Building the Project
Running `yarn build` will build all the three packages.
### Running Unit Tests
Running `yarn test` will run unit tests for all the projects.
### Running E2E Tests
Running `yarn e2e` will run e2e tests for all the projects.
### Linking
The bazel package depends on the schematics package. If you make a change in the schematics that you want the bazel package ot pick up, run `yarn link`.
### Packaging
Running `yarn package` will create tgz files for all the projects. You can install them via npm.
### Release
Running `yarn release` will build the packages and push them to `github.com/nrwl/nx-build`, `github.com/nrwl/schematics-build`, `github.com/nrwl/bazel-build`
## Installing
Add the following dependencies to your project's `package.json` and run `npm install`:
```
{
dependencies: {
"@ngrx/store": "4.0.2",
"@ngrx/effects": "4.0.2",
"@nrwl/nx": "https://github.com/nrwl/nx-build",
"@angular-devkit/schematics": "0.0.17"
}
}
```
## Schematics
### addNgRxToModule
#### Root
Run `schematics @nrwl/nx:addNgRxToModule --module=src/app/app.module.ts --root`, and you will see the following files created:
```
/src/app/+state/app.actions.ts
/src/app/+state/app.effects.ts
/src/app/+state/app.effects.spec.ts
/src/app/+state/app.init.ts
/src/app/+state/app.interfaces.ts
/src/app/+state/app.reducer.ts
/src/app/+state/app.reducer.spec.ts
```
Also, `app.module.ts` will have `StoreModule.forRoot` and `EffectsModule.forRoot` configured.
#### EmptyRoot
Run `schematics @nrwl/nx:addNgRxToModule --module=src/app/app.module.ts --emptyRoot` to only add the `StoreModule.forRoot` and `EffectsModule.forRoot` calls.
#### Feature
Run `schematics @nrwl/nx:addNgRxToModule --module=src/app/mymodule/mymodule.module.ts `, and you will see the following files created:
```
/src/app/mymodule/+state/app.actions.ts
/src/app/mymodule/+state/app.effects.ts
/src/app/mymodule/+state/app.effects.spec.ts
/src/app/mymodule/+state/app.init.ts
/src/app/mymodule/+state/app.interfaces.ts
/src/app/mymodule/+state/app.reducer.ts
/src/app/mymodule/+state/app.reducer.spec.ts
```
Also, `mymodule.module.ts` will have `StoreModule.forFeature` and `EffectsModule.forFeature` configured.
#### skipImport
Add `--skipImport` to generate files without adding imports to the module.
## Data Persistence
Nrwl Extensions come with utilities to simplify data persistence (data fetching, optimistic and pessimistic updates).
### Optimistic Updates
```typescript
class TodoEffects {
@Effect() updateTodo = this.s.optimisticUpdate('UPDATE_TODO', {
// provides an action and the current state of the store
run(a: UpdateTodo, state: TodosState) {
return this.backend(state.user, a.payload);
},
undoAction(a: UpdateTodo, e: any): Action {
// dispatch an undo action to undo the changes in the client state
return ({
type: 'UNDO_UPDATE_TODO',
payload: a
});
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Pessimistic Updates
```typescript
@Injectable()
class TodoEffects {
@Effect() updateTodo = this.s.pessimisticUpdate('UPDATE_TODO', {
// provides an action and the current state of the store
run(a: UpdateTodo, state: TodosState) {
// update the backend first, and then dispatch an action that will
// update the client side
return this.backend(state.user, a.payload).map(updated => ({
type: 'TODO_UPDATED',
payload: updated
}));
},
onError(a: UpdateTodo, e: any) {
// we don't need to undo the changes on the client side.
// we can dispatch an error, or simply log the error here and return `null`
return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Date Fetching
```typescript
@Injectable()
class TodoEffects {
@Effect() loadTodo = this.s.fetch('GET_TODOS', {
// provides an action and the current state of the store
run(a: GetTodos, state: TodosState) {
return this.backend(state.user, a.payload).map(r => ({
type: 'TODOS',
payload: r
});
},
onError(a: GetTodos, e: any): Action {
// dispatch an undo action to undo the changes in the client state
// return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Date Fetching On Router Navigation
```typescript
@Injectable()
class TodoEffects {
@Effect() loadTodo = this.s.navigation(TodoComponent, {
run: (a: ActivatedRouteSnapshot, state: TodosState) => {
return this.backend.fetchTodo(a.params['id']).map(todo => ({
type: 'TODO_LOADED',
payload: todo
}));
},
onError: (a: ActivatedRouteSnapshot, e: any) => {
// we can log and error here and return null
// we can also navigate back
return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
## Testing
Nrwl Extensions come with utilities to simplify testing Angular applications. See `app.effects.spec.ts`. Read https://github.com/vsavkin/testing_ngrx_effects for more information.

View File

@ -1,11 +1,11 @@
import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from './utils'; import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from '../utils';
describe('application', () => { describe('application', () => {
beforeEach(cleanup); beforeEach(cleanup);
it('creates a new application in a workspace', () => { fit('creates a new application in a workspace', () => {
runSchematic('@nrwl/nx:application --name=proj'); runSchematic('@nrwl/bazel:application --name=proj');
runSchematic('@nrwl/nx:app --name=myApp', {projectName: 'proj'}); runSchematic('@nrwl/bazel:app --name=myApp', {projectName: 'proj'});
checkFilesExists( checkFilesExists(
`proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/apps/my-app/BUILD.bazel`, `proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/apps/my-app/BUILD.bazel`,
@ -21,9 +21,9 @@ describe('application', () => {
}); });
it('creates multiple applications in a workspace', () => { it('creates multiple applications in a workspace', () => {
runSchematic('@nrwl/nx:application --name=proj2'); runSchematic('@nrwl/bazel:application --name=proj2');
runSchematic('@nrwl/nx:app --name=first', {projectName: 'proj2'}); runSchematic('@nrwl/bazel:app --name=first', {projectName: 'proj2'});
runSchematic('@nrwl/nx:app --name=second', {projectName: 'proj2'}); runSchematic('@nrwl/bazel:app --name=second', {projectName: 'proj2'});
const cliConfig = JSON.parse(readFile('proj2/.angular-cli.json')); const cliConfig = JSON.parse(readFile('proj2/.angular-cli.json'));
expect(cliConfig.apps[0].name).toEqual('first'); expect(cliConfig.apps[0].name).toEqual('first');

View File

@ -1,11 +1,11 @@
import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from './utils'; import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from '../utils';
describe('library', () => { describe('library', () => {
beforeEach(cleanup); beforeEach(cleanup);
it('creates a new library in a workspace', () => { it('creates a new library in a workspace', () => {
runSchematic('@nrwl/nx:application --name=proj'); runSchematic('@nrwl/bazel:application --name=proj');
runSchematic('@nrwl/nx:lib --name=myLib', {projectName: 'proj'}); runSchematic('@nrwl/bazel:lib --name=myLib', {projectName: 'proj'});
checkFilesExists( checkFilesExists(
`proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/libs/my-lib/BUILD.bazel`, `proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/libs/my-lib/BUILD.bazel`,

View File

@ -1,11 +1,11 @@
import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from './utils'; import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from '../utils';
describe('angular library', () => { describe('angular library', () => {
beforeEach(cleanup); beforeEach(cleanup);
it('creates a new angularlibrary in a workspace', () => { it('creates a new angularlibrary in a workspace', () => {
runSchematic('@nrwl/nx:application --name=proj'); runSchematic('@nrwl/bazel:application --name=proj');
runSchematic('@nrwl/nx:nglib --name=myLib', {projectName: 'proj'}); runSchematic('@nrwl/bazel:nglib --name=myLib', {projectName: 'proj'});
checkFilesExists( checkFilesExists(
`proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/libs/my-lib/BUILD.bazel`, `proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`, `proj/libs/my-lib/BUILD.bazel`,

View File

@ -1,10 +1,10 @@
import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from './utils'; import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from '../utils';
describe('workspace', () => { describe('workspace', () => {
beforeEach(cleanup); beforeEach(cleanup);
it('creates a new workspace for developing angular applications', () => { it('creates a new workspace for developing angular applications', () => {
runSchematic('@nrwl/nx:application --name=proj --version=0.1'); runSchematic('@nrwl/bazel:application --name=proj --version=0.1');
checkFilesExists(`proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`); checkFilesExists(`proj/tsconfig.json`, `proj/WORKSPACE`, `proj/BUILD.bazel`);
}); });

View File

@ -1,11 +1,11 @@
import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from './utils'; import {addNgRx, checkFilesExists, cleanup, newApp, readFile, runCLI, runCommand, runSchematic, updateFile} from '../utils';
describe('addNgRxToModule', () => { describe('addNgRxToModule', () => {
beforeEach(cleanup); beforeEach(cleanup);
it('should add root configuration', () => { it('should add root configuration', () => {
newApp('new proj --skipInstall'); newApp('new proj --skipInstall');
runSchematic('@nrwl/nx:addNgRxToModule --module=src/app/app.module.ts --root', {projectName: 'proj'}); runSchematic('@nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts --root', {projectName: 'proj'});
checkFilesExists( checkFilesExists(
`proj/src/app/+state/app.actions.ts`, `proj/src/app/+state/app.effects.ts`, `proj/src/app/+state/app.actions.ts`, `proj/src/app/+state/app.effects.ts`,
@ -26,7 +26,7 @@ describe('addNgRxToModule', () => {
it('should add empty root configuration', () => { it('should add empty root configuration', () => {
newApp('new proj2 --skipInstall'); newApp('new proj2 --skipInstall');
runSchematic('@nrwl/nx:addNgRxToModule --module=src/app/app.module.ts --emptyRoot', {projectName: 'proj2'}); runSchematic('@nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts --emptyRoot', {projectName: 'proj2'});
const contents = readFile('proj2/src/app/app.module.ts'); const contents = readFile('proj2/src/app/app.module.ts');
expect(contents).toContain('StoreModule.forRoot'); expect(contents).toContain('StoreModule.forRoot');
@ -39,7 +39,7 @@ describe('addNgRxToModule', () => {
it('should add feature configuration', () => { it('should add feature configuration', () => {
newApp('new proj3 --skipInstall'); newApp('new proj3 --skipInstall');
runSchematic('@nrwl/nx:addNgRxToModule --module=src/app/app.module.ts', {projectName: 'proj3'}); runSchematic('@nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts', {projectName: 'proj3'});
checkFilesExists( checkFilesExists(
`proj3/src/app/+state/app.actions.ts`, `proj3/src/app/+state/app.effects.ts`, `proj3/src/app/+state/app.actions.ts`, `proj3/src/app/+state/app.effects.ts`,
@ -54,7 +54,7 @@ describe('addNgRxToModule', () => {
it('should generate files without importing them', () => { it('should generate files without importing them', () => {
newApp('new proj4 --skipInstall'); newApp('new proj4 --skipInstall');
runSchematic('@nrwl/nx:addNgRxToModule --module=src/app/app.module.ts --skipImport', {projectName: 'proj4'}); runSchematic('@nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts --skipImport', {projectName: 'proj4'});
checkFilesExists( checkFilesExists(
`proj4/src/app/+state/app.actions.ts`, `proj4/src/app/+state/app.effects.ts`, `proj4/src/app/+state/app.actions.ts`, `proj4/src/app/+state/app.effects.ts`,

View File

@ -9,7 +9,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser // list of files / patterns to load in the browser
files: [ files: [
{ pattern: 'build/test/test.js', watched: false} { pattern: 'build/test.js', watched: false}
], ],
// list of files to exclude // list of files to exclude
@ -18,7 +18,7 @@ module.exports = function(config) {
// preprocess matching files before serving them to the browser // preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: { preprocessors: {
'build/test/test.js': ['webpack'] 'build/test.js': ['webpack']
}, },
reporters: ['dots'], reporters: ['dots'],

View File

@ -1,12 +1,13 @@
{ {
"name": "@nrwl/nx", "name": "@nrwl/nx",
"version": "0.0.1", "version": "0.0.1",
"description": "Nrwl Extensions for Angular", "description": "Angular Extensions",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "./scripts/build.sh", "build": "./scripts/build.sh",
"e2e": "yarn build && ./scripts/e2e.sh", "e2e": "yarn build && ./scripts/e2e.sh",
"format": "./scripts/format.sh", "format": "./scripts/format.sh",
"link": "./scripts/link.sh",
"package": "./scripts/package.sh", "package": "./scripts/package.sh",
"release": "./scripts/release.sh", "release": "./scripts/release.sh",
"test": "yarn build && ./scripts/test.sh" "test": "yarn build && ./scripts/test.sh"
@ -14,18 +15,6 @@
"dependencies" :{ "dependencies" :{
"jasmine-marbles": "0.1.0" "jasmine-marbles": "0.1.0"
}, },
"peerDependencies" :{
"@angular-devkit/schematics": "*",
"rxjs": ">5.4.2",
"@angular/core": ">4.0.0",
"@angular/common": ">4.0.0",
"@angular/router": ">4.0.0",
"@angular/platform-browser": ">4.0.0",
"@ngrx/store": ">4.0.0",
"@ngrx/router-store": ">4.0.0",
"@ngrx/effects": ">4.0.0"
},
"devDependencies": { "devDependencies": {
"rxjs": "5.4.3", "rxjs": "5.4.3",
"@angular/core": "4.3.5", "@angular/core": "4.3.5",
@ -41,8 +30,7 @@
"@types/node": "8.0.7", "@types/node": "8.0.7",
"@types/jasmine": "2.5.53", "@types/jasmine": "2.5.53",
"jest": "20.0.4", "jest": "20.0.4",
"@angular-devkit/schematics": "0.0.17", "@schematics/angular": "0.0.30",
"@schematics/angular": "git+https://github.com/Brocco/zzz-ng-schematics.git",
"jasmine-core": "~2.6.2", "jasmine-core": "~2.6.2",
"karma": "~1.7.0", "karma": "~1.7.0",
"karma-chrome-launcher": "~2.1.1", "karma-chrome-launcher": "~2.1.1",
@ -57,6 +45,5 @@
"tmp", "tmp",
"files" "files"
] ]
}, }
"schematics": "schematics/collection.json"
} }

22
packages/bazel/LICENSE Normal file
View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017 Narwhal Technologies
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
packages/bazel/README.md Normal file
View File

@ -0,0 +1 @@
# Angular Extensions: Bazel Backend for CLI

View File

@ -0,0 +1,12 @@
{
"name": "@nrwl/bazel",
"version": "0.0.1",
"description": "Angular Extensions: Schematics",
"main": "src/index.js",
"peerDependencies" :{
"@nrwl/schematics": "0.0.1"
},
"author": "Victor Savkin",
"license": "MIT",
"schematics": "src/collection.json"
}

View File

@ -1,10 +1,9 @@
import {apply, chain, mergeWith, move, Rule, externalSchematic, template, url, Tree,} from '@angular-devkit/schematics'; import {apply, chain, mergeWith, move, Rule, externalSchematic, template, url, Tree,} from '@angular-devkit/schematics';
import {Schema} from './schema'; import {Schema} from './schema';
import {names, toFileName} from '../name-utils'; import {names, toFileName, insert} from '@nrwl/schematics';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {addBootstrapToModule, addImportToModule} from '@schematics/angular/utility/ast-utils'; import {addBootstrapToModule, addImportToModule} from '@schematics/angular/utility/ast-utils';
import {insert} from '../utility/ast-utils';
function addBootstrap(path: string): Rule { function addBootstrap(path: string): Rule {
return (host: Tree) => { return (host: Tree) => {

View File

@ -1,5 +1,5 @@
{ {
"name": "nrwl", "name": "bazel",
"version": "0.1", "version": "0.1",
"schematics": { "schematics": {
"application": { "application": {
@ -21,11 +21,6 @@
"factory": "./nglib", "factory": "./nglib",
"schema": "./nglib/schema.json", "schema": "./nglib/schema.json",
"description": "Create an Angular library." "description": "Create an Angular library."
},
"addNgRxToModule": {
"factory": "./addNgRxToModule",
"schema": "./addNgRxToModule/schema.json",
"description": "Add NgRx support to a module."
} }
} }
} }

View File

@ -1,7 +1,7 @@
import {apply, branchAndMerge, chain, mergeWith, Rule, template, Tree, url} from '@angular-devkit/schematics'; import {apply, branchAndMerge, chain, mergeWith, Rule, template, Tree, url} from '@angular-devkit/schematics';
import {Schema} from './schema'; import {Schema} from './schema';
import * as path from 'path'; import * as path from 'path';
import {names, toFileName} from '../name-utils'; import {names, toFileName} from '@nrwl/schematics';
function addLibToAngularCliJson(fullPath: string, schema: Schema): Rule { function addLibToAngularCliJson(fullPath: string, schema: Schema): Rule {
return (host: Tree) => { return (host: Tree) => {

View File

@ -1,7 +1,7 @@
import {apply, branchAndMerge, chain, mergeWith, Rule, template, Tree, url} from '@angular-devkit/schematics'; import {apply, branchAndMerge, chain, mergeWith, Rule, template, Tree, url} from '@angular-devkit/schematics';
import {Schema} from './schema'; import {Schema} from './schema';
import * as path from 'path'; import * as path from 'path';
import {names, toFileName} from '../name-utils'; import {names, toFileName} from '@nrwl/schematics';
function addLibToAngularCliJson(fullPath: string, schema: Schema): Rule { function addLibToAngularCliJson(fullPath: string, schema: Schema): Rule {
return (host: Tree) => { return (host: Tree) => {

View File

@ -20,7 +20,7 @@ module.exports = function(config) {
}, },
resolveLoader: { resolveLoader: {
alias: { alias: {
"template-loader": '@nrwl/nx/bazel/template-loader' "template-loader": '@nrwl/bazel/src/utils/template-loader'
} }
}, },
module: { module: {

View File

@ -18,7 +18,7 @@ local_repository(
local_repository( local_repository(
name = "nrwl", name = "nrwl",
path = "node_modules/@nrwl/nx/bazel", path = "node_modules/@nrwl/bazel/src/utils",
) )
git_repository( git_repository(

View File

@ -7,12 +7,12 @@
], ],
"test": { "test": {
"karma": { "karma": {
"config": "node_modules/@nrwl/nx/bazel/karma.conf.js" "config": "node_modules/@nrwl/bazel/src/utils/karma.conf.js"
} }
}, },
"defaults": { "defaults": {
"schematics": { "schematics": {
"collection": "@nrwl/nx" "collection": "@nrwl/bazel"
}, },
"component": { "component": {
"spec": true, "spec": true,

View File

@ -29,7 +29,7 @@
"devDependencies": { "devDependencies": {
"@angular/cli": "https://github.com/nrwl/bazel-cli-build", "@angular/cli": "https://github.com/nrwl/bazel-cli-build",
"@bazel/typescript": "~0.0.7", "@bazel/typescript": "~0.0.7",
"@nrwl/nx": "https://github.com/nrwl/nx-build", "@nrwl/bazel": "https://github.com/nrwl/bazel-build",
"@angular/bazel": "angular/bazel-builds#6f50535", "@angular/bazel": "angular/bazel-builds#6f50535",
"@angular/compiler-cli": "angular/compiler-cli-builds#797acb2", "@angular/compiler-cli": "angular/compiler-cli-builds#797acb2",
"@angular/language-service": "angular/language-service-builds#57583ee", "@angular/language-service": "angular/language-service-builds#57583ee",

View File

@ -1,6 +1,6 @@
import {apply, chain, mergeWith, move, Rule, schematic, template, url,} from '@angular-devkit/schematics'; import {apply, chain, mergeWith, move, Rule, schematic, template, url,} from '@angular-devkit/schematics';
import {Schema} from './schema'; import {Schema} from './schema';
import {names} from '../name-utils'; import {names} from '@nrwl/schematics';
export default function(options: Schema): Rule { export default function(options: Schema): Rule {
return chain([mergeWith(apply( return chain([mergeWith(apply(

22
packages/nx/LICENSE Normal file
View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017 Narwhal Technologies
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

120
packages/nx/README.md Normal file
View File

@ -0,0 +1,120 @@
# Angular Extensions
Nx (Angular Extensions) is a set of libraries and schematics for the Angular framework.
## Installing
Add the following dependencies to your project's `package.json` and run `npm install`:
```
{
dependencies: {
"@ngrx/store": "4.0.2",
"@ngrx/effects": "4.0.2",
"@nrwl/nx": "https://github.com/nrwl/nx-build"
}
}
```
## Data Persistence
Nrwl Extensions come with utilities to simplify data persistence (data fetching, optimistic and pessimistic updates).
### Optimistic Updates
```typescript
class TodoEffects {
@Effect() updateTodo = this.s.optimisticUpdate('UPDATE_TODO', {
// provides an action and the current state of the store
run(a: UpdateTodo, state: TodosState) {
return this.backend(state.user, a.payload);
},
undoAction(a: UpdateTodo, e: any): Action {
// dispatch an undo action to undo the changes in the client state
return ({
type: 'UNDO_UPDATE_TODO',
payload: a
});
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Pessimistic Updates
```typescript
@Injectable()
class TodoEffects {
@Effect() updateTodo = this.s.pessimisticUpdate('UPDATE_TODO', {
// provides an action and the current state of the store
run(a: UpdateTodo, state: TodosState) {
// update the backend first, and then dispatch an action that will
// update the client side
return this.backend(state.user, a.payload).map(updated => ({
type: 'TODO_UPDATED',
payload: updated
}));
},
onError(a: UpdateTodo, e: any) {
// we don't need to undo the changes on the client side.
// we can dispatch an error, or simply log the error here and return `null`
return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Date Fetching
```typescript
@Injectable()
class TodoEffects {
@Effect() loadTodo = this.s.fetch('GET_TODOS', {
// provides an action and the current state of the store
run(a: GetTodos, state: TodosState) {
return this.backend(state.user, a.payload).map(r => ({
type: 'TODOS',
payload: r
});
},
onError(a: GetTodos, e: any): Action {
// dispatch an undo action to undo the changes in the client state
// return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
### Date Fetching On Router Navigation
```typescript
@Injectable()
class TodoEffects {
@Effect() loadTodo = this.s.navigation(TodoComponent, {
run: (a: ActivatedRouteSnapshot, state: TodosState) => {
return this.backend.fetchTodo(a.params['id']).map(todo => ({
type: 'TODO_LOADED',
payload: todo
}));
},
onError: (a: ActivatedRouteSnapshot, e: any) => {
// we can log and error here and return null
// we can also navigate back
return null;
}
});
constructor(private s: DataPersistence<TodosState>, private backend: Backend) {}
}
```
## Testing
Nx comes with utilities to simplify testing Angular applications. Read https://github.com/vsavkin/testing_ngrx_effects for more information.

1
packages/nx/index.ts Normal file
View File

@ -0,0 +1 @@
export {DataPersistence} from './src/data-persistence';

22
packages/nx/package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "@nrwl/nx",
"version": "0.0.1",
"description": "Angular Extensions",
"main": "index.js",
"dependencies" :{
"jasmine-marbles": "0.1.0",
"@nrwl/schematics": "0.0.1"
},
"peerDependencies" :{
"rxjs": ">5.4.2",
"@angular/core": ">4.0.0",
"@angular/common": ">4.0.0",
"@angular/router": ">4.0.0",
"@angular/platform-browser": ">4.0.0",
"@ngrx/store": ">4.0.0",
"@ngrx/router-store": ">4.0.0",
"@ngrx/effects": ">4.0.0"
},
"author": "Victor Savkin",
"license": "MIT"
}

View File

@ -1,4 +1,4 @@
import {DataPersistence} from '../../src/index'; import {DataPersistence} from '../index';
import {Actions, Effect, EffectsModule} from '@ngrx/effects'; import {Actions, Effect, EffectsModule} from '@ngrx/effects';
import {ActivatedRouteSnapshot, Router} from '@angular/router'; import {ActivatedRouteSnapshot, Router} from '@angular/router';
import {Store, StoreModule} from '@ngrx/store'; import {Store, StoreModule} from '@ngrx/store';
@ -11,7 +11,7 @@ import {Observable} from 'rxjs/Observable';
import {_throw} from 'rxjs/observable/throw'; import {_throw} from 'rxjs/observable/throw';
import {provideMockActions} from '@ngrx/effects/testing'; import {provideMockActions} from '@ngrx/effects/testing';
import {Subject} from 'rxjs/Subject'; import {Subject} from 'rxjs/Subject';
import {readAll} from '../../src/utils/testing'; import {readAll} from '../testing';
// interfaces // interfaces
type Todo = { id: number; user: string; }; type Todo = { id: number; user: string; };

2
packages/nx/testing.ts Normal file
View File

@ -0,0 +1,2 @@
export {cold, hot} from 'jasmine-marbles';
export {readAll} from './src/testing-utils';

View File

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2017 Narwhal Technologies
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,63 @@
# Angular Extensions: Schematics
Nx (Angular Extensions) is a set of libraries and schematics for the Angular framework.
## Installing
Add the following dependencies to your project's `package.json` and run `npm install`:
```
{
dependencies: {
"@nrwl/schematics": "https://github.com/nrwl/schematics-build"
}
}
```
## Schematics
### addNgRxToModule
#### Root
Run `schematics @nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts --root`, and you will see the following files created:
```
/src/app/+state/app.actions.ts
/src/app/+state/app.effects.ts
/src/app/+state/app.effects.spec.ts
/src/app/+state/app.init.ts
/src/app/+state/app.interfaces.ts
/src/app/+state/app.reducer.ts
/src/app/+state/app.reducer.spec.ts
```
Also, `app.module.ts` will have `StoreModule.forRoot` and `EffectsModule.forRoot` configured.
#### EmptyRoot
Run `schematics @nrwl/schematics:addNgRxToModule --module=src/app/app.module.ts --emptyRoot` to only add the `StoreModule.forRoot` and `EffectsModule.forRoot` calls.
#### Feature
Run `schematics @nrwl/schematics:addNgRxToModule --module=src/app/mymodule/mymodule.module.ts `, and you will see the following files created:
```
/src/app/mymodule/+state/app.actions.ts
/src/app/mymodule/+state/app.effects.ts
/src/app/mymodule/+state/app.effects.spec.ts
/src/app/mymodule/+state/app.init.ts
/src/app/mymodule/+state/app.interfaces.ts
/src/app/mymodule/+state/app.reducer.ts
/src/app/mymodule/+state/app.reducer.spec.ts
```
Also, `mymodule.module.ts` will have `StoreModule.forFeature` and `EffectsModule.forFeature` configured.
#### skipImport
Add `--skipImport` to generate files without adding imports to the module.

View File

@ -0,0 +1,2 @@
export {names, toClassName, toFileName, toPropertyName} from './src/utility/name-utils';
export {addImportToModule, addProviderToModule, insert} from './src/utility/ast-utils';

View File

@ -0,0 +1,14 @@
{
"name": "@nrwl/schematics",
"version": "0.0.1",
"description": "Angular Extensions: Schematics",
"main": "index.js",
"peerDependencies" :{
"rxjs": ">5.4.2",
"@angular-devkit/schematics": "0.0.18",
"@schematics/angular": "0.0.30"
},
"author": "Victor Savkin",
"license": "MIT",
"schematics": "src/collection.json"
}

View File

@ -1,6 +1,6 @@
import {apply, branchAndMerge, chain, mergeWith, move, Rule, template, Tree, url} from '@angular-devkit/schematics'; import {apply, branchAndMerge, chain, mergeWith, move, Rule, template, Tree, url} from '@angular-devkit/schematics';
import {names, toClassName, toFileName, toPropertyName} from '../name-utils'; import {names, toClassName, toFileName, toPropertyName} from '../utility/name-utils';
import * as path from 'path'; import * as path from 'path';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {addImportToModule, addProviderToModule, insert} from '../utility/ast-utils'; import {addImportToModule, addProviderToModule, insert} from '../utility/ast-utils';

View File

@ -0,0 +1,11 @@
{
"name": "nx",
"version": "0.1",
"schematics": {
"addNgRxToModule": {
"factory": "./addNgRxToModule",
"schema": "./addNgRxToModule/schema.json",
"description": "Add NgRx support to a module."
}
}
}

View File

@ -2,4 +2,4 @@
rm -rf build rm -rf build
tsc tsc
rsync -a --exclude=*.ts src/ build/src rsync -a --exclude=*.ts packages/ build/packages

View File

@ -1,9 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
rm -rf node_modules/@nrwl ./scripts/link.sh
mkdir -p node_modules/@nrwl
cp -r build/src node_modules/@nrwl/nx
cp package.json node_modules/@nrwl/nx/package.json
rm -rf tmp rm -rf tmp
jest --maxWorkers=1 ./build/e2e jest --maxWorkers=1 ./build/e2e/schematics

6
scripts/link.sh Executable file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
./scripts/build.sh
rm -rf node_modules/@nrwl
cp -r build/packages node_modules/@nrwl

View File

@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
./scripts/build.sh ./scripts/build.sh
cp package.json build/src/package.json
cp README.md build/src/README.md cd build/packages
cp LICENSE build/src/LICENSE
tar -czf bazel.tgz bazel
tar -czf nx.tgz nx
tar -czf schematics.tgz schematics

View File

@ -1,9 +1,24 @@
#!/usr/bin/env bash #!/usr/bin/env bash
./scripts/package.sh ./scripts/package.sh
cd build/src
cd build/packages/bazel
git init
git add .
git commit -am 'init commit'
git remote add origin git@github.com:nrwl/bazel-build.git
git push -f origin master
cd ../nx
git init git init
git add . git add .
git commit -am 'init commit' git commit -am 'init commit'
git remote add origin git@github.com:nrwl/nx-build.git git remote add origin git@github.com:nrwl/nx-build.git
git push -f origin master git push -f origin master
cd ../schematics
git init
git add .
git commit -am 'init commit'
git remote add origin git@github.com:nrwl/schematics-build.git
git push -f origin master

View File

@ -1 +0,0 @@
export {DataPersistence} from './utils/data-persistence';

View File

@ -1,2 +0,0 @@
export {cold, hot} from 'jasmine-marbles';
export {readAll} from './utils/testing';

View File

@ -24,7 +24,7 @@ getTestBed().initTestEnvironment(
platformBrowserDynamicTesting() platformBrowserDynamicTesting()
); );
const context = (<any>require).context('./', true, /\.spec\.js$/); const context = (<any>require).context('./packages/nx', true, /\.spec\.js$/);
// And load the modules. // And load the modules.
context.keys().map(context); context.keys().map(context);
// Finally, start Karma to run the tests. // Finally, start Karma to run the tests.

View File

@ -12,6 +12,11 @@
"es2017" "es2017"
] ]
}, },
"path": {
"@nrwl/schematics": [
"packages/schematics/src/index"
]
},
"exclude": [ "exclude": [
"tmp", "tmp",
"node_modules" "node_modules"