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

Commit

Permalink
Merge pull request #73 from urish/angular
Browse files Browse the repository at this point in the history
Angular support
  • Loading branch information
urish authored Nov 25, 2018
2 parents 767b4f4 + 8512cf1 commit 90d2831
Show file tree
Hide file tree
Showing 12 changed files with 696 additions and 28 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"
}
}
56 changes: 56 additions & 0 deletions packages/typewiz-angular/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# typewiz-angular

An Angular Schematic that automatically adds types to TypeScript code using [TypeWiz](https://www.npmjs.com/package/typewiz-core)

[![Build Status](https://travis-ci.org/urish/typewiz.png?branch=master)](https://travis-ci.org/urish/typewiz)
[![Coverage Status](https://coveralls.io/repos/github/urish/typewiz/badge.svg?branch=master)](https://coveralls.io/github/urish/typewiz?branch=master)

## Installation

Run the following command in your project's folder:

```
ng add typewiz-angular
```

## Usage

Start your project normally, by running `ng serve`. You should see a new `collected-types.json` file, which will contain all the new types discovered by TypeWiz. To update your source code with these types, run the following command:

```
npm run typewiz:apply-types
```

For more information, check out [the blog post]().

## Example

Given the following input file:

```typescript
export class AppComponent {
title = this.greet('World');

greet(who) {
return `Hello, ${who}`;
}
}
```

After running the app with `ng serve`, opening it in the browser, and then applying the discovered types by running `npm run typewiz:apply-types`, your class will be updated as follows:

```typescript
export class AppComponent {
title = this.greet('World');

greet(who: string) {
return `Hello, ${who}`;
}
}
```

Note the addition of the `: string` type for the `who` of the `greet` method.

## License

Copyright (C) 2018, Uri Shaked and contributors. Distributed under the terms of the MIT license.
29 changes: 29 additions & 0 deletions packages/typewiz-angular/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "typewiz-angular",
"version": "0.1.1",
"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 && copyfiles -u 1 src/**/*.json dist",
"lint": "tslint -p .",
"prepare": "yarn build"
},
"files": [
"dist"
],
"engines": {
"node": ">= 6.4.0"
},
"dependencies": {
"@angular-devkit/core": "^0.6.8",
"@angular-devkit/schematics": "^0.6.8",
"@phenomnomnominal/tsquery": "^2.1.1",
"typescript": ">= 2.4.2 <4.0.0",
"typewiz-core": "^1.2.0"
},
"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');
9 changes: 9 additions & 0 deletions packages/typewiz-angular/src/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Adds TypeWiz to your Angular project",
"factory": "./install"
}
}
}
84 changes: 84 additions & 0 deletions packages/typewiz-angular/src/install/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
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: '1.2.0',
['typewiz-webpack']: '1.2.0',
},
scripts: {
prepare: 'node node_modules/typewiz-angular/dist/cli',
['typewiz:apply-types']: 'typewiz applyTypes collected-types.json',
},
});
});

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: '1.2.0',
['typewiz-webpack']: '1.2.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).toMatchObject({
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).toMatchObject({
prepare: 'npm run build && node node_modules/typewiz-angular/dist/cli',
});
});
});
43 changes: 43 additions & 0 deletions packages/typewiz-angular/src/install/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { ISchema } from './schema';

const TYPEWIZ_VERSION = '1.2.0';

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 = TYPEWIZ_VERSION;
json[type]['typewiz-webpack'] = TYPEWIZ_VERSION;

// Add prepare 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;
}

// Add typewiz:apply-types script
if (!json.scripts['typewiz:apply-types']) {
json.scripts['typewiz:apply-types'] = 'typewiz applyTypes collected-types.json';
}

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;
}
150 changes: 150 additions & 0 deletions packages/typewiz-angular/src/patch-angular.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { patchCompiler, patchDevserver, patchTypescriptModel } from './patch-angular';

describe('patch-angular', () => {
describe('patchCompiler', () => {
it('should patch the compiler sources', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@angular-devkit/core");
class AngularCompilerPlugin {
_makeTransformers() {
// foo
}
}
`;

expect(patchCompiler(input)).toEqual(`
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const { typewizTransformer } = require('typewiz-core');
const core_1 = require("@angular-devkit/core");
class AngularCompilerPlugin {
_makeTransformers() {if (this._JitMode) { this._transformers.push(typewizTransformer({})); }
// foo
}
}
`);
});

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', () => {
it('should patch the typescript model file', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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)]
};
}
`;
// tslint:disable:max-line-length
expect(patchTypescriptModel(input)).toEqual(`
"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()]
};
}
`);
});

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', () => {
it('should patch the devserver configuration', () => {
const input = `
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
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': '*' },
}
}
}
`;

expect(patchDevserver(input)).toEqual(`
"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'); },}
}
}
`);
});

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);
});
});
});
Loading

0 comments on commit 90d2831

Please sign in to comment.