Skip to content

Commit 199db79

Browse files
fix(RejectFactory): stringify rejections with circular dependency-aware stringify
refactor(common): Move string functions to common/string.ts closes #2538
1 parent 7a68ade commit 199db79

12 files changed

+108
-83
lines changed

src/common/common.ts

+3-46
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/** @module common */ /** for typedoc */
22

33
import {isFunction, isString, isArray, isRegExp, isDate} from "./predicates";
4-
import { all, pattern, any, not, prop, curry, val } from "./hof";
4+
import { all, any, not, prop, curry } from "./hof";
55

66
let angular = (<any> window).angular || {};
7-
export const fromJson = angular.fromJson || _fromJson;
8-
export const toJson = angular.toJson || _toJson;
7+
export const fromJson = angular.fromJson || JSON.parse.bind(JSON);
8+
export const toJson = angular.toJson || JSON.stringify.bind(JSON);
99
export const copy = angular.copy || _copy;
1010
export const forEach = angular.forEach || _forEach;
1111
export const extend = angular.extend || _extend;
@@ -467,54 +467,11 @@ export function applyPairs(memo: TypedMap<any>, keyValTuple: any[]) {
467467
return memo;
468468
}
469469

470-
export function fnToString(fn: IInjectable) {
471-
let _fn = isArray(fn) ? fn.slice(-1)[0] : fn;
472-
return _fn && _fn.toString() || "undefined";
473-
}
474-
475-
/**
476-
* Returns a string shortened to a maximum length
477-
*
478-
* If the string is already less than the `max` length, return the string.
479-
* Else return the string, shortened to `max - 3` and append three dots ("...").
480-
*
481-
* @param max the maximum length of the string to return
482-
* @param str the input string
483-
*/
484-
export function maxLength(max: number, str: string) {
485-
if (str.length <= max) return str;
486-
return str.substr(0, max - 3) + "...";
487-
}
488-
489-
/**
490-
* Returns a string, with spaces added to the end, up to a desired str length
491-
*
492-
* If the string is already longer than the desired length, return the string.
493-
* Else returns the string, with extra spaces on the end, such that it reaches `length` characters.
494-
*
495-
* @param length the desired length of the string to return
496-
* @param str the input string
497-
*/
498-
export function padString(length: number, str: string) {
499-
while (str.length < length) str += " ";
500-
return str;
501-
}
502-
503470
/** Get the last element of an array */
504471
export function tail<T>(arr: T[]): T {
505472
return arr.length && arr[arr.length - 1] || undefined;
506473
}
507474

