Skip to content

Commit

Permalink
feat: add schematics (#2032)
Browse files Browse the repository at this point in the history
* feat(schematics): bring files from @ngxs/schematics repo into this repo

* chore: update build to include schematics

* test(store): fix jest and linter configs - update ignored patterns (#1827)

* test(store): fix jest config - update ignored patterns

- [x] exclude template files with 'spec' in file name when executing unit tests;

* fix(store): update excluded patterns in eslintignore

- [x] exclude template files from eslint check;

* fix(tslint): update tslint excludes

- [x] exclude template files form tslint check;

* feat(ngadd): improve the ngxs ng-add schema and factory, add unit tests (#1838)

* feat(ngadd): improve the ngxs ng-add schema and factory, add unit test

- [x] update the lib.config - add more available ngxs plugins;
- [x] ng-add schema - add selectable packages, add optional application project name;
- [x] ng-add factory - add selectable ngxs packages if they are not present yet;
- [x] ng-add factory - import the ngxs module in the root app module if project name is provided;
- [x] implement a basic ng-add factory unit test;
- [x] add the missing angular-devkit/schematics-cli package;
- [x] add the package commands to run existing ngxs schematics;

* Update packages/store/schematics/factories/ng-add/ng-add.factory.spec.ts

- [x] add comments matching preferred test style;

Co-authored-by: Mark Whitfeld <[email protected]>

* Update packages/store/schematics/factories/ng-add/ng-add.factory.spec.ts

- [x] add comments matching preferred test style;
- [x] tweak module import to be more typically formatted;

Co-authored-by: Mark Whitfeld <[email protected]>

* Update packages/store/schematics/factories/ng-add/ng-add.factory.ts

- [x] fix typos;

Co-authored-by: Mark Whitfeld <[email protected]>

* Update packages/store/schematics/factories/ng-add/ng-add.factory.ts

- [x] format tweaks, so that prettier has less work to do;

Co-authored-by: Mark Whitfeld <[email protected]>

* refactor(schematics): add a shared library configuration options object

Co-authored-by: Mark Whitfeld <[email protected]>

* refactor: support latest schematics version

* refactor: updated templates and using nx targets

* refactor: cleanup schematics

* refactor: rename packages option to plugins in ng-add

* refactor: make codeclimate happy

---------

Co-authored-by: apolo <[email protected]>
Co-authored-by: Vadim <[email protected]>
Co-authored-by: Mark Whitfeld <[email protected]>
Co-authored-by: apolo <[email protected]>
  • Loading branch information
5 people authored Sep 15, 2023
1 parent 98beeea commit 22c3643
Show file tree
Hide file tree
Showing 58 changed files with 1,730 additions and 4 deletions.
1 change: 1 addition & 0 deletions integrations/tempreponx
Submodule tempreponx added at ba31de
2 changes: 1 addition & 1 deletion nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"default": {
"runner": "@nrwl/nx-cloud",
"options": {
"cacheableOperations": ["build", "test", "lint"],
"cacheableOperations": ["build", "build-package", "test", "lint"],
"accessToken": "MWFlODQ3YjEtZGIzOC00OTJmLWE1NTYtMDcyZmNhYjU0NmU4fHJlYWQtd3JpdGU=",
"parallel": 1
}
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
"devDependencies": {
"@angular-builders/jest": "15.0.0",
"@angular-devkit/build-angular": "15.2.4",
"@angular-devkit/core": "15.2.4",
"@angular-devkit/schematics": "15.2.4",
"@angular-devkit/schematics-cli": "15.2.4",
"@angular/animations": "15.2.4",
"@angular/cli": "15.2.4",
"@angular/common": "15.2.4",
Expand Down Expand Up @@ -137,6 +140,7 @@
"@types/semver": "^7.2.0",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
"@schematics/angular": "15.2.4",
"bundlemon": "1.4.0",
"core-js": "^3.6.5",
"cpx": "^1.5.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"peerDependencies": {
"@angular/core": ">=12.0.0 <17.0.0",
"rxjs": ">=6.5.5"
}
},
"schematics": "./schematics/collection.json"
}
10 changes: 9 additions & 1 deletion packages/store/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,22 @@
"sourceRoot": "packages/store",
"projectType": "library",
"targets": {
"build": {
"build-package": {
"executor": "@angular-devkit/build-angular:ng-packagr",
"outputs": ["@ngxs/store"],
"options": {
"tsConfig": "tsconfig.build.json",
"project": "packages/store/ng-package.json"
}
},
"build": {
"executor": "nx:run-commands",
"options": {
"command": "node tools/build-schematics.mjs --projectRoot=packages/store --distPath=@ngxs/store",
"outputPath": "@ngxs/store"
},
"dependsOn": ["build-package"]
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
Expand Down
36 changes: 36 additions & 0 deletions packages/store/schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"extends": ["@schematics/angular"],
"$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"store": {
"factory": "./src/store/store.factory#store",
"description": "Create a NGXS full store",
"aliases": ["ngxs-store"],
"schema": "./src/store/schema.json"
},
"state": {
"factory": "./src/state/state.factory#state",
"description": "Create a NGXS state",
"aliases": ["ngxs-state"],
"schema": "./src/state/schema.json"
},
"actions": {
"factory": "./src/actions/actions.factory#actions",
"description": "Create a NGXS actions",
"aliases": ["ngxs-actions"],
"schema": "./src/actions/schema.json"
},
"starter-kit": {
"factory": "./src/starter-kit/starter-kit.factory#starterKit",
"description": "Create a NGXS starter kit with simple or best practice type",
"aliases": ["ngxs-sk"],
"schema": "./src/starter-kit/schema.json"
},
"ng-add": {
"factory": "./src/ng-add/ng-add.factory#ngAdd",
"description": "Add Ngxs Store and plugins in package.json",
"aliases": ["ngxs-init"],
"schema": "./src/ng-add/schema.json"
}
}
}
30 changes: 30 additions & 0 deletions packages/store/schematics/src/actions/actions.factory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { workspaceRoot } from '@nrwl/devkit';

import * as path from 'path';

import { ActionsSchema } from './actions.schema';

describe('NGXS Actions', () => {
const runner: SchematicTestRunner = new SchematicTestRunner(
'.',
path.join(workspaceRoot, 'packages/store/schematics/collection.json')
);
it('should create action in a folder by default', async () => {
const options: ActionsSchema = {
name: 'todos'
};
const tree: UnitTestTree = await runner.runSchematicAsync('actions', options).toPromise();
const files: string[] = tree.files;
expect(files).toEqual(['/todos/todos.actions.ts']);
});
it('should create action without folder if "flat" is set to "true"', async () => {
const options: ActionsSchema = {
name: 'todos',
flat: true
};
const tree: UnitTestTree = await runner.runSchematicAsync('actions', options).toPromise();
const files: string[] = tree.files;
expect(files).toEqual(['/todos.actions.ts']);
});
});
21 changes: 21 additions & 0 deletions packages/store/schematics/src/actions/actions.factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Rule, SchematicsException, url } from '@angular-devkit/schematics';
import { ActionsSchema } from './actions.schema';
import { generateFiles } from '../utils/generate-utils';
import { isEmpty } from '../utils/common/properties';
import { normalizeBaseOptions } from '../utils/normalize-options';
import { join } from 'path';

export function actions(options: ActionsSchema): Rule {
if (isEmpty(options.name)) {
throw new SchematicsException('Invalid options, "name" is required.');
}

const normalizedOptions = normalizeBaseOptions(options);
const path = options.flat
? normalizedOptions.path
: join(normalizedOptions.path, normalizedOptions.name);

return generateFiles(url('./files'), path, {
name: normalizedOptions.name
});
}
14 changes: 14 additions & 0 deletions packages/store/schematics/src/actions/actions.schema.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface ActionsSchema {
/**
* The name of the actions.
*/
name: string;
/**
* The path to create the actions.
*/
path?: string;
/**
* Flag to indicate if a dir is created.
*/
flat?: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class <%= classify(name) %>Action {
public static readonly type = '[<%= classify(name) %>] Add item';
constructor(public payload: any) { }
}
28 changes: 28 additions & 0 deletions packages/store/schematics/src/actions/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "SchematicsNgxsActions",
"title": "Ngxs Actions Options Schema",
"type": "object",
"properties": {
"name": {
"description": "The name of the actions.",
"type": "string",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the actions?"
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the actions."
},
"flat": {
"type": "boolean",
"default": false,
"description": "Flag to indicate if a dir is created."
}
},
"required": ["name"]
}
111 changes: 111 additions & 0 deletions packages/store/schematics/src/ng-add/ng-add.factory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { workspaceRoot } from '@nrwl/devkit';
import { join } from 'path';
import { Schema as ApplicationOptions } from '@schematics/angular/application/schema';
import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema';

import { LIBRARIES } from '../utils/common/lib.config';
import { NgxsPackageSchema } from './ng-add.schema';

describe('Ngxs ng-add Schematic', () => {
const angularSchematicRunner = new SchematicTestRunner(
'@schematics/angular',
join(workspaceRoot, 'node_modules/@schematics/angular/collection.json')
);

const ngxsSchematicRunner = new SchematicTestRunner(
'@ngxs/store/schematics',
join(workspaceRoot, 'packages/store/schematics/collection.json')
);

const defaultOptions: NgxsPackageSchema = {
skipInstall: false,
plugins: []
};

const workspaceOptions: WorkspaceOptions = {
name: 'workspace',
newProjectRoot: 'projects',
version: '1.0.0'
};

const appOptions: ApplicationOptions = {
name: 'foo',
inlineStyle: false,
inlineTemplate: false,
routing: true,
skipTests: false,
skipPackageJson: false
};

let appTree: UnitTestTree;
beforeEach(async () => {
appTree = await angularSchematicRunner
.runSchematicAsync('workspace', workspaceOptions)
.toPromise();
appTree = await angularSchematicRunner
.runSchematicAsync('application', appOptions, appTree)
.toPromise();
});

describe('importing the Ngxs module', () => {
test.each`
project
${undefined}
${'foo'}
`('should import the module when project is $project ', async ({ project }) => {
// Arrange
const options: NgxsPackageSchema = { ...defaultOptions, project };
// Act
const tree = await ngxsSchematicRunner
.runSchematicAsync('ngxs-init', options, appTree)
.toPromise();
// Assert
const content = tree.readContent('/projects/foo/src/app/app.module.ts');
expect(content).toMatch(/import { NgxsModule } from '@ngxs\/store'/);
expect(content).toMatch(/imports: \[[^\]]*NgxsModule.forRoot\(\[\],[^\]]*\]/m);
expect(content).toContain(
'NgxsModule.forRoot([], { developmentMode: /** !environment.production */ false, selectorOptions: { suppressErrors: false, injectContainerState: false } })'
);
});
it('should throw if invalid project is specified', async () => {
// Arrange
const options: NgxsPackageSchema = { ...defaultOptions, project: 'hello' };
await expect(
ngxsSchematicRunner.runSchematicAsync('ng-add', options, appTree).toPromise()
).rejects.toThrow(`Project "${options.project}" does not exist.`);
});
});

describe('ng-add package in package.json', () => {
it('should add ngxs store with provided plugins in package.json', async () => {
const plugins = [LIBRARIES.DEVTOOLS, LIBRARIES.LOGGER];
const options: NgxsPackageSchema = { plugins };
appTree = await ngxsSchematicRunner
.runSchematicAsync('ng-add', options, appTree)
.toPromise();
const packageJsonText = appTree.readContent('/package.json');
const packageJson = JSON.parse(packageJsonText);
expect(plugins.every(p => !!packageJson.dependencies[p])).toBeTruthy();
});

it('should add ngxs store with all plugins in package.json', async () => {
const packages = Object.values(LIBRARIES).filter(v => v !== LIBRARIES.STORE);
const options: NgxsPackageSchema = { plugins: packages };
appTree = await ngxsSchematicRunner
.runSchematicAsync('ng-add', options, appTree)
.toPromise();
const packageJsonText = appTree.readContent('/package.json');
const packageJson = JSON.parse(packageJsonText);
expect(packages.every(p => !!packageJson.dependencies[p])).toBeTruthy();
});

it('should not attempt to add non-existent package', async () => {
const packages = ['who-am-i'];
const options: NgxsPackageSchema = { plugins: packages };
await expect(
ngxsSchematicRunner.runSchematicAsync('ng-add', options, appTree).toPromise()
).rejects.toThrow();
});
});
});
Loading

0 comments on commit 22c3643

Please sign in to comment.