Skip to content

Commit 003a3d7

Browse files
committed
fix(@angular/schematics): use package.json for name and version of collection
Also clarify some documentation.
1 parent 93f7382 commit 003a3d7

File tree

11 files changed

+225
-121
lines changed

11 files changed

+225
-121
lines changed

packages/angular/schematics/README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ What distinguish Schematics from other generators, such as Yeoman or Yarn Create
2020
| **Sink** | The final destination of all `Action`s. |
2121

2222
# Tooling
23-
Schematics is a library, and does not work by itself. A reference CLI is available in [`@angular/schematics-cli`](../schematics_cli/README.md). This document explain the library usage and the tooling API, but does not go into the tool implementation itself.
23+
Schematics is a library, and does not work by itself. A [reference CLI](https://github.com/angular/devkit/tree/master/packages/_schematics_cli) is available on this repository, but is not published on NPM. This document explain the library usage and the tooling API, but does not go into the tool implementation itself.
2424

2525
The tooling is responsible for the following tasks:
2626

@@ -34,7 +34,19 @@ The tooling is responsible for the following tasks:
3434
The tooling API is composed of the following pieces:
3535

3636
## Engine
37-
The `SchematicEngine` is responsible for loading and constructing `Collection`s and `Schematics`'. When creating an engine, the tooling provides an `EngineHost` interface that understands how to create a `CollectionDescription` from a name, and how to create a `Schematic
37+
The `SchematicEngine` is responsible for loading and constructing `Collection`s and `Schematics`'. When creating an engine, the tooling provides an `EngineHost` interface that understands how to create a `CollectionDescription` from a name, and how to create a `SchematicDescription`.
38+
39+
# Schematics (Generators)
40+
Schematics are generators and part of a `Collection`.
41+
42+
## Collection
43+
A Collection is defined by a `collection.json` file (in the reference CLI). This JSON defines the following properties:
44+
45+
| Prop Name | Type | Description |
46+
|---|---|---|
47+
| **name** | `string` | The name of the collection. |
48+
| **version** | `string` | Unused fi
49+
3850

3951
# Examples
4052

@@ -57,13 +69,10 @@ A few things from this example:
5769
1. The function receives the list of options from the tooling.
5870
1. It returns a [`Rule`](src/engine/interface.ts#L73), which is a transformation from a `Tree` to another `Tree`.
5971

60-
61-
6272
# Future Work
6373

6474
Schematics is not done yet. Here's a list of things we are considering:
6575

6676
* Smart defaults for Options. Having a JavaScript function for default values based on other default values.
6777
* Prompt for input options. This should only be prompted for the original schematics, dependencies to other schematics should not trigger another prompting.
68-
* Tasks for running tooling-specific jobs before and after a schematics has been scaffolded. Such tasks can involve initialize git, or npm install. A specific list of tasks should be provided by the tool, with unsupported tasks generating an error.
69-
78+
* Tasks for running tooling-specific jobs before and after a schematics has been scaffolded. Such tasks can involve initialize git, or npm install. A specific list of tasks should be provided by the tool, with unsupported tasks generating an error.

packages/angular/schematics/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ export * from './utility/path';
3434

3535

3636
export interface TreeConstructor {
37-
new (): TreeInterface;
38-
3937
empty(): TreeInterface;
4038
branch(tree: TreeInterface): TreeInterface;
4139
merge(tree: TreeInterface, other: TreeInterface, strategy?: MergeStrategy): TreeInterface;
@@ -56,4 +54,4 @@ export const Tree: TreeConstructor = {
5654
return partition(tree, predicate);
5755
},
5856
optimize(tree: TreeInterface) { return optimize(tree); }
59-
} as any;
57+
};

packages/angular/schematics/src/tree/static.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function merge(tree: Tree,
2222
return VirtualTree.merge(tree, other, strategy);
2323
}
2424

25-
export function partition(tree: Tree, predicate: FilePredicate<boolean>) {
25+
export function partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree, Tree] {
2626
return [
2727
new FilteredTree(tree, predicate),
2828
new FilteredTree(tree, (path, entry) => !predicate(path, entry))
Lines changed: 2 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,7 @@
1-
# Schematics
1+
# Schematics Tooling
22
> A scaffolding library for the modern web.
33
44
## Description
55
Schematics are generators that transform an existing filesystem. It can create files, refactor existing files, or move files around.
66

7-
What distinguish Schematics from other generators, such as Yeoman or Yarn Create, is that schematics are purely descriptive; no changes are applied to the actual filesystem until everything is ready to be committed. There is no side effect, by design, in Schematics.
8-
9-
# Glossary
10-
11-
| Term | Description |
12-
|------|-------------|
13-
| **Schematics** | A generator that execute descriptive code without side effects on an existing file system. |
14-
| **Collection** | A list of schematics metadata. Schematics can be referred by name inside a collection. |
15-
| **Tool** | The code using the Schematics library. |
16-
| **Tree** | A staging area for changes, containing the original file system, and a list of changes to apply to it. |
17-
| **Rule** | A function that applies actions to a `Tree`. It returns a new Tree that will contain all transformations to be applied. |
18-
| **Source** | A function that creates an entirely new `Tree` from an empty filesystem. For example, a file source could read files from disk and create a Create Action for each of those.
19-
| **Action** | A atomic operation to be validated and committed to a filesystem or a `Tree`. Actions are created by schematics. |
20-
| **Sink** | The final destination of all `Action`s. |
21-
22-
# Tooling
23-
Schematics is a library, and does not work by itself. A reference CLI is available in [`@angular/schematics-cli`](../schematics_cli/README.md). This document explain the library usage and the tooling API, but does not go into the tool implementation itself.
24-
25-
The tooling is responsible for the following tasks:
26-
27-
1. Create the Schematic Engine, and pass in a Collection and Schematic loader.
28-
1. Understand and respect the Schematics metadata and dependencies between collections. Schematics can refer to dependencies, and it's the responsibility of the tool to honor those dependencies. The reference CLI uses NPM packages for its collections.
29-
1. Create the Options object. Options can be anything, but the schematics can specify a JSON Schema that should be respected. The reference CLI, for example, parse the arguments as a JSON object and validate it with the Schema specified by the collection.
30-
1. Call the schematics with the original Tree. The tree should represent the initial state of the filesystem. The reference CLI uses the current directory for this.
31-
1. Create a Sink and commit the result of the schematics to the Sink. Many sinks are provided by the library; FileSystemSink and DryRunSink are examples.
32-
1. Output any logs propagated by the library, including debugging information.
33-
34-
The tooling API is composed of the following pieces:
35-
36-
## Engine
37-
The `SchematicEngine` is responsible for loading and constructing `Collection`s and `Schematics`'. When creating an engine, the tooling provides an `EngineHost` interface that understands how to create a `CollectionDescription` from a name, and how to create a `Schematic
38-
39-
# Examples
40-
41-
## Simple
42-
An example of a simple Schematics which creates a "hello world" file, using an option to determine its path:
43-
44-
```typescript
45-
import {Tree} from '@angular/schematics';
46-
47-
export default function MySchematic(options: any) {
48-
return (tree: Tree) => {
49-
tree.create(options.path + '/hi', 'Hello world!');
50-
return tree;
51-
};
52-
}
53-
```
54-
55-
A few things from this example:
56-
57-
1. The function receives the list of options from the tooling.
58-
1. It returns a [`Rule`](src/engine/interface.ts#L73), which is a transformation from a `Tree` to another `Tree`.
59-
60-
61-
62-
# Future Work
63-
64-
Schematics is not done yet. Here's a list of things we are considering:
65-
66-
* Smart defaults for Options. Having a JavaScript function for default values based on other default values.
67-
* Prompt for input options. This should only be prompted for the original schematics, dependencies to other schematics should not trigger another prompting.
68-
* Tasks for running tooling-specific jobs before and after a schematics has been scaffolded. Such tasks can involve initialize git, or npm install. A specific list of tasks should be provided by the tool, with unsupported tasks generating an error.
69-
7+
This library has helpers and conventions for making tools that uses the Schematics as backend to run generators.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {
9+
Collection,
10+
CollectionDescription,
11+
RuleFactory,
12+
SchematicDescription,
13+
TypedSchematicContext
14+
} from '@angular/schematics';
15+
16+
17+
export interface FileSystemCollectionDescription {
18+
readonly path: string;
19+
readonly version?: string;
20+
readonly schematics: { [name: string]: FileSystemSchematicJsonDescription };
21+
}
22+
23+
24+
export interface FileSystemSchematicJsonDescription {
25+
readonly factory: string;
26+
readonly description: string;
27+
readonly schema?: string;
28+
}
29+
30+
export interface FileSystemSchematicDescription extends FileSystemSchematicJsonDescription {
31+
// Processed by the EngineHost.
32+
readonly path: string;
33+
readonly schemaJson?: Object;
34+
// Using `any` here is okay because the type isn't resolved when we read this value,
35+
// but rather when the Engine asks for it.
36+
readonly factoryFn: RuleFactory<any>;
37+
}
38+
39+
40+
/**
41+
* Used to simplify typings.
42+
*/
43+
export declare type FileSystemCollection
44+
= Collection<FileSystemCollectionDescription, FileSystemSchematicDescription>;
45+
export declare type FileSystemCollectionDesc
46+
= CollectionDescription<FileSystemCollectionDescription>;
47+
export declare type FileSystemSchematicDesc
48+
= SchematicDescription<FileSystemCollectionDescription, FileSystemSchematicDescription>;
49+
export declare type FileSystemSchematicContext
50+
= TypedSchematicContext<FileSystemCollectionDescription, FileSystemSchematicDescription>;
51+
52+

packages/angular/schematics_tools/src/file-system-engine-host-base.ts

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8+
import {FileSystemCollectionDescription, FileSystemSchematicDescription} from './description';
89
import {FileSystemHost} from './file-system-host';
910
import {readJsonFile} from './file-system-utility';
1011

@@ -23,29 +24,6 @@ import {dirname, join, resolve} from 'path';
2324
import {Url} from 'url';
2425

2526

26-
export interface FileSystemCollectionDescription {
27-
readonly path: string;
28-
readonly version?: string;
29-
readonly schematics: { [name: string]: FileSystemSchematicJsonDescription };
30-
}
31-
32-
33-
export interface FileSystemSchematicJsonDescription {
34-
readonly factory: string;
35-
readonly description: string;
36-
readonly schema?: string;
37-
}
38-
39-
export interface FileSystemSchematicDescription extends FileSystemSchematicJsonDescription {
40-
// Processed by the EngineHost.
41-
readonly path: string;
42-
readonly schemaJson?: Object;
43-
// Using `any` here is okay because the type isn't resolved when we read this value,
44-
// but rather when the Engine asks for it.
45-
readonly factoryFn: RuleFactory<any>;
46-
}
47-
48-
4927
/**
5028
* Used to simplify typings.
5129
*/
@@ -65,9 +43,15 @@ export declare type FileSystemSchematicContext
6543
*/
6644
export abstract class FileSystemEngineHostBase implements
6745
EngineHost<FileSystemCollectionDescription, FileSystemSchematicDescription> {
68-
protected abstract _resolveCollectionPath(_name: string): string | null;
46+
protected abstract _resolveCollectionPath(name: string): string | null;
6947
protected abstract _resolveReferenceString(
70-
_name: string, _parentPath: string): { ref: RuleFactory<any>, path: string } | null;
48+
name: string, parentPath: string): { ref: RuleFactory<any>, path: string } | null;
49+
protected abstract _transformCollectionDescription(
50+
name: string, desc: Partial<FileSystemCollectionDesc>): FileSystemCollectionDesc | null;
51+
protected abstract _transformSchematicDescription(
52+
name: string,
53+
collection: FileSystemCollectionDesc,
54+
desc: Partial<FileSystemSchematicDesc>): FileSystemSchematicDesc | null;
7155

7256
listSchematics(collection: FileSystemCollection) {
7357
return Object.keys(collection.description.schematics);
@@ -85,15 +69,20 @@ export abstract class FileSystemEngineHostBase implements
8569
return null;
8670
}
8771

88-
const description: FileSystemCollectionDesc = readJsonFile(path);
89-
if (!description.name) {
72+
const partialDesc: Partial<FileSystemCollectionDesc> | null = readJsonFile(path);
73+
if (!partialDesc) {
9074
return null;
9175
}
9276

93-
return {
94-
...description,
77+
const description = this._transformCollectionDescription(name, {
78+
...partialDesc,
9579
path,
96-
};
80+
});
81+
if (!description || !description.name) {
82+
return null;
83+
}
84+
85+
return description;
9786
} catch (e) {
9887
return null;
9988
}
@@ -106,36 +95,44 @@ export abstract class FileSystemEngineHostBase implements
10695
}
10796

10897
const collectionPath = dirname(collection.path);
109-
const description = collection.schematics[name];
110-
111-
if (!description) {
98+
const partialDesc: Partial<FileSystemSchematicDesc> | null = collection.schematics[name];
99+
if (!partialDesc) {
112100
return null;
113101
}
114102

115103
// Use any on this ref as we don't have the OptionT here, but we don't need it (we only need
116104
// the path).
117-
const resolvedRef = this._resolveReferenceString(description.factory, collectionPath);
105+
if (!partialDesc.factory) {
106+
return null;
107+
}
108+
const resolvedRef = this._resolveReferenceString(partialDesc.factory, collectionPath);
118109
if (!resolvedRef) {
119110
return null;
120111
}
121112

122113
const { path } = resolvedRef;
123-
let schema = description.schema;
114+
let schema = partialDesc.schema;
124115
let schemaJson = undefined;
125116
if (schema) {
126117
schema = join(collectionPath, schema);
127118
schemaJson = readJsonFile(schema);
128119
}
129120

130-
return {
131-
...description,
121+
const description = this._transformSchematicDescription(name, collection, {
122+
...partialDesc,
132123
schema,
133124
schemaJson,
134125
name,
135126
path,
136127
factoryFn: resolvedRef.ref,
137128
collection
138-
};
129+
});
130+
131+
if (!description) {
132+
return null;
133+
}
134+
135+
return description;
139136
}
140137

141138
createSourceFromUrl(url: Url): Source | null {

packages/angular/schematics_tools/src/file-system-engine-host.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,28 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
import {ExportStringRef} from './export-ref';
9+
import {FileSystemCollectionDescription, FileSystemSchematicDescription} from './description';
910
import {FileSystemEngineHostBase} from './file-system-engine-host-base';
1011

11-
import {RuleFactory} from '@angular/schematics';
12+
import {
13+
CollectionDescription,
14+
RuleFactory,
15+
SchematicDescription,
16+
} from '@angular/schematics';
1217

1318
import {join} from 'path';
1419
import {existsSync} from 'fs';
1520

1621

22+
/**
23+
* Used to simplify typings.
24+
*/
25+
export declare type FileSystemCollectionDesc
26+
= CollectionDescription<FileSystemCollectionDescription>;
27+
export declare type FileSystemSchematicDesc
28+
= SchematicDescription<FileSystemCollectionDescription, FileSystemSchematicDescription>;
29+
30+
1731
/**
1832
* A simple EngineHost that uses a root with one directory per collection inside of it. The
1933
* collection declaration follows the same rules as the regular FileSystemEngineHostBase.
@@ -40,4 +54,24 @@ export class FileSystemEngineHost extends FileSystemEngineHostBase {
4054
const ref = new ExportStringRef<RuleFactory<any>>(refString, parentPath);
4155
return { ref: ref.ref, path: ref.module };
4256
}
57+
58+
protected _transformCollectionDescription(_name: string,
59+
desc: Partial<FileSystemCollectionDesc>) {
60+
if (!desc.name || !desc.path || !desc.schematics || !desc.version) {
61+
return null;
62+
}
63+
if (typeof desc.schematics != 'object') {
64+
return null;
65+
}
66+
return <FileSystemCollectionDesc>desc;
67+
}
68+
69+
protected _transformSchematicDescription(_name: string,
70+
_collection: FileSystemCollectionDesc,
71+
desc: Partial<FileSystemSchematicDesc>) {
72+
if (!desc.factoryFn || !desc.path || !desc.description) {
73+
return null;
74+
}
75+
return <FileSystemSchematicDesc>desc;
76+
}
4377
}

packages/angular/schematics_tools/src/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
export * from './file-system-host';
9-
export * from './file-system-engine-host';
10-
export * from './node-module-engine-host';
11-
export * from './registry-engine-host';
9+
export * from './file-system-engine-host-base';
10+
11+
export {FileSystemEngineHost} from './file-system-engine-host';
12+
export {NodeModulesEngineHost} from './node-module-engine-host';
13+
export {RegistryEngineHost} from './registry-engine-host';

0 commit comments

Comments
 (0)