Skip to content

Commit

Permalink
feat(core/transform): routes returns collection, routes, and tree
Browse files Browse the repository at this point in the history
  • Loading branch information
rafamel committed Oct 18, 2019
1 parent 3448089 commit 3fa7bdd
Showing 1 changed file with 103 additions and 25 deletions.
128 changes: 103 additions & 25 deletions packages/core/src/transform/routes.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,134 @@
import { CollectionTree, ServicesRoutes } from '~/types';
import { traverse } from '~/inspect/traverse';
import { isElementService, isElementType, isTypeResponse } from '~/inspect/is';
import {
CollectionTree,
ServicesTree,
ServicesRoutes,
GenericCollection,
Service,
ResponseTypeChildren,
GenericService
} from '~/types';
import {
isElementService,
isElementType,
isTypeResponse,
isElementTree
} from '~/inspect/is';
import { normalize } from './normalize';
import { replace, ReplaceTransformData } from './replace';

export interface RoutesTransformOptions {
export interface RoutesTransform<T extends CollectionTree> {
collection: GenericCollection<T>;
routes: ServicesRoutes<T>;
tree: ServicesTree<T>;
}

export interface RoutesTransformOptions<
T extends CollectionTree = CollectionTree
> {
/**
* Whether to include response types with children as routes. Default: `true`.
*/
children?: boolean;
/**
* A non word character containing string. Default: `':'`.
*/
separator?: string;
/**
* Whether to include response types with children as routes. Default: `true`.
* Maps services. Default: `(service) => service`.
*/
children?: boolean;
map?: (service: GenericService<T>, data: ReplaceTransformData) => Service;
}

/**
* Given a collection, it will normalize it -see `normalize`- and return an object with *values* of all services, and *keys* of their full route.
* Given a collection, it will return an object with *values* of all services, and *keys* of their full route.
*/
export function routes<T extends CollectionTree>(
collection: T,
options?: RoutesTransformOptions
): ServicesRoutes<T> {
const opts = Object.assign({ separator: ':', children: true }, options);
const routes: ServicesRoutes<any> = {};
options?: RoutesTransformOptions<T>
): RoutesTransform<T> {
const opts = Object.assign(
{
children: true,
separator: ':',
map: (service: Service, _data: ReplaceTransformData) => service
},
options
);

if (!/[^\w]/.exec(opts.separator)) {
throw Error(`Separator must contain a non word character`);
}

traverse(normalize(collection), (element, next, { route }) => {
if (isElementService(element)) {
const str = route.join(opts.separator);
if (Object.hasOwnProperty.call(routes, str)) {
throw Error(`Collection routes collision: ${str}`);
collection = opts.children
? (normalize(collection, {
liftInlineType(type) {
if (!isTypeResponse(type) || !type.children) return false;
return Object.keys(type.children).length > 0;
}
}) as any)
: (replace(collection, (element, next) => {
element = next(element);
if (
isElementType(element) &&
isTypeResponse(element) &&
element.children
) {
const { children, ...other } = element;
return other;
}
return element;
}) as any);

const routes: ServicesRoutes<any> = {};
const responses: ServicesTree<any> = {};
const flatter: any = replace(
collection,
(element, next, { route, path }): any => {
if (isElementService(element)) {
const str = route.join(opts.separator);
if (Object.hasOwnProperty.call(routes, str)) {
throw Error(`Collection routes collision: ${str}`);
}
const service = opts.map(element, { route, path });
routes[str] = service;
return service;
}
routes[str] = element;
} else if (isElementType(element)) {
if (opts.children && isTypeResponse(element) && element.children) {
const name = route[route.length - 1];
if (isElementType(element)) {
if (!isTypeResponse(element) || !element.children) return element;

const entries = Object.entries(element.children);
if (!entries.length) return element;

const name = route[route.length - 1];

const children: ResponseTypeChildren = {};
for (const [key, service] of entries) {
const str = name + opts.separator + key;
if (Object.hasOwnProperty.call(routes, str)) {
throw Error(`Collection routes collision: ${str}`);
}
routes[str] = service;
const result = opts.map(service, {
route: route.concat([key]),
path: path.concat(['children', key])
});
children[key] = result as any;
routes[str] = result;
}
responses[name] = { ...children };

return { ...element, children };
}
} else {
return next();

element = next(element);
return isElementTree(element)
? { ...element.scopes, ...element.services }
: element;
}
});
);

return routes;
return {
collection: collection as any,
routes,
tree: { ...responses, ...flatter }
};
}

0 comments on commit 3fa7bdd

Please sign in to comment.