508-
export const kebobString = (camelCase: string) => camelCase.replace(/([A-Z])/g, $1 => "-"+$1.toLowerCase());
509-
510-
function _toJson(obj) {
511-
return JSON.stringify(obj);
512-
}
513-
514-
function _fromJson(json) {
515-
return isString(json) ? JSON.parse(json) : json;
516-
}
517-
518475
/**
519476
* shallow copy from src to dest
520477
*

src/common/module.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export * from "./glob";
55
export * from "./hof";
66
export * from "./predicates";
77
export * from "./queue";
8+
export * from "./strings";
89
export * from "./trace";

src/common/strings.ts

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/** @module common */ /** */
2+
3+
import {isString, isArray, isDefined, isNull, isPromise, isInjectable, isObject} from "./predicates";
4+
import {TransitionRejection} from "../transition/rejectFactory";
5+
import {IInjectable, identity} from "./common";
6+
import {pattern, is, not, val, invoke} from "./hof";
7+
import {Transition} from "../transition/transition";
8+
import {Resolvable} from "../resolve/resolvable";
9+
10+
/**
11+
* Returns a string shortened to a maximum length
12+
*
13+
* If the string is already less than the `max` length, return the string.
14+
* Else return the string, shortened to `max - 3` and append three dots ("...").
15+
*
16+
* @param max the maximum length of the string to return
17+
* @param str the input string
18+
*/
19+
export function maxLength(max: number, str: string) {
20+
if (str.length <= max) return str;
21+
return str.substr(0, max - 3) + "...";
22+
}
23+
24+
/**
25+
* Returns a string, with spaces added to the end, up to a desired str length
26+
*
27+
* If the string is already longer than the desired length, return the string.
28+
* Else returns the string, with extra spaces on the end, such that it reaches `length` characters.
29+
*
30+
* @param length the desired length of the string to return
31+
* @param str the input string
32+
*/
33+
export function padString(length: number, str: string) {
34+
while (str.length < length) str += " ";
35+
return str;
36+
}
37+
38+
export const kebobString = (camelCase: string) => camelCase.replace(/([A-Z])/g, $1 => "-"+$1.toLowerCase());
39+
40+
function _toJson(obj) {
41+
return JSON.stringify(obj);
42+
}
43+
44+
function _fromJson(json) {
45+
return isString(json) ? JSON.parse(json) : json;
46+
}
47+
48+
49+
function promiseToString(p) {
50+
if (is(TransitionRejection)(p.reason)) return p.reason.toString();
51+
return `Promise(${JSON.stringify(p)})`;
52+
}
53+
54+
export function functionToString(fn) {
55+
let fnStr = fnToString(fn);
56+
let namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/);
57+
return namedFunctionMatch ? namedFunctionMatch[1] : fnStr;
58+
}
59+
60+
export function fnToString(fn: IInjectable) {
61+
let _fn = isArray(fn) ? fn.slice(-1)[0] : fn;
62+
return _fn && _fn.toString() || "undefined";
63+
}
64+
65+
66+
let stringifyPattern = pattern([
67+
[not(isDefined), val("undefined")],
68+
[isNull, val("null")],
69+
[isPromise, promiseToString],
70+
[is(Transition), invoke("toString")],
71+
[is(Resolvable), invoke("toString")],
72+
[isInjectable, functionToString],
73+
[val(true), identity]
74+
]);
75+
76+
export function stringify(o) {
77+
var seen = [];
78+
79+
function format(val) {
80+
if (isObject(val)) {
81+
if (seen.indexOf(val) !== -1) return '[circular ref]';
82+
seen.push(val);
83+
}
84+
return stringifyPattern(val);
85+
}
86+
87+
return JSON.stringify(o, (key, val) => format(val)).replace(/\\"/g, '"');
88+
}
89+

src/common/trace.ts

+3-29
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
/** @module common */ /** for typedoc */
2-
import {fnToString, maxLength, padString, identity} from "../common/common";
3-
import {is, invoke, not, val, pattern, parse} from "../common/hof";
4-
import {isNull, isPromise, isNumber, isInjectable, isDefined} from "../common/predicates";
5-
import {Resolvable} from "../resolve/resolvable";
2+
import {parse} from "../common/hof";
3+
import {isNumber} from "../common/predicates";
64
import {Transition} from "../transition/transition";
7-
import {TransitionRejection} from "../transition/rejectFactory";
85
import {ActiveUIView, ViewConfig} from "../view/interface";
9-
10-
function promiseToString(p) {
11-
if (is(TransitionRejection)(p.reason)) return p.reason.toString();
12-
return `Promise(${JSON.stringify(p)})`;
13-
}
14-
15-
function functionToString(fn) {
16-
let fnStr = fnToString(fn);
17-
let namedFunctionMatch = fnStr.match(/^(function [^ ]+\([^)]*\))/);
18-
return namedFunctionMatch ? namedFunctionMatch[1] : fnStr;
19-
}
6+
import {stringify, functionToString, maxLength, padString} from "./strings";
207

