~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
827ac811
—
Peter John 2 years ago
get example working
- bun.toml +3 -0
- package.json +1 -4
- packages/example/components/Todo.jsx +4 -2
- packages/example/containers/TodoList.jsx +4 -2
- packages/example/jsconfig.json +3 -1
- packages/example/package.json +2 -1
- packages/example/routes/index/page.jsx +10 -10
- packages/example/routes/todos/page.jsx +5 -2
- packages/muffinjs/bin/{muffin.js → muffin} +0 -0
- packages/muffinjs/index.js +44 -39
- packages/muffinjs/jsxPlugin.js +0 -50
bun.toml
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
[loaders]
|
|
2
|
+
".jsx" = "js"
|
|
3
|
+
".css" = "css"
|
package.json
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-project",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"workspaces": ["packages/example", "packages/muffinjs"]
|
|
4
|
+
"workspaces": ["packages/example", "packages/muffinjs"]
|
|
5
|
-
"scripts": {
|
|
6
|
-
"start": "muffin"
|
|
7
|
-
}
|
|
8
5
|
}
|
packages/example/components/Todo.jsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const Todo = ({ id, text }) => {
|
|
2
2
|
return (
|
|
3
3
|
<div id={id}>
|
|
4
4
|
<p>
|
|
@@ -6,4 +6,6 @@ export default ({ id, text }) => {
|
|
|
6
6
|
</p>
|
|
7
7
|
</div>
|
|
8
8
|
)
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default Todo;
|
packages/example/containers/TodoList.jsx
CHANGED
|
@@ -5,7 +5,7 @@ const todos = [
|
|
|
5
5
|
{ id: '2', text: "AEQ" }
|
|
6
6
|
];
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const TodoList = () => {
|
|
9
9
|
return (
|
|
10
10
|
<div className="todo-list">
|
|
11
11
|
<h1>Todos</h1>
|
|
@@ -18,4 +18,6 @@ export default () => {
|
|
|
18
18
|
</ul>
|
|
19
19
|
</div>
|
|
20
20
|
);
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default TodoList;
|
packages/example/jsconfig.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"paths": {
|
|
4
|
-
"@/*": [
|
|
4
|
+
"@/*": [
|
|
5
|
+
"./*"
|
|
6
|
+
]
|
|
5
7
|
}
|
|
6
8
|
}
|
|
7
9
|
}
|
packages/example/package.json
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
"name": "quickstart",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"dev": "bun --hot main.js"
|
|
5
|
+
"dev": "bun --hot main.js",
|
|
6
|
+
"start": "NODE_ENV=production bun main.js"
|
|
6
7
|
},
|
|
7
8
|
"dependencies": {
|
|
8
9
|
"muffinjs": "0.1.0",
|
packages/example/routes/index/page.jsx
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
|
-
import { useRouter } from "muffinjs/router.js";
|
|
2
|
+
// import { useRouter } from "muffinjs/router.js";
|
|
3
|
-
import TodoList from "@/containers/TodoList.jsx";
|
|
4
|
-
|
|
3
|
+
import "./page.css";
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const HomePage = () => {
|
|
7
|
-
const router = useRouter();
|
|
6
|
+
// const router = useRouter();
|
|
8
7
|
const [count, setCount] = useState(5);
|
|
9
|
-
// const { data: todos, isLoading, isRevalidating } = usePromise("/todos");
|
|
10
8
|
return (
|
|
11
9
|
<div className="home-page">
|
|
12
10
|
<div>
|
|
11
|
+
<h1>Home Page</h1>
|
|
13
|
-
<p>
|
|
12
|
+
{/* <p>
|
|
14
13
|
Hello from server path 123: {router.pathname}
|
|
15
|
-
</p>
|
|
14
|
+
</p> */}
|
|
16
|
-
<TodoList />
|
|
17
15
|
<div>
|
|
18
16
|
<button onClick={() => setCount(count - 1)}>-</button>
|
|
19
17
|
<span className="count">
|
|
@@ -24,4 +22,6 @@ export default () => {
|
|
|
24
22
|
</div>
|
|
25
23
|
</div>
|
|
26
24
|
)
|
|
27
|
-
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default HomePage;
|
packages/example/routes/todos/page.jsx
CHANGED
|
@@ -3,14 +3,17 @@ import { useRouter } from "muffinjs/router.js";
|
|
|
3
3
|
import TodoList from "@/containers/TodoList.jsx";
|
|
4
4
|
// import "./index.css";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
const TodosPage = () => {
|
|
7
7
|
const router = useRouter();
|
|
8
8
|
const { data: todos, isRevalidating } = usePromise("/todos");
|
|
9
9
|
return (
|
|
10
10
|
<div className="todos-page">
|
|
11
|
+
<h1>Todos</h1>
|
|
11
12
|
<Suspense>
|
|
12
13
|
<TodoList todos={todos} />
|
|
13
14
|
</Suspense>
|
|
14
15
|
</div>
|
|
15
16
|
)
|
|
16
|
-
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default TodosPage;
|
packages/muffinjs/bin/{muffin.js → muffin}
RENAMED
|
File without changes
|
packages/muffinjs/index.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import './jsxPlugin.js';
|
|
1
|
+
// import './jsxPlugin.js';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { rmSync, readFileSync } from "fs";
|
|
5
|
+
import postcss from "postcss"
|
|
6
|
+
import autoprefixer from "autoprefixer";
|
|
7
|
+
import postcssCustomMedia from "postcss-custom-media";
|
|
8
|
+
// import postcssNormalize from 'postcss-normalize';
|
|
9
|
+
import postcssNesting from "postcss-nesting";
|
|
4
10
|
import { renderToReadableStream } from 'react-dom/server';
|
|
5
11
|
import { routerAtom } from './router.js';
|
|
6
12
|
|
|
13
|
+
console.log("running in folder:", path.basename(process.cwd()), "env:", process.env.NODE_ENV);
|
|
7
|
-
console.log("
|
|
14
|
+
console.log("deleting cache");
|
|
15
|
+
rmSync(path.join(process.cwd(), ".cache"), { force: true, recursive: true })
|
|
16
|
+
|
|
17
|
+
const isProd = process.env.NODE_ENV === "production";
|
|
8
18
|
|
|
9
19
|
const transpiler = new Bun.Transpiler({
|
|
10
20
|
loader: "jsx",
|
|
@@ -28,28 +38,28 @@ const renderPage = async (filePath, url, params) => {
|
|
|
28
38
|
pathname: url.pathname,
|
|
29
39
|
}
|
|
30
40
|
routerAtom.update(() => initialRouteValue);
|
|
31
|
-
const routeImport = await import(filePath);
|
|
41
|
+
const routeImport = await import(path.join(process.cwd(), filePath));
|
|
32
42
|
const packageJson = await import(path.join(process.cwd(), "package.json"));
|
|
33
43
|
const dependencies = packageJson.default.dependencies;
|
|
44
|
+
const devTag = !isProd ? "?dev" : "";
|
|
34
45
|
const imports = Object.keys(dependencies).reduce((acc, dep) => {
|
|
35
|
-
acc[dep] = `https://esm.sh/${dep}@${dependencies[dep]}
|
|
46
|
+
acc[dep] = `https://esm.sh/${dep}@${dependencies[dep]}${devTag}`;
|
|
36
47
|
return acc;
|
|
37
48
|
}, {})
|
|
38
49
|
const Page = routeImport.default;
|
|
50
|
+
console.log(filePath)
|
|
39
51
|
const stream = await renderToReadableStream(
|
|
40
52
|
<html lang="en">
|
|
41
53
|
<head>
|
|
42
|
-
<link rel="stylesheet" href=
|
|
54
|
+
<link rel="stylesheet" href={`${filePath.replace("jsx", "css")}`} />
|
|
43
55
|
<script type="importmap" dangerouslySetInnerHTML={{
|
|
44
56
|
__html: JSON.stringify(
|
|
45
57
|
{
|
|
46
58
|
"imports": {
|
|
47
59
|
...imports,
|
|
48
|
-
"react-dom/client":
|
|
60
|
+
"react-dom/client": `https://esm.sh/react-dom@18.2.0/client${devTag}`,
|
|
49
|
-
"react/jsx-dev-runtime":
|
|
61
|
+
"react/jsx-dev-runtime": `https://esm.sh/react@18.2.0/jsx-dev-runtime${devTag}`,
|
|
50
|
-
"
|
|
62
|
+
"muffinjs": "https://esm.sh/muffinjs",
|
|
51
|
-
"@/router.js": "/assets/js/src/router.js",
|
|
52
|
-
"@/routes/index/page.jsx": "/routes/index/page.js",
|
|
53
63
|
"@/components/Todo.jsx": "/components/Todo.js",
|
|
54
64
|
"@/containers/TodoList.jsx": "/containers/TodoList.js"
|
|
55
65
|
}
|
|
@@ -61,10 +71,10 @@ const renderPage = async (filePath, url, params) => {
|
|
|
61
71
|
__html: `
|
|
62
72
|
import React from 'react';
|
|
63
73
|
import { hydrateRoot } from 'react-dom/client';
|
|
64
|
-
import { routerAtom } from "
|
|
74
|
+
// import { routerAtom } from "muffinjs/router.js";
|
|
65
|
-
import Page from "
|
|
75
|
+
import Page from "${filePath}";
|
|
66
76
|
|
|
67
|
-
routerAtom.update(() => (${JSON.stringify(initialRouteValue)}));
|
|
77
|
+
// routerAtom.update(() => (${JSON.stringify(initialRouteValue)}));
|
|
68
78
|
|
|
69
79
|
hydrateRoot(document.getElementById("root"), React.createElement(Page, {}, undefined, false, undefined, this));
|
|
70
80
|
`}}></script>
|
|
@@ -77,36 +87,31 @@ const renderPage = async (filePath, url, params) => {
|
|
|
77
87
|
</html >
|
|
78
88
|
);
|
|
79
89
|
return new Response(stream, {
|
|
80
|
-
headers: {
|
|
81
|
-
|
|
90
|
+
headers: { 'Content-Type': 'text/html' },
|
|
82
|
-
},
|
|
83
91
|
status: 200,
|
|
84
92
|
});
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
const
|
|
95
|
+
const renderCss = async (url) => {
|
|
88
|
-
const
|
|
96
|
+
const cssText = readFileSync(path.join(process.cwd(), url.pathname), "utf-8");
|
|
89
|
-
const src = await Bun.file(localFile).text();
|
|
90
|
-
const result = await
|
|
97
|
+
const result = await postcss([
|
|
98
|
+
autoprefixer(),
|
|
99
|
+
postcssCustomMedia(),
|
|
100
|
+
// postcssNormalize({ browsers: 'last 2 versions' }),
|
|
101
|
+
postcssNesting,
|
|
102
|
+
]).process(cssText);
|
|
91
|
-
return new Response(result, {
|
|
103
|
+
return new Response(result.css, {
|
|
92
|
-
headers: {
|
|
93
|
-
|
|
104
|
+
headers: { 'Content-Type': 'text/css' },
|
|
94
|
-
},
|
|
95
105
|
status: 200,
|
|
96
106
|
});
|
|
97
107
|
}
|
|
98
108
|
|
|
99
|
-
const
|
|
109
|
+
const renderJs = async (url) => {
|
|
100
|
-
const localFile = url.pathname.replace("/assets/js/", "").replace("src/", "");
|
|
101
|
-
const
|
|
110
|
+
const jsText = readFileSync(path.join(process.cwd(), url.pathname), "utf-8");
|
|
102
|
-
|
|
111
|
+
const result = await transpiler.transform(jsText);
|
|
103
|
-
|
|
112
|
+
const js = result.replaceAll(`import"./page.css";`, "");
|
|
104
|
-
contentType = 'text/css';
|
|
105
|
-
}
|
|
106
|
-
return new Response(
|
|
113
|
+
return new Response(js, {
|
|
107
|
-
headers: {
|
|
108
|
-
|
|
114
|
+
headers: { 'Content-Type': 'application/javascript' },
|
|
109
|
-
},
|
|
110
115
|
status: 200,
|
|
111
116
|
});
|
|
112
117
|
}
|
|
@@ -115,10 +120,10 @@ export default {
|
|
|
115
120
|
port: 3000,
|
|
116
121
|
async fetch(req) {
|
|
117
122
|
const url = new URL(req.url);
|
|
118
|
-
if (url.pathname.
|
|
123
|
+
if (url.pathname.endsWith(".css")) {
|
|
119
|
-
return
|
|
124
|
+
return renderCss(url);
|
|
120
125
|
}
|
|
121
|
-
if (url.pathname.
|
|
126
|
+
if (url.pathname.endsWith(".js") || url.pathname.endsWith(".jsx")) {
|
|
122
127
|
return renderJs(url);
|
|
123
128
|
}
|
|
124
129
|
if (url.pathname.includes("/favicon")) {
|
|
@@ -128,7 +133,7 @@ export default {
|
|
|
128
133
|
});
|
|
129
134
|
}
|
|
130
135
|
if (url.pathname.includes("/")) {
|
|
131
|
-
return renderPage(
|
|
136
|
+
return renderPage("/routes/index/page.jsx", url, {});
|
|
132
137
|
}
|
|
133
138
|
return new Response(`Not Found`, {
|
|
134
139
|
headers: { 'Content-Type': 'text/html' },
|
packages/muffinjs/jsxPlugin.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { plugin } from "bun";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
4
|
-
import postcss from "postcss"
|
|
5
|
-
import autoprefixer from "autoprefixer";
|
|
6
|
-
import postcssCustomMedia from "postcss-custom-media";
|
|
7
|
-
// import postcssNormalize from 'postcss-normalize';
|
|
8
|
-
import postcssNesting from "postcss-nesting";
|
|
9
|
-
|
|
10
|
-
const transpiler = new Bun.Transpiler({
|
|
11
|
-
loader: "jsx",
|
|
12
|
-
autoImportJSX: true,
|
|
13
|
-
jsxOptimizationInline: true,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
plugin({
|
|
17
|
-
name: "JsxPlugin",
|
|
18
|
-
async setup(build) {
|
|
19
|
-
build.onLoad({ filter: /\.jsx$/ }, async (args) => {
|
|
20
|
-
const folder = path.dirname(args.path).replace(process.cwd(), "");
|
|
21
|
-
const filename = path.basename(args.path).replace("jsx", "js");
|
|
22
|
-
const cssFile = args.path.replace("jsx", "css");
|
|
23
|
-
const outputFolder = path.join(process.cwd(), ".cache", folder);
|
|
24
|
-
const outputFile = path.join(outputFolder, filename);
|
|
25
|
-
const jsxCode = readFileSync(args.path, "utf8");
|
|
26
|
-
const code = await transpiler.transform(jsxCode);
|
|
27
|
-
if (!existsSync(outputFolder)) {
|
|
28
|
-
mkdirSync(outputFolder, { recursive: true });
|
|
29
|
-
}
|
|
30
|
-
writeFileSync(outputFile, code);
|
|
31
|
-
if (existsSync(cssFile)) {
|
|
32
|
-
const cssText = readFileSync(cssFile, "utf-8");
|
|
33
|
-
const result = postcss([
|
|
34
|
-
autoprefixer(),
|
|
35
|
-
postcssCustomMedia(),
|
|
36
|
-
// postcssNormalize({ browsers: 'last 2 versions' }),
|
|
37
|
-
postcssNesting,
|
|
38
|
-
]).process(cssText);
|
|
39
|
-
writeFileSync(outputFile.replace("js", "css"), result.css);
|
|
40
|
-
}
|
|
41
|
-
const src = await import(outputFile);
|
|
42
|
-
return {
|
|
43
|
-
exports: {
|
|
44
|
-
default: src.default,
|
|
45
|
-
},
|
|
46
|
-
loader: "object",
|
|
47
|
-
};
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
});
|