bun-router 0.7.4-experimental.4 → 0.7.4-experimental.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
- import { splitFilePath } from '../fs/fsys';
2
1
  import path from 'node:path';
3
2
 
4
3
  type File = {
@@ -22,8 +21,8 @@ function createFile(name: string): File {
22
21
  const FileTree = (dir: string) => {
23
22
  const root = createFile(dir);
24
23
 
25
- const addFile = (_path: string) => {
26
- const pathParts = splitFilePath(_path);
24
+ const addFile = (filepath: string) => {
25
+ const pathParts = filepath.split(path.sep);
27
26
  let current = root;
28
27
 
29
28
  for (let i = 0; i < pathParts.length; i++) {
@@ -35,8 +34,8 @@ const FileTree = (dir: string) => {
35
34
  }
36
35
 
37
36
  current.isLast = true;
38
- current.path = _path;
39
- current.extension = path.extname(_path);
37
+ current.path = filepath;
38
+ current.extension = path.extname(filepath);
40
39
  };
41
40
 
42
41
  const getFilesByExtension = (extension: string): string[] => {
@@ -53,9 +52,9 @@ const FileTree = (dir: string) => {
53
52
  return files;
54
53
  };
55
54
 
56
- const getFileByName = (path: string): string | undefined => {
55
+ const getFileByName = (filepath: string): string | undefined => {
57
56
  let current = root;
58
- const pathParts = splitFilePath(path);
57
+ const pathParts = filepath.split(path.sep);
59
58
  for (let i = 0; i < pathParts.length; i++) {
60
59
  const part = pathParts[i];
61
60
  if (current.children.has(part)) {
package/lib/fs/fsys.ts CHANGED
@@ -22,15 +22,18 @@ async function readDir(dirpath: string, handler: (filepath: string, entry: BunFi
22
22
  }
23
23
  }
24
24
 
25
- // get the extension of a file (unnecessary)
26
- function ext(p: string): string {
27
- return path.extname(p);
25
+ // resolve module paths relative to the current working directory
26
+ function resolveModulePath(module: string) {
27
+ return path.join(process.cwd(), module);
28
28
  }
29
29
 
30
- // split a file path into an array of strings (unnecessary)
31
- function splitFilePath(p: string): string[] {
32
- return p.split(path.sep);
30
+ function exists(filepath: string): boolean {
31
+ try {
32
+ fs.access(filepath);
33
+ return true;
34
+ } catch (err) {
35
+ return false;
36
+ }
33
37
  }
34
38
 
35
-
36
- export { readDir, ext, splitFilePath };
39
+ export { readDir, resolveModulePath, exists };
@@ -1,36 +1,36 @@
1
1
  const Colors: Record<string,string> = {
2
- reset: "\x1b[0m",
2
+ reset: '\x1b[0m',
3
3
 
4
- // foreground
5
- black: "\x1b[30m",
6
- red: "\x1b[31m",
7
- green: "\x1b[32m",
8
- yellow: "\x1b[33m",
9
- blue: "\x1b[34m",
10
- magenta: "\x1b[35m",
11
- cyan: "\x1b[36m",
12
- white: "\x1b[37m",
4
+ // foreground
5
+ black: '\x1b[30m',
6
+ red: '\x1b[31m',
7
+ green: '\x1b[32m',
8
+ yellow: '\x1b[33m',
9
+ blue: '\x1b[34m',
10
+ magenta: '\x1b[35m',
11
+ cyan: '\x1b[36m',
12
+ white: '\x1b[37m',
13
13
 
14
- // background
15
- bgBlack: "\x1b[40m",
16
- bgRed: "\x1b[41m",
17
- bgGreen: "\x1b[42m",
18
- bgYellow: "\x1b[43m",
19
- bgBlue: "\x1b[44m",
20
- bgMagenta: "\x1b[45m",
21
- bgCyan: "\x1b[46m",
22
- bgWhite: "\x1b[47m",
23
- } as const;
14
+ // background
15
+ bgBlack: '\x1b[40m',
16
+ bgRed: '\x1b[41m',
17
+ bgGreen: '\x1b[42m',
18
+ bgYellow: '\x1b[43m',
19
+ bgBlue: '\x1b[44m',
20
+ bgMagenta: '\x1b[45m',
21
+ bgCyan: '\x1b[46m',
22
+ bgWhite: '\x1b[47m',
23
+ } as const;
24
24
 
25
25
 
26
26
 
27
27
  function color(foreground: string, background: string, message: string) {
28
- const _foreground = Colors[foreground];
29
- const _background = Colors[background];
30
- const reset = Colors.reset;
31
- return `${_foreground}${_background}${message}${reset}`;
28
+ const _foreground = Colors[foreground];
29
+ const _background = Colors[background];
30
+ const reset = Colors.reset;
31
+ return `${_foreground}${_background}${message}${reset}`;
32
32
  }
33
33
 
34
34
 
35
35
 
36
- export { color }
36
+ export { color };
@@ -5,4 +5,4 @@ type BunLogger = {
5
5
  message: (message: string) => void,
6
6
  }
7
7
 
8
- export { BunLogger }
8
+ export { BunLogger };
@@ -9,7 +9,7 @@ _ _
9
9
  |___|___|_|_| |_| |___|___|_| |___|_|
10
10
 
11
11
  `;
12
- const VERSION = '0.7.4-experimental.4';
12
+ const VERSION = '0.7.4-experimental.6';
13
13
  const Logger = (): BunLogger => {
14
14
  return {
15
15
  info: async (statusCode: number, routePath: string, method: string, message?: string) => {
@@ -6,8 +6,8 @@ import { http } from './router';
6
6
  import { ReactNode } from 'react';
7
7
 
8
8
  async function createContext(path: string, route: Route, request: Request): Promise<Context> {
9
- const params = extractParams(path, route);
10
9
  const query = new URLSearchParams(path);
10
+ const params = extractParams(path, route);
11
11
  const formData = isMultiPartForm(request.headers) ? await request.formData() : new FormData();
12
12
 
13
13
  return Promise.resolve({
@@ -21,16 +21,16 @@ async function createContext(path: string, route: Route, request: Request): Prom
21
21
  });
22
22
  }
23
23
 
24
- function extractParams(path: string, route: Route): Map<string, string> {
24
+ function extractParams(pattern: string, route: Route): Map<string, string> {
25
25
  const params: Map<string, string> = new Map();
26
- const pathSegments = path.split('/');
26
+ const pathSegments = pattern.split('/');
27
27
  const routeSegments = route.path.split('/');
28
28
 
29
29
  if (pathSegments.length !== routeSegments.length) return params;
30
30
 
31
31
  for (let i = 0; i < pathSegments.length; i++) {
32
32
  if (routeSegments[i][0] === ':') {
33
- const key = routeSegments[i].replace(':', '');
33
+ const key = routeSegments[i].slice(1);
34
34
  const value = pathSegments[i];
35
35
  params.set(key, value);
36
36
  }
@@ -41,8 +41,7 @@ function extractParams(path: string, route: Route): Map<string, string> {
41
41
 
42
42
  function getContentType(headers: Headers): string {
43
43
  const contentType = headers.get('Content-Type');
44
- if (!contentType) return '';
45
- return contentType;
44
+ return contentType ?? '';
46
45
  }
47
46
 
48
47
  function isMultiPartForm(headers: Headers): boolean {
@@ -1,7 +1,6 @@
1
1
  import { HttpHandler, Route } from './router.d';
2
2
  import { http } from '../http/http';
3
3
  import { createContext } from './context';
4
- import { splitPath } from '../util/strings';
5
4
 
6
5
  const createRoute = (path: string, method: string, handler: HttpHandler): Route => {
7
6
  const route: Route = {
@@ -19,8 +18,8 @@ const createRoute = (path: string, method: string, handler: HttpHandler): Route
19
18
  const RouteTree = () => {
20
19
  const root = createRoute('', 'GET', () => http.notFound());
21
20
 
22
- const addRoute = (path: string, method: string, handler: HttpHandler) => {
23
- const pathParts = splitPath(path);
21
+ const addRoute = (pattern: string, method: string, handler: HttpHandler) => {
22
+ const pathParts = pattern.split('/');
24
23
  let current = root;
25
24
 
26
25
  for (let i = 0; i < pathParts.length; i++) {
@@ -36,11 +35,11 @@ const RouteTree = () => {
36
35
 
37
36
  current.handler = handler;
38
37
  current.isLast = true;
39
- current.path = path;
38
+ current.path = pattern;
40
39
  };
41
40
 
42
- function findRoute(path: string): Route | undefined {
43
- const pathParts = splitPath(path);
41
+ function findRoute(pathname: string): Route | undefined {
42
+ const pathParts = pathname.split('/');
44
43
  let current = root;
45
44
  for (let i = 0; i < pathParts.length; i++) {
46
45
  const part = pathParts[i];
@@ -2,10 +2,10 @@ import path from 'path';
2
2
  import { Database } from 'bun:sqlite';
3
3
  import { Route, BunRouter, RouterOptions, Options, HttpHandler } from './router.d';
4
4
  import { httpStatusCodes } from '../http/status';
5
- import { readDir } from '../fs/fsys';
5
+ import { readDir, exists } from '../fs/fsys';
6
6
  import { Logger, startMessage } from '../logger/logger';
7
7
  import { http } from '../http/http';
8
- import { RouteTree } from './tree';
8
+ import { RouteTree } from './routeTree';
9
9
  import { createContext } from './context';
10
10
 
11
11
  const Router: BunRouter = (port?: number | string, options?: RouterOptions<Options>) => {
@@ -13,7 +13,8 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
13
13
  const logger = Logger();
14
14
 
15
15
  async function loadComponent(name: string) {
16
- const module = await import(name);
16
+ const modulePath = path.join(process.cwd(), name);
17
+ const module = await import(modulePath);
17
18
  return module.default;
18
19
  }
19
20
 
@@ -26,16 +27,16 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
26
27
  delete: (pattern, callback) => { addRoute(pattern, 'DELETE', callback); },
27
28
 
28
29
  // add static routes to the router tree
29
- // .tsx and .html are rendered as components
30
+ // .tsx and .html are rendered as components, or pages
30
31
  // all other file extensions are served as files
31
32
  // the root directory is traversed recursively
32
33
  static: async (pattern: string, root: string) => {
34
+ if (!exists(root)) console.log(`Cannot find directory ${root}`);
33
35
  await readDir(root, async (fp) => {
34
36
  const ext = path.extname(fp);
35
37
 
36
38
  let base = path.basename(fp);
37
39
 
38
- //FIXME: this can be improved
39
40
  if (ext === '.html' || ext === '.tsx') base = base.replace(ext, '');
40
41
 
41
42
  if (pattern[0] !== '/') pattern = '/' + pattern;
@@ -50,7 +51,7 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
50
51
  children: new Map(),
51
52
  dynamicPath: '',
52
53
  isLast: true,
53
- path: patternPath,
54
+ path: patternPath.slice(1),
54
55
  method: 'GET',
55
56
  handler: async () => {
56
57
  if (ext === '.tsx') {
@@ -62,6 +63,8 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
62
63
  },
63
64
  };
64
65
 
66
+ console.log(route.path);
67
+
65
68
  addRoute(route.path, 'GET', route.handler);
66
69
  });
67
70
  },
@@ -75,7 +78,7 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
75
78
  ...options,
76
79
  async fetch(req) {
77
80
  const url = new URL(req.url);
78
- const path = url.pathname;
81
+ const pathname = url.pathname;
79
82
 
80
83
  // set the database
81
84
  if (options) {
@@ -83,16 +86,16 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
83
86
  opts.db = o.db;
84
87
  }
85
88
 
86
- const route = findRoute(path);
89
+ const route = findRoute(pathname);
87
90
 
88
- // if the route exists, execute the handler
91
+ // if the route exists, call the handler
89
92
  if (route) {
90
93
  if (route.method !== req.method) {
91
94
  logger.info(405, url.pathname, req.method, httpStatusCodes[405]);
92
95
  return Promise.resolve(http.methodNotAllowed());
93
96
  }
94
97
 
95
- const context = await createContext(path, route, req);
98
+ const context = await createContext(pathname, route, req);
96
99
  context.db = new Database(opts.db);
97
100
 
98
101
  const response = await route.handler(context);
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "peerDependencies": {
15
15
  "typescript": "^5.0.0"
16
16
  },
17
- "version": "0.7.4-experimental.4",
17
+ "version": "0.7.4-experimental.6",
18
18
  "dependencies": {
19
19
  "@types/react": "^18.2.22",
20
20
  "eslint-plugin-react-hooks": "^4.6.0",
package/tsconfig.json CHANGED
@@ -19,7 +19,6 @@
19
19
  "bun-types" // add Bun global
20
20
  ],
21
21
  "paths": {
22
- "examples/*": ["./examples/*"],
23
22
  }
24
23
  },
25
24
  }
@@ -1,5 +0,0 @@
1
- import { resolveModules } from './resolver';
2
-
3
- const modules = await resolveModules('../lib/resolver/testdir');
4
-
5
- console.log(modules.length);
@@ -1,43 +0,0 @@
1
- import { FileTree } from '../fs/filetree';
2
- import { readDir } from '../fs/fsys';
3
- import { ComponentType } from 'react';
4
-
5
- async function createFileTree(root: string) {
6
- const tree = FileTree(root);
7
-
8
- await readDir(root, (fp) => {
9
- tree.addFile(fp);
10
- });
11
-
12
- return tree;
13
- }
14
-
15
- async function resolveModules(root: string) {
16
-
17
- if (!await doesExist(root)) {
18
- throw new Error(`Directory ${root} does not exist`);
19
- }
20
-
21
- const tree = await createFileTree(root);
22
- const files = tree.getFilesByExtension('.tsx');
23
- const modules: ComponentType[] = [];
24
-
25
- for (const file of files) {
26
- const module = await import(file);
27
- modules.push(module);
28
- }
29
- return modules;
30
- }
31
-
32
- async function doesExist(root: string): Promise<boolean> {
33
- const file = Bun.file(root);
34
- return await file.exists();
35
- }
36
-
37
- async function isDir(root: string): Promise<boolean> {
38
- const file = Bun.file(root);
39
- return await file.isDir();
40
- }
41
-
42
-
43
- export { resolveModules };
@@ -1,5 +0,0 @@
1
- import React from 'react';
2
-
3
- export default function foo() {
4
- return <div>foo</div>;
5
- }
@@ -1,3 +0,0 @@
1
- const splitPath = (path: string): string[] => path.split('/').filter(Boolean);
2
-
3
- export { splitPath }