~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


e417de07 Peter John

2 years ago
move all logic to single file
packages/example/containers/TodoList/TodoList.jsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import Todo from "@/components/Todo/Todo";
3
3
  import { getTodos } from "@/services/todos.service";
4
- import { useRpc } from "parotta/rpc";
4
+ import { useRpc } from "parotta/runtime";
5
5
 
6
6
  const TodoList = () => {
7
7
  const { data } = useRpc(getTodos, {});
packages/example/pages/about/page.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { Link, useRouter } from "parotta/router";
2
+ import { Link, useRouter } from "parotta/runtime";
3
3
  import "./page.css";
4
4
 
5
5
  export const Head = () => {
packages/example/pages/layout.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { Suspense } from 'react';
2
- import { Link } from "parotta/router";
2
+ import { Link } from "parotta/runtime";
3
- import { ErrorBoundary } from "parotta/error";
3
+ import { ErrorBoundary } from "parotta/runtime";
4
4
  import "./layout.css";
5
5
 
6
6
  const Layout = ({ children }) => {
packages/example/pages/page.jsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect } from 'react';
2
- import { useRouter } from "parotta/router";
2
+ import { useRouter } from "parotta/runtime";
3
3
  import Counter from "@/components/Counter/Counter";
4
4
  import "./page.css";
5
5
 
packages/example/pages/todos/page.jsx CHANGED
@@ -1,6 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React from 'react';
2
- import { useFetch } from "parotta/fetch";
3
- // import Todo from "@/components/Todo/Todo";
4
2
  import TodoList from "@/containers/TodoList/TodoList";
5
3
  import "./page.css";
6
4
 
@@ -11,21 +9,10 @@ export const Head = () => {
11
9
  }
12
10
 
13
11
  export const Body = () => {
14
- // const { data, cache } = useFetch("/api/todos");
15
- // useEffect(() => {
16
- // setTimeout(() => {
17
- // cache.invalidate(/todos/);
18
- // }, 3000)
19
- // }, []);
20
12
  return (
21
13
  <div>
22
14
  <h1>Todos</h1>
23
15
  <ul>
24
- {/* {data?.map((item) => (
25
- <li key={item.id}>
26
- <Todo todo={item} />
27
- </li>
28
- ))} */}
29
16
  <TodoList />
30
17
  </ul>
31
18
  </div>
packages/parotta/cli.js CHANGED
@@ -12,7 +12,7 @@ import postcssNesting from "postcss-nesting";
12
12
  import { createMemoryHistory } from "history";
13
13
  import { createRouter } from 'radix3';
14
14
  import mimeTypes from "mime-types";
15
- import { HeadApp, BodyApp } from "./router";
15
+ import { HeadApp, BodyApp } from "./runtime";
16
16
  import { renderToReadableStream } from 'react-dom/server';
17
17
  // import { renderToStream } from './render';
18
18
 
@@ -142,13 +142,8 @@ const renderPage = async (url) => {
142
142
  "react/jsx-dev-runtime": `https://esm.sh/react@18.2.0${devTag}/jsx-dev-runtime`,
143
143
  "react-dom/client": `https://esm.sh/react-dom@18.2.0${devTag}/client`,
144
144
  "nprogress": "https://esm.sh/nprogress@0.2.0",
145
- // "parotta/router": `https://esm.sh/parotta@${version}/router.js`,
145
+ // "parotta/runtime": `https://esm.sh/parotta@${version}/runtime.js`,
146
- // "parotta/error": `https://esm.sh/parotta@${version}/error.js`,
147
- // "parotta/fetch": `https://esm.sh/parotta@${version}/fetch.js`,
148
- "parotta/router": `/parotta/router.js`,
146
+ "parotta/runtime": `/parotta/runtime.js`,
149
- "parotta/error": `/parotta/error.js`,
150
- "parotta/fetch": `/parotta/fetch.js`,
151
- "parotta/rpc": `/parotta/rpc.js`,
152
147
  ...nodeDeps,
153
148
  ...components,
154
149
  ...containers,
@@ -175,7 +170,7 @@ import React from "react";
175
170
  import { hydrateRoot } from "react-dom/client";
176
171
  import { createBrowserHistory } from "history";
177
172
  import { createRouter } from "radix3";
178
- import { HeadApp, BodyApp } from "parotta/router";
173
+ import { HeadApp, BodyApp } from "parotta/runtime";
179
174
 
180
175
 
181
176
  const history = createBrowserHistory();
@@ -264,7 +259,7 @@ const renderJs = async (src) => {
264
259
  }
265
260
  })
266
261
  if (addRpcImport) {
267
- lines.unshift(`import rpc from "parotta/rpc"`);
262
+ lines.unshift(`import { rpc } from "parotta/runtime"`);
268
263
  }
269
264
  // remove .css and .service imports
270
265
  const filteredJsx = lines.filter((ln) => !ln.includes(".css") && !ln.includes(".service")).join("\n");
packages/parotta/error.js DELETED
@@ -1,42 +0,0 @@
1
- import React from 'react';
2
-
3
- const changedArray = (a = [], b = []) =>
4
- a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]))
5
-
6
- export class ErrorBoundary extends React.Component {
7
- // static propTypes = {
8
- // resetKeys: PropTypes.arrayOf(PropTypes.any),
9
- // }
10
- static getDerivedStateFromError(error) {
11
- return { error }
12
- }
13
-
14
- state = {}
15
-
16
- componentDidCatch(error, info) {
17
- this.props.onError?.(error, info)
18
- }
19
-
20
- componentDidUpdate(prevProps, prevState) {
21
- const { error } = this.state
22
- const { resetKeys } = this.props
23
- if (
24
- error !== null &&
25
- prevState.error !== null &&
26
- changedArray(prevProps.resetKeys, resetKeys)
27
- ) {
28
- this.setState({});
29
- }
30
- }
31
-
32
- render() {
33
- const { error } = this.state;
34
- const { children, fallback } = this.props;
35
- if (error) {
36
- if (React.isValidElement(fallback)) {
37
- return fallback;
38
- }
39
- }
40
- return children;
41
- }
42
- }
packages/parotta/fetch.js DELETED
@@ -1,55 +0,0 @@
1
- import { useState, useMemo } from "react";
2
-
3
- export const domain = () => typeof window !== 'undefined' ? window.origin : "http://0.0.0.0:3000";
4
- export const globalCache = new Map();
5
-
6
- export const useCache = () => {
7
- const [_, rerender] = useState(false);
8
- const cache = useMemo(() => globalCache, []);
9
- const get = (k) => cache.get(k)
10
- const set = (k, v) => {
11
- cache.set(k, v);
12
- rerender((c) => !c);
13
- }
14
- const invalidate = (regex) => {
15
- Array.from(cache.keys())
16
- .filter((k) => regex.test(k))
17
- .forEach((k) => {
18
- fetchData(k).then((v) => set(k, v));
19
- });
20
- }
21
- return {
22
- get,
23
- set,
24
- invalidate,
25
- }
26
- }
27
-
28
- const fetchData = async (route) => {
29
- const url = `${domain()}${route}`;
30
- const res = await fetch(url, {
31
- headers: {
32
- "Accept": "application/json",
33
- },
34
- });
35
- if (res.ok) {
36
- return await res.json();
37
- } else {
38
- return new Error(await res.text());
39
- }
40
- }
41
-
42
- export const useFetch = (url) => {
43
- const cache = useCache();
44
- const value = cache.get(url);
45
- if (value) {
46
- if (value instanceof Promise) {
47
- throw value;
48
- } else if (value instanceof Error) {
49
- throw value;
50
- }
51
- return { data: value, cache };
52
- }
53
- cache.set(url, fetchData(url).then((v) => cache.set(url, v)));
54
- throw cache.get(url);
55
- }
packages/parotta/rpc.js DELETED
@@ -1,56 +0,0 @@
1
- import { useState, useMemo } from "react";
2
-
3
- export const domain = () => typeof window !== 'undefined' ? window.origin : "http://0.0.0.0:3000";
4
- export const globalCache = new Map();
5
-
6
- const rpc = (serviceName) => async (params = {}) => {
7
- const res = await fetch(`${domain()}/services/${serviceName}`, {
8
- method: "POST",
9
- headers: {
10
- "Accept": "application/json",
11
- "Content-Type": "application/json",
12
- },
13
- body: JSON.stringify(params),
14
- })
15
- return await res.json();
16
- }
17
-
18
- export const useCache = () => {
19
- const [_, rerender] = useState(false);
20
- const cache = useMemo(() => globalCache, []);
21
- const get = (k) => cache.get(k)
22
- const set = (k, v) => {
23
- cache.set(k, v);
24
- rerender((c) => !c);
25
- }
26
- const invalidate = (regex) => {
27
- Array.from(cache.keys())
28
- .filter((k) => regex.test(k))
29
- .forEach((k) => {
30
- fetchData(k).then((v) => set(k, v));
31
- });
32
- }
33
- return {
34
- get,
35
- set,
36
- invalidate,
37
- }
38
- }
39
-
40
- export const useRpc = (fn, params) => {
41
- const cache = useCache();
42
- const key = `${fn.name}:${JSON.stringify(params)}`;
43
- const value = cache.get(key);
44
- if (value) {
45
- if (value instanceof Promise) {
46
- throw value;
47
- } else if (value instanceof Error) {
48
- throw value;
49
- }
50
- return { data: value, cache };
51
- }
52
- cache.set(key, fn(params).then((v) => cache.set(key, v)));
53
- throw cache.get(key);
54
- }
55
-
56
- export default rpc;
packages/parotta/{router.js → runtime.js} RENAMED
@@ -1,9 +1,65 @@
1
- import {
1
+ import React, {
2
2
  Fragment, Suspense, createElement, createContext,
3
3
  useContext, useState, useEffect, useMemo, useSyncExternalStore, useTransition
4
4
  } from "react";
5
5
  import nProgress from "nprogress";
6
6
 
7
+ export const domain = () => typeof window !== 'undefined' ? window.origin : "http://0.0.0.0:3000";
8
+ export const globalCache = new Map();
9
+
10
+ const changedArray = (a = [], b = []) =>
11
+ a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]))
12
+
13
+ export const rpc = (serviceName) => async (params = {}) => {
14
+ const res = await fetch(`${domain()}/services/${serviceName}`, {
15
+ method: "POST",
16
+ headers: {
17
+ "Accept": "application/json",
18
+ "Content-Type": "application/json",
19
+ },
20
+ body: JSON.stringify(params),
21
+ })
22
+ return await res.json();
23
+ }
24
+
25
+ export const useCache = () => {
26
+ const [_, rerender] = useState(false);
27
+ const cache = useMemo(() => globalCache, []);
28
+ const get = (k) => cache.get(k)
29
+ const set = (k, v) => {
30
+ cache.set(k, v);
31
+ rerender((c) => !c);
32
+ }
33
+ const invalidate = (regex) => {
34
+ Array.from(cache.keys())
35
+ .filter((k) => regex.test(k))
36
+ .forEach((k) => {
37
+ fetchData(k).then((v) => set(k, v));
38
+ });
39
+ }
40
+ return {
41
+ get,
42
+ set,
43
+ invalidate,
44
+ }
45
+ }
46
+
47
+ export const useRpc = (fn, params) => {
48
+ const cache = useCache();
49
+ const key = `${fn.name}:${JSON.stringify(params)}`;
50
+ const value = cache.get(key);
51
+ if (value) {
52
+ if (value instanceof Promise) {
53
+ throw value;
54
+ } else if (value instanceof Error) {
55
+ throw value;
56
+ }
57
+ return { data: value, cache };
58
+ }
59
+ cache.set(key, fn(params).then((v) => cache.set(key, v)));
60
+ throw cache.get(key);
61
+ }
62
+
7
63
  export const RouterContext = createContext(undefined);
