@quilted/create 0.3.1 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +117 -0
- package/build/esm/node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/dist/prompts.mjs +1 -1
- package/build/esm/node_modules/.pnpm/prompts@2.4.2/node_modules/prompts/lib/prompts.mjs +1 -1
- package/package.json +1 -1
- package/templates/app-basic/App.tsx +8 -3
- package/templates/app-basic/browser.css +10 -0
- package/templates/app-basic/browser.tsx +23 -46
- package/templates/app-basic/context/browser.ts +10 -0
- package/templates/app-basic/context/navigation.ts +17 -0
- package/templates/app-basic/{shared/context.ts → context/preact.ts} +1 -1
- package/templates/app-basic/context/server.ts +10 -0
- package/templates/app-basic/context/types.ts +5 -0
- package/templates/app-basic/foundation/frame/Frame.module.css +0 -11
- package/templates/app-basic/server.tsx +5 -14
- package/templates/app-basic/tests/render/render.tsx +6 -3
- package/templates/app-basic/tests/render/types.ts +2 -2
- package/templates/app-basic/tsconfig.json +2 -4
- package/templates/app-graphql/App.tsx +4 -3
- package/templates/app-graphql/browser.css +10 -0
- package/templates/app-graphql/browser.tsx +23 -56
- package/templates/app-graphql/context/browser.ts +21 -0
- package/templates/app-graphql/context/navigation.ts +17 -0
- package/templates/{app-trpc/shared/context.ts → app-graphql/context/preact.ts} +1 -1
- package/templates/app-graphql/context/server.ts +23 -0
- package/templates/app-graphql/context/types.ts +10 -0
- package/templates/app-graphql/server.tsx +4 -24
- package/templates/app-graphql/tests/render/render.tsx +11 -8
- package/templates/app-graphql/tests/render/types.ts +9 -4
- package/templates/app-graphql/tsconfig.json +1 -1
- package/templates/app-trpc/App.tsx +5 -7
- package/templates/app-trpc/browser.css +10 -0
- package/templates/app-trpc/browser.tsx +22 -55
- package/templates/app-trpc/context/browser.ts +20 -0
- package/templates/app-trpc/context/navigation.ts +17 -0
- package/templates/{app-graphql/shared/context.ts → app-trpc/context/preact.ts} +1 -1
- package/templates/app-trpc/context/server.ts +18 -0
- package/templates/app-trpc/context/trpc.ts +6 -0
- package/templates/app-trpc/context/types.ts +11 -0
- package/templates/app-trpc/features/home/Home.tsx +1 -1
- package/templates/app-trpc/server.tsx +4 -19
- package/templates/app-trpc/tests/render/render.tsx +9 -4
- package/templates/app-trpc/tests/render/types.ts +2 -2
- package/templates/app-trpc/tsconfig.json +1 -1
- package/templates/app-basic/shared/navigation.ts +0 -15
- package/templates/app-graphql/shared/graphql.ts +0 -22
- package/templates/app-graphql/shared/navigation.ts +0 -15
- package/templates/app-trpc/shared/navigation.ts +0 -15
- package/templates/app-trpc/shared/trpc.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,122 @@
|
|
|
1
1
|
# @quilted/create
|
|
2
2
|
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#890](https://github.com/lemonmade/quilt/pull/890) [`b0f2334`](https://github.com/lemonmade/quilt/commit/b0f23340945280c951998bf77b3be8b8df13338c) Thanks [@lemonmade](https://github.com/lemonmade)! - Redesign asset loading APIs for better performance and clearer structure.
|
|
8
|
+
|
|
9
|
+
## Breaking changes
|
|
10
|
+
|
|
11
|
+
### `@quilted/assets`: `BrowserAssetsEntry` redesigned
|
|
12
|
+
|
|
13
|
+
The `BrowserAssetsEntry` type has been completely restructured. The previous flat `scripts` and `styles` arrays have been replaced with structured `script` and `style` objects that separate the entry asset from its dependencies:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// Before
|
|
17
|
+
interface BrowserAssetsEntry {
|
|
18
|
+
scripts: Asset[];
|
|
19
|
+
styles: Asset[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// After
|
|
23
|
+
interface BrowserAssetsEntry {
|
|
24
|
+
script?: {
|
|
25
|
+
asset: Asset;
|
|
26
|
+
syncDependencies: readonly Asset[];
|
|
27
|
+
asyncDependencies: readonly Asset[];
|
|
28
|
+
};
|
|
29
|
+
style?: {
|
|
30
|
+
asset: Asset;
|
|
31
|
+
syncDependencies: readonly Asset[];
|
|
32
|
+
asyncDependencies: readonly Asset[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This separation enables the renderer to treat the entry script, its preloadable sync dependencies, and its async dependencies differently in the HTML output.
|
|
38
|
+
|
|
39
|
+
### `@quilted/assets`: `BrowserAssets.modules()` return type changed
|
|
40
|
+
|
|
41
|
+
`modules()` now returns `readonly BrowserAssetsEntry[]` (one entry per module ID) instead of a single merged `BrowserAssetsEntry`:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
// Before
|
|
45
|
+
modules(modules: Iterable<...>, options?): BrowserAssetsEntry;
|
|
46
|
+
|
|
47
|
+
// After
|
|
48
|
+
modules(modules: Iterable<string>, options?): readonly BrowserAssetsEntry[];
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### `@quilted/assets`: `BrowserAssetModuleSelector` removed
|
|
52
|
+
|
|
53
|
+
The `BrowserAssetModuleSelector` interface and the `modules` field on `BrowserAssetSelector` have been removed. If you want to get the asset details for modules in addition to the entrypoints, use `BrowserAssets.modules()` instead.
|
|
54
|
+
|
|
55
|
+
### `@quilted/assets`: `AssetBuildManifest` module entry format changed
|
|
56
|
+
|
|
57
|
+
`AssetBuildManifest.modules` values changed from `number[]` to the new `AssetBuildModuleEntry` tuple type. The tuple uses positional slots for each asset category and is serialized in JSON as an object with numeric string keys (omitting empty positions):
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
type AssetBuildModuleEntry = [
|
|
61
|
+
script?: number, // [0] entry JS chunk
|
|
62
|
+
style?: number, // [1] entry CSS file
|
|
63
|
+
scriptSync?: number[], // [2] sync JS dependency indices
|
|
64
|
+
styleSync?: number[], // [3] CSS from sync JS dependencies
|
|
65
|
+
scriptAsync?: number[], // [4] dynamic JS dependency indices
|
|
66
|
+
styleAsync?: number[], // [5] CSS from dynamic JS dependencies
|
|
67
|
+
];
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `@quilted/browser`: `BrowserResponseAssets.get()` return type changed
|
|
71
|
+
|
|
72
|
+
`get()` now returns `string[]` (module IDs) instead of `BrowserAssetModuleSelector[]`.
|
|
73
|
+
|
|
74
|
+
## New features
|
|
75
|
+
|
|
76
|
+
### `@quilted/preact-browser`: `BrowserApp` class
|
|
77
|
+
|
|
78
|
+
A new `BrowserApp` class simplifies constructing and running a browser app. It handles waiting for the `#app` DOM element via `MutationObserver`, which is necessary now that the entry script runs with the `async` attribute:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import {BrowserApp} from '@quilted/quilt/browser';
|
|
82
|
+
import {BrowserAppContext} from '~/context/browser.ts';
|
|
83
|
+
import {App} from './App.tsx';
|
|
84
|
+
|
|
85
|
+
const context = new BrowserAppContext();
|
|
86
|
+
const app = new BrowserApp(<App context={context} />, {context});
|
|
87
|
+
await app.hydrate();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Render behavior changes
|
|
91
|
+
|
|
92
|
+
### Entry script rendered as `async` module
|
|
93
|
+
|
|
94
|
+
The browser entry script is now rendered as `<script type="module" async>` instead of a blocking `<script type="module">`. This means the script no longer blocks HTML parsing, and does not wait for DOMContentLoaded. This change is meant to allow streaming HTML responses to begin executing JavaScript earlier.
|
|
95
|
+
|
|
96
|
+
### Sync dependencies rendered as `modulepreload` links
|
|
97
|
+
|
|
98
|
+
Sync JS dependencies (previously rendered as additional `<script type="module">` tags) are now rendered as `<link rel="modulepreload">` hints. This tells the browser to fetch them eagerly without executing them, since the entry script will import them when it runs.
|
|
99
|
+
|
|
100
|
+
### Stylesheets rendered after all script references
|
|
101
|
+
|
|
102
|
+
Entry and async stylesheets are now emitted from the `<HTMLTemplate.Assets async />` placeholder, after all script and modulepreload tags. This ensures the stylesheet `<link>` elements appear after all JS references in the HTML.
|
|
103
|
+
|
|
104
|
+
### Asset deduplication across streamed chunks
|
|
105
|
+
|
|
106
|
+
Asset deduplication (preventing the same `src`/`href` from appearing more than once) now works across all streamed HTML chunks within a single response, not just within a single placeholder.
|
|
107
|
+
|
|
108
|
+
## 0.3.2
|
|
109
|
+
|
|
110
|
+
### Patch Changes
|
|
111
|
+
|
|
112
|
+
- [`be4e753`](https://github.com/lemonmade/quilt/commit/be4e7532de2bdda83302c2eb8bc2a0a09aeae07e) Thanks [@lemonmade](https://github.com/lemonmade)! - Refactor app template context structure: replace `shared/` directory with `context/` directory
|
|
113
|
+
|
|
114
|
+
The `shared/` directory, which used TypeScript declaration merging to build up the `AppContext` interface across multiple files, has been replaced with a `context/` directory that defines the full interface explicitly in a single `types.ts` file. This makes the shape of the application context immediately visible without needing to trace module augmentations.
|
|
115
|
+
|
|
116
|
+
The new structure separates each concern into its own file: `types.ts` owns the interface, `navigation.ts` provides the router and route helpers, `preact.ts` holds the Preact context binding, `browser.ts` exports `BrowserAppContext`, and `server.ts` exports `ServerAppContext`. The context classes have been extracted from `browser.tsx` and `server.tsx` into these dedicated files, keeping the entry points lean.
|
|
117
|
+
|
|
118
|
+
Navigation is now accessed as `context.navigation.router` instead of `context.router`, grouping router state under a `navigation` namespace. A `browser.css` file has also been added to each app template for global CSS resets.
|
|
119
|
+
|
|
3
120
|
## 0.3.1
|
|
4
121
|
|
|
5
122
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -7,8 +7,9 @@ import {Frame} from './foundation/frame.ts';
|
|
|
7
7
|
|
|
8
8
|
import {Home} from './features/home.ts';
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
10
|
+
import type {AppContext} from './context/types.ts';
|
|
11
|
+
import {AppContextPreact} from './context/preact.ts';
|
|
12
|
+
import {routeWithAppContext} from './context/navigation.ts';
|
|
12
13
|
|
|
13
14
|
export interface AppProps {
|
|
14
15
|
context: AppContext;
|
|
@@ -38,7 +39,11 @@ export function App({context}: AppProps) {
|
|
|
38
39
|
<AppContextPreact.Provider value={context}>
|
|
39
40
|
<Localization>
|
|
40
41
|
<Head />
|
|
41
|
-
<Navigation
|
|
42
|
+
<Navigation
|
|
43
|
+
router={context.navigation.router}
|
|
44
|
+
routes={routes}
|
|
45
|
+
context={context}
|
|
46
|
+
/>
|
|
42
47
|
</Localization>
|
|
43
48
|
</AppContextPreact.Provider>
|
|
44
49
|
);
|
|
@@ -1,49 +1,26 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {hydrate} from '@quilted/quilt/browser';
|
|
3
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
1
|
+
import './browser.css';
|
|
4
2
|
|
|
5
|
-
import
|
|
6
|
-
import {App} from './App.tsx';
|
|
7
|
-
|
|
8
|
-
class BrowserAppContext implements AppContext {
|
|
9
|
-
readonly router: Router;
|
|
10
|
-
|
|
11
|
-
constructor() {
|
|
12
|
-
this.router = new Router();
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
class BrowserApp {
|
|
17
|
-
/**
|
|
18
|
-
* The app’s globally-available context.
|
|
19
|
-
*/
|
|
20
|
-
readonly context: BrowserAppContext;
|
|
3
|
+
import {BrowserApp} from '@quilted/quilt/browser';
|
|
21
4
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
*/
|
|
25
|
-
readonly rendered: ComponentChild;
|
|
26
|
-
|
|
27
|
-
constructor() {
|
|
28
|
-
this.context = new BrowserAppContext();
|
|
29
|
-
this.rendered = <App context={this.context} />;
|
|
30
|
-
|
|
31
|
-
// Makes key parts of the app available in the browser console.
|
|
32
|
-
//
|
|
33
|
-
// @example
|
|
34
|
-
// ```js
|
|
35
|
-
// // Log the current URL
|
|
36
|
-
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
37
|
-
// ```
|
|
38
|
-
Object.defineProperty(globalThis, 'app', {
|
|
39
|
-
value: this,
|
|
40
|
-
enumerable: false,
|
|
41
|
-
configurable: true,
|
|
42
|
-
writable: true,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const app = new BrowserApp();
|
|
5
|
+
import {BrowserAppContext} from '~/context/browser.ts';
|
|
6
|
+
import {App} from './App.tsx';
|
|
48
7
|
|
|
49
|
-
|
|
8
|
+
const context = new BrowserAppContext();
|
|
9
|
+
|
|
10
|
+
const app = new BrowserApp(<App context={context} />, {context});
|
|
11
|
+
|
|
12
|
+
// Makes key parts of the app available in the browser console.
|
|
13
|
+
//
|
|
14
|
+
// @example
|
|
15
|
+
// ```js
|
|
16
|
+
// // Log the current URL
|
|
17
|
+
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
18
|
+
// ```
|
|
19
|
+
Object.defineProperty(globalThis, 'app', {
|
|
20
|
+
value: app,
|
|
21
|
+
enumerable: false,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await app.hydrate();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type {AppContext} from './types.ts';
|
|
2
|
+
import {NavigationForApp} from './navigation.ts';
|
|
3
|
+
|
|
4
|
+
export class BrowserAppContext implements AppContext {
|
|
5
|
+
readonly navigation: NavigationForApp;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.navigation = new NavigationForApp();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {createContextRouteFunction, Router} from '@quilted/quilt/navigation';
|
|
2
|
+
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
|
+
|
|
5
|
+
export interface Navigation {
|
|
6
|
+
readonly router: Router;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class NavigationForApp implements Navigation {
|
|
10
|
+
readonly router: Router;
|
|
11
|
+
|
|
12
|
+
constructor(url?: string | URL) {
|
|
13
|
+
this.router = new Router(url);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createOptionalContext} from '@quilted/quilt/context';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
4
|
|
|
5
5
|
export const AppContextPreact = createOptionalContext<AppContext>();
|
|
6
6
|
export const useAppContext = AppContextPreact.use;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type {AppContext} from './types.ts';
|
|
2
|
+
import {NavigationForApp} from './navigation.ts';
|
|
3
|
+
|
|
4
|
+
export class ServerAppContext implements AppContext {
|
|
5
|
+
readonly navigation: NavigationForApp;
|
|
6
|
+
|
|
7
|
+
constructor(request: Request) {
|
|
8
|
+
this.navigation = new NavigationForApp(request.url);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -6,30 +6,21 @@ import {
|
|
|
6
6
|
PermissionsPolicyHeader,
|
|
7
7
|
StrictTransportSecurityHeader,
|
|
8
8
|
} from '@quilted/quilt/server';
|
|
9
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
10
|
-
|
|
11
9
|
import Env from 'quilt:module/env';
|
|
12
10
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
13
11
|
|
|
14
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
15
|
-
|
|
16
|
-
import {App} from './App.tsx';
|
|
17
|
-
|
|
18
12
|
const app = new Hono();
|
|
19
13
|
const assets = new BrowserAssets();
|
|
20
14
|
|
|
21
|
-
class ServerAppContext implements AppContext {
|
|
22
|
-
readonly router: Router;
|
|
23
|
-
|
|
24
|
-
constructor(request: Request) {
|
|
25
|
-
this.router = new Router(request.url);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
15
|
// For all GET requests, render our Preact application.
|
|
30
16
|
app.get('*', async (c) => {
|
|
31
17
|
const request = c.req.raw;
|
|
32
18
|
|
|
19
|
+
const [{App}, {ServerAppContext}] = await Promise.all([
|
|
20
|
+
import('./App.tsx'),
|
|
21
|
+
import('./context/server.ts'),
|
|
22
|
+
]);
|
|
23
|
+
|
|
33
24
|
const context = new ServerAppContext(request);
|
|
34
25
|
|
|
35
26
|
const isHttps = request.url.startsWith('https://');
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
|
|
7
7
|
import {Localization} from '@quilted/quilt/localize';
|
|
8
8
|
|
|
9
|
-
import {AppContextPreact} from '~/
|
|
9
|
+
import {AppContextPreact} from '~/context/preact.ts';
|
|
10
10
|
|
|
11
11
|
import {RenderOptions, RenderContext, RenderActions} from './types.ts';
|
|
12
12
|
|
|
@@ -24,11 +24,14 @@ export const renderApp = createRender<
|
|
|
24
24
|
// authors on the `root.context` property. Context is used to share data between your
|
|
25
25
|
// React tree and your test code, and is ideal for mocking out global context providers.
|
|
26
26
|
context({router = new TestRouter(), browser = new BrowserTestMock()}) {
|
|
27
|
-
return {router, browser};
|
|
27
|
+
return {navigation: {router}, browser};
|
|
28
28
|
},
|
|
29
29
|
// Render all of our app-wide context providers around each component under test.
|
|
30
30
|
render(element, context, {locale = 'en'}) {
|
|
31
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
navigation: {router},
|
|
33
|
+
browser,
|
|
34
|
+
} = context;
|
|
32
35
|
|
|
33
36
|
return (
|
|
34
37
|
<AppContextPreact.Provider value={context}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {TestRouter} from '@quilted/quilt/navigation/testing';
|
|
2
2
|
import type {BrowserTestMock} from '@quilted/quilt/browser/testing';
|
|
3
3
|
|
|
4
|
-
import type {AppContext} from '~/
|
|
4
|
+
import type {AppContext} from '~/context/types.ts';
|
|
5
5
|
|
|
6
6
|
export interface RenderOptions {
|
|
7
7
|
/**
|
|
@@ -27,7 +27,7 @@ export interface RenderContext extends AppContext {
|
|
|
27
27
|
/**
|
|
28
28
|
* The router used for this component test.
|
|
29
29
|
*/
|
|
30
|
-
readonly router: TestRouter;
|
|
30
|
+
readonly navigation: {router: TestRouter};
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* The browser environment for this component test.
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"jsxImportSource": "preact",
|
|
5
5
|
"paths": {
|
|
6
|
-
"~/
|
|
7
|
-
"~/tests/*": ["./tests/*"]
|
|
8
|
-
"react": ["./node_modules/preact/compat"],
|
|
9
|
-
"react-dom": ["./node_modules/preact/compat"]
|
|
6
|
+
"~/context/*": ["./context/*"],
|
|
7
|
+
"~/tests/*": ["./tests/*"]
|
|
10
8
|
}
|
|
11
9
|
},
|
|
12
10
|
"include": ["**/*"],
|
|
@@ -8,8 +8,9 @@ import {Frame} from './foundation/frame.ts';
|
|
|
8
8
|
|
|
9
9
|
import {Home, homeQuery} from './features/home.ts';
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
11
|
+
import type {AppContext} from './context/types.ts';
|
|
12
|
+
import {AppContextPreact} from './context/preact.ts';
|
|
13
|
+
import {routeWithAppContext} from './context/navigation.ts';
|
|
13
14
|
|
|
14
15
|
export interface AppProps {
|
|
15
16
|
context: AppContext;
|
|
@@ -44,7 +45,7 @@ export function App({context}: AppProps) {
|
|
|
44
45
|
<Localization>
|
|
45
46
|
<Head />
|
|
46
47
|
<Navigation
|
|
47
|
-
router={context.router}
|
|
48
|
+
router={context.navigation.router}
|
|
48
49
|
routes={routes}
|
|
49
50
|
context={context}
|
|
50
51
|
/>
|
|
@@ -1,60 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {hydrate} from '@quilted/quilt/browser';
|
|
3
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
4
|
-
import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
1
|
+
import './browser.css';
|
|
5
2
|
|
|
6
|
-
import
|
|
3
|
+
import {BrowserApp} from '@quilted/quilt/browser';
|
|
7
4
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
class BrowserAppContext implements AppContext {
|
|
11
|
-
readonly router: Router;
|
|
12
|
-
readonly graphql: AppContext['graphql'];
|
|
13
|
-
|
|
14
|
-
constructor() {
|
|
15
|
-
this.router = new Router();
|
|
16
|
-
|
|
17
|
-
const graphQLFetch = createGraphQLFetch({url: '/api/graphql'});
|
|
18
|
-
const graphQLCache = new GraphQLCache({fetch: graphQLFetch});
|
|
19
|
-
|
|
20
|
-
this.graphql = {
|
|
21
|
-
fetch: graphQLFetch,
|
|
22
|
-
cache: graphQLCache,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
}
|
|
5
|
+
import {BrowserAppContext} from '~/context/browser.ts';
|
|
26
6
|
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* The app's globally-available context.
|
|
30
|
-
*/
|
|
31
|
-
readonly context: BrowserAppContext;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The app's root JSX element, seeded with the necessary app context.
|
|
35
|
-
*/
|
|
36
|
-
readonly rendered: ComponentChild;
|
|
37
|
-
|
|
38
|
-
constructor() {
|
|
39
|
-
this.context = new BrowserAppContext();
|
|
40
|
-
this.rendered = <App context={this.context} />;
|
|
41
|
-
|
|
42
|
-
// Makes key parts of the app available in the browser console.
|
|
43
|
-
//
|
|
44
|
-
// @example
|
|
45
|
-
// ```js
|
|
46
|
-
// // Log the current URL
|
|
47
|
-
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
48
|
-
// ```
|
|
49
|
-
Object.defineProperty(globalThis, 'app', {
|
|
50
|
-
value: this,
|
|
51
|
-
enumerable: false,
|
|
52
|
-
configurable: true,
|
|
53
|
-
writable: true,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const app = new BrowserApp();
|
|
7
|
+
import {App} from './App.tsx';
|
|
59
8
|
|
|
60
|
-
|
|
9
|
+
const context = new BrowserAppContext();
|
|
10
|
+
|
|
11
|
+
const app = new BrowserApp(<App context={context} />, {context});
|
|
12
|
+
|
|
13
|
+
// Makes key parts of the app available in the browser console.
|
|
14
|
+
//
|
|
15
|
+
// @example
|
|
16
|
+
// ```js
|
|
17
|
+
// // Log the current URL
|
|
18
|
+
// console.log(globalThis.app.context.navigation.router.currentRequest.url);
|
|
19
|
+
// ```
|
|
20
|
+
Object.defineProperty(globalThis, 'app', {
|
|
21
|
+
value: app,
|
|
22
|
+
enumerable: false,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await app.hydrate();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
2
|
+
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
|
+
import {NavigationForApp} from './navigation.ts';
|
|
5
|
+
|
|
6
|
+
export class BrowserAppContext implements AppContext {
|
|
7
|
+
readonly navigation: NavigationForApp;
|
|
8
|
+
readonly graphql: AppContext['graphql'];
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this.navigation = new NavigationForApp();
|
|
12
|
+
|
|
13
|
+
const graphQLFetch = createGraphQLFetch({url: '/api/graphql'});
|
|
14
|
+
const graphQLCache = new GraphQLCache({fetch: graphQLFetch});
|
|
15
|
+
|
|
16
|
+
this.graphql = {
|
|
17
|
+
fetch: graphQLFetch,
|
|
18
|
+
cache: graphQLCache,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {createContextRouteFunction, Router} from '@quilted/quilt/navigation';
|
|
2
|
+
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
|
+
|
|
5
|
+
export interface Navigation {
|
|
6
|
+
readonly router: Router;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class NavigationForApp implements Navigation {
|
|
10
|
+
readonly router: Router;
|
|
11
|
+
|
|
12
|
+
constructor(url?: string | URL) {
|
|
13
|
+
this.router = new Router(url);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createOptionalContext} from '@quilted/quilt/context';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
4
|
|
|
5
5
|
export const AppContextPreact = createOptionalContext<AppContext>();
|
|
6
6
|
export const useAppContext = AppContextPreact.use;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {GraphQLCache} from '@quilted/quilt/graphql';
|
|
2
|
+
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
|
+
import {NavigationForApp} from './navigation.ts';
|
|
5
|
+
|
|
6
|
+
export class ServerAppContext implements AppContext {
|
|
7
|
+
readonly navigation: NavigationForApp;
|
|
8
|
+
readonly graphql: AppContext['graphql'];
|
|
9
|
+
|
|
10
|
+
constructor(request: Request) {
|
|
11
|
+
this.navigation = new NavigationForApp(request.url);
|
|
12
|
+
|
|
13
|
+
const graphQLFetch: AppContext['graphql']['fetch'] = async (...args) => {
|
|
14
|
+
const {performGraphQLOperation} = await import('../server/graphql.ts');
|
|
15
|
+
return performGraphQLOperation(...args);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
this.graphql = {
|
|
19
|
+
fetch: graphQLFetch,
|
|
20
|
+
cache: new GraphQLCache({fetch: graphQLFetch}),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type {Navigation} from './navigation.ts';
|
|
2
|
+
import type {GraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
3
|
+
|
|
4
|
+
export interface AppContext {
|
|
5
|
+
readonly navigation: Navigation;
|
|
6
|
+
readonly graphql: {
|
|
7
|
+
readonly fetch: GraphQLFetch;
|
|
8
|
+
readonly cache: GraphQLCache;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {Hono} from 'hono';
|
|
2
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
3
2
|
import {
|
|
4
3
|
renderAppToHTMLResponse,
|
|
5
4
|
CacheControlHeader,
|
|
@@ -7,35 +6,13 @@ import {
|
|
|
7
6
|
PermissionsPolicyHeader,
|
|
8
7
|
StrictTransportSecurityHeader,
|
|
9
8
|
} from '@quilted/quilt/server';
|
|
10
|
-
import {GraphQLCache} from '@quilted/quilt/graphql';
|
|
11
9
|
|
|
12
10
|
import Env from 'quilt:module/env';
|
|
13
11
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
14
12
|
|
|
15
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
16
|
-
|
|
17
13
|
const app = new Hono();
|
|
18
14
|
const assets = new BrowserAssets();
|
|
19
15
|
|
|
20
|
-
class ServerAppContext implements AppContext {
|
|
21
|
-
readonly router: Router;
|
|
22
|
-
readonly graphql: AppContext['graphql'];
|
|
23
|
-
|
|
24
|
-
constructor(request: Request) {
|
|
25
|
-
this.router = new Router(request.url);
|
|
26
|
-
|
|
27
|
-
const graphQLFetch: AppContext['graphql']['fetch'] = async (...args) => {
|
|
28
|
-
const {performGraphQLOperation} = await import('./server/graphql.ts');
|
|
29
|
-
return performGraphQLOperation(...args);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
this.graphql = {
|
|
33
|
-
fetch: graphQLFetch,
|
|
34
|
-
cache: new GraphQLCache({fetch: graphQLFetch}),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
16
|
// GraphQL API, called from the client
|
|
40
17
|
app.post('/api/graphql', async (c) => {
|
|
41
18
|
const request = c.req.raw;
|
|
@@ -55,7 +32,10 @@ app.post('/api/graphql', async (c) => {
|
|
|
55
32
|
app.get('*', async (c) => {
|
|
56
33
|
const request = c.req.raw;
|
|
57
34
|
|
|
58
|
-
const [{App}] = await Promise.all([
|
|
35
|
+
const [{App}, {ServerAppContext}] = await Promise.all([
|
|
36
|
+
import('./App.tsx'),
|
|
37
|
+
import('./context/server.ts'),
|
|
38
|
+
]);
|
|
59
39
|
|
|
60
40
|
const context = new ServerAppContext(request);
|
|
61
41
|
|
|
@@ -9,7 +9,7 @@ import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
|
|
|
9
9
|
import {Localization} from '@quilted/quilt/localize';
|
|
10
10
|
import {GraphQLCache} from '@quilted/quilt/graphql';
|
|
11
11
|
|
|
12
|
-
import {AppContextPreact} from '~/
|
|
12
|
+
import {AppContextPreact} from '~/context/preact.ts';
|
|
13
13
|
|
|
14
14
|
import {GraphQLTesting, GraphQLController} from '../graphql.ts';
|
|
15
15
|
|
|
@@ -32,18 +32,21 @@ export const renderApp = createRender<
|
|
|
32
32
|
router = new TestRouter(),
|
|
33
33
|
browser = new BrowserTestMock(),
|
|
34
34
|
graphql = new GraphQLController(),
|
|
35
|
-
graphQLCache = new GraphQLCache(),
|
|
35
|
+
graphQLCache = new GraphQLCache({fetch: graphql.fetch}),
|
|
36
36
|
}) {
|
|
37
37
|
return {
|
|
38
|
-
router,
|
|
38
|
+
navigation: {router},
|
|
39
39
|
browser,
|
|
40
|
-
graphql: {fetch: graphql.fetch, cache: graphQLCache},
|
|
41
|
-
graphQLController: graphql,
|
|
40
|
+
graphql: {fetch: graphql.fetch, cache: graphQLCache, controller: graphql},
|
|
42
41
|
};
|
|
43
42
|
},
|
|
44
43
|
// Render all of our app-wide context providers around each component under test.
|
|
45
44
|
render(element, context, {locale = 'en'}) {
|
|
46
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
navigation: {router},
|
|
47
|
+
browser,
|
|
48
|
+
graphql,
|
|
49
|
+
} = context;
|
|
47
50
|
|
|
48
51
|
return (
|
|
49
52
|
<AppContextPreact.Provider value={context}>
|
|
@@ -51,7 +54,7 @@ export const renderApp = createRender<
|
|
|
51
54
|
<Localization locale={locale}>
|
|
52
55
|
<Navigation router={router}>
|
|
53
56
|
<GraphQLTesting
|
|
54
|
-
controller={
|
|
57
|
+
controller={graphql.controller}
|
|
55
58
|
cache={graphql.cache}
|
|
56
59
|
>
|
|
57
60
|
<Suspense fallback={null}>{element}</Suspense>
|
|
@@ -69,7 +72,7 @@ export const renderApp = createRender<
|
|
|
69
72
|
// once the data is ready.
|
|
70
73
|
|
|
71
74
|
await wrapper.act(async () => {
|
|
72
|
-
await wrapper.context.
|
|
75
|
+
await wrapper.context.graphql.controller.resolveAll();
|
|
73
76
|
});
|
|
74
77
|
},
|
|
75
78
|
});
|
|
@@ -2,7 +2,7 @@ import type {TestRouter} from '@quilted/quilt/navigation/testing';
|
|
|
2
2
|
import type {BrowserTestMock} from '@quilted/quilt/browser/testing';
|
|
3
3
|
import type {GraphQLCache} from '@quilted/quilt/graphql';
|
|
4
4
|
|
|
5
|
-
import type {AppContext} from '~/
|
|
5
|
+
import type {AppContext} from '~/context/types.ts';
|
|
6
6
|
|
|
7
7
|
import type {GraphQLController} from '../graphql.ts';
|
|
8
8
|
|
|
@@ -58,7 +58,7 @@ export interface RenderContext extends AppContext {
|
|
|
58
58
|
/**
|
|
59
59
|
* The router used for this component test.
|
|
60
60
|
*/
|
|
61
|
-
readonly router: TestRouter;
|
|
61
|
+
readonly navigation: {router: TestRouter};
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* The browser environment for this component test.
|
|
@@ -66,9 +66,14 @@ export interface RenderContext extends AppContext {
|
|
|
66
66
|
readonly browser: BrowserTestMock;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* The GraphQL
|
|
69
|
+
* The GraphQL context used for this component test.
|
|
70
70
|
*/
|
|
71
|
-
readonly
|
|
71
|
+
readonly graphql: AppContext['graphql'] & {
|
|
72
|
+
/**
|
|
73
|
+
* The GraphQL controller used for this component test.
|
|
74
|
+
*/
|
|
75
|
+
readonly controller: GraphQLController;
|
|
76
|
+
};
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
export interface RenderActions extends Record<string, never> {}
|
|
@@ -9,12 +9,10 @@ import {Frame} from './foundation/frame.ts';
|
|
|
9
9
|
|
|
10
10
|
import {Home} from './features/home.ts';
|
|
11
11
|
|
|
12
|
-
import {trpc} from './
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from './shared/context.ts';
|
|
17
|
-
import {routeWithAppContext} from './shared/navigation.ts';
|
|
12
|
+
import {trpc} from './context/trpc.ts';
|
|
13
|
+
import type {AppContext as AppContextType} from './context/types.ts';
|
|
14
|
+
import {AppContextPreact} from './context/preact.ts';
|
|
15
|
+
import {routeWithAppContext} from './context/navigation.ts';
|
|
18
16
|
|
|
19
17
|
export interface AppProps {
|
|
20
18
|
context: AppContextType;
|
|
@@ -47,7 +45,7 @@ export function App({context}: AppProps) {
|
|
|
47
45
|
<Localization>
|
|
48
46
|
<Head />
|
|
49
47
|
<Navigation
|
|
50
|
-
router={context.router}
|
|
48
|
+
router={context.navigation.router}
|
|
51
49
|
routes={routes}
|
|
52
50
|
context={context}
|
|
53
51
|
/>
|
|
@@ -1,60 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {hydrate} from '@quilted/quilt/browser';
|
|
3
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
1
|
+
import './browser.css';
|
|
4
2
|
|
|
5
|
-
import {
|
|
6
|
-
import {QueryClient} from '@tanstack/react-query';
|
|
3
|
+
import {BrowserApp} from '@quilted/quilt/browser';
|
|
7
4
|
|
|
8
|
-
import
|
|
9
|
-
import {trpc} from '~/shared/trpc.ts';
|
|
5
|
+
import {BrowserAppContext} from '~/context/browser.ts';
|
|
10
6
|
|
|
11
7
|
import {App} from './App.tsx';
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The app's root JSX element, seeded with the necessary app context.
|
|
35
|
-
*/
|
|
36
|
-
readonly rendered: ComponentChild;
|
|
37
|
-
|
|
38
|
-
constructor() {
|
|
39
|
-
this.context = new BrowserAppContext();
|
|
40
|
-
this.rendered = <App context={this.context} />;
|
|
41
|
-
|
|
42
|
-
// Makes key parts of the app available in the browser console.
|
|
43
|
-
//
|
|
44
|
-
// @example
|
|
45
|
-
// ```js
|
|
46
|
-
// // Log the current URL
|
|
47
|
-
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
48
|
-
// ```
|
|
49
|
-
Object.defineProperty(globalThis, 'app', {
|
|
50
|
-
value: this,
|
|
51
|
-
enumerable: false,
|
|
52
|
-
configurable: true,
|
|
53
|
-
writable: true,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const app = new BrowserApp();
|
|
59
|
-
|
|
60
|
-
hydrate(app.rendered);
|
|
9
|
+
const context = new BrowserAppContext();
|
|
10
|
+
|
|
11
|
+
const app = new BrowserApp(<App context={context} />, {context});
|
|
12
|
+
|
|
13
|
+
// Makes key parts of the app available in the browser console.
|
|
14
|
+
//
|
|
15
|
+
// @example
|
|
16
|
+
// ```js
|
|
17
|
+
// // Log the current URL
|
|
18
|
+
// console.log(globalThis.app.context.navigation.router.currentRequest.url);
|
|
19
|
+
// ```
|
|
20
|
+
Object.defineProperty(globalThis, 'app', {
|
|
21
|
+
value: app,
|
|
22
|
+
enumerable: false,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await app.hydrate();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {httpBatchLink} from '@trpc/client';
|
|
2
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
import type {AppContext} from './types.ts';
|
|
5
|
+
import {NavigationForApp} from './navigation.ts';
|
|
6
|
+
import {trpc} from './trpc.ts';
|
|
7
|
+
|
|
8
|
+
export class BrowserAppContext implements AppContext {
|
|
9
|
+
readonly navigation: NavigationForApp;
|
|
10
|
+
readonly trpc: AppContext['trpc'];
|
|
11
|
+
readonly queryClient: QueryClient;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.navigation = new NavigationForApp();
|
|
15
|
+
this.queryClient = new QueryClient();
|
|
16
|
+
this.trpc = trpc.createClient({
|
|
17
|
+
links: [httpBatchLink({url: new URL('/api', window.location.href).href})],
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {createContextRouteFunction, Router} from '@quilted/quilt/navigation';
|
|
2
|
+
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
|
+
|
|
5
|
+
export interface Navigation {
|
|
6
|
+
readonly router: Router;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class NavigationForApp implements Navigation {
|
|
10
|
+
readonly router: Router;
|
|
11
|
+
|
|
12
|
+
constructor(url?: string | URL) {
|
|
13
|
+
this.router = new Router(url);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {createOptionalContext} from '@quilted/quilt/context';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import type {AppContext} from './types.ts';
|
|
4
4
|
|
|
5
5
|
export const AppContextPreact = createOptionalContext<AppContext>();
|
|
6
6
|
export const useAppContext = AppContextPreact.use;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {createDirectClient} from '@quilted/trpc/server';
|
|
2
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
import type {AppContext} from './types.ts';
|
|
5
|
+
import {NavigationForApp} from './navigation.ts';
|
|
6
|
+
import {appRouter} from '../trpc.ts';
|
|
7
|
+
|
|
8
|
+
export class ServerAppContext implements AppContext {
|
|
9
|
+
readonly navigation: NavigationForApp;
|
|
10
|
+
readonly trpc: AppContext['trpc'];
|
|
11
|
+
readonly queryClient: QueryClient;
|
|
12
|
+
|
|
13
|
+
constructor(request: Request) {
|
|
14
|
+
this.navigation = new NavigationForApp(request.url);
|
|
15
|
+
this.trpc = createDirectClient(appRouter);
|
|
16
|
+
this.queryClient = new QueryClient();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {Navigation} from './navigation.ts';
|
|
2
|
+
import type {TRPCUntypedClient} from '@trpc/client';
|
|
3
|
+
import type {QueryClient} from '@tanstack/react-query';
|
|
4
|
+
|
|
5
|
+
import type {AppRouter} from '../trpc.ts';
|
|
6
|
+
|
|
7
|
+
export interface AppContext {
|
|
8
|
+
readonly navigation: Navigation;
|
|
9
|
+
readonly trpc: TRPCUntypedClient<AppRouter>;
|
|
10
|
+
readonly queryClient: QueryClient;
|
|
11
|
+
}
|
|
@@ -6,34 +6,16 @@ import {
|
|
|
6
6
|
PermissionsPolicyHeader,
|
|
7
7
|
StrictTransportSecurityHeader,
|
|
8
8
|
} from '@quilted/quilt/server';
|
|
9
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
10
|
-
|
|
11
9
|
import Env from 'quilt:module/env';
|
|
12
10
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
13
11
|
|
|
14
|
-
import {createDirectClient} from '@quilted/trpc/server';
|
|
15
12
|
import {fetchRequestHandler} from '@trpc/server/adapters/fetch';
|
|
16
|
-
import {QueryClient} from '@tanstack/react-query';
|
|
17
|
-
|
|
18
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
19
13
|
|
|
20
14
|
import {appRouter} from './trpc.ts';
|
|
21
15
|
|
|
22
16
|
const app = new Hono();
|
|
23
17
|
const assets = new BrowserAssets();
|
|
24
18
|
|
|
25
|
-
class ServerAppContext implements AppContext {
|
|
26
|
-
readonly router: Router;
|
|
27
|
-
readonly trpc: AppContext['trpc'];
|
|
28
|
-
readonly queryClient: QueryClient;
|
|
29
|
-
|
|
30
|
-
constructor(request: Request) {
|
|
31
|
-
this.router = new Router(request.url);
|
|
32
|
-
this.trpc = createDirectClient(appRouter);
|
|
33
|
-
this.queryClient = new QueryClient();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
19
|
// TRPC API
|
|
38
20
|
app.all('/api/*', async (c) => {
|
|
39
21
|
const request = c.req.raw;
|
|
@@ -49,7 +31,10 @@ app.all('/api/*', async (c) => {
|
|
|
49
31
|
app.get('*', async (c) => {
|
|
50
32
|
const request = c.req.raw;
|
|
51
33
|
|
|
52
|
-
const [{App}] = await Promise.all([
|
|
34
|
+
const [{App}, {ServerAppContext}] = await Promise.all([
|
|
35
|
+
import('./App.tsx'),
|
|
36
|
+
import('./context/server.ts'),
|
|
37
|
+
]);
|
|
53
38
|
|
|
54
39
|
const context = new ServerAppContext(request);
|
|
55
40
|
|
|
@@ -8,8 +8,8 @@ import {Localization} from '@quilted/quilt/localize';
|
|
|
8
8
|
|
|
9
9
|
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
|
|
10
10
|
|
|
11
|
-
import {trpc} from '~/
|
|
12
|
-
import {AppContextPreact} from '~/
|
|
11
|
+
import {trpc} from '~/context/trpc.ts';
|
|
12
|
+
import {AppContextPreact} from '~/context/preact.ts';
|
|
13
13
|
|
|
14
14
|
import {RenderOptions, RenderContext, RenderActions} from './types.ts';
|
|
15
15
|
|
|
@@ -28,7 +28,7 @@ export const renderApp = createRender<
|
|
|
28
28
|
// React tree and your test code, and is ideal for mocking out global context providers.
|
|
29
29
|
context({router = new TestRouter(), browser = new BrowserTestMock()}) {
|
|
30
30
|
return {
|
|
31
|
-
router,
|
|
31
|
+
navigation: {router},
|
|
32
32
|
browser,
|
|
33
33
|
trpc: trpc.createClient(),
|
|
34
34
|
queryClient: new QueryClient(),
|
|
@@ -36,7 +36,12 @@ export const renderApp = createRender<
|
|
|
36
36
|
},
|
|
37
37
|
// Render all of our app-wide context providers around each component under test.
|
|
38
38
|
render(element, context, {locale = 'en'}) {
|
|
39
|
-
const {
|
|
39
|
+
const {
|
|
40
|
+
navigation: {router},
|
|
41
|
+
browser,
|
|
42
|
+
trpc: trpcClient,
|
|
43
|
+
queryClient,
|
|
44
|
+
} = context;
|
|
40
45
|
|
|
41
46
|
return (
|
|
42
47
|
<AppContextPreact.Provider value={context}>
|
|
@@ -2,7 +2,7 @@ import type {TestRouter} from '@quilted/quilt/navigation/testing';
|
|
|
2
2
|
import type {BrowserTestMock} from '@quilted/quilt/browser/testing';
|
|
3
3
|
import type {QueryClient} from '@tanstack/react-query';
|
|
4
4
|
|
|
5
|
-
import type {AppContext} from '~/
|
|
5
|
+
import type {AppContext} from '~/context/types.ts';
|
|
6
6
|
|
|
7
7
|
export interface RenderOptions {
|
|
8
8
|
/**
|
|
@@ -28,7 +28,7 @@ export interface RenderContext extends AppContext {
|
|
|
28
28
|
/**
|
|
29
29
|
* The router used for this component test.
|
|
30
30
|
*/
|
|
31
|
-
readonly router: TestRouter;
|
|
31
|
+
readonly navigation: {router: TestRouter};
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* The browser environment for this component test.
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type {Router} from '@quilted/quilt/navigation';
|
|
2
|
-
import {createContextRouteFunction} from '@quilted/quilt/navigation';
|
|
3
|
-
|
|
4
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
5
|
-
|
|
6
|
-
declare module '~/shared/context.ts' {
|
|
7
|
-
interface AppContext {
|
|
8
|
-
/**
|
|
9
|
-
* The router used to control navigation throughout the application.
|
|
10
|
-
*/
|
|
11
|
-
readonly router: Router;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type {GraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
2
|
-
|
|
3
|
-
declare module '~/shared/context.ts' {
|
|
4
|
-
interface AppContext {
|
|
5
|
-
/**
|
|
6
|
-
* The app’s GraphQL client.
|
|
7
|
-
*/
|
|
8
|
-
readonly graphql: {
|
|
9
|
-
/**
|
|
10
|
-
* A function to perform GraphQL operations.
|
|
11
|
-
*/
|
|
12
|
-
readonly fetch: GraphQLFetch;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The app’s GraphQL cache.
|
|
16
|
-
*/
|
|
17
|
-
readonly cache: GraphQLCache;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export {};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type {Router} from '@quilted/quilt/navigation';
|
|
2
|
-
import {createContextRouteFunction} from '@quilted/quilt/navigation';
|
|
3
|
-
|
|
4
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
5
|
-
|
|
6
|
-
declare module '~/shared/context.ts' {
|
|
7
|
-
interface AppContext {
|
|
8
|
-
/**
|
|
9
|
-
* The router used to control navigation throughout the application.
|
|
10
|
-
*/
|
|
11
|
-
readonly router: Router;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type {Router} from '@quilted/quilt/navigation';
|
|
2
|
-
import {createContextRouteFunction} from '@quilted/quilt/navigation';
|
|
3
|
-
|
|
4
|
-
import type {AppContext} from '~/shared/context.ts';
|
|
5
|
-
|
|
6
|
-
declare module '~/shared/context.ts' {
|
|
7
|
-
interface AppContext {
|
|
8
|
-
/**
|
|
9
|
-
* The router used to control navigation throughout the application.
|
|
10
|
-
*/
|
|
11
|
-
readonly router: Router;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const routeWithAppContext = createContextRouteFunction<AppContext>();
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type {TRPCUntypedClient} from '@trpc/client';
|
|
2
|
-
import {createTRPCReact, type CreateTRPCReact} from '@trpc/react-query';
|
|
3
|
-
import type {QueryClient} from '@tanstack/react-query';
|
|
4
|
-
|
|
5
|
-
// Get access to our app’s router type signature, which will
|
|
6
|
-
// provide strong typing on the queries and mutations we can
|
|
7
|
-
// perform.
|
|
8
|
-
import type {AppRouter} from '../trpc.ts';
|
|
9
|
-
|
|
10
|
-
export const trpc: CreateTRPCReact<AppRouter, {}> =
|
|
11
|
-
createTRPCReact<AppRouter>();
|
|
12
|
-
|
|
13
|
-
declare module '~/shared/context.ts' {
|
|
14
|
-
interface AppContext {
|
|
15
|
-
trpc: TRPCUntypedClient<AppRouter>;
|
|
16
|
-
queryClient: QueryClient;
|
|
17
|
-
}
|
|
18
|
-
}
|