edge-city

#react#js#ssr

git clone https://git.pyrossh.dev/edge-city

edge-city is a next level meta-framework for react that runs only on edge runtimes


72a995bPeter John 2023-03-08T23:03:13+05:30
initial commit
.gitignore ADDED
@@ -0,0 +1,170 @@
1
+ # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2
+
3
+ # Logs
4
+
5
+ logs
6
+ _.log
7
+ npm-debug.log_
8
+ yarn-debug.log*
9
+ yarn-error.log*
10
+ lerna-debug.log*
11
+ .pnpm-debug.log*
12
+
13
+ # Diagnostic reports (https://nodejs.org/api/report.html)
14
+
15
+ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
16
+
17
+ # Runtime data
18
+
19
+ pids
20
+ _.pid
21
+ _.seed
22
+ \*.pid.lock
23
+
24
+ # Directory for instrumented libs generated by jscoverage/JSCover
25
+
26
+ lib-cov
27
+
28
+ # Coverage directory used by tools like istanbul
29
+
30
+ coverage
31
+ \*.lcov
32
+
33
+ # nyc test coverage
34
+
35
+ .nyc_output
36
+
37
+ # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
38
+
39
+ .grunt
40
+
41
+ # Bower dependency directory (https://bower.io/)
42
+
43
+ bower_components
44
+
45
+ # node-waf configuration
46
+
47
+ .lock-wscript
48
+
49
+ # Compiled binary addons (https://nodejs.org/api/addons.html)
50
+
51
+ build/Release
52
+
53
+ # Dependency directories
54
+
55
+ node_modules/
56
+ jspm_packages/
57
+
58
+ # Snowpack dependency directory (https://snowpack.dev/)
59
+
60
+ web_modules/
61
+
62
+ # TypeScript cache
63
+
64
+ \*.tsbuildinfo
65
+
66
+ # Optional npm cache directory
67
+
68
+ .npm
69
+
70
+ # Optional eslint cache
71
+
72
+ .eslintcache
73
+
74
+ # Optional stylelint cache
75
+
76
+ .stylelintcache
77
+
78
+ # Microbundle cache
79
+
80
+ .rpt2_cache/
81
+ .rts2_cache_cjs/
82
+ .rts2_cache_es/
83
+ .rts2_cache_umd/
84
+
85
+ # Optional REPL history
86
+
87
+ .node_repl_history
88
+
89
+ # Output of 'npm pack'
90
+
91
+ \*.tgz
92
+
93
+ # Yarn Integrity file
94
+
95
+ .yarn-integrity
96
+
97
+ # dotenv environment variable files
98
+
99
+ .env
100
+ .env.development.local
101
+ .env.test.local
102
+ .env.production.local
103
+ .env.local
104
+
105
+ # parcel-bundler cache (https://parceljs.org/)
106
+
107
+ .cache
108
+ .parcel-cache
109
+
110
+ # Next.js build output
111
+
112
+ .next
113
+ out
114
+
115
+ # Nuxt.js build / generate output
116
+
117
+ .nuxt
118
+ dist
119
+
120
+ # Gatsby files
121
+
122
+ .cache/
123
+
124
+ # Comment in the public line in if your project uses Gatsby and not Next.js
125
+
126
+ # https://nextjs.org/blog/next-9-1#public-directory-support
127
+
128
+ # public
129
+
130
+ # vuepress build output
131
+
132
+ .vuepress/dist
133
+
134
+ # vuepress v2.x temp and cache directory
135
+
136
+ .temp
137
+ .cache
138
+
139
+ # Docusaurus cache and generated files
140
+
141
+ .docusaurus
142
+
143
+ # Serverless directories
144
+
145
+ .serverless/
146
+
147
+ # FuseBox cache
148
+
149
+ .fusebox/
150
+
151
+ # DynamoDB Local files
152
+
153
+ .dynamodb/
154
+
155
+ # TernJS port file
156
+
157
+ .tern-port
158
+
159
+ # Stores VSCode versions used for testing VSCode extensions
160
+
161
+ .vscode-test
162
+
163
+ # yarn v2
164
+
165
+ .yarn/cache
166
+ .yarn/unplugged
167
+ .yarn/build-state.yml
168
+ .yarn/install-state.gz
169
+ .pnp.\*
170
+ dist
README.md ADDED
@@ -0,0 +1,15 @@
1
+ # quickstart
2
+
3
+ To install dependencies:
4
+
5
+ ```bash
6
+ bun install
7
+ ```
8
+
9
+ To run:
10
+
11
+ ```bash
12
+ bun run index.js
13
+ ```
14
+
15
+ This project was created using `bun init` in bun v0.5.7. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
astroPlugin.js ADDED
@@ -0,0 +1,40 @@
1
+ import { plugin } from "bun";
2
+
3
+ const transpiler = new Bun.Transpiler({
4
+ loader: "jsx",
5
+ autoImportJSX: true,
6
+ jsxOptimizationInline: true,
7
+ });
8
+
9
+ plugin({
10
+ name: "Astro",
11
+ async setup(build) {
12
+ const { readFileSync, writeFileSync } = await import("fs");
13
+ build.onLoad({ filter: /\.astro$/ }, async (args) => {
14
+ const text = readFileSync(args.path, "utf8");
15
+ const [_, js, html] = text.split("---");
16
+ const imports = js.split("\n").filter((line) => line.startsWith("import")).join("\n");
17
+ const body = js.split("\n").filter((line) => !line.startsWith("import")).join("\n");
18
+ const code = await transpiler.transform(`
19
+ ${imports}
20
+
21
+ export default () => {
22
+ ${body}
23
+
24
+ return (
25
+ ${html}
26
+ );
27
+ }
28
+ `);
29
+ // console.log('code', code);
30
+ writeFileSync("./dist/index.js", code)
31
+ const src = await import("./dist/index.js");
32
+ return {
33
+ exports: {
34
+ default: src.default,
35
+ },
36
+ loader: "object",
37
+ };
38
+ });
39
+ },
40
+ });
bun.lockb ADDED
Binary file
index.css ADDED
@@ -0,0 +1,3 @@
1
+ .ccs {
2
+ display: flex;
3
+ }
index.js ADDED
@@ -0,0 +1,131 @@
1
+ import './astroPlugin.js';
2
+ import { renderToString, renderToReadableStream } from 'react-dom/server';
3
+ import { RouterProvider } from './router.js';
4
+
5
+ const router = new Bun.FileSystemRouter({
6
+ style: "nextjs",
7
+ dir: "./routes",
8
+ origin: "https://mydomain.com",
9
+ assetPrefix: "./public"
10
+ });
11
+
12
+ const transpiler = new Bun.Transpiler({
13
+ loader: "jsx",
14
+ autoImportJSX: true,
15
+ jsxOptimizationInline: true,
16
+ });
17
+
18
+ const renderApi = async (route, req) => {
19
+ const routeImport = await import(route.filePath);
20
+ console.log('routeImport', routeImport);
21
+ }
22
+
23
+ const renderPage = async (filePath, url, params) => {
24
+ const query = {};
25
+ for (const key of url.searchParams.keys()) {
26
+ query[key] = url.searchParams.get(key);
27
+ }
28
+ const initialRouteValue = {
29
+ query: query,
30
+ params: params,
31
+ pathname: url.pathname,
32
+ }
33
+ const routeImport = await import(filePath);
34
+ const Page = routeImport.default;
35
+ const stream = await renderToReadableStream(
36
+ <html lang="en">
37
+ <head>
38
+ {/* {routeImport.head()} */}
39
+ <script id="initial_route_context" type='application/json' dangerouslySetInnerHTML={{
40
+ __html: JSON.stringify(initialRouteValue)
41
+ }} />
42
+ <script type="importmap" dangerouslySetInnerHTML={{
43
+ __html: JSON.stringify(
44
+ {
45
+ "imports": {
46
+ "react": "https://esm.sh/[email protected]?dev",
47
+ "react-dom": "https://esm.sh/[email protected]?dev",
48
+ "react-dom/client": "https://esm.sh/[email protected]/client?dev",
49
+ "react/jsx-dev-runtime": "https://esm.sh/[email protected]/jsx-dev-runtime?dev",
50
+ "@/utils": "/assets/js/src/utils.js",
51
+ }
52
+ }
53
+ )
54
+ }}>
55
+ </script>
56
+ {/* <script id="initial_swr_fallback" dangerouslySetInnerHTML={{ __html: JSON.stringify(value) }} /> */}
57
+ <script type="module" src="/assets/js/routes/index.astro" defer></script>
58
+ </head>
59
+ <body>
60
+ <div id="root">
61
+ <RouterProvider value={initialRouteValue}>
62
+ <Page />
63
+ </RouterProvider>
64
+ </div>
65
+ </body>
66
+ </html >
67
+ );
68
+ return new Response(stream, {
69
+ headers: {
70
+ 'Content-Type': 'text/html',
71
+ },
72
+ status: 200,
73
+ });
74
+ }
75
+
76
+ export default {
77
+ port: 3000,
78
+ async fetch(req) {
79
+ const url = new URL(req.url);
80
+ if (url.pathname.startsWith("/dist/js")) {
81
+ const src = await Bun.file("./dist/index.js").text();
82
+ return new Response(src, {
83
+ headers: {
84
+ 'Content-Type': 'application/javascript',
85
+ },
86
+ status: 200,
87
+ });
88
+ }
89
+ if (url.pathname.startsWith("/assets/js")) {
90
+ const lib = url.pathname.replace("/assets/js/", "");
91
+ const localFile = lib.replace("src/", "");
92
+ let result;
93
+ if (localFile.includes("routes")) {
94
+ result = await transpiler.transform(`
95
+ import { hydrateRoot } from 'react-dom/client';
96
+ import {RouterProvider} from "@/utils";
97
+ import Page from "/dist/js/index.js";
98
+
99
+ const initialRouteValue = JSON.parse(document.getElementById('initial_route_context').textContent);
100
+ const root = hydrateRoot(document.getElementById('root'), (
101
+ <RouterProvider value={initialRouteValue}>
102
+ <Page />
103
+ </RouterProvider>
104
+ ));
105
+ `);
106
+ } else {
107
+ const src = await Bun.file(localFile).text();
108
+ result = await transpiler.transform(src);
109
+ }
110
+ return new Response(result, {
111
+ headers: {
112
+ 'Content-Type': 'application/javascript',
113
+ },
114
+ status: 200,
115
+ });
116
+ }
117
+ if (url.pathname.startsWith("/api")) {
118
+ console.log('api', url.pathname);
119
+ // return renderApi(route, req);
120
+ }
121
+ return renderPage("./routes/index.astro", url, {});
122
+ // const route = router.match(url.pathname);
123
+ // if (route) {
124
+ // return renderPage(route, url);
125
+ // }
126
+ return new Response(`Not Found`, {
127
+ headers: { 'Content-Type': 'text/html' },
128
+ status: 404,
129
+ });
130
+ },
131
+ };
index.test.js ADDED
@@ -0,0 +1,11 @@
1
+ import { expect, test } from "bun:test";
2
+
3
+ describe("arithmetic", () => {
4
+ test("2 + 2", () => {
5
+ expect(2 + 2).toBe(4);
6
+ });
7
+
8
+ test("2 * 2", () => {
9
+ expect(2 * 2).toBe(4);
10
+ });
11
+ });
jsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "ESNext"
5
+ ],
6
+ "module": "esnext",
7
+ "target": "esnext",
8
+ "moduleResolution": "nodenext",
9
+ "strict": true,
10
+ "downlevelIteration": true,
11
+ "skipLibCheck": true,
12
+ "jsx": "react",
13
+ "jsxFactory": "React.createElement",
14
+ "jsxImportSource": "react",
15
+ "allowSyntheticDefaultImports": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "allowJs": true,
18
+ "types": [
19
+ "bun-types" // add Bun global
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ }
25
+ }
package-lock.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "quickstart",
3
+ "lockfileVersion": 2,
4
+ "requires": true,
5
+ "packages": {
6
+ "": {
7
+ "name": "quickstart",
8
+ "dependencies": {
9
+ "react": "18.2.0",
10
+ "react-dom": "^18.2.0",
11
+ "zod": "^3.21.0"
12
+ },
13
+ "devDependencies": {
14
+ "bun-types": "^0.5.0"
15
+ }
16
+ },
17
+ "node_modules/bun-types": {
18
+ "version": "0.5.7",
19
+ "dev": true
20
+ },
21
+ "node_modules/js-tokens": {
22
+ "version": "4.0.0",
23
+ "license": "MIT"
24
+ },
25
+ "node_modules/loose-envify": {
26
+ "version": "1.4.0",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "js-tokens": "^3.0.0 || ^4.0.0"
30
+ },
31
+ "bin": {
32
+ "loose-envify": "cli.js"
33
+ }
34
+ },
35
+ "node_modules/react": {
36
+ "version": "18.2.0",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "loose-envify": "^1.1.0"
40
+ },
41
+ "engines": {
42
+ "node": ">=0.10.0"
43
+ }
44
+ },
45
+ "node_modules/react-dom": {
46
+ "version": "18.2.0",
47
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
48
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
49
+ "dependencies": {
50
+ "loose-envify": "^1.1.0",
51
+ "scheduler": "^0.23.0"
52
+ },
53
+ "peerDependencies": {
54
+ "react": "^18.2.0"
55
+ }
56
+ },
57
+ "node_modules/scheduler": {
58
+ "version": "0.23.0",
59
+ "license": "MIT",
60
+ "dependencies": {
61
+ "loose-envify": "^1.1.0"
62
+ }
63
+ },
64
+ "node_modules/zod": {
65
+ "version": "3.21.0",
66
+ "license": "MIT",
67
+ "funding": {
68
+ "url": "https://github.com/sponsors/colinhacks"
69
+ }
70
+ }
71
+ },
72
+ "dependencies": {
73
+ "bun-types": {
74
+ "version": "0.5.7",
75
+ "dev": true
76
+ },
77
+ "js-tokens": {
78
+ "version": "4.0.0"
79
+ },
80
+ "loose-envify": {
81
+ "version": "1.4.0",
82
+ "requires": {
83
+ "js-tokens": "^3.0.0 || ^4.0.0"
84
+ }
85
+ },
86
+ "react": {
87
+ "version": "18.2.0",
88
+ "requires": {
89
+ "loose-envify": "^1.1.0"
90
+ }
91
+ },
92
+ "react-dom": {
93
+ "version": "18.2.0",
94
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
95
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
96
+ "requires": {
97
+ "loose-envify": "^1.1.0",
98
+ "scheduler": "^0.23.0"
99
+ }
100
+ },
101
+ "scheduler": {
102
+ "version": "0.23.0",
103
+ "requires": {
104
+ "loose-envify": "^1.1.0"
105
+ }
106
+ },
107
+ "zod": {
108
+ "version": "3.21.0"
109
+ }
110
+ }
111
+ }
package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "quickstart",
3
+ "module": "index.js",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "bun --hot index.js",
7
+ "start": "bun index.js"
8
+ },
9
+ "devDependencies": {
10
+ "bun-types": "^0.5.0"
11
+ },
12
+ "dependencies": {
13
+ "react": "18.2.0",
14
+ "react-dom": "^18.2.0",
15
+ "zod": "^3.21.0"
16
+ }
17
+ }
public/favicon.ico ADDED
Binary file
public/index.html ADDED
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <link rel="icon" href="favicon.ico" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta name="theme-color" content="#000000" />
8
+ <meta
9
+ name="description"
10
+ content="Web site created using create-react-app"
11
+ />
12
+ <link rel="apple-touch-icon" href="/logo192.png" />
13
+ <!--
14
+ manifest.json provides metadata used when your web app is installed on a
15
+ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16
+ -->
17
+ <link rel="manifest" href="/manifest.json" />
18
+ <!--
19
+ Notice the use of %PUBLIC_URL% in the tags above.
20
+ It will be replaced with the URL of the `public` folder during the build.
21
+ Only files inside the `public` folder can be referenced from the HTML.
22
+
23
+ Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24
+ work correctly both with client-side routing and a non-root public URL.
25
+ Learn how to configure a non-root public URL by running `npm run build`.
26
+ -->
27
+ <title>React App</title>
28
+ </head>
29
+ <body>
30
+ <noscript>You need to enable JavaScript to run this app.</noscript>
31
+ <div id="root"></div>
32
+ <!--
33
+ This HTML file is a template.
34
+ If you open it directly in the browser, you will see an empty page.
35
+
36
+ You can add webfonts, meta tags, or analytics to this file.
37
+ The build step will place the bundled scripts into the <body> tag.
38
+
39
+ To begin the development, run `npm start` or `yarn start`.
40
+ To create a production bundle, use `npm run build` or `yarn build`.
41
+ -->
42
+ <script src="/src/index.jsx" async type="module"></script>
43
+ </body>
44
+ </html>
public/logo192.png ADDED
Binary file
public/logo512.png ADDED
Binary file
public/manifest.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "short_name": "React App",
3
+ "name": "Create React App Sample",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ },
10
+ {
11
+ "src": "logo192.png",
12
+ "type": "image/png",
13
+ "sizes": "192x192"
14
+ },
15
+ {
16
+ "src": "logo512.png",
17
+ "type": "image/png",
18
+ "sizes": "512x512"
19
+ }
20
+ ],
21
+ "start_url": ".",
22
+ "display": "standalone",
23
+ "theme_color": "#000000",
24
+ "background_color": "#ffffff"
25
+ }
public/robots.txt ADDED
@@ -0,0 +1,3 @@
1
+ # https://www.robotstxt.org/robotstxt.html
2
+ User-agent: *
3
+ Disallow:
router.js ADDED
@@ -0,0 +1,26 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ const Context = createContext(undefined)
4
+
5
+ export const RouterProvider = ({ value, children }) => {
6
+ return <Context.Provider value={value}>{children}</Context.Provider>
7
+ }
8
+
9
+ export const useRouter = () => {
10
+ const state = useContext(Context);
11
+ return {
12
+ query: state.query,
13
+ pathname: state.pathname,
14
+ params: state.params,
15
+ push: () => {
16
+ },
17
+ replace: () => {
18
+ },
19
+ prefetch: () => {
20
+ },
21
+ beforePopState: () => {
22
+ },
23
+ back: () => { },
24
+ reload: () => window.location.reload(),
25
+ }
26
+ }
routes/about/index.jsx ADDED
@@ -0,0 +1,12 @@
1
+ import { page, useRouter } from "@/utils";
2
+
3
+ export default page(() => {
4
+ const router = useRouter();
5
+ return (
6
+ <div>
7
+ <p>
8
+ Hello from server
9
+ </p>
10
+ </div>
11
+ );
12
+ });
routes/api/todos/index.js ADDED
@@ -0,0 +1,5 @@
1
+ import { json } from "@/utils";
2
+
3
+ export default async () => {
4
+ return json([])
5
+ };
routes/index.astro ADDED
@@ -0,0 +1,33 @@
1
+ ---
2
+ import { useState } from 'react';
3
+
4
+ const head = {
5
+ title: "pyros.sh",
6
+ description:
7
+ "Hi there, I'm Peter John, a fullstack dev from Bangalore, India",
8
+ image: "/favicon.png",
9
+ keywords: "pyros.sh,pyrossh",
10
+ };
11
+
12
+ const router = {};
13
+ const [count, setCount] = useState(5);
14
+ ---
15
+
16
+ <div className="home-page">
17
+ <div>
18
+ <p>
19
+ Hello from server path: {router.pathname}
20
+ </p>
21
+ <div>
22
+ <button onClick={() => setCount(count - 1)}>
23
+ -
24
+ </button>
25
+ <div>
26
+ {count}
27
+ </div>
28
+ <button onClick={() => setCount(count + 1)}>
29
+ +
30
+ </button>
31
+ </div>
32
+ </div>
33
+ </div>
utils.js ADDED
@@ -0,0 +1,21 @@
1
+ import React, { createContext, useContext } from 'react';
2
+
3
+ const Context = createContext(undefined)
4
+
5
+ export const RouterProvider = ({ value, children }) => {
6
+ return <Context.Provider value={value}>{children}</Context.Provider>
7
+ }
8
+
9
+ export const json = async (body, status = 200, headers = {}) => {
10
+ return new Response(body, {
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ ...headers,
14
+ },
15
+ status
16
+ });
17
+ }
18
+
19
+ // const ErrorBoundary = () => {
20
+
21
+ // }