Skip to content

Commit

Permalink
feat: add ng-add schematic
Browse files Browse the repository at this point in the history
  • Loading branch information
tonivj5 committed Jun 4, 2020
1 parent 689553b commit 2e442cb
Show file tree
Hide file tree
Showing 11 changed files with 1,290 additions and 145 deletions.
471 changes: 341 additions & 130 deletions package-lock.json

Large diffs are not rendered by default.

36 changes: 21 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
"hooks:pre-commit": "node hooks/pre-commit.js",
"commit": "git-cz",
"release:first": "npm run release -- --first-release",
"build:lib": "ng build @ngneat/error-tailor",
"build:lib": "ng build @ngneat/error-tailor && npm run schematics:build",
"test:lib": "ng test @ngneat/error-tailor",
"release": "cd projects/ngneat/error-tailor && standard-version --infile ../../../CHANGELOG.md",
"test:lib:headless": "cross-env CI=true npm run test:lib"
"test:lib:headless": "cross-env CI=true npm run test:lib",
"schematics:build": "tsc -p tsconfig.schematics.json && npm run schematics:copy",
"schematics:copy": "cp schematics/src/collection.json dist/ngneat/error-tailor/schematics/src && cp schematics/src/ng-add/schema.json dist/ngneat/error-tailor/schematics/src/ng-add",
"test:schematics": "TS_NODE_PROJECT='tsconfig.schematics.spec.json' node -r ts-node/register node_modules/.bin/jasmine ./schematics/**/*.spec.ts"
},
"private": true,
"dependencies": {
Expand All @@ -29,41 +32,44 @@
"@angular/router": "~9.1.9",
"@ngneat/lib": "^1.0.1",
"rxjs": "~6.5.4",
"schematics-utilities": "1.1.1",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.7",
"@angular-devkit/build-ng-packagr": "~0.901.7",
"@angular-devkit/schematics": "^9.1.1",
"@angular-devkit/schematics-cli": "^0.901.7",
"@angular/cli": "~9.1.7",
"@angular/compiler-cli": "~9.1.9",
"@types/node": "^12.11.1",
"@commitlint/cli": "8.1.0",
"@commitlint/config-angular": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"@ngneat/spectator": "^4.11.1",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"all-contributors-cli": "^6.8.1",
"codelyzer": "^5.1.2",
"cross-env": "^5.2.0",
"git-cz": "^3.2.1",
"husky": "^3.0.1",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~3.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"lint-staged": "^9.2.0",
"ng-packagr": "^9.0.0",
"prettier": "^1.18.2",
"protractor": "~7.0.0",
"standard-version": "^6.0.1",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~3.8.3",
"@commitlint/cli": "8.1.0",
"@commitlint/config-angular": "^8.1.0",
"@commitlint/config-conventional": "^8.1.0",
"git-cz": "^3.2.1",
"all-contributors-cli": "^6.8.1",
"lint-staged": "^9.2.0",
"prettier": "^1.18.2",
"standard-version": "^6.0.1",
"husky": "^3.0.1",
"cross-env": "^5.2.0",
"@ngneat/spectator": "^4.11.1"
"typescript": "~3.8.3"
},
"config": {
"commitizen": {
Expand Down
1 change: 1 addition & 0 deletions projects/ngneat/error-tailor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "@ngneat/error-tailor",
"version": "1.0.0",
"schematics": "./schematics/src/collection.json",
"peerDependencies": {
"@angular/common": "^9.1.9",
"@angular/core": "^9.1.9"
Expand Down
10 changes: 10 additions & 0 deletions schematics/src/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 error-tailor configuration for root module."
}
}
}
71 changes: 71 additions & 0 deletions schematics/src/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';

import { join } from 'path';

const angularJSON = {
projects: {
test: {
sourceRoot: 'src',
architect: {
build: {
options: {
main: 'src/main.ts'
}
}
}
}
},
defaultProject: 'test'
};
const appModule = `
import { NgModule } from '@angular/core';
@NgModule({declarations: [], imports: [], bootstrap: [], })
export class AppModule { }
`;
const main = `
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
`;

const collectionPath = join(__dirname, '../collection.json');

