Skip to content

Commit

Permalink
Merge pull request #181 from storyofams/fix/nested-folders
Browse files Browse the repository at this point in the history
fix: use error stack to determine the directory
  • Loading branch information
ggurkal authored Jun 30, 2021
2 parents 93c830e + e30d672 commit 23231f6
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 4 deletions.
9 changes: 5 additions & 4 deletions lib/createHandler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';
import { findRoute } from './internals/findRoute';
import { getFileDirectory } from './internals/getFileDirectory';
import { getParams } from './internals/getParams';
import { notFound } from './internals/notFound';
import { parseRequestUrl } from './internals/parseRequestUrl';

/**
* Prepares a router for the given class.
Expand All @@ -24,16 +26,15 @@ import { notFound } from './internals/notFound';
*/
export function createHandler(cls: new (...args: any[]) => any): NextApiHandler {
const instance = new cls();
const directory = getFileDirectory();

return (req: NextApiRequest, res: NextApiResponse) => {
if (!req.url || !req.method) {
return notFound(req, res);
}

const [, , , ...path] = req.url.split('?')[0].split('/');
const joinedPath = `/${path.join('/')}`;

const [keys, match, method] = findRoute(cls, req.method, joinedPath);
const path = parseRequestUrl(req.url, directory);
const [keys, match, method] = findRoute(cls, req.method, path);
if (!method) {
return notFound(req, res);
}
Expand Down
18 changes: 18 additions & 0 deletions lib/internals/getFileDirectory.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getFileDirectory } from './getFileDirectory';

it('getFileDirectory', () => {
const spyError = jest.spyOn(global, 'Error');
spyError.mockImplementation(() => {
const err = {
name: 'Error',
message: 'An error occurred.',
stack: 'Object at (/example-path/.next/server/pages/api/tags/[id]/[[...params]].ts:1:1)'
};
return err;
});

const dir = getFileDirectory();
expect(dir).toStrictEqual('/example-path/.next/server/pages/api/tags/[id]');

spyError.mockRestore();
});
21 changes: 21 additions & 0 deletions lib/internals/getFileDirectory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { dirname } from 'path';

export function getFileDirectory(): string | undefined {
let directoryPath: string | undefined;

const parenthesisRegExp = /\(([^)]+)\)/;
const pathInError = new Error().stack
?.split('at ')
.find(line => parenthesisRegExp.test(line) && line.includes('/.next/server/pages/api'));

/* istanbul ignore else */
if (pathInError) {
const [, pathWithRowCol] = parenthesisRegExp.exec(pathInError) ?? [];
/* istanbul ignore else */
if (pathWithRowCol) {
directoryPath = dirname(pathWithRowCol.replace(/:(\d+):(\d+)$/, ''));
}
}

return directoryPath;
}
23 changes: 23 additions & 0 deletions lib/internals/parseRequestUrl.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { parseRequestUrl } from './parseRequestUrl';

describe('parseRequestUrl', () => {
it('Should return "/"', () => {
expect(parseRequestUrl('/api/user')).toStrictEqual('/');
});

it('Should return "/1"', () => {
expect(parseRequestUrl('/api/user/1')).toStrictEqual('/1');
});

it('Should return "/1/articles"', () => {
expect(parseRequestUrl('/api/user/1/articles')).toStrictEqual('/1/articles');
});

it('Should return "/" when directory name is a paremeter ([id])', () =>
expect(parseRequestUrl('/api/user/1', '/next-api-decorators/.next/server/pages/api/user/[id]')).toStrictEqual('/'));

it('Should return "/articles" when directory name is a paremeter ([id])', () =>
expect(
parseRequestUrl('/api/user/1/articles', '/next-api-decorators/.next/server/pages/api/user/[id]')
).toStrictEqual('/articles'));
});
17 changes: 17 additions & 0 deletions lib/internals/parseRequestUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function parseRequestUrl(url: string, directoryPath?: string): string {
let path = url.split('?')[0].split('/').slice(3).join('/');

if (directoryPath) {
const pathRegExp = new RegExp(directoryPath.split('/.next/server/pages')[1].replace(/(\[\w+\])/, '(\\w+)'));
/* istanbul ignore else */
if (pathRegExp.test(url)) {
path = url.replace(pathRegExp, '');
}
}

if (!path.startsWith('/')) {
path = `/${path}`;
}

return path;
}

0 comments on commit 23231f6

Please sign in to comment.