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


072de50 — pyrossh 2023-05-23T10:49:01+05:30
use fse

Generated files not shown: pnpm-lock.yaml

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 isProd = process.env.NODE_ENV === "production";
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
- ensureDir(`build/functions/_rpc${dest}`)
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
- ensureDir(`build/static/css`)
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
- const ensureDir = (d) => {
230
+ fse.ensureDirSync(buildDir);
222
- if (!fs.existsSync(d)) {
231
+ fse.ensureDirSync(staticDir);
223
- fs.mkdirSync(d, { recursive: true });
232
+ fse.copySync(inputStaticDir, staticDir);
224
- }
225
- }
226
-
227
- const cleanDir = (d) => {
228
- if (fs.existsSync(d)) {
233
+ if (setProd) {
229
- fs.rmSync(d, { recursive: true });
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
- // await bundleServices();
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:prod": " edge-city build -p cloudflare",
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
- <div className="notfound-page">
9
+ <div className="notfound-page">
8
- <Helmet>
10
+ <Helmet>
9
- <title>Page not found</title>
11
+ <title>Page not found</title>
10
- </Helmet>
12
+ </Helmet>
11
- <h1>404 - Page not found</h1>
13
+ <h1>404 - Page not found</h1>
12
- <div className="content">
14
+ <div className="content">
13
- <h2>This page could not be found</h2>
15
+ <h2>This page could not be found</h2>
16
+ </div>
14
17
  </div>
15
- </div>
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
- <div className="err-page">
9
+ <div className="err-page">
8
- <Helmet>
10
+ <Helmet>
9
- <title>Oop's Something went wrong</title>
11
+ <title>Oop's Something went wrong</title>
10
- </Helmet>
12
+ </Helmet>
11
- <h1>Oop's Something went wrong</h1>
13
+ <h1>Oop's Something went wrong</h1>
12
- <div className="content">
14
+ <div className="content">
13
- <h2>Internal Server Error</h2>
15
+ <h2>Internal Server Error</h2>
16
+ </div>
14
17
  </div>
15
- </div>
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("/js/_404.js")
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"