Skip to content
This repository has been archived by the owner on Oct 5, 2021. It is now read-only.

Commit

Permalink
feat(angular): add schematics
Browse files Browse the repository at this point in the history
so eventually users will be able to install typewiz by running:
ng add typewiz-angular
  • Loading branch information
urish committed Jun 29, 2018
1 parent 4eb23bc commit b5721cf
Show file tree
Hide file tree
Showing 11 changed files with 398 additions and 27 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,8 @@
"tslint --fix",
"git add"
]
},
"resolutions": {
"source-map": "0.6.1"
}
}
14 changes: 10 additions & 4 deletions packages/typewiz-angular/package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
{
"name": "typewiz-angular",
"version": "1.1.0-1",
"version": "1.1.0",
"main": "dist/patch-angular.js",
"typings": "dist/patch-angular.d.ts",
"repository": "https://github.com/urish/typewiz",
"author": "Uri Shaked <uri@urishaked.com>",
"license": "MIT",
"scripts": {
"build": "rimraf dist && tsc",
"build": "rimraf dist && tsc && copyfiles -u 1 src/**/*.json dist",
"lint": "tslint -p .",
"prepublish": "yarn build"
"prepare": "yarn build"
},
"files": ["dist"],
"files": [
"dist"
],
"engines": {
"node": ">= 6.4.0"
},
"dependencies": {
"@angular-devkit/core": "^0.6.8",
"@angular-devkit/schematics": "^0.6.8",
"@phenomnomnominal/tsquery": "^2.0.0-beta.4",
"typescript": "^2.4.2",
"typewiz-core": "^1.0.2"
},
"schematics": "./dist/collection.json",
"devDependencies": {}
}
3 changes: 3 additions & 0 deletions packages/typewiz-angular/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { patchAngular } from './patch-angular';

patchAngular('node_modules');
11 changes: 11 additions & 0 deletions packages/typewiz-angular/src/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// This is the root config file where the schematics are defined.
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
// Adds Angular Material to an application without changing any templates
"ng-add": {
"description": "Adds TypeWiz to your Angular project",
"factory": "./install"
}
}
}
81 changes: 81 additions & 0 deletions packages/typewiz-angular/src/install/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import * as path from 'path';

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

describe('my-full-schematic', () => {
it('should install typewiz-webpack and add `prepare` script to package.json', () => {
const runner = new SchematicTestRunner('schematics', collectionPath);
const inputTree = Tree.empty();
inputTree.create('package.json', '{}');
const tree = runner.runSchematic('ng-add', {}, inputTree);

const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson).toEqual({
devDependencies: {
'typewiz-webpack': '1.1.0',
},
scripts: {
prepare: 'node node_modules/typewiz-angular/dist/cli',
},
});
});

it('should upgrade typewiz-webpack to latest if it already exists', () => {
const runner = new SchematicTestRunner('schematics', collectionPath);
const inputTree = Tree.empty();
inputTree.create(
'package.json',
JSON.stringify({
devDependencies: {
'typewiz-webpack': '1.0.0',
},
}),
);
const tree = runner.runSchematic('ng-add', {}, inputTree);

const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.devDependencies).toEqual({
'typewiz-webpack': '1.1.0',
});
});

it('should update the `prepare` script in package.json if it already exists', () => {
const runner = new SchematicTestRunner('schematics', collectionPath);
const inputTree = Tree.empty();
inputTree.create(
'package.json',
JSON.stringify({
scripts: {
prepare: 'npm run build',
},
}),
);
const tree = runner.runSchematic('ng-add', {}, inputTree);

const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.scripts).toEqual({
prepare: 'npm run build && node node_modules/typewiz-angular/dist/cli',
});
});

it('should not update the `prepare` script if it already contains typewiz', () => {
const runner = new SchematicTestRunner('schematics', collectionPath);
const inputTree = Tree.empty();
inputTree.create(
'package.json',
JSON.stringify({
scripts: {
prepare: 'npm run build && node node_modules/typewiz-angular/dist/cli',
},
}),
);
const tree = runner.runSchematic('ng-add', {}, inputTree);

const packageJson = JSON.parse(tree.readContent('package.json'));
expect(packageJson.scripts).toEqual({
prepare: 'npm run build && node node_modules/typewiz-angular/dist/cli',
});
});
});
35 changes: 35 additions & 0 deletions packages/typewiz-angular/src/install/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { ISchema } from './schema';

export default function(options: ISchema): Rule {
return chain([addTypewizToPackageJson]);
}

