Skip to content

Commit 4816f9b

Browse files
authored
Merge pull request #7 from reduxjs/ng-add
feat: Support `ng add`
2 parents 01cf046 + 49aacb3 commit 4816f9b

37 files changed

+3594
-14
lines changed

angular.json

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
"test": {
2828
"builder": "@angular-devkit/build-angular:jest",
2929
"options": {
30+
"exclude": [
31+
"**/schematics/ng-add/*.spec.ts"
32+
],
3033
"tsConfig": "projects/angular-redux/tsconfig.spec.json",
3134
"polyfills": [
3235
"zone.js",

package.json

+15-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",
7-
"build": "ng build",
7+
"build": "ng build angular-redux && tsc -p projects/angular-redux/tsconfig.schematics.json && yarn build:copy",
8+
"build:copy": "cd ./projects/angular-redux/schematics && copyfiles \"**/*.json\" ../../../dist/angular-redux/schematics",
9+
"build:ng": "ng build",
810
"watch": "ng build --watch --configuration development",
9-
"test": "ng test"
11+
"test": "yarn test:ng && yarn test:schematics",
12+
"test:ng": "ng test",
13+
"test:schematics": "cd projects/angular-redux/schematics && jest"
14+
},
15+
"workspaces": {
16+
"packages": [
17+
"projects/*"
18+
]
1019
},
1120
"private": true,
1221
"dependencies": {
@@ -32,12 +41,16 @@
3241
"@testing-library/dom": "^10.0.0",
3342
"@testing-library/jest-dom": "^6.4.8",
3443
"@testing-library/user-event": "^14.5.2",
44+
"@types/copyfiles": "^2",
3545
"@types/jasmine": "~5.1.0",
3646
"@types/jest": "^29.5.12",
47+
"@types/node": "^22.5.4",
48+
"copyfiles": "^2.4.1",
3749
"jasmine-core": "~5.2.0",
3850
"jest": "^29.7.0",
3951
"jest-environment-jsdom": "^29.7.0",
4052
"ng-packagr": "^18.2.0",
53+
"ts-jest": "^29.2.5",
4154
"typescript": "~5.5.2"
4255
},
4356
"packageManager": "[email protected]"

projects/angular-redux/README.md

+27-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
Official Angular bindings for [Redux](https://github.com/reduxjs/redux).
44
Performant and flexible.
55

6-
76
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/angular-redux/test.yml?style=flat-square) [![npm version](https://img.shields.io/npm/v/@reduxjs/angular-redux.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/angular-redux)
87
[![npm downloads](https://img.shields.io/npm/dm/@reduxjs/angular-redux.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/angular-redux)
98

@@ -14,6 +13,32 @@ Performant and flexible.
1413

1514
Angular Redux requires **Angular 17.3 or later**.
1615

16+
### Installing with `ng add`
17+
18+
You can install the Store to your project with the following `ng add` command <a href="https://angular.dev/cli/add" target="_blank">(details here)</a>:
19+
20+
```sh
21+
ng add @reduxjs/angular-redux@latest
22+
```
23+
24+
#### Optional `ng add` flags
25+
26+
| flag | description | value type | default value |
27+
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|---------------
28+
| `--path` | Path to the module that you wish to add the import for the StoreModule to. | `string` |
29+
| `--project` | Name of the project defined in your `angular.json` to help locating the module to add the `provideRedux` to. | `string` |
30+
| `--module` | Name of file containing the module that you wish to add the import for the `provideRedux` to. Can also include the relative path to the file. For example, `src/app/app.module.ts`. | `string` | `app`
31+
| `--storePath` | The file path to create the state in. | `string` | `store` |
32+
33+
This command will automate the following steps:
34+
35+
1. Update `package.json` > `dependencies` with Redux, Redux Toolkit, and Angular Redux
36+
2. Run `npm install` to install those dependencies.
37+
3. Update your `src/app/app.module.ts` > `imports` array with `provideRedux({store})`
38+
4. If the project is using a `standalone bootstrap`, it adds `provideRedux({store})` into the application config.
39+
40+
## Installing with `npm` or `yarn`
41+
1742
To use React Redux with your Angular app, install it as a dependency:
1843

1944
```bash
@@ -35,7 +60,7 @@ modules](https://webpack.js.org/api/module-methods/#commonjs).
3560

3661
The following Angular component works as-expected:
3762

38-
```angular-ts
63+
```typescript
3964
import { Component } from '@angular/core'
4065
import { injectSelector, injectDispatch } from "@reduxjs/angular-redux";
4166
import { decrement, increment } from './store/counter-slice'

projects/angular-redux/package.json

+5
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
"peerDependencies": {
1414
"@angular/common": ">=17.3.0",
1515
"@angular/core": ">=17.3.0",
16+
"@reduxjs/toolkit": "^2.2.7",
1617
"redux": "^5.0.0"
1718
},
1819
"peerDependenciesMeta": {
20+
"@reduxjs/toolkit": {
21+
"optional": true
22+
},
1923
"redux": {
2024
"optional": true
2125
}
2226
},
27+
"schematics": "./schematics/collection.json",
2328
"dependencies": {
2429
"tslib": "^2.3.0"
2530
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This code is originally from NgRx:
2+
3+
https://github.com/ngrx/platform/tree/main/modules/schematics-core
4+
https://github.com/ngrx/platform/tree/main/modules/store/schematics-core
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import {
2+
dasherize,
3+
decamelize,
4+
camelize,
5+
classify,
6+
underscore,
7+
group,
8+
capitalize,
9+
featurePath,
10+
pluralize,
11+
} from './utility/strings';
12+
13+
export {
14+
findNodes,
15+
getSourceNodes,
16+
getDecoratorMetadata,
17+
getContentOfKeyLiteral,
18+
insertAfterLastOccurrence,
19+
insertImport,
20+
addBootstrapToModule,
21+
addDeclarationToModule,
22+
addExportToModule,
23+
addImportToModule,
24+
addProviderToComponent,
25+
addProviderToModule,
26+
replaceImport,
27+
containsProperty,
28+
} from './utility/ast-utils';
29+
30+
export {
31+
NoopChange,
32+
InsertChange,
33+
RemoveChange,
34+
ReplaceChange,
35+
createReplaceChange,
36+
createChangeRecorder,
37+
commitChanges
38+
} from './utility/change';
39+
export type {
40+
Host,
41+
Change
42+
} from './utility/change';
43+
44+
export {getWorkspace, getWorkspacePath} from './utility/config';
45+
export type { AppConfig } from './utility/config';
46+
47+
export { findComponentFromOptions } from './utility/find-component';
48+
49+
export {
50+
findModule,
51+
findModuleFromOptions,
52+
buildRelativePath
53+
} from './utility/find-module';
54+
export type { ModuleOptions } from './utility/find-module';
55+
56+
export { findPropertyInAstObject } from './utility/json-utilts';
57+
58+
export { getProjectPath, getProject, isLib } from './utility/project';
59+
60+
export const stringUtils = {
61+
dasherize,
62+
decamelize,
63+
camelize,
64+
classify,
65+
underscore,
66+
group,
67+
capitalize,
68+
featurePath,
69+
pluralize,
70+
};
71+
72+
export { parseName } from './utility/parse-name';
73+
74+
export { addPackageToPackageJson } from './utility/package';
75+
76+
export {
77+
visitTSSourceFiles,
78+
visitNgModuleImports,
79+
visitNgModuleExports,
80+
visitComponents,
81+
visitDecorator,
82+
visitNgModules,
83+
visitTemplates,
84+
} from './utility/visitors';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { UnitTestTree } from '@angular-devkit/schematics/testing';
2+
3+
export function createAppModule(
4+
tree: UnitTestTree,
5+
path?: string
6+
): UnitTestTree {
7+
tree.create(
8+
path || '/src/app/app.module.ts',
9+
`
10+
import { BrowserModule } from '@angular/platform-browser';
11+
import { NgModule } from '@angular/core';
12+
import { AppComponent } from './app.component';
13+
14+
@NgModule({
15+
declarations: [
16+
AppComponent
17+
],
18+
imports: [
19+
BrowserModule
20+
],
21+
providers: [],
22+
bootstrap: [AppComponent]
23+
})
24+
export class AppModule { }
25+
`
26+
);
27+
28+
return tree;
29+
}
30+
31+
export function createAppModuleWithEffects(
32+
tree: UnitTestTree,
33+
path: string,
34+
effects?: string
35+
): UnitTestTree {
36+
tree.create(
37+
path || '/src/app/app.module.ts',
38+
`
39+
import { BrowserModule } from '@angular/platform-browser';
40+
import { NgModule } from '@angular/core';
41+
import { AppComponent } from './app.component';
42+
import { EffectsModule } from '@ngrx/effects';
43+
44+
@NgModule({
45+
declarations: [
46+
AppComponent
47+
],
48+
imports: [
49+
BrowserModule,
50+
${effects}
51+
],
52+
providers: [],
53+
bootstrap: [AppComponent]
54+
})
55+
export class AppModule { }
56+
`
57+
);
58+
59+
return tree;
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Tree } from '@angular-devkit/schematics';
2+
import {
3+
UnitTestTree,
4+
SchematicTestRunner,
5+
} from '@angular-devkit/schematics/testing';
6+
7+
export const packagePath = '/package.json';
8+
9+
export function createPackageJson(
10+
prefix: string,
11+
pkg: string,
12+
tree: UnitTestTree,
13+
version = '5.2.0',
14+
packagePath = '/package.json'
15+
) {
16+
tree.create(
17+
packagePath,
18+
`{
19+
"dependencies": {
20+
"@ngrx/${pkg}": "${prefix}${version}"
21+
}
22+
}`
23+
);
24+
25+
return tree;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { UnitTestTree } from '@angular-devkit/schematics/testing';
2+
3+
export function createReducers(
4+
tree: UnitTestTree,
5+
path?: string,
6+
project = 'bar'
7+
) {
8+
tree.create(
9+
path || `/projects/${project}/src/app/reducers/index.ts`,
10+
`
11+
import { isDevMode } from '@angular/core';
12+
import {
13+
ActionReducer,
14+
ActionReducerMap,
15+
createFeatureSelector,
16+
createSelector,
17+
MetaReducer
18+
} from '@ngrx/${'store'}';
19+
20+
export interface State {
21+
22+
}
23+
24+
export const reducers: ActionReducerMap<State> = {
25+
26+
};
27+
28+
29+
export const metaReducers: MetaReducer<State>[] = isDevMode() ? [] : [];
30+
`
31+
);
32+
33+
return tree;
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {
2+
SchematicTestRunner,
3+
UnitTestTree,
4+
} from '@angular-devkit/schematics/testing';
5+
6+
export const defaultWorkspaceOptions = {
7+
name: 'workspace',
8+
newProjectRoot: 'projects',
9+
version: '6.0.0',
10+
};
11+
12+
export const defaultAppOptions = {
13+
name: 'bar',
14+
inlineStyle: false,
15+
inlineTemplate: false,
16+
viewEncapsulation: 'Emulated',
17+
routing: false,
18+
style: 'css',
19+
skipTests: false,
20+
standalone: false,
21+
};
22+
23+
const defaultLibOptions = {
24+
name: 'baz',
25+
};
26+
27+
export function getTestProjectPath(
28+
workspaceOptions: any = defaultWorkspaceOptions,
29+
appOptions: any = defaultAppOptions
30+
) {
31+
return `/${workspaceOptions.newProjectRoot}/${appOptions.name}`;
32+
}
33+
34+
export async function createWorkspace(
35+
schematicRunner: SchematicTestRunner,
36+
appTree: UnitTestTree,
37+
workspaceOptions = defaultWorkspaceOptions,
38+
appOptions = defaultAppOptions,
39+
libOptions = defaultLibOptions
40+
) {
41+
appTree = await schematicRunner.runExternalSchematic(
42+
'@schematics/angular',
43+
'workspace',
44+
workspaceOptions
45+
);
46+
47+
appTree = await schematicRunner.runExternalSchematic(
48+
'@schematics/angular',
49+
'application',
50+
appOptions,
51+
appTree
52+
);
53+
54+
appTree = await schematicRunner.runExternalSchematic(
55+
'@schematics/angular',
56+
'application',
57+
{ ...appOptions, name: 'bar-standalone', standalone: true },
58+
appTree
59+
);
60+
61+
appTree = await schematicRunner.runExternalSchematic(
62+
'@schematics/angular',
63+
'library',
64+
libOptions,
65+
appTree
66+
);
67+
68+
return appTree;
69+
}

0 commit comments

Comments
 (0)