Skip to content

Commit 77184e2

Browse files
committed
refactor: add a symbol to distinguish Tree instances
instanceof is fickle and does not work very well here.
1 parent ac6d0e8 commit 77184e2

File tree

4 files changed

+70
-27
lines changed

4 files changed

+70
-27
lines changed

packages/angular_devkit/schematics/src/rules/call.ts

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,63 @@
88
import { BaseException } from '@angular-devkit/core';
99
import { Observable } from 'rxjs/Observable';
1010
import { Rule, SchematicContext, Source } from '../engine/interface';
11-
import { Tree } from '../tree/interface';
12-
import { VirtualTree } from '../tree/virtual';
11+
import { Tree, TreeSymbol } from '../tree/interface';
1312

1413

1514
declare const Symbol: Symbol & {
1615
readonly observable: symbol;
1716
};
1817

1918

19+
function _getTypeOfResult(value?: {}): string {
20+
if (value === undefined) {
21+
return 'undefined';
22+
} else if (value === null) {
23+
return 'null';
24+
} else if (typeof value == 'function') {
25+
return `Function()`;
26+
} else if (typeof value != 'object') {
27+
return `${typeof value}(${JSON.stringify(value)})`;
28+
} else {
29+
if (Object.getPrototypeOf(value) == Object) {
30+
return `Object(${JSON.stringify(value)})`;
31+
} else if (value.constructor) {
32+
return `Instance of class ${value.constructor.name}`;
33+
} else {
34+
return 'Unknown Object';
35+
}
36+
}
37+
}
38+
39+
2040
/**
2141
* When a rule or source returns an invalid value.
2242
*/
2343
export class InvalidRuleResultException extends BaseException {
2444
constructor(value?: {}) {
25-
let v = 'Unknown Type';
26-
if (value === undefined) {
27-
v = 'undefined';
28-
} else if (value === null) {
29-
v = 'null';
30-
} else if (typeof value == 'function') {
31-
v = `Function()`;
32-
} else if (typeof value != 'object') {
33-
v = `${typeof value}(${JSON.stringify(value)})`;
34-
} else {
35-
if (Object.getPrototypeOf(value) == Object) {
36-
v = `Object(${JSON.stringify(value)})`;
37-
} else if (value.constructor) {
38-
v = `Instance of class ${value.constructor.name}`;
39-
} else {
40-
v = 'Unknown Object';
41-
}
42-
}
43-
super(`Invalid rule or source result: ${v}.`);
45+
super(`Invalid rule result: ${_getTypeOfResult(value)}.`);
46+
}
47+
}
48+
49+
50+
export class InvalidSourceResultException extends BaseException {
51+
constructor(value?: {}) {
52+
super(`Invalid source result: ${_getTypeOfResult(value)}.`);
4453
}
4554
}
4655

4756

4857
export function callSource(source: Source, context: SchematicContext): Observable<Tree> {
49-
const result = source(context);
58+
const result = source(context) as object;
5059

51-
if (result instanceof VirtualTree) {
52-
return Observable.of(result);
60+
if (result === undefined) {
61+
throw new InvalidSourceResultException(result);
62+
} else if (TreeSymbol in result) {
63+
return Observable.of(result as Tree);
5364
} else if (Symbol.observable in result) {
5465
return result as Observable<Tree>;
5566
} else {
56-
throw new InvalidRuleResultException(result);
67+
throw new InvalidSourceResultException(result);
5768
}
5869
}
5970

@@ -64,7 +75,9 @@ export function callRule(rule: Rule,
6475
return input.mergeMap(inputTree => {
6576
const result = rule(inputTree, context) as object;
6677

67-
if (result instanceof VirtualTree) {
78+
if (result === undefined) {
79+
return Observable.of(inputTree);
80+
} else if (TreeSymbol in result) {
6881
return Observable.of(result as Tree);
6982
} else if (Symbol.observable in result) {
7083
return result as Observable<Tree>;

packages/angular_devkit/schematics/src/tree/interface.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,27 @@ export const FileVisitorCancelToken = Symbol();
5555
export type FileVisitor = FilePredicate<void>;
5656

5757

58+
declare const window: { Symbol: { schematicTree: symbol }, window: {} };
59+
declare const self: { Symbol: { schematicTree: symbol }, self: {} };
60+
declare const global: { Symbol: { schematicTree: symbol }, global: {} };
61+
62+
export const TreeSymbol: symbol = (function() {
63+
const globalSymbol = (typeof window == 'object' && window.window === window && window.Symbol)
64+
|| (typeof self == 'object' && self.self === self && self.Symbol)
65+
|| (typeof global == 'object' && global.global === global && global.Symbol);
66+
67+
if (!globalSymbol) {
68+
return Symbol('schematic-tree');
69+
}
70+
71+
if (!globalSymbol.schematicTree) {
72+
globalSymbol.schematicTree = Symbol('schematic-tree');
73+
}
74+
75+
return globalSymbol.schematicTree;
76+
})();
77+
78+
5879
export interface Tree {
5980
branch(): Tree;
6081
merge(other: Tree, strategy?: MergeStrategy): void;

packages/angular_devkit/schematics/src/tree/null.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from '@angular-devkit/core';
1616
import { FileDoesNotExistException } from '../exception/exception';
1717
import { Action } from './action';
18-
import { DirEntry, MergeStrategy, Tree, UpdateRecorder } from './interface';
18+
import { DirEntry, MergeStrategy, Tree, TreeSymbol, UpdateRecorder } from './interface';
1919
import { UpdateRecorderBase } from './recorder';
2020

2121

@@ -42,6 +42,10 @@ export class NullTreeDirEntry implements DirEntry {
4242

4343

4444
export class NullTree implements Tree {
45+
[TreeSymbol]() {
46+
return this;
47+
}
48+
4549
branch(): Tree {
4650
return new NullTree();
4751
}

packages/angular_devkit/schematics/src/tree/virtual.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
FileVisitorCancelToken,
3131
MergeStrategy,
3232
Tree,
33+
TreeSymbol,
3334
UpdateRecorder,
3435
} from './interface';
3536
import { UpdateRecorderBase } from './recorder';
@@ -93,6 +94,10 @@ export class VirtualTree implements Tree {
9394
protected get tree(): ReadonlyMap<Path, FileEntry> { return this._tree; }
9495
get staging(): ReadonlyMap<Path, FileEntry> { return this._cacheMap; }
9596

97+
[TreeSymbol]() {
98+
return this;
99+
}
100+
96101
/**
97102
* A list of file names contained by this Tree.
98103
* @returns {[string]} File paths.

0 commit comments

Comments
 (0)