Skip to content

Commit

Permalink
refactor(justjs): Replaced justjs with vanilla/*.js
Browse files Browse the repository at this point in the history
test(StateService): Add state service tests from angular-ui-router
test(*): Use vanilla services and hashBangLocation for tests
chore(build): Use typescript@rc for async/await in tests
  • Loading branch information
christopherthielen committed Dec 1, 2016
1 parent bbd7d25 commit 241d972
Show file tree
Hide file tree
Showing 20 changed files with 1,019 additions and 147 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"shelljs": "^0.7.0",
"shx": "^0.1.4",
"tslint": "=2.5.0",
"typescript": "~2.0.2",
"typescript": "rc",
"webpack": "^1.13.3"
}
}
103 changes: 0 additions & 103 deletions src/justjs.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/vanilla.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./vanilla/index";
33 changes: 33 additions & 0 deletions src/vanilla/$injector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
extend, assertPredicate, isFunction, isArray, isInjectable, $InjectorLike, IInjectable
} from "../common/module";

/** angular1-like injector api */
// globally available injectables
let globals = {};
let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
let ARGUMENT_NAMES = /([^\s,]+)/g;

export const $injector = {
get: name => globals[name],

has: (name) => $injector.get(name) != null,

invoke: (fn: IInjectable, context?, locals?) => {
let all = extend({}, globals, locals || {});
let params = $injector.annotate(fn);
let ensureExist = assertPredicate(key => all.hasOwnProperty(key), key => `DI can't find injectable: '${key}'`);
let args = params.filter(ensureExist).map(x => all[x]);
if (isFunction(fn)) return fn.apply(context, args);
else return (fn as any[]).slice(-1)[0].apply(context, args);
},

annotate: (fn: IInjectable): any[] => {
if (!isInjectable(fn)) throw new Error(`Not an injectable function: ${fn}`);
if (fn && (fn as any).$inject) return (fn as any).$inject;
if (isArray(fn)) return fn.slice(0, -1);
let fnStr = fn.toString().replace(STRIP_COMMENTS, '');
let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
return result || [];
}
} as $InjectorLike;
37 changes: 37 additions & 0 deletions src/vanilla/$q.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { isArray, isObject, $QLike } from "../common/module";

/** $q-like promise api */
export const $q = {
when: (val) => new Promise((resolve, reject) => resolve(val)),

reject: (val) => new Promise((resolve, reject) => { reject(val); }),

defer: () => {
let deferred: any = {};
deferred.promise = new Promise((resolve, reject) => {
deferred.resolve = resolve;
deferred.reject = reject;
});
return deferred;
},

all: (promises: { [key: string]: Promise<any> } | Promise<any>[]) => {
if (isArray(promises)) {
return new Promise((resolve, reject) => {
let results = [];
promises.reduce((wait4, promise) => wait4.then(() => promise.then(val => results.push(val))), $q.when())
.then(() => { resolve(results); }, reject);
});
}

if (isObject(promises)) {
// Convert promises map to promises array.
// When each promise resolves, map it to a tuple { key: key, val: val }
let chain = Object.keys(promises)
.map(key => promises[key].then(val => ({key, val})));
// Then wait for all promises to resolve, and convert them back to an object
return $q.all(chain).then(values =>
values.reduce((acc, tuple) => { acc[tuple.key] = tuple.val; return acc; }, {}));
}
}
} as $QLike;
52 changes: 52 additions & 0 deletions src/vanilla/browserHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { services, isDefined } from '../common/module';
import { LocationConfig, LocationServices } from '../common/coreservices';
import { HistoryImplementation } from './interface';
import { splitQuery, trimHashVal, getParams } from './utils';

let hashPrefix: string = '';
let baseHref: string = '';

const locationServiceConfig: LocationConfig = {
port: () =>
parseInt(location.port),
protocol: () =>
location.protocol,
host: () =>
location.host,
baseHref: () =>
baseHref,
html5Mode: () =>
true,
hashPrefix: (newprefix?: string): string => {
if (isDefined(newprefix)) {
hashPrefix = newprefix;
}
return hashPrefix;
}
};

const locationService: LocationServices = {
hash: () =>
trimHashVal(location.hash),
path: () => {
let base = services.locationConfig.baseHref();
let path = location.pathname;
let idx = path.indexOf(base);
if (idx !== 0) throw new Error(`current url: ${path} does not start with <base> tag ${base}`);
return path.substr(base.length);
},
search: () =>
getParams(splitQuery(location.search)[1]),
setUrl: (url: string, replace: boolean = false) => {
if (isDefined(url)) {
if (replace) history.replaceState(null, null, services.locationConfig.baseHref() + url);
else history.pushState(null, null, services.locationConfig.baseHref() + url);
}
},
onChange: (cb: EventListener) => window.addEventListener("popstate", cb, false) as any
};

export const browserHistory: HistoryImplementation = {
service: locationService,
configuration: locationServiceConfig
};
44 changes: 44 additions & 0 deletions src/vanilla/hashHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {services, isDefined} from '../common/module';
import {LocationConfig, LocationServices} from '../common/coreservices';
import {HistoryImplementation} from './interface';
import {splitHash, splitQuery, trimHashVal, getParams} from './utils';