8
64
 
9
65
  const getMatch = (radixRouter, pathname) => {
@@ -131,4 +187,42 @@ export const NavLink = ({ children, className, activeClassName, ...props }) => {
131
187
  const { pathname } = useRouter();
132
188
  const classNames = pathname === props.href ? [activeClassName, className] : [className];
133
189
  return <Link className={classNames} {...props} >{children}</Link>
190
+ }
191
+
192
+ export class ErrorBoundary extends React.Component {
193
+ // static propTypes = {
194
+ // resetKeys: PropTypes.arrayOf(PropTypes.any),
195
+ // }
196
+ static getDerivedStateFromError(error) {
197
+ return { error }
198
+ }
199
+
200
+ state = {}
201
+
202
+ componentDidCatch(error, info) {
203
+ this.props.onError?.(error, info)
204
+ }
205
+
206
+ componentDidUpdate(prevProps, prevState) {
207
+ const { error } = this.state
208
+ const { resetKeys } = this.props
209
+ if (
210
+ error !== null &&
211
+ prevState.error !== null &&
212
+ changedArray(prevProps.resetKeys, resetKeys)
213
+ ) {
214
+ this.setState({});
215
+ }
216
+ }
217
+
218
+ render() {
219
+ const { error } = this.state;
220
+ const { children, fallback } = this.props;
221
+ if (error) {
222
+ if (React.isValidElement(fallback)) {
223
+ return fallback;
224
+ }
225
+ }
226
+ return children;
227
+ }
134
228
  }