Skip to content

Commit

Permalink
feat(sorter): apply sorter on route configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
imjuni committed Feb 5, 2024
1 parent ae03ab0 commit 94b2567
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 2 deletions.
2 changes: 1 addition & 1 deletion examples/handlers/avengers/heroes/[id]/put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import type { HTTPMethods } from 'fastify';

export const methods: HTTPMethods[] = ['PATCH'];

export async function hello() {
export async function handler() {
return 'hello';
}
3 changes: 3 additions & 0 deletions examples/handlers/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export async function handler() {
return 'hello';
}
3 changes: 2 additions & 1 deletion src/modules/commands/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getIncludePatterns } from '#/modules/files/getIncludePatterns';
import { ExcludeContainer } from '#/modules/scopes/ExcludeContainer';
import { IncludeContainer } from '#/modules/scopes/IncludeContainer';
import { defaultExclude } from '#/modules/scopes/defaultExclude';
import { sortRoutePath } from '#/routes/sort/sortRoutePath';
import chalk from 'chalk';
import consola from 'consola';
import { isDescendant } from 'my-node-fp';
Expand Down Expand Up @@ -93,7 +94,7 @@ export async function routing(optionParams: TRouteOption, projectParams?: tsm.Pr

const result = {
imports: appendFastifyInstance(dedupeImportConfiguration(importConfigurations)),
routes: routeConfigurations,
routes: sortRoutePath(routeConfigurations),
reasons,
};

Expand Down
162 changes: 162 additions & 0 deletions src/routes/sort/__tests__/sort.route.path.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import type { IRouteConfiguration } from '#/routes/interfaces/IRouteConfiguration';
import { sortRoutePath } from '#/routes/sort/sortRoutePath';
import { describe, expect, it } from 'vitest';

const routings: IRouteConfiguration[] = [
{
methods: ['get'],
routePath: '/',
hash: 'jjaZTV7G4bZewxbekhbGVgAfZRoDcCBP',
hasOption: false,
handlerName: 'handler_jjaZTV7G4bZewxbekhbGVgAfZRoDcCBP',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/get.ts',
},
{
methods: ['get'],
routePath: '/avengers/heroes/:id',
hash: 'E6sTFTY5HVG6ZoiyOfO78twMQtegfNKM',
hasOption: false,
handlerName: 'handler_E6sTFTY5HVG6ZoiyOfO78twMQtegfNKM',
typeArgument: {
request: 'fastify-request',
kind: 187,
text: '{ Body: IAbility }',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/[id]/get.ts',
},
{
methods: ['put', 'patch'],
routePath: '/avengers/heroes/:id',
hash: 'JcsG7kAN9Rhl7Bj66sC4AJjcG81hc0o7',
hasOption: false,
handlerName: 'handler_JcsG7kAN9Rhl7Bj66sC4AJjcG81hc0o7',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/[id]/put.ts',
},
{
methods: ['get', 'search'],
routePath: '/avengers/heroes',
hash: 'RKQsowJ59rvtTzit3ceAu0YOnIOLK4ZH',
hasOption: false,
handlerName: 'handler_RKQsowJ59rvtTzit3ceAu0YOnIOLK4ZH',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/get.ts',
},
{
methods: ['post'],
routePath: '/avengers/heroes',
hash: 'PyYwrekj8FU77SIYAcghL29WTKosRRP0',
hasOption: false,
handlerName: 'handler_PyYwrekj8FU77SIYAcghL29WTKosRRP0',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/post.ts',
},
{
methods: ['get', 'search'],
routePath: '/avengers/heroes/:id?',
hash: 'dIptpGuAKFzf0qeTAHtzx2eULDtPLFdB',
hasOption: false,
handlerName: 'handler_dIptpGuAKFzf0qeTAHtzx2eULDtPLFdB',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/[[id]]/get.ts',
},
{
methods: ['post'],
routePath: '/avengers/heroes/:id/:hour(^\\d{2})h:minute(^\\d{2})m',
hash: 'r4PZQn5zGL7qwViJ1L07z1WK9sbfNLFK',
hasOption: false,
handlerName: 'handler_r4PZQn5zGL7qwViJ1L07z1WK9sbfNLFK',
typeArgument: {
request: 'fastify-request',
kind: 187,
text: '{ Body: IAbility }',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/avengers/heroes/[id]/[$time]/post.ts',
},
{
methods: ['delete'],
routePath: '/hello',
hash: 'DggTo0LTLK8Hpxjtd4DZ2DL2WkuKa9pL',
hasOption: false,
handlerName: 'handler_DggTo0LTLK8Hpxjtd4DZ2DL2WkuKa9pL',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/hello/delete.ts',
},
{
methods: ['post'],
routePath: '/hello',
hash: 'yMyTHNwiACuaCaoPmXsAMsWdfIhWMJCS',
hasOption: false,
handlerName: 'handler_yMyTHNwiACuaCaoPmXsAMsWdfIhWMJCS',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/hello/post.ts',
},
{
methods: ['put'],
routePath: '/hello',
hash: 'qFaWzYBLA8Du9hWEGQsjrEPqLbauwjQN',
hasOption: false,
handlerName: 'handler_qFaWzYBLA8Du9hWEGQsjrEPqLbauwjQN',
typeArgument: {
request: 'property-signature',
text: '',
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/hello/put.ts',
},
{
methods: ['get'],
routePath: '/po-ke/world',
hash: 'm7vOoboyWbScOSEpWeCimw2qbMtNNrSc',
hasOption: true,
handlerName: 'handler_m7vOoboyWbScOSEpWeCimw2qbMtNNrSc',
typeArgument: {
request: 'fastify-request',
kind: 187,
text: "{\n Querystring: IReqPokeHello['querystring'];\n Body: IReqPokeHello['Body'];\n Headers: {\n 'access-token': string;\n 'refresh-token': string;\n 'expire-time': {\n token: string;\n expire: number;\n site: {\n host: string;\n port: number;\n };\n };\n };\n }",
},
sourceFilePath: '/Users/imjuni/project/github/fast-maker/examples/handlers/po-ke/world/get.ts',
},
];

describe('sortRoutePath', () => {
it('sorting', () => {
const sorted = sortRoutePath(routings);
const routePaths = sorted.map((route) => `${route.methods.join(', ')}-${route.routePath}`);

expect(routePaths).toEqual([
'get-/',
'get-/avengers/heroes/:id',
'put, patch-/avengers/heroes/:id',
'get, search-/avengers/heroes',
'post-/avengers/heroes',
'get, search-/avengers/heroes/:id?',
'post-/avengers/heroes/:id/:hour(^\\d{2})h:minute(^\\d{2})m',
'delete-/hello',
'post-/hello',
'put-/hello',
'get-/po-ke/world',
]);
});
});
64 changes: 64 additions & 0 deletions src/routes/sort/sortRoutePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { IRouteConfiguration } from '#/routes/interfaces/IRouteConfiguration';
import consola from 'consola';
import { isError, type PartialRecord } from 'my-easy-fp';
import { isDescendant } from 'my-node-fp';

export function sortRoutePath(routes: IRouteConfiguration[]): IRouteConfiguration[] {
if (routes.length <= 0) {
return routes;
}

const desc = routes.sort((l, r) => r.routePath.localeCompare(l.routePath));

const chunked = desc.reduce<{ prev: string; chunk: PartialRecord<string, IRouteConfiguration[]> }>(
(chunking, current) => {
const next = { ...chunking };

try {
// skip chunking action for root route path
if (current.routePath === '/') {
next.chunk[current.routePath] = [current];
return next;
}

if (chunking.prev === '') {
next.prev = current.routePath;
next.chunk[current.routePath] = [...(next.chunk[current.routePath] ?? []), current];
return next;
}

if (isDescendant(current.routePath, chunking.prev, '/') === true) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const backup = next.chunk[next.prev]!;

next.chunk[next.prev] = undefined;
next.prev = current.routePath;
next.chunk[current.routePath] = [...backup, current];

return next;
}

next.prev = current.routePath;
next.chunk[current.routePath] = [current];

return next;
} catch (caught) {
const err = isError(caught, new Error('unknown error raised'));

consola.debug(err.message);
consola.debug(err.stack);

return next;
}
},
{ prev: '', chunk: {} },
);

const keys = Object.keys(chunked.chunk);
const asc = keys.sort((l, r) => l.localeCompare(r));

return asc
.map((key) => chunked.chunk[key])
.filter((value): value is IRouteConfiguration[] => value != null)
.flat();
}

0 comments on commit 94b2567

Please sign in to comment.