describe('Schematics', () => {
let testTree: Tree;
let runner: SchematicTestRunner;

beforeEach(() => {
testTree = Tree.empty();
testTree.create('./angular.json', JSON.stringify(angularJSON));
testTree.create('./package.json', JSON.stringify({ dependencies: {} }));
testTree.create('./src/main.ts', main);
testTree.create('./src/app/app.module.ts', appModule);

runner = new SchematicTestRunner('schematics', collectionPath);
});

describe('ng-add', () => {
it('should add package to package.json and import it in app.module', async () => {
const tree = await runner.runSchematicAsync('ng-add', {}, testTree).toPromise();

const appModuleModified = tree.readContent('./src/app/app.module.ts');

console.log(appModuleModified);

expect(appModuleModified).toContain(`import { ErrorTailorModule, FORM_ERRORS } from '@ngneat/error-tailor';`);
expect(appModuleModified).toContain(`imports: [ErrorTailorModule]`);
expect(appModuleModified).toContain(`{ provide: FORM_ERRORS, useValue: { } }`);

const packageJSONModified = JSON.parse(tree.readContent('./package.json'));

expect(packageJSONModified).toEqual({
dependencies: {
'@ngneat/error-tailor': '1.0.0'
}
});
});
});
});
113 changes: 113 additions & 0 deletions schematics/src/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { chain, Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import {
addPackageJsonDependency,
getAppModulePath,
getProjectFromWorkspace,
getWorkspace,
InsertChange,
NodeDependency,
NodeDependencyType,
addModuleImportToRootModule
} from 'schematics-utilities';
import * as ts from 'typescript';

import { Schema } from './schema';
import { addProviderToModule, applyChanges, getModuleFile, insertImport } from './utils';

function addPackageJsonDependencies(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const dependencies: NodeDependency[] = [
{
type: NodeDependencyType.Default,
name: '@ngneat/error-tailor',
version: '1.0.0'
}
];

dependencies.forEach(dependency => {
addPackageJsonDependency(host, dependency);
context.logger.log('info', `✅️ Added "${dependency.name}" into ${dependency.type}`);
});

return host;
};
}

function installPackageJsonDependencies(): Rule {
return (host: Tree, context: SchematicContext) => {
context.addTask(new NodePackageInstallTask());
context.logger.log('info', `🔍 Installing package...`);

return host;
};
}

function getTsSourceFile(host: Tree, path: string): ts.SourceFile {
const buffer = host.read(path);
if (!buffer) {
throw new SchematicsException(`Could not read file (${path}).`);
}
const content = buffer.toString();
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);

return source;
}

function injectImports(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, Object.keys(workspace.projects)[0]);
const modulePath = getAppModulePath(host, (project as any).architect.build.options.main);

const moduleSource = getTsSourceFile(host, modulePath);

const change = insertImport(moduleSource, modulePath, 'ErrorTailorModule, FORM_ERRORS', '@ngneat/error-tailor');

if (change) {
const recorder = host.beginUpdate(modulePath);
recorder.insertLeft((change as InsertChange).pos, (change as InsertChange).toAdd);
host.commitUpdate(recorder);
}

return host;
};
}

function addModuleToImports(options: Schema): Rule {
return (host: Tree, context: SchematicContext) => {
const workspace = getWorkspace(host);
const project = getProjectFromWorkspace(workspace, Object.keys(workspace.projects)[0]);

addModuleImportToRootModule(host, 'ErrorTailorModule', null as any, project);

const provider = `{ provide: FORM_ERRORS, useValue: { } }`;

const modulePath = getAppModulePath(host, project.architect.build.options.main);
const module = getModuleFile(host, modulePath);
const providerChanges = addProviderToModule(module, modulePath, provider, null);
applyChanges(host, modulePath, providerChanges as InsertChange[]);

context.logger.log('info', `🌈 @ngneat/error-tailor is imported`);

return host;
};
}

function log(): Rule {
return (host: Tree, context: SchematicContext) => {
context.logger.log('info', `👏 @ngneat/error-tailor ready to use`);

return host;
};
}

export default function errorTailorNgAdd(options: Schema): Rule {
return chain([
addPackageJsonDependencies(options),
installPackageJsonDependencies(),
addModuleToImports(options),
injectImports(options),
log()
]);
}
8 changes: 8 additions & 0 deletions schematics/src/ng-add/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "http:https://json-schema.org/schema",
"id": "SchematicsErrorTailorAdd",
"title": "Add error-tailor to Your Project",
"type": "object",
"properties": {},
"required": []
}
1 change: 1 addition & 0 deletions schematics/src/ng-add/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export interface Schema {}
Loading

0 comments on commit 2e442cb

Please sign in to comment.