~repos /edge-city
git clone https://pyrossh.dev/repos/edge-city.git
edge-city is a next level meta-framework for react that runs only on edge runtimes
0754c43a
—
Peter John 2 years ago
add router
- .gitignore +2 -1
- package.json +4 -1
- packages/example/Dockerfile +6 -0
- packages/example/package.json +3 -1
- packages/example/routes/{health/api.js → api.js} +0 -0
- packages/example/routes/{index/page.css → page.css} +0 -0
- packages/example/routes/{index/page.jsx → page.jsx} +0 -0
- packages/example/routes/todos/{[id]/server.js → :id/api.js} +1 -1
- packages/muffinjs/bin/muffin +0 -0
- packages/muffinjs/bun.lockb +0 -0
- packages/muffinjs/index.js +107 -32
- packages/muffinjs/package.json +4 -1
- packages/muffinjs/router.js +1 -0
.gitignore
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
node_modules
|
|
1
|
+
node_modules
|
|
2
|
+
.vercel
|
package.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-project",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
+
"workspaces": [
|
|
4
|
-
|
|
5
|
+
"packages/example",
|
|
6
|
+
"packages/muffinjs"
|
|
7
|
+
]
|
|
5
8
|
}
|
packages/example/Dockerfile
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
FROM oven/bun
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
COPY . /app
|
|
5
|
+
|
|
6
|
+
CMD ["bun", "start"]
|
packages/example/package.json
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
"type": "module",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "bun --hot main.js",
|
|
6
|
-
"start": "NODE_ENV=production bun main.js"
|
|
6
|
+
"start": "NODE_ENV=production bun main.js",
|
|
7
|
+
"build": "docker build . -t example",
|
|
8
|
+
"run": "docker run -p 3000:3000 example"
|
|
7
9
|
},
|
|
8
10
|
"dependencies": {
|
|
9
11
|
"muffinjs": "0.1.0",
|
packages/example/routes/{health/api.js → api.js}
RENAMED
|
File without changes
|
packages/example/routes/{index/page.css → page.css}
RENAMED
|
File without changes
|
packages/example/routes/{index/page.jsx → page.jsx}
RENAMED
|
File without changes
|
packages/example/routes/todos/{[id]/server.js → :id/api.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import tigrisDB from "./db.js.js.js";
|
|
1
|
+
import tigrisDB from "./db.js.js.js.js.js.js.js.js";
|
|
2
2
|
|
|
3
3
|
export const todosCollection = tigrisDB.getCollection("todoItems");
|
|
4
4
|
|
packages/muffinjs/bin/muffin
CHANGED
|
File without changes
|
packages/muffinjs/bun.lockb
ADDED
|
Binary file
|
packages/muffinjs/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// import './jsxPlugin.js';
|
|
2
|
-
import React from 'react';
|
|
3
1
|
import path from 'path';
|
|
4
|
-
import
|
|
2
|
+
import walkdir from 'walkdir';
|
|
5
3
|
import postcss from "postcss"
|
|
6
4
|
import autoprefixer from "autoprefixer";
|
|
7
5
|
import postcssCustomMedia from "postcss-custom-media";
|
|
8
6
|
// import postcssNormalize from 'postcss-normalize';
|
|
9
7
|
import postcssNesting from "postcss-nesting";
|
|
10
8
|
import { renderToReadableStream } from 'react-dom/server';
|
|
9
|
+
import { createRouter } from 'radix3';
|
|
10
|
+
import mimeTypes from "mime-types";
|
|
11
11
|
import { routerAtom } from './router.js';
|
|
12
12
|
|
|
13
13
|
console.log("running in folder:", path.basename(process.cwd()), "env:", process.env.NODE_ENV);
|
|
14
|
-
console.log("deleting cache");
|
|
14
|
+
// console.log("deleting cache");
|
|
15
|
-
rmSync(path.join(process.cwd(), ".cache"), { force: true, recursive: true })
|
|
15
|
+
// rmSync(path.join(process.cwd(), ".cache"), { force: true, recursive: true })
|
|
16
16
|
|
|
17
17
|
const isProd = process.env.NODE_ENV === "production";
|
|
18
18
|
|
|
@@ -22,7 +22,48 @@ const transpiler = new Bun.Transpiler({
|
|
|
22
22
|
jsxOptimizationInline: true,
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
const mapFiles = () => {
|
|
27
|
+
const routes = {};
|
|
28
|
+
const dirs = walkdir.sync(path.join(process.cwd(), "routes"))
|
|
29
|
+
.map((s) => s.replace(process.cwd(), "")
|
|
30
|
+
.replace("/routes", "")
|
|
31
|
+
// .replaceAll("[", ":")
|
|
32
|
+
// .replaceAll("]", "")
|
|
33
|
+
);
|
|
34
|
+
dirs.filter((p) => p.includes('page.jsx'))
|
|
35
|
+
.map((s) => ({ path: s, route: s.replace("/page.jsx", "") }))
|
|
36
|
+
.forEach((page) => {
|
|
37
|
+
const key = page.route || "/";
|
|
38
|
+
routes[key] = { key: key, page: page.path };
|
|
39
|
+
});
|
|
40
|
+
dirs.filter((p) => p.includes('api.js'))
|
|
41
|
+
.map((s) => s.replace(process.cwd(), ""))
|
|
42
|
+
.map((s) => ({ path: s, route: s.replace("/api.js", "") }))
|
|
43
|
+
.forEach((api) => {
|
|
44
|
+
const key = api.route || "/";
|
|
45
|
+
routes[key] = routes[key] || { key };
|
|
46
|
+
routes[key].api = api.path;
|
|
47
|
+
});
|
|
48
|
+
walkdir.sync(path.join(process.cwd(), "static"))
|
|
49
|
+
.map((s) => s.replace(process.cwd(), "").replace("/static", ""))
|
|
50
|
+
.forEach((route) => {
|
|
51
|
+
routes[route] = { key: route, file: route }
|
|
52
|
+
});
|
|
53
|
+
return routes;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const radixRouter = createRouter({
|
|
57
|
+
strictTrailingSlash: true,
|
|
58
|
+
routes: mapFiles(),
|
|
59
|
+
});
|
|
60
|
+
console.log(radixRouter.lookup('/'));
|
|
61
|
+
console.log(radixRouter.lookup('/about'));
|
|
62
|
+
console.log(radixRouter.lookup('/todos'));
|
|
63
|
+
console.log(radixRouter.lookup('/todos/123'));
|
|
64
|
+
console.log(radixRouter.lookup('/robots.txt'));
|
|
65
|
+
|
|
25
|
-
const renderApi = async (
|
|
66
|
+
const renderApi = async (filePath, req) => {
|
|
26
67
|
const routeImport = await import(route.filePath);
|
|
27
68
|
console.log('routeImport', routeImport);
|
|
28
69
|
}
|
|
@@ -57,6 +98,7 @@ const renderPage = async (filePath, url, params) => {
|
|
|
57
98
|
{
|
|
58
99
|
"imports": {
|
|
59
100
|
...imports,
|
|
101
|
+
"radix3": `https://esm.sh/radix3`,
|
|
60
102
|
"react-dom/client": `https://esm.sh/react-dom@18.2.0/client${devTag}`,
|
|
61
103
|
"react/jsx-dev-runtime": `https://esm.sh/react@18.2.0/jsx-dev-runtime${devTag}`,
|
|
62
104
|
"muffinjs": "https://esm.sh/muffinjs",
|
|
@@ -93,27 +135,57 @@ const renderPage = async (filePath, url, params) => {
|
|
|
93
135
|
}
|
|
94
136
|
|
|
95
137
|
const renderCss = async (url) => {
|
|
138
|
+
try {
|
|
96
|
-
|
|
139
|
+
const cssText = await Bun.file(path.join(process.cwd(), url.pathname)).text();
|
|
97
|
-
|
|
140
|
+
const result = await postcss([
|
|
98
|
-
|
|
141
|
+
autoprefixer(),
|
|
99
|
-
|
|
142
|
+
postcssCustomMedia(),
|
|
100
|
-
|
|
143
|
+
// postcssNormalize({ browsers: 'last 2 versions' }),
|
|
101
|
-
|
|
144
|
+
postcssNesting,
|
|
102
|
-
|
|
145
|
+
]).process(cssText, { from: url.pathname, to: url.pathname });
|
|
103
|
-
|
|
146
|
+
return new Response(result.css, {
|
|
104
|
-
|
|
147
|
+
headers: { 'Content-Type': 'text/css' },
|
|
105
|
-
|
|
148
|
+
status: 200,
|
|
106
|
-
|
|
149
|
+
});
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return new Response(`Not Found`, {
|
|
152
|
+
headers: { 'Content-Type': 'text/html' },
|
|
153
|
+
status: 404,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
107
156
|
}
|
|
108
157
|
|
|
109
158
|
const renderJs = async (url) => {
|
|
159
|
+
try {
|
|
110
|
-
|
|
160
|
+
const jsText = await Bun.file(path.join(process.cwd(), url.pathname)).text();
|
|
111
|
-
|
|
161
|
+
const result = await transpiler.transform(jsText);
|
|
112
|
-
|
|
162
|
+
const js = result.replaceAll(`import"./page.css";`, "");
|
|
113
|
-
|
|
163
|
+
return new Response(js, {
|
|
114
|
-
|
|
164
|
+
headers: { 'Content-Type': 'application/javascript' },
|
|
115
|
-
|
|
165
|
+
status: 200,
|
|
116
|
-
|
|
166
|
+
});
|
|
167
|
+
} catch (err) {
|
|
168
|
+
return new Response(`Not Found`, {
|
|
169
|
+
headers: { 'Content-Type': 'text/html' },
|
|
170
|
+
status: 404,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const sendFile = async (file) => {
|
|
176
|
+
try {
|
|
177
|
+
const contentType = mimeTypes.lookup(file) || "application/octet-stream";
|
|
178
|
+
const stream = await Bun.file(path.join(process.cwd(), file)).stream();
|
|
179
|
+
return new Response(stream, {
|
|
180
|
+
headers: { 'Content-Type': contentType },
|
|
181
|
+
status: 200,
|
|
182
|
+
});
|
|
183
|
+
} catch (err) {
|
|
184
|
+
return new Response(`Not Found`, {
|
|
185
|
+
headers: { 'Content-Type': 'text/html' },
|
|
186
|
+
status: 404,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
117
189
|
}
|
|
118
190
|
|
|
119
191
|
export default {
|
|
@@ -126,14 +198,17 @@ export default {
|
|
|
126
198
|
if (url.pathname.endsWith(".js") || url.pathname.endsWith(".jsx")) {
|
|
127
199
|
return renderJs(url);
|
|
128
200
|
}
|
|
129
|
-
|
|
201
|
+
const match = radixRouter.lookup(url.pathname);
|
|
202
|
+
if (match) {
|
|
203
|
+
if (match.file) {
|
|
130
|
-
|
|
204
|
+
return sendFile(`/static${match.file}`);
|
|
131
|
-
headers: { 'Content-Type': 'text/html' },
|
|
132
|
-
status: 404,
|
|
133
|
-
});
|
|
134
|
-
|
|
205
|
+
}
|
|
135
|
-
|
|
206
|
+
if (match.page) {
|
|
136
|
-
|
|
207
|
+
return renderPage(`/routes${match.page}`, url, match.params);
|
|
208
|
+
}
|
|
209
|
+
if (match.api) {
|
|
210
|
+
return renderApi(`/routes${match.api}`, req);
|
|
211
|
+
}
|
|
137
212
|
}
|
|
138
213
|
return new Response(`Not Found`, {
|
|
139
214
|
headers: { 'Content-Type': 'text/html' },
|
packages/muffinjs/package.json
CHANGED
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
"postcss": "^8.4.21",
|
|
16
16
|
"postcss-custom-media": "^9.1.2",
|
|
17
17
|
"postcss-nesting": "^11.2.1",
|
|
18
|
-
"postcss-normalize": "^10.0.1"
|
|
18
|
+
"postcss-normalize": "^10.0.1",
|
|
19
|
+
"radix3": "^1.0.0",
|
|
20
|
+
"walkdir": "0.4.1",
|
|
21
|
+
"mime-types": "2.1.35"
|
|
19
22
|
},
|
|
20
23
|
"bin": {
|
|
21
24
|
"muffin": "./bin/muffin"
|
packages/muffinjs/router.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// import { signal, useSignal } from "@preact/signals-react";
|
|
2
|
+
import { createRouter } from 'radix3';
|
|
2
3
|
import { atom, useAtom } from "./atom.js";
|
|
3
4
|
|
|
4
5
|
export const routerAtom = atom({
|