export function addTypewizToPackageJson(host: Tree, context: SchematicContext): Tree {
if (host.exists('package.json')) {
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);

// add dependency
const type = 'devDependencies';
json[type] = json[type] || {};
json[type]['typewiz-webpack'] = '1.1.0';

// add script
const typewizAngularCommand = 'node node_modules/typewiz-angular/dist/cli';
json.scripts = json.scripts || {};
if (json.scripts.prepare) {
if (json.scripts.prepare.indexOf('typewiz-angular') === -1) {
json.scripts.prepare += ' && ' + typewizAngularCommand;
}
} else {
json.scripts.prepare = typewizAngularCommand;
}

host.overwrite('package.json', JSON.stringify(json, null, 2));
context.addTask(new NodePackageInstallTask());
}

return host;
}
4 changes: 4 additions & 0 deletions packages/typewiz-angular/src/install/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ISchema {
/** Name of the project to target. */
project?: string;
}
55 changes: 55 additions & 0 deletions packages/typewiz-angular/src/patch-angular.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('patch-angular', () => {
}
}
`;

expect(patchCompiler(input)).toEqual(`
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
Expand All @@ -25,6 +26,22 @@ const core_1 = require("@angular-devkit/core");
}
`);
});

it('should not modify the source if it has already been patched', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { typewizTransformer } = require('typewiz-core');
const core_1 = require("@angular-devkit/core");
class AngularCompilerPlugin {
_makeTransformers() {this._transformers.push(typewizTransformer({}));
// foo
}
}
`;

expect(patchCompiler(input)).toEqual(input);
});
});

describe('patchTypescriptModel', () => {
Expand Down Expand Up @@ -56,6 +73,24 @@ const path = require("path");
}
`);
});

it('should not modify the source if it has already been patched', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { TypewizPlugin } = require('typewiz-webpack');
const path = require("path");
function getNonAotConfig(wco, host) {
const { tsConfigPath } = wco;
return {
module: { rules: [{ test: /\.ts$/, loader: webpackLoader }] },
plugins: [_createAotPlugin(wco, { tsConfigPath, skipCodeGeneration: true }, host), new TypewizPlugin()]
};
}
`;

expect(patchTypescriptModel(input)).toEqual(input);
});
});

describe('patchDevServer', () => {
Expand Down Expand Up @@ -91,5 +126,25 @@ const core_1 = require("@angular-devkit/core");
}
`);
});

it('should not modify the source if it has already been patched', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { typewizCollectorMiddleware } = require('typewiz-webpack');
const core_1 = require("@angular-devkit/core");
class DevServerBuilder {
_buildServerConfig(root, projectRoot, options, browserOptions) {
const systemRoot = core_1.getSystemPath(root);
const servePath = this._buildServePath(options, browserOptions);
const config = {
headers: { 'Access-Control-Allow-Origin': '*' },
before(app) { typewizCollectorMiddleware(app, 'collected-types.json'); },}
}
}
`;

expect(patchDevserver(input)).toEqual(input);
});
});
});
18 changes: 18 additions & 0 deletions packages/typewiz-angular/src/patch-angular.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { tsquery } from '@phenomnomnominal/tsquery';
import * as fs from 'fs';
import * as path from 'path';
import { SourceFile } from 'typescript';
import { applyReplacements, Replacement } from 'typewiz-core/dist/replacement';

function hasRequireStatement(ast: SourceFile, packageName: string) {
const matches = tsquery.query(ast, `CallExpression[expression.name=require][arguments.0.text="${packageName}"]`);
return matches.length > 0;
}

export function patchCompiler(compilerSource: string) {
const compilerAst = tsquery.ast(compilerSource);
if (hasRequireStatement(compilerAst, 'typewiz-core')) {
return compilerSource;
}

const [firstConst] = tsquery.query(compilerAst, 'VariableDeclarationList');
const [makeTransformersMethod] = tsquery.query(compilerAst, 'MethodDeclaration[name.name=_makeTransformers]>Block');

Expand All @@ -17,6 +27,10 @@ export function patchCompiler(compilerSource: string) {

export function patchTypescriptModel(source: string) {
const ast = tsquery.ast(source);
if (hasRequireStatement(ast, 'typewiz-webpack')) {
return source;
}

const [firstConst] = tsquery.query(ast, 'VariableDeclarationList');
const [pluginListEnd] = tsquery.query(
ast,
Expand All @@ -33,6 +47,10 @@ export function patchTypescriptModel(source: string) {

export function patchDevserver(source: string) {
const ast = tsquery.ast(source);
if (hasRequireStatement(ast, 'typewiz-webpack')) {
return source;
}

const [firstConst] = tsquery.query(ast, 'VariableDeclarationList');
const [webpackConfigObject] = tsquery.query(
ast,
Expand Down
2 changes: 2 additions & 0 deletions packages/typewiz-angular/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"lib": ["es2017"],
"rootDir": "src",
"target": "es6",
"outDir": "dist"
},
"include": ["src/**/*.ts"]
Expand Down
Loading

0 comments on commit b5721cf

Please sign in to comment.