Skip to content

Commit

Permalink
feat: added cdk schematic
Browse files Browse the repository at this point in the history
  • Loading branch information
pimenovoleg committed Oct 6, 2018
1 parent 6bb0a40 commit 3bca363
Show file tree
Hide file tree
Showing 34 changed files with 1,407 additions and 126 deletions.
20 changes: 20 additions & 0 deletions angular.tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// WORKAROUND https://github.com/angular/angular/issues/18810
// This file is required to run ngc on angular libraries, to write files like
// node_modules/@angular/core/core.ngsummary.json
{
"compilerOptions": {
"lib": [
"dom",
"es2015"
],
"experimentalDecorators": true,
"types": []
},
"include": [
"node_modules/@angular/**/*"
],
"exclude": [
"node_modules/@angular/compiler-cli/**",
"node_modules/@angular/**/testing/**"
]
}
577 changes: 461 additions & 116 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,29 @@
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular-devkit/core": "7.0.0-rc.2",
"@angular-devkit/schematics": "7.0.0-rc.2",
"@angular/compiler-cli": "^7.0.0-rc.0",
"@angular/http": "^7.0.0-rc.0",
"@angular/platform-browser-dynamic": "^7.0.0-rc.0",
"@angular/platform-server": "^7.0.0-rc.0",
"@angular/router": "^7.0.0-rc.0",
"@angular/upgrade": "^7.0.0-rc.0",
"@commitlint/cli": "^7.1.2",
"@commitlint/config-conventional": "^7.1.2",
"@ptsecurity/commitlint-config": "^0.1.3",
"@ptsecurity/prettier-config": "0.1.0",
"@ptsecurity/tslint-config": "^0.8.0",
"@schematics/angular": "7.0.0-rc.2",
"@types/chalk": "^2.2.0",
"@types/fs-extra": "^5.0.4",
"@types/glob": "^5.0.36",
"@types/gulp": "3.8.32",
"@types/jasmine": "^2.8.9",
"@types/node": "~8.9.4",
"@types/rx": "4.1.1",
"@types/source-map": "^0.5.7",
"@types/uglify-js": "^3.0.3",
"@types/run-sequence": "^0.0.29",
"@types/webpack": "^4.4.14",
"angular2-template-loader": "^0.6.2",
"autoprefixer": "^9.1.5",
Expand Down
4 changes: 4 additions & 0 deletions src/cdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,9 @@
"dependencies": {
"tslib": "^1.7.1"
},
"schematics": "./schematics/collection.json",
"ng-update": {
"migrations": "./schematics/migration.json"
},
"sideEffects": false
}
13 changes: 13 additions & 0 deletions src/cdk/schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
"ng-add": {
"description": "Installs the Mosaic CDK",
"factory": "./ng-add/index",
"schema": "./ng-add/schema.json",
"aliases": [
"install"
]
}
}
}
2 changes: 2 additions & 0 deletions src/cdk/schematics/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './utils';
export * from './testing';
5 changes: 5 additions & 0 deletions src/cdk/schematics/migration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",
"schematics": {
}
}
26 changes: 26 additions & 0 deletions src/cdk/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Tree } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { getFileContent } from '@schematics/angular/utility/test';

import { createTestApp } from '../testing';


describe('CDK ng-add', () => {
let runner: SchematicTestRunner;
let appTree: Tree;

beforeEach(() => {
runner = new SchematicTestRunner('schematics', require.resolve('../collection.json'));
appTree = createTestApp(runner);
});

it('should update the package.json', () => {
const tree = runner.runSchematic('ng-add', {}, appTree);
const packageJson = JSON.parse(getFileContent(tree, '/package.json'));
const dependencies = packageJson.dependencies;

expect(dependencies['@ptsecurity/cdk']).toBeDefined();
expect(Object.keys(dependencies)).toEqual(Object.keys(dependencies).sort(),
'Expected the modified "dependencies" to be sorted alphabetically.');
});
});
28 changes: 28 additions & 0 deletions src/cdk/schematics/ng-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Rule, Tree } from '@angular-devkit/schematics';

import { addPackageToPackageJson } from './package-config';


/** Name of the CDK version that is shipped together with the schematics. */
export const cdkVersion = loadPackageVersionGracefully('@ptsecurity/cdk');

/**
* Schematic factory entry-point for the `ng-add` schematic. The ng-add schematic will be
* automatically executed if developers run `ng add @angular/cdk`.
*/
export default function(): Rule {
return (host: Tree) => {
// By default, the CLI already installs the package that has been installed through `ng add`.
// We just store the version in the `package.json` in case the package manager didn't.
addPackageToPackageJson(host, '@ptsecurity/cdk', `^${cdkVersion}`);
};
}

/** Loads the full version from the given Angular package gracefully. */
function loadPackageVersionGracefully(packageName: string): string | null {
try {
return require(`${packageName}/package.json`).version;
} catch {
return null;
}
}
32 changes: 32 additions & 0 deletions src/cdk/schematics/ng-add/package-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Tree } from '@angular-devkit/schematics';


/**
* Sorts the keys of the given object.
* @returns A new object instance with sorted keys
*/
function sortObjectByKeys(obj: object) {
return Object.keys(obj).sort().reduce((result, key) => (result[key] = obj[key]) && result, {});
}

/** Adds a package to the package.json in the given host tree. */
export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree {

if (host.exists('package.json')) {
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);

if (!json.dependencies) {
json.dependencies = {};
}

if (!json.dependencies[pkg]) {
json.dependencies[pkg] = version;
json.dependencies = sortObjectByKeys(json.dependencies);
}

host.overwrite('package.json', JSON.stringify(json, null, 2));
}

