edge-city
git clone https://git.pyrossh.dev/edge-city
edge-city is a next level meta-framework for react that runs only on edge runtimes
072de50
— pyrossh
2023-05-23T10:49:01+05:30
use fse
Generated files not shown: pnpm-lock.yaml
- cli.js +25 -25
- example/package.json +1 -1
- example/pages/_404/page.css +0 -1
- example/pages/_404/page.jsx +11 -8
- example/pages/_500/page.jsx +11 -8
- index.js +10 -2
- package.json +1 -0
- pnpm-workspace.yaml +1 -0
cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { hideBin } from 'yargs/helpers'
|
|
|
4
4
|
import esbuild from 'esbuild';
|
|
5
5
|
import resolve from 'esbuild-plugin-resolve';
|
|
6
6
|
import fs from "fs";
|
|
7
|
+
import fse from "fs-extra";
|
|
7
8
|
import path from 'path';
|
|
8
9
|
import walkdir from 'walkdir';
|
|
9
10
|
import postcss from "postcss"
|
|
@@ -14,7 +15,8 @@ import bytes from 'bytes';
|
|
|
14
15
|
import pc from 'picocolors';
|
|
15
16
|
import ms from 'ms';
|
|
16
17
|
|
|
18
|
+
let isProd = false;
|
|
17
|
-
const
|
|
19
|
+
const inputStaticDir = path.join(process.cwd(), "static");
|
|
18
20
|
const buildDir = path.join(process.cwd(), "build");
|
|
19
21
|
const staticDir = path.join(buildDir, "static");
|
|
20
22
|
|
|
@@ -50,6 +52,8 @@ const bundleJs = async ({ entryPoints, outfile, ...options }, plg) => {
|
|
|
50
52
|
plugins: [
|
|
51
53
|
resolve({
|
|
52
54
|
"/routemap.json": `${staticDir}/routemap.json`,
|
|
55
|
+
"@/_404/page": `${process.cwd()}/pages/_404/page.jsx`,
|
|
56
|
+
"@/_500/page": `${process.cwd()}/pages/_500/page.jsx`,
|
|
53
57
|
}),
|
|
54
58
|
plg,
|
|
55
59
|
]
|
|
@@ -87,6 +91,9 @@ const bundlePages = async () => {
|
|
|
87
91
|
const data = fs.readFileSync(args.path);
|
|
88
92
|
const newSrc = `
|
|
89
93
|
import { renderPage } from "edge-city";
|
|
94
|
+
import NotFoundPage from "@/_404/page";
|
|
95
|
+
import ErrorPage from "@/_500/page";
|
|
96
|
+
|
|
90
97
|
${data.toString()}
|
|
91
98
|
|
|
92
99
|
export function onRequest(context) {
|
|
@@ -159,7 +166,7 @@ const bundleServices = async () => {
|
|
|
159
166
|
const services = walkdir.sync(path.join(process.cwd(), "services"))
|
|
160
167
|
.filter((s) => s.includes(".service.js"));
|
|
161
168
|
for (const s of services) {
|
|
162
|
-
const dest = s.replace(process.cwd(), "").replace("/services", "").replace(".service.js", "")
|
|
169
|
+
const dest = s.replace(process.cwd(), "").replace("/services", "").replace(".service.js", "");
|
|
163
170
|
const pkg = await import(s);
|
|
164
171
|
for (const p of Object.keys(pkg)) {
|
|
165
172
|
const buildStart = Date.now();
|
|
@@ -200,7 +207,7 @@ const bundleServices = async () => {
|
|
|
200
207
|
});
|
|
201
208
|
}
|
|
202
209
|
})
|
|
203
|
-
|
|
210
|
+
fse.ensureDirSync(`build/functions/_rpc${dest}`)
|
|
204
211
|
const outfile = `build/functions/_rpc${dest}/${p}.js`;
|
|
205
212
|
fs.writeFileSync(outfile, result.outputFiles[0].contents);
|
|
206
213
|
recordSize(buildStart, outfile);
|
|
@@ -214,32 +221,25 @@ const bundleCss = async () => {
|
|
|
214
221
|
postcssCustomMedia(),
|
|
215
222
|
postcssNesting,
|
|
216
223
|
]).process(generatedCss, { from: "app.css", to: "app.css" });
|
|
217
|
-
|
|
224
|
+
fse.ensureDirSync(`build/static/css`)
|
|
218
225
|
fs.writeFileSync(`${process.cwd()}/build/static/css/app.css`, result.toString());
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
const build = async (platform, setProd) => {
|
|
229
|
+
fse.removeSync(buildDir);
|
|
221
|
-
|
|
230
|
+
fse.ensureDirSync(buildDir);
|
|
222
|
-
|
|
231
|
+
fse.ensureDirSync(staticDir);
|
|
223
|
-
|
|
232
|
+
fse.copySync(inputStaticDir, staticDir);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const cleanDir = (d) => {
|
|
228
|
-
if (
|
|
233
|
+
if (setProd) {
|
|
229
|
-
|
|
234
|
+
process.env.NODE_ENV = "production";
|
|
235
|
+
isProd = true;
|
|
230
236
|
}
|
|
231
|
-
}
|
|
232
|
-
const createDirs = () => {
|
|
233
|
-
cleanDir(buildDir);
|
|
234
|
-
ensureDir(buildDir);
|
|
235
|
-
ensureDir(staticDir);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const build = async () => {
|
|
239
|
-
createDirs();
|
|
240
237
|
await bundlePages();
|
|
241
|
-
|
|
238
|
+
await bundleServices();
|
|
242
239
|
await bundleCss();
|
|
240
|
+
if (platform === "cloudflare") {
|
|
241
|
+
// create _routes.json for cloudflare which only includes the pages and services
|
|
242
|
+
}
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
yargs(hideBin(process.argv))
|
|
@@ -253,7 +253,7 @@ yargs(hideBin(process.argv))
|
|
|
253
253
|
})
|
|
254
254
|
.demandOption("p")
|
|
255
255
|
}, ({ platform }) => {
|
|
256
|
-
build(platform);
|
|
256
|
+
build(platform, true);
|
|
257
257
|
})
|
|
258
258
|
.command('dev', 'run the dev server', (y) => {
|
|
259
259
|
y.option('platform', {
|
|
@@ -263,7 +263,7 @@ yargs(hideBin(process.argv))
|
|
|
263
263
|
choices: ['cloudflare', 'vercel']
|
|
264
264
|
})
|
|
265
265
|
}, ({ platform }) => {
|
|
266
|
-
build(platform);
|
|
266
|
+
build(platform, false);
|
|
267
267
|
})
|
|
268
268
|
.demandCommand(1)
|
|
269
269
|
.parse()
|
example/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"type": "module",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "edge-city dev -p cloudflare",
|
|
6
|
-
"build
|
|
6
|
+
"build": " edge-city build -p cloudflare",
|
|
7
7
|
"test": "jest",
|
|
8
8
|
"test-e2e": "playwright test"
|
|
9
9
|
},
|
example/pages/_404/page.css
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
color: #000;
|
|
7
7
|
background: #fff;
|
|
8
8
|
font-family: -apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif;
|
|
9
|
-
height: 100vh;
|
|
10
9
|
text-align: center;
|
|
11
10
|
|
|
12
11
|
& h1 {
|
example/pages/_404/page.jsx
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Helmet } from 'react-helmet-async';
|
|
3
|
+
import Layout from '@/components/Layout/Layout';
|
|
3
4
|
import "./page.css";
|
|
4
5
|
|
|
5
6
|
const Page = () => {
|
|
6
7
|
return (
|
|
8
|
+
<Layout>
|
|
7
|
-
|
|
9
|
+
<div className="notfound-page">
|
|
8
|
-
|
|
10
|
+
<Helmet>
|
|
9
|
-
|
|
11
|
+
<title>Page not found</title>
|
|
10
|
-
|
|
12
|
+
</Helmet>
|
|
11
|
-
|
|
13
|
+
<h1>404 - Page not found</h1>
|
|
12
|
-
|
|
14
|
+
<div className="content">
|
|
13
|
-
|
|
15
|
+
<h2>This page could not be found</h2>
|
|
16
|
+
</div>
|
|
14
17
|
</div>
|
|
15
|
-
</
|
|
18
|
+
</Layout>
|
|
16
19
|
)
|
|
17
20
|
}
|
|
18
21
|
|
example/pages/_500/page.jsx
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Helmet } from 'react-helmet-async';
|
|
3
|
+
import Layout from '@/components/Layout/Layout';
|
|
3
4
|
import "./page.css";
|
|
4
5
|
|
|
5
6
|
const Page = () => {
|
|
6
7
|
return (
|
|
8
|
+
<Layout>
|
|
7
|
-
|
|
9
|
+
<div className="err-page">
|
|
8
|
-
|
|
10
|
+
<Helmet>
|
|
9
|
-
|
|
11
|
+
<title>Oop's Something went wrong</title>
|
|
10
|
-
|
|
12
|
+
</Helmet>
|
|
11
|
-
|
|
13
|
+
<h1>Oop's Something went wrong</h1>
|
|
12
|
-
|
|
14
|
+
<div className="content">
|
|
13
|
-
|
|
15
|
+
<h2>Internal Server Error</h2>
|
|
16
|
+
</div>
|
|
14
17
|
</div>
|
|
15
|
-
</
|
|
18
|
+
</Layout>
|
|
16
19
|
)
|
|
17
20
|
}
|
|
18
21
|
|
index.js
CHANGED
|
@@ -125,7 +125,7 @@ export const RouterContext = createContext(undefined);
|
|
|
125
125
|
const getMatch = (router, pathname) => {
|
|
126
126
|
const matchedPage = router.lookup(pathname);
|
|
127
127
|
if (!matchedPage) {
|
|
128
|
-
return router.lookup("/
|
|
128
|
+
return router.lookup("/_404")
|
|
129
129
|
}
|
|
130
130
|
return matchedPage;
|
|
131
131
|
}
|
|
@@ -250,6 +250,14 @@ export const renderPage = async (PageComponent, req) => {
|
|
|
250
250
|
const history = createMemoryHistory({
|
|
251
251
|
initialEntries: [url.pathname + url.search],
|
|
252
252
|
});
|
|
253
|
+
const router = createRouter({
|
|
254
|
+
strictTrailingSlash: true,
|
|
255
|
+
routes: Object.keys(routemap).reduce((acc, r) => {
|
|
256
|
+
acc[r] = r;
|
|
257
|
+
return acc;
|
|
258
|
+
}, {}),
|
|
259
|
+
});
|
|
260
|
+
const AppPage = router.lookup(url.pathname) ? PageComponent : NotFoundPage;
|
|
253
261
|
const jsScript = url.pathname === "/" ? "index" : url.pathname;
|
|
254
262
|
const helmetContext = {}
|
|
255
263
|
const stream = await renderToReadableStream(
|
|
@@ -271,7 +279,7 @@ export const renderPage = async (PageComponent, req) => {
|
|
|
271
279
|
router: null,
|
|
272
280
|
rpcCache: {},
|
|
273
281
|
helmetContext,
|
|
274
|
-
PageComponent,
|
|
282
|
+
PageComponent: AppPage,
|
|
275
283
|
}), _jsx(_Fragment, {
|
|
276
284
|
children: _jsx("script", {
|
|
277
285
|
type: "module",
|
package.json
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"esbuild": "0.17.19",
|
|
21
21
|
"yargs": "17.7.2",
|
|
22
22
|
"mime-types": "2.1.35",
|
|
23
|
+
"fs-extra": "11.1.1",
|
|
23
24
|
"ms": "2.1.3",
|
|
24
25
|
"picocolors": "1.0.0",
|
|
25
26
|
"postcss": "^8.4.21",
|
pnpm-workspace.yaml
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
packages:
|
|
2
2
|
- "./"
|
|
3
3
|
- "example"
|
|
4
|
+
- "docs"
|