Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(schematics): implement router store ng-add #1487

Merged
merged 2 commits into from
Dec 31, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion modules/router-store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"keywords": [
"RxJS",
"Angular",
"Redux"
"Redux",
"Schematics",
"Angular CLI"
],
"author": "NgRx",
"license": "MIT",
Expand All @@ -24,6 +26,7 @@
"@ngrx/store": "0.0.0-PLACEHOLDER",
"rxjs": "RXJS_VERSION"
},
"schematics": "MODULE_SCHEMATICS_COLLECTION",
"ng-update": {
"packageGroup": "NG_UPDATE_PACKAGE_GROUP",
"migrations": "NG_UPDATE_MIGRATIONS"
Expand Down
35 changes: 35 additions & 0 deletions modules/router-store/schematics/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])

load("//tools:defaults.bzl", "npm_package", "ts_library")

ts_library(
name = "schematics",
srcs = glob(
[
"**/*.ts",
],
exclude = [
"**/*.spec.ts",
"**/files/**/*",
],
),
module_name = "@ngrx/router-store/schematics",
deps = [
"//modules/router-store/schematics-core",
"@npm//@angular-devkit/schematics",
"@npm//typescript",
],
)

npm_package(
name = "npm_package",
srcs = [
":collection.json",
] + glob([
"**/files/**/*",
"**/schema.json",
]),
deps = [
":schematics",
],
)
10 changes: 10 additions & 0 deletions modules/router-store/schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"schematics": {
"ng-add": {
"aliases": ["init"],
"factory": "./ng-add",
"schema": "./ng-add/schema.json",
"description": "Add router store class"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update description to "Register @ngrx/router-store within your application"

}
}
}
79 changes: 79 additions & 0 deletions modules/router-store/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
SchematicTestRunner,
UnitTestTree,
} from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { Schema as RouterStoreOptions } from './schema';
import {
getTestProjectPath,
createWorkspace,
} from '../../../schematics-core/testing';

describe('Router Store ng-add Schematic', () => {
const schematicRunner = new SchematicTestRunner(
'@ngrx/router-store',
path.join(__dirname, '../collection.json')
);
const defaultOptions: RouterStoreOptions = {
skipPackageJson: false,
module: 'app',
};

const projectPath = getTestProjectPath();

let appTree: UnitTestTree;

beforeEach(() => {
appTree = createWorkspace(schematicRunner, appTree);
});

it('should update package.json', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const packageJson = JSON.parse(tree.readContent('/package.json'));

expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
});

it('should skip package.json update', () => {
const options = { ...defaultOptions, skipPackageJson: true };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const packageJson = JSON.parse(tree.readContent('/package.json'));

expect(packageJson.dependencies['@ngrx/router-store']).toBeUndefined();
});

it('should be provided by default', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
expect(content).toMatch(
/import { StoreRouterConnectingModule } from '@ngrx\/router-store';/
);
expect(content).toMatch(/StoreRouterConnectingModule.forRoot\(\)/);
});

it('should import into a specified module', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
expect(content).toMatch(
/import { StoreRouterConnectingModule } from '@ngrx\/router-store';/
);
});

it('should fail if specified module does not exist', () => {
const options = { ...defaultOptions, module: '/src/app/app.moduleXXX.ts' };
let thrownError: Error | null = null;
try {
schematicRunner.runSchematic('ng-add', options, appTree);
} catch (err) {
thrownError = err;
}
expect(thrownError).toBeDefined();
});
});
115 changes: 115 additions & 0 deletions modules/router-store/schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
Rule,
SchematicContext,
SchematicsException,
Tree,
branchAndMerge,
chain,
noop,
} from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import * as ts from 'typescript';
import {
InsertChange,
addImportToModule,
addPackageToPackageJson,
buildRelativePath,
findModuleFromOptions,
getProjectPath,
insertImport,
parseName,
platformVersion,
stringUtils,
} from '@ngrx/router-store/schematics-core';
import { Schema as RouterStoreOptions } from './schema';

function addImportToNgModule(options: RouterStoreOptions): Rule {
return (host: Tree) => {
const modulePath = options.module;

if (!modulePath) {
return host;
}

if (!host.exists(modulePath)) {
throw new Error('Specified module does not exist');
}

const text = host.read(modulePath);
if (text === null) {
throw new SchematicsException(`File ${modulePath} does not exist.`);
}
const sourceText = text.toString('utf-8');

const source = ts.createSourceFile(
modulePath,
sourceText,
ts.ScriptTarget.Latest,
true
);

const [routerStoreNgModuleImport] = addImportToModule(
source,
modulePath,
`StoreRouterConnectingModule.forRoot()`,
`@ngrx/router-store`
);

const changes = [
insertImport(
source,
modulePath,
'StoreRouterConnectingModule',
'@ngrx/router-store'
),
routerStoreNgModuleImport,
];
const recorder = host.beginUpdate(modulePath);

for (const change of changes) {
if (change instanceof InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}
}
host.commitUpdate(recorder);

return host;
};
}

function addNgRxRouterStoreToPackageJson() {
return (host: Tree, context: SchematicContext) => {
addPackageToPackageJson(
host,
'dependencies',
'@ngrx/router-store',
platformVersion
);
context.addTask(new NodePackageInstallTask());
return host;
};
}

export default function(options: RouterStoreOptions): Rule {
return (host: Tree, context: SchematicContext) => {
options.path = getProjectPath(host, options);

if (options.module) {
options.module = findModuleFromOptions(host, {
name: '',
module: options.module,
path: options.path,
});
}

const parsedPath = parseName(options.path, '');
options.path = parsedPath.path;

return chain([
branchAndMerge(chain([addImportToNgModule(options)])),
options && options.skipPackageJson
? noop()
: addNgRxRouterStoreToPackageJson(),
])(host, context);
};
}
27 changes: 27 additions & 0 deletions modules/router-store/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsNgRxRouterStore",
"title": "NgRx Router Store Schema",
"type": "object",
"properties": {
"skipPackageJson": {
"type": "boolean",
"default": false,
"description": "Do not add @ngrx/router-store as dependency to package.json (e.g., --skipPackageJson)."
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the router store.",
"visible": false
},
"module": {
"type": "string",
"default": "app",
"description": "Allows specification of the declaring module.",
"alias": "m",
"subtype": "filepath"
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project key is missing here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project keys are also missing in /modules/MODULE/schematics/ng-add/schema.json as they are merged with #1067, at least for effects, store or store-devtools. But for example in /modules/schematics/src/MODULE/schema.json they are included.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. That can be done in a separate PR.

},
"required": []
}
6 changes: 6 additions & 0 deletions modules/router-store/schematics/ng-add/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Schema {
skipPackageJson?: boolean;
path?: string;
project?: string;
module?: string;
}