~repos /edge-city

#react#js#ssr

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 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
- export default ({ id, text }) => {
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
- export default () => {
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
- // import "./index.css";
3
+ import "./page.css";
5
4
 
6
- export default () => {
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
- export default () => {
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("running in", process.cwd());
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]}?dev`;
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="/routes/index/page.css" />
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": "https://esm.sh/react-dom@18.2.0/client?dev",
60
+ "react-dom/client": `https://esm.sh/react-dom@18.2.0/client${devTag}`,
49
- "react/jsx-dev-runtime": "https://esm.sh/react@18.2.0/jsx-dev-runtime?dev",
61
+ "react/jsx-dev-runtime": `https://esm.sh/react@18.2.0/jsx-dev-runtime${devTag}`,
50
- "@/atom.js": "/assets/js/src/atom.js",
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 "@/router.js";
74
+ // import { routerAtom } from "muffinjs/router.js";
65
- import Page from "@/routes/index/page.jsx";
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
- 'Content-Type': 'text/html',
90
+ headers: { 'Content-Type': 'text/html' },
82
- },
83
91
  status: 200,
84
92
  });
85
93
  }
86
94
 
87
- const renderJs = async (url) => {
95
+ const renderCss = async (url) => {
88
- const localFile = url.pathname.replace("/assets/js/", "").replace("src/", "");
96
+ const cssText = readFileSync(path.join(process.cwd(), url.pathname), "utf-8");
89
- const src = await Bun.file(localFile).text();
90
- const result = await transpiler.transform(src);
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
- 'Content-Type': 'application/javascript',
104
+ headers: { 'Content-Type': 'text/css' },
94
- },
95
105
  status: 200,
96
106
  });
97
107
  }
98
108
 
99
- const sendFile = async (url) => {
109
+ const renderJs = async (url) => {
100
- const localFile = url.pathname.replace("/assets/js/", "").replace("src/", "");
101
- const result = await Bun.file(path.join(".cache", localFile)).text();
110
+ const jsText = readFileSync(path.join(process.cwd(), url.pathname), "utf-8");
102
- let contentType = "application/javascript";
111
+ const result = await transpiler.transform(jsText);
103
- if (url.pathname.endsWith(".css")) {
112
+ const js = result.replaceAll(`import"./page.css";`, "");
104
- contentType = 'text/css';
105
- }
106
- return new Response(result, {
113
+ return new Response(js, {
107
- headers: {
108
- 'Content-Type': contentType,
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.includes("/components/") || url.pathname.includes("/containers/") || url.pathname.includes("/routes/")) {
123
+ if (url.pathname.endsWith(".css")) {
119
- return sendFile(url);
124
+ return renderCss(url);
120
125
  }
121
- if (url.pathname.includes("/assets/js")) {
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(process.cwd() + "/routes/index/page.jsx", url, {});
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
- });