return host;
}
16 changes: 16 additions & 0 deletions src/cdk/schematics/ng-add/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/schema",
"id": "angular-cdk-ng-add",
"title": "Angular CDK ng-add",
"type": "object",
"properties": {
"project": {
"type": "string",
"description": "The name of the project.",
"$default": {
"$source": "projectName"
}
}
},
"required": []
}
5 changes: 5 additions & 0 deletions src/cdk/schematics/ng-add/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Schema {

/** Name of the project to target. */
project: string;
}
2 changes: 2 additions & 0 deletions src/cdk/schematics/testing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './post-scheduled-tasks';
export * from './test-app';
36 changes: 36 additions & 0 deletions src/cdk/schematics/testing/post-scheduled-tasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { EngineHost, TaskExecutor, TaskScheduler } from '@angular-devkit/schematics';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { from as observableFrom, Observable } from 'rxjs';
import { concatMap, filter, last } from 'rxjs/operators';


/**
* Due to the fact that the Angular devkit does not support running scheduled tasks from a
* schematic that has been launched through the TestRunner, we need to manually find the task
* executor for the given task name and run all scheduled instances.
*
* Note that this means that there can be multiple tasks with the same name. The observable emits
* only when all tasks finished executing.
*/
export function runPostScheduledTasks(runner: SchematicTestRunner, taskName: string)
: Observable<any> {

// Workaround until there is a public API to run scheduled tasks in the @angular-devkit.
// See: https://github.com/angular/angular-cli/issues/11739
const host = runner.engine['_host'] as EngineHost<{}, {}>;
const tasks = runner.engine['_taskSchedulers'] as TaskScheduler[];
const createTaskExecutor = (name: string) =>
(host.createTaskExecutor(name) as any) as Observable<TaskExecutor<any>>;

return observableFrom(tasks).pipe(
concatMap((scheduler) => scheduler.finalize()),
filter((task) => task.configuration.name === taskName),
concatMap((task) => {
return createTaskExecutor(task.configuration.name)
.pipe(concatMap((executor) => executor(task.configuration.options, task.context)));
}),
// Only emit the last emitted value because there can be multiple tasks with the same name.
// The observable should only emit a value if all tasks completed.
last()
);
}
14 changes: 14 additions & 0 deletions src/cdk/schematics/testing/test-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';


/** Create a base app used for testing. */
export function createTestApp(runner: SchematicTestRunner, appOptions = {}): UnitTestTree {
const workspaceTree = runner.runExternalSchematic('@schematics/angular', 'workspace', {
name: 'workspace',
version: '7.0.0',
newProjectRoot: 'projects'
});

return runner.runExternalSchematic('@schematics/angular', 'application',
{...appOptions, name: 'material'}, workspaceTree);
}
26 changes: 26 additions & 0 deletions src/cdk/schematics/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"composite": true,
"declaration": true,
"lib": [
"es2017"
],
"module": "commonjs",
"moduleResolution": "node",
"outDir": "../../../dist/packages/cdk/schematics",
"noEmitOnError": false,
"strictNullChecks": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"target": "es2015",
"types": [
"jasmine",
"node"
]
},
"exclude": [
"**/files/**/*",
"**/*.spec.ts"
]
}
81 changes: 81 additions & 0 deletions src/cdk/schematics/utils/ast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {WorkspaceProject} from '@angular-devkit/core/src/workspace';
import {SchematicsException, Tree} from '@angular-devkit/schematics';
import {Schema as ComponentOptions} from '@schematics/angular/component/schema';
import {addImportToModule} from '@schematics/angular/utility/ast-utils';
import {InsertChange} from '@schematics/angular/utility/change';
import {getWorkspace} from '@schematics/angular/utility/config';
import {findModuleFromOptions as internalFindModule} from '@schematics/angular/utility/find-module';
import {getAppModulePath} from '@schematics/angular/utility/ng-ast-utils';
import {getProjectMainFile} from './project-main-file';
import {ts} from './version-agnostic-typescript';


/** Reads file given path and returns TypeScript source file. */
export function getSourceFile(host: Tree, path: string) {
const buffer = host.read(path);
if (!buffer) {
throw new SchematicsException(`Could not find file for path: ${path}`);
}
const content = buffer.toString();
return ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
}

/** Import and add module to root app module. */
export function addModuleImportToRootModule(host: Tree, moduleName: string, src: string,
project: WorkspaceProject) {
const modulePath = getAppModulePath(host, getProjectMainFile(project));
addModuleImportToModule(host, modulePath, moduleName, src);
}

/**
* Import and add module to specific module path.
* @param host the tree we are updating
* @param modulePath src location of the module to import
* @param moduleName name of module to import
* @param src src location to import
*/
export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string,
src: string) {

const moduleSource = getSourceFile(host, modulePath);

if (!moduleSource) {
throw new SchematicsException(`Module not found: ${modulePath}`);
}

const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
const recorder = host.beginUpdate(modulePath);

changes.forEach((change) => {
if (change instanceof InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}
});

host.commitUpdate(recorder);
}

/** Wraps the internal find module from options with undefined path handling */
export function findModuleFromOptions(host: Tree, options: ComponentOptions): string | undefined {
const workspace = getWorkspace(host);

if (!options.project) {
options.project = Object.keys(workspace.projects)[0];
}

const project = workspace.projects[options.project];

if (options.path === undefined) {
options.path = `/${project.root}/src/app`;
}

return internalFindModule(host, options);
}
Loading

0 comments on commit 3bca363

Please sign in to comment.