218
function uiViewString (viewData) {
229
if (!viewData) return 'ui-view (defunct)';
@@ -30,19 +17,6 @@ function normalizedCat(input: Category): string {
3017
return isNumber(input) ? Category[input] : Category[Category[input]];
3118
}
3219

33-
function stringify(o) {
34-
let format = pattern([
35-
[not(isDefined), val("undefined")],
36-
[isNull, val("null")],
37-
[isPromise, promiseToString],
38-
[is(Transition), invoke("toString")],
39-
[is(Resolvable), invoke("toString")],
40-
[isInjectable, functionToString],
41-
[val(true), identity]
42-
]);
43-
44-
return JSON.stringify(o, (key, val) => format(val)).replace(/\\"/g, '"');
45-
}
4620

4721
export enum Category {
4822
RESOLVE, TRANSITION, HOOK, INVOKE, UIVIEW, VIEWCONFIG

src/ng1/viewDirective.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @module view */ /** for typedoc */
22
"use strict";
3-
import {extend, map, unnestR, filter, kebobString} from "../common/common";
3+
import {extend, map, unnestR, filter} from "../common/common";
44
import {isDefined, isFunction} from "../common/predicates";
55
import {trace} from "../common/trace";
66
import {ActiveUIView} from "../view/interface";
@@ -12,6 +12,7 @@ import {ResolveContext} from "../resolve/resolveContext";
1212
import {Transition} from "../transition/transition";
1313
import {Node} from "../path/node";
1414
import {Param} from "../params/param";
15+
import {kebobString} from "../common/strings";
1516

1617
export type UIViewData = {
1718
$cfg: Ng1ViewConfig;

src/ng1/viewsBuilder.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @module ng1 */ /** */
22
import {State} from "../state/stateObject";
3-
import {pick, forEach, anyTrueR, unnestR, kebobString} from "../common/common";
3+
import {pick, forEach, anyTrueR, unnestR} from "../common/common";
4+
import {kebobString} from "../common/strings";
45
import {ViewConfig, ViewContext} from "../view/interface";
56
import {Ng1ViewDeclaration} from "./interface";
67
import {ViewService} from "../view/view";

src/resolve/module.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @module path */ /** for typedoc */
1+
/** @module resolve */ /** for typedoc */
22
export * from "./interface";
33
export * from "./resolvable";
44
export * from "./resolveContext";

src/resolve/resolvable.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @module path */ /** for typedoc */
1+
/** @module resolve */ /** for typedoc */
22
import {extend, pick, map, filter} from "../common/common";
33
import {not} from "../common/hof";
44
import {isInjectable} from "../common/predicates";

src/resolve/resolveContext.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @module path */ /** for typedoc */
1+
/** @module resolve */ /** for typedoc */
22
import {IInjectable, find, filter, map, tail, defaults, extend, pick, omit} from "../common/common";
33
import {prop, propEq} from "../common/hof";
44
import {isString, isObject} from "../common/predicates";

src/resolve/resolveInjector.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @module path */ /** for typedoc */
1+
/** @module resolve */ /** for typedoc */
22
import {map} from "../common/common";
33

44
import {Resolvable} from "./resolvable";

src/transition/rejectFactory.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"use strict";
33
import {extend} from "../common/common";
44
import {services} from "../common/coreservices";
5+
import {stringify} from "../common/strings";
56

67
export enum RejectType {
78
SUPERSEDED = 2, ABORTED = 3, INVALID = 4, IGNORED = 5
@@ -22,7 +23,7 @@ export class TransitionRejection {
2223
}
2324

2425
toString() {
25-
const detailString = d => d && d.toString !== Object.prototype.toString ? d.toString() : JSON.stringify(d);
26+
const detailString = d => d && d.toString !== Object.prototype.toString ? d.toString() : stringify(d);
2627
let type = this.type, message = this.message, detail = detailString(this.detail);
2728
return `TransitionRejection(type: ${type}, message: ${message}, detail: ${detail})`;
2829
}

src/transition/transitionHook.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/** @module transition */ /** for typedoc */
22
import {TransitionHookOptions} from "./interface";
3-
import {IInjectable, defaults, extend, noop, fnToString, maxLength, Predicate} from "../common/common";
3+
import {IInjectable, defaults, extend, noop, Predicate} from "../common/common";
4+
import {fnToString, maxLength} from "../common/strings";
45
import {isDefined, isPromise } from "../common/predicates";
56
import {not, pattern, val, eq, is, parse } from "../common/hof";
67
import {trace} from "../common/trace";

0 commit comments

Comments
 (0)