ui-router
Version:
State-based routing for Javascript
287 lines (258 loc) • 10.2 kB
text/typescript
/**
* UI-Router Transition Tracing
*
* Enable transition tracing to print transition information to the console, in order to help debug your application.
* Tracing logs detailed information about each Transition to your console.
*
* To enable tracing, import the [[trace]] singleton and enable one or more categories.
*
* ES6
* ```
*
* import {trace} from "ui-router-ng2"; // or "angular-ui-router"
* trace.enable(1, 5); // TRANSITION and VIEWCONFIG
* ```
*
* CJS
* ```
*
* let trace = require("angular-ui-router").trace; // or "ui-router-ng2"
* trace.enable("TRANSITION", "VIEWCONFIG");
* ```
*
* Globals
* ```
*
* let trace = window["angular-ui-router"].trace; // or "ui-router-ng2"
* trace.enable(); // Trace everything (very verbose)
* ```
*
* @module trace
*/ /** for typedoc */
import {parse} from "../common/hof";
import {isNumber} from "../common/predicates";
import {Transition} from "../transition/transition";
import {ActiveUIView, ViewConfig} from "../view/interface";
import {stringify, functionToString, maxLength, padString} from "./strings";
/** @hidden */
function uiViewString (viewData) {
if (!viewData) return 'ui-view (defunct)';
return `ui-view id#${viewData.id}, contextual name '${viewData.name}@${viewData.creationContext}', fqn: '${viewData.fqn}'`;
}
/** @hidden */
const viewConfigString = (viewConfig: ViewConfig) =>
`ViewConfig targeting ui-view: '${viewConfig.viewDecl.$uiViewName}@${viewConfig.viewDecl.$uiViewContextAnchor}', context: '${viewConfig.viewDecl.$context.name}'`;
/** @hidden */
function normalizedCat(input: Category): string {
return isNumber(input) ? Category[input] : Category[Category[input]];
}
/**
* Trace categories
*
* [[Trace.enable]] or [[Trace.disable]] a category
*
* `trace.enable(Category.TRANSITION)`
*
* These can also be provided using a matching string, or position ordinal
*
* `trace.enable("TRANSITION")`
*
* `trace.enable(1)`
*/
export enum Category {
RESOLVE, TRANSITION, HOOK, INVOKE, UIVIEW, VIEWCONFIG
}
/**
* Prints UI-Router Transition trace information to the console.
*/
export class Trace {
approximateDigests: number;
constructor() {
this.approximateDigests = 0;
}
/** @hidden */
private _enabled: { [key: string]: boolean } = {};
/** @hidden */
private _set(enabled: boolean, categories: Category[]) {
if (!categories.length) {
categories = Object.keys(Category)
.filter(k => isNaN(parseInt(k, 10)))
.map(key => Category[key]);
}
categories.map(normalizedCat).forEach(category => this._enabled[category] = enabled);
}
/**
* Enables a trace [[Category]]
*
* ```
* trace.enable("TRANSITION");
* ```
*
* @param categories categories to enable. If `categories` is omitted, all categories are enabled.
* Also takes strings (category name) or ordinal (category position)
*/
enable(...categories: Category[]) { this._set(true, categories) }
/**
* Disables a trace [[Category]]
*
* ```
* trace.disable("VIEWCONFIG");
* ```
*
* @param categories categories to disable. If `categories` is omitted, all categories are disabled.
* Also takes strings (category name) or ordinal (category position)
*/
disable(...categories: Category[]) { this._set(false, categories) }
/**
* Retrieves the enabled stateus of a [[Category]]
*
* ```
* trace.enabled("VIEWCONFIG"); // true or false
* ```
*
* @returns boolean true if the category is enabled
*/
enabled(category: Category): boolean {
return !!this._enabled[normalizedCat(category)];
}
/** called by ui-router code */
traceTransitionStart(transition: Transition) {
if (!this.enabled(Category.TRANSITION)) return;
let tid = transition.$id,
digest = this.approximateDigests,
transitionStr = stringify(transition);
console.log(`Transition #${tid} Digest #${digest}: Started -> ${transitionStr}`);
}
/** called by ui-router code */
traceTransitionIgnored(transition: Transition) {
if (!this.enabled(Category.TRANSITION)) return;
let tid = transition.$id,
digest = this.approximateDigests,
transitionStr = stringify(transition);
console.log(`Transition #${tid} Digest #${digest}: Ignored <> ${transitionStr}`);
}
/** called by ui-router code */
traceHookInvocation(step, options) {
if (!this.enabled(Category.HOOK)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
event = parse("traceData.hookType")(options) || "internal",
context = parse("traceData.context.state.name")(options) || parse("traceData.context")(options) || "unknown",
name = functionToString(step.fn);
console.log(`Transition #${tid} Digest #${digest}: Hook -> ${event} context: ${context}, ${maxLength(200, name)}`);
}
/** called by ui-router code */
traceHookResult(hookResult, transitionResult, transitionOptions) {
if (!this.enabled(Category.HOOK)) return;
let tid = parse("transition.$id")(transitionOptions),
digest = this.approximateDigests,
hookResultStr = stringify(hookResult),
transitionResultStr = stringify(transitionResult);
console.log(`Transition #${tid} Digest #${digest}: <- Hook returned: ${maxLength(200, hookResultStr)}, transition result: ${maxLength(200, transitionResultStr)}`);
}
/** called by ui-router code */
traceResolvePath(path, options) {
if (!this.enabled(Category.RESOLVE)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
pathStr = path && path.toString(),
policyStr = options && options.resolvePolicy;
console.log(`Transition #${tid} Digest #${digest}: Resolving ${pathStr} (${policyStr})`);
}
/** called by ui-router code */
traceResolvePathElement(pathElement, resolvablePromises, options) {
if (!this.enabled(Category.RESOLVE)) return;
if (!resolvablePromises.length) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
resolvablePromisesStr = Object.keys(resolvablePromises).join(", "),
pathElementStr = pathElement && pathElement.toString(),
policyStr = options && options.resolvePolicy;
console.log(`Transition #${tid} Digest #${digest}: Resolve ${pathElementStr} resolvables: [${resolvablePromisesStr}] (${policyStr})`);
}
/** called by ui-router code */
traceResolveResolvable(resolvable, options) {
if (!this.enabled(Category.RESOLVE)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
resolvableStr = resolvable && resolvable.toString();
console.log(`Transition #${tid} Digest #${digest}: Resolving -> ${resolvableStr}`);
}
/** called by ui-router code */
traceResolvableResolved(resolvable, options) {
if (!this.enabled(Category.RESOLVE)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
resolvableStr = resolvable && resolvable.toString(),
result = stringify(resolvable.data);
console.log(`Transition #${tid} Digest #${digest}: <- Resolved ${resolvableStr} to: ${maxLength(200, result)}`);
}
/** called by ui-router code */
tracePathElementInvoke(node, fn, deps, options) {
if (!this.enabled(Category.INVOKE)) return;
let tid = parse("transition.$id")(options),
digest = this.approximateDigests,
stateName = node && node.state && node.state.toString(),
fnName = functionToString(fn);
console.log(`Transition #${tid} Digest #${digest}: Invoke ${options.when}: context: ${stateName} ${maxLength(200, fnName)}`);
}
/** called by ui-router code */
traceError(error, transition: Transition) {
if (!this.enabled(Category.TRANSITION)) return;
let tid = transition.$id,
digest = this.approximateDigests,
transitionStr = stringify(transition);
console.log(`Transition #${tid} Digest #${digest}: <- Rejected ${transitionStr}, reason: ${error}`);
}
/** called by ui-router code */
traceSuccess(finalState, transition: Transition) {
if (!this.enabled(Category.TRANSITION)) return;
let tid = transition.$id,
digest = this.approximateDigests,
state = finalState.name,
transitionStr = stringify(transition);
console.log(`Transition #${tid} Digest #${digest}: <- Success ${transitionStr}, final state: ${state}`);
}
/** called by ui-router code */
traceUiViewEvent(event: string, viewData: ActiveUIView, extra = "") {
if (!this.enabled(Category.UIVIEW)) return;
console.log(`ui-view: ${padString(30, event)} ${uiViewString(viewData)}${extra}`);
}
/** called by ui-router code */
traceUiViewConfigUpdated(viewData: ActiveUIView, context) {
if (!this.enabled(Category.UIVIEW)) return;
this.traceUiViewEvent("Updating", viewData, ` with ViewConfig from context='${context}'`);
}
/** called by ui-router code */
traceUiViewScopeCreated(viewData: ActiveUIView, newScope) {
if (!this.enabled(Category.UIVIEW)) return;
this.traceUiViewEvent("Created scope for", viewData, `, scope #${newScope.$id}`);
}
/** called by ui-router code */
traceUiViewFill(viewData: ActiveUIView, html) {
if (!this.enabled(Category.UIVIEW)) return;
this.traceUiViewEvent("Fill", viewData, ` with: ${maxLength(200, html)}`);
}
/** called by ui-router code */
traceViewServiceEvent(event: string, viewConfig: ViewConfig) {
if (!this.enabled(Category.VIEWCONFIG)) return;
console.log(`$view.ViewConfig: ${event} ${viewConfigString(viewConfig)}`);
}
/** called by ui-router code */
traceViewServiceUiViewEvent(event: string, viewData: ActiveUIView) {
if (!this.enabled(Category.VIEWCONFIG)) return;
console.log(`$view.ViewConfig: ${event} ${uiViewString(viewData)}`);
}
}
/**
* The [[Trace]] singleton
*
* @example
* ```js
*
* import {trace} from "angular-ui-router";
* trace.enable(1, 5);
* ```
*/
let trace = new Trace();
export {trace};