let hashPrefix: string = '';
let baseHref: string = '';

const locationServiceConfig: LocationConfig = {
port: () =>
parseInt(location.port),
protocol: () =>
location.protocol,
host: () =>
location.host,
baseHref: () =>
baseHref,
html5Mode: () =>
false,
hashPrefix: (newprefix?: string): string => {
if(isDefined(newprefix)) {
hashPrefix = newprefix;
}
return hashPrefix;
}
}

const locationService: LocationServices = {
hash: () =>
splitHash(trimHashVal(location.hash))[1],
path: () =>
splitHash(splitQuery(trimHashVal(location.hash))[0])[0],
search: () =>
getParams(splitQuery(splitHash(trimHashVal(location.hash))[0])[1]),
setUrl: (url: string, replace: boolean = true) => {
if (url) location.hash = url;
},
onChange: (cb: EventListener) => window.addEventListener("hashchange", cb, false) as any
}

export const hashHistory: HistoryImplementation = {
service: locationService,
configuration: locationServiceConfig
}
38 changes: 38 additions & 0 deletions src/vanilla/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Naive, pure JS implementation of core ui-router services
*
* @module vanilla
*/ /** */
import { UIRouter } from "../router";

import { services as coreservices } from "../common/coreservices";
import { $q } from "./$q";
import { $injector } from "./$injector";
import { hashHistory } from "./hashHistory";
import { browserHistory } from "./browserHistory";
import { HistoryImplementationPlugin, ServicesPlugin, HistoryImplementation } from "./interface";
import { extend } from "../common/common";

export { $q, $injector, hashHistory, browserHistory };

export function services(router: UIRouter): ServicesPlugin {
coreservices.$injector = $injector;
coreservices.$q = $q;

return { name: "vanilla.services", $q, $injector };
}

const HistoryImplementationPluginFactory = (name: string, historyImpl: HistoryImplementation) =>
(router: UIRouter) => {
const { service, configuration } = historyImpl;
extend(coreservices.location, service);
extend(coreservices.locationConfig, configuration);

return { name, service, configuration };
};

export const hashLocation: (router: UIRouter) => HistoryImplementationPlugin =
HistoryImplementationPluginFactory("vanilla.hashBangLocation", hashHistory);
export const pushStateLocation: (router: UIRouter) => HistoryImplementationPlugin =
HistoryImplementationPluginFactory("vanilla.pushStateLocation", browserHistory);

17 changes: 17 additions & 0 deletions src/vanilla/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { LocationConfig, LocationServices } from '../common/coreservices';
import { UIRouterPlugin } from "../interface";
import { $InjectorLike, $QLike } from "../common/module";

export interface HistoryImplementation {
service: LocationServices;
configuration: LocationConfig;
}

export interface HistoryImplementationPlugin extends UIRouterPlugin, HistoryImplementation {

}

export interface ServicesPlugin extends UIRouterPlugin {
$q: $QLike,
$injector: $InjectorLike
}
27 changes: 27 additions & 0 deletions src/vanilla/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {isArray} from "../common/module";

const beforeAfterSubstr = (char: string) => (str: string): string[] => {
if (!str) return ["", ""];
let idx = str.indexOf(char);
if (idx === -1) return [str, ""];
return [str.substr(0, idx), str.substr(idx + 1)];
};

export const splitHash = beforeAfterSubstr("#");
export const splitQuery = beforeAfterSubstr("?");
export const splitEqual = beforeAfterSubstr("=");
export const trimHashVal = (str):string => str ? str.replace(/^#/, "") : "";

export const keyValsToObjectR = (accum, [key, val]) => {
if (!accum.hasOwnProperty(key)) {
accum[key] = val;
} else if (isArray(accum[key])) {
accum[key].push(val);
} else {
accum[key] = [accum[key], val]
}
return accum;
};

export const getParams = (queryString: string): any =>
queryString.split("&").map(splitEqual).reduce(keyValsToObjectR, {});
7 changes: 5 additions & 2 deletions test/hookBuilderSpec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { UIRouter, TransitionService, StateService, State, PathNode } from "../src/index";
import { tree2Array } from "./_testUtils";
import "../src/justjs";
import {TransitionHookPhase} from "../src/transition/interface";
import { TransitionHookPhase } from "../src/transition/interface";
import * as vanilla from "../src/vanilla/index";

describe('HookBuilder:', function() {
let uiRouter: UIRouter = null;
Expand All @@ -16,6 +16,9 @@ describe('HookBuilder:', function() {
beforeEach(() => {
log = "";
uiRouter = new UIRouter();
uiRouter.plugin(vanilla.services);
uiRouter.plugin(vanilla.hashLocation);

$trans = uiRouter.transitionService;
$state = uiRouter.stateService;
root = uiRouter.stateRegistry.root();
Expand Down
Loading

0 comments on commit 241d972

Please sign in to comment.