@quilted/create 0.2.41 → 0.2.43
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 +14 -0
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/index.mjs +2 -2
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/async.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/sync.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/async.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/stream.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/sync.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/async.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/readers/sync.mjs +1 -1
- package/package.json +1 -1
- package/templates/app-basic/App.tsx +3 -4
- package/templates/app-basic/browser.tsx +8 -13
- package/templates/app-basic/foundation/html/Head.test.tsx +42 -0
- package/templates/app-basic/foundation/html/Head.tsx +34 -0
- package/templates/app-basic/foundation/html.ts +1 -1
- package/templates/app-basic/server.tsx +89 -3
- package/templates/app-basic/tests/render/render.tsx +6 -3
- package/templates/app-empty/server.tsx +4 -3
- package/templates/app-graphql/App.tsx +3 -4
- package/templates/app-graphql/browser.tsx +9 -14
- package/templates/app-graphql/foundation/html/Head.test.tsx +42 -0
- package/templates/app-graphql/foundation/html/Head.tsx +34 -0
- package/templates/app-graphql/foundation/html.ts +1 -1
- package/templates/app-graphql/server.tsx +93 -9
- package/templates/app-graphql/tests/render/render.tsx +6 -3
- package/templates/app-trpc/App.tsx +3 -4
- package/templates/app-trpc/browser.tsx +11 -15
- package/templates/app-trpc/foundation/html/Head.test.tsx +42 -0
- package/templates/app-trpc/foundation/html/Head.tsx +34 -0
- package/templates/app-trpc/foundation/html.ts +1 -1
- package/templates/app-trpc/server.tsx +93 -8
- package/templates/app-trpc/tests/render/render.tsx +6 -3
- package/templates/app-basic/foundation/html/HTML.test.tsx +0 -46
- package/templates/app-basic/foundation/html/HTML.tsx +0 -159
- package/templates/app-graphql/foundation/html/HTML.test.tsx +0 -46
- package/templates/app-graphql/foundation/html/HTML.tsx +0 -159
- package/templates/app-trpc/foundation/html/HTML.test.tsx +0 -46
- package/templates/app-trpc/foundation/html/HTML.tsx +0 -159
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @quilted/create
|
|
2
2
|
|
|
3
|
+
## 0.2.43
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`93c5767`](https://github.com/lemonmade/quilt/commit/93c5767e31a5c968bf1fe23cb448bf1c297c91ca) Thanks [@lemonmade](https://github.com/lemonmade)! - Update browser global definitions in app template
|
|
8
|
+
|
|
9
|
+
- [`33cf6c8`](https://github.com/lemonmade/quilt/commit/33cf6c87e899b54865fe6f1d82b5dab469e7a5fe) Thanks [@lemonmade](https://github.com/lemonmade)! - Move headers to be server-only in default templates
|
|
10
|
+
|
|
11
|
+
## 0.2.42
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- [#843](https://github.com/lemonmade/quilt/pull/843) [`5a8036d`](https://github.com/lemonmade/quilt/commit/5a8036d39d93c576812428ecc8fe537a30696dba) Thanks [@lemonmade](https://github.com/lemonmade)! - Make browser context creation implicit on the client
|
|
16
|
+
|
|
3
17
|
## 0.2.41
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -3,8 +3,8 @@ import './providers/async.mjs';
|
|
|
3
3
|
import './providers/sync.mjs';
|
|
4
4
|
import './settings.mjs';
|
|
5
5
|
import { __exports as settings } from '../../../../../../../_virtual/settings3.mjs';
|
|
6
|
-
import { __exports as async$1 } from '../../../../../../../_virtual/
|
|
7
|
-
import { __exports as sync$1 } from '../../../../../../../_virtual/
|
|
6
|
+
import { __exports as async$1 } from '../../../../../../../_virtual/async5.mjs';
|
|
7
|
+
import { __exports as sync$1 } from '../../../../../../../_virtual/sync5.mjs';
|
|
8
8
|
|
|
9
9
|
Object.defineProperty(out, "__esModule", {
|
|
10
10
|
value: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exports as async } from '../../../../../../../../_virtual/async3.mjs';
|
|
2
2
|
import '../readers/async.mjs';
|
|
3
|
-
import { __exports as async$1 } from '../../../../../../../../_virtual/
|
|
3
|
+
import { __exports as async$1 } from '../../../../../../../../_virtual/async4.mjs';
|
|
4
4
|
|
|
5
5
|
Object.defineProperty(async, "__esModule", {
|
|
6
6
|
value: true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __exports as stream } from '../../../../../../../../_virtual/stream4.mjs';
|
|
2
2
|
import require$$0 from 'node:stream';
|
|
3
3
|
import '../readers/async.mjs';
|
|
4
|
-
import { __exports as async } from '../../../../../../../../_virtual/
|
|
4
|
+
import { __exports as async } from '../../../../../../../../_virtual/async4.mjs';
|
|
5
5
|
|
|
6
6
|
Object.defineProperty(stream, "__esModule", {
|
|
7
7
|
value: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exports as sync } from '../../../../../../../../_virtual/sync3.mjs';
|
|
2
2
|
import '../readers/sync.mjs';
|
|
3
|
-
import { __exports as sync$1 } from '../../../../../../../../_virtual/
|
|
3
|
+
import { __exports as sync$1 } from '../../../../../../../../_virtual/sync4.mjs';
|
|
4
4
|
|
|
5
5
|
Object.defineProperty(sync, "__esModule", {
|
|
6
6
|
value: true
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as async } from '../../../../../../../../_virtual/
|
|
1
|
+
import { __exports as async } from '../../../../../../../../_virtual/async4.mjs';
|
|
2
2
|
import require$$2 from 'node:events';
|
|
3
3
|
import '../../../../../../@nodelib_fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/index.mjs';
|
|
4
4
|
import { queueExports } from '../../../../../../fastq@1.13.0/node_modules/fastq/queue.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as sync } from '../../../../../../../../_virtual/
|
|
1
|
+
import { __exports as sync } from '../../../../../../../../_virtual/sync4.mjs';
|
|
2
2
|
import '../../../../../../@nodelib_fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/index.mjs';
|
|
3
3
|
import './common.mjs';
|
|
4
4
|
import './reader.mjs';
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import {NotFound} from '@quilted/quilt/server';
|
|
|
4
4
|
import {Navigation} from '@quilted/quilt/navigation';
|
|
5
5
|
import {Localization} from '@quilted/quilt/localize';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {Head} from './foundation/html.ts';
|
|
8
8
|
import {Frame} from './foundation/frame.ts';
|
|
9
9
|
|
|
10
10
|
import {Home} from './features/home.ts';
|
|
@@ -41,9 +41,8 @@ const routes = [
|
|
|
41
41
|
export function App({context}: AppProps) {
|
|
42
42
|
return (
|
|
43
43
|
<AppContext context={context}>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
</HTML>
|
|
44
|
+
<Head />
|
|
45
|
+
<Navigation router={context.router} routes={routes} context={context} />
|
|
47
46
|
</AppContext>
|
|
48
47
|
);
|
|
49
48
|
}
|
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
|
-
import {hydrate} from '
|
|
3
|
-
import {Browser, BrowserContext} from '@quilted/quilt/browser';
|
|
2
|
+
import {hydrate} from '@quilted/quilt/browser';
|
|
4
3
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
4
|
|
|
6
5
|
import type {AppContext} from '~/shared/context.ts';
|
|
7
6
|
import {App} from './App.tsx';
|
|
8
7
|
|
|
9
|
-
const element = document.querySelector('#app')!;
|
|
10
|
-
const browser = new Browser();
|
|
11
|
-
|
|
12
8
|
const context = {
|
|
13
|
-
router: new Router(
|
|
9
|
+
router: new Router(),
|
|
14
10
|
} satisfies AppContext;
|
|
15
11
|
|
|
16
12
|
// Makes key parts of the app available in the browser console
|
|
17
|
-
Object.
|
|
13
|
+
Object.defineProperty(globalThis, 'app', {
|
|
14
|
+
value: {context},
|
|
15
|
+
enumerable: false,
|
|
16
|
+
configurable: true,
|
|
17
|
+
});
|
|
18
18
|
|
|
19
|
-
hydrate(
|
|
20
|
-
<BrowserContext browser={browser}>
|
|
21
|
-
<App context={context} />
|
|
22
|
-
</BrowserContext>,
|
|
23
|
-
element,
|
|
24
|
-
);
|
|
19
|
+
hydrate(<App context={context} />);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import {Title, Favicon} from '@quilted/quilt/browser';
|
|
3
|
+
import {SearchRobots, Viewport} from '@quilted/quilt/server';
|
|
4
|
+
|
|
5
|
+
import {renderApp} from '~/tests/render.ts';
|
|
6
|
+
|
|
7
|
+
import {Head} from './Head.tsx';
|
|
8
|
+
|
|
9
|
+
describe('<Head />', () => {
|
|
10
|
+
it('sets the default title', async () => {
|
|
11
|
+
const head = await renderApp(<Head />);
|
|
12
|
+
|
|
13
|
+
expect(head).toContainPreactComponent(Title, {
|
|
14
|
+
children: 'App',
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('uses a fun, emoji favicon', async () => {
|
|
19
|
+
const head = await renderApp(<Head />);
|
|
20
|
+
|
|
21
|
+
expect(head).toContainPreactComponent(Favicon, {
|
|
22
|
+
emoji: '🧶',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('includes a responsive viewport tag', async () => {
|
|
27
|
+
const head = await renderApp(<Head />);
|
|
28
|
+
|
|
29
|
+
expect(head).toContainPreactComponent(Viewport, {
|
|
30
|
+
cover: true,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('prevents search robots from indexing the application', async () => {
|
|
35
|
+
const head = await renderApp(<Head />);
|
|
36
|
+
|
|
37
|
+
expect(head).toContainPreactComponent(SearchRobots, {
|
|
38
|
+
index: false,
|
|
39
|
+
follow: false,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {Title, Favicon} from '@quilted/quilt/browser';
|
|
2
|
+
import {SearchRobots, Viewport} from '@quilted/quilt/server';
|
|
3
|
+
|
|
4
|
+
// This component sets default `<head>` details of the HTML page. If you need
|
|
5
|
+
// to customize any of these details based on conditions or application data, you
|
|
6
|
+
// can render these same components deeper in the application to override these defaults.
|
|
7
|
+
//
|
|
8
|
+
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/html.md
|
|
9
|
+
export function Head() {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
{/* Sets the default `<title>` for this application. */}
|
|
13
|
+
<Title>App</Title>
|
|
14
|
+
|
|
15
|
+
{/*
|
|
16
|
+
* Sets the default favicon used by the application. You can
|
|
17
|
+
* change this to a different emoji, make it `blank`, or pass
|
|
18
|
+
* a URL with the `source` prop.
|
|
19
|
+
*/}
|
|
20
|
+
<Favicon emoji="🧶" />
|
|
21
|
+
|
|
22
|
+
{/* Adds a responsive-friendly `viewport` `<meta>` tag. */}
|
|
23
|
+
<Viewport cover />
|
|
24
|
+
|
|
25
|
+
{/*
|
|
26
|
+
* Disables all search indexing for this application. If you are
|
|
27
|
+
* building an unauthenticated app, you probably want to remove
|
|
28
|
+
* this component, or update it to control how your site is indexed
|
|
29
|
+
* by search engines.
|
|
30
|
+
*/}
|
|
31
|
+
<SearchRobots index={false} follow={false} />
|
|
32
|
+
</>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {Head} from './html/Head.tsx';
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
|
+
|
|
2
3
|
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
3
|
-
import {renderToResponse} from '@quilted/quilt/server';
|
|
4
4
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
|
+
import {
|
|
6
|
+
renderAppToHTMLResponse,
|
|
7
|
+
cacheControlHeader,
|
|
8
|
+
contentSecurityPolicyHeader,
|
|
9
|
+
permissionsPolicyHeader,
|
|
10
|
+
strictTransportSecurityHeader,
|
|
11
|
+
} from '@quilted/quilt/server';
|
|
12
|
+
|
|
13
|
+
import Env from 'quilt:module/env';
|
|
5
14
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
6
15
|
|
|
7
16
|
import type {AppContext} from '~/shared/context.ts';
|
|
@@ -11,15 +20,92 @@ import {App} from './App.tsx';
|
|
|
11
20
|
const router = new RequestRouter();
|
|
12
21
|
const assets = new BrowserAssets();
|
|
13
22
|
|
|
14
|
-
// For all GET requests, render our
|
|
23
|
+
// For all GET requests, render our Preact application.
|
|
15
24
|
router.get(async (request) => {
|
|
16
25
|
const context = {
|
|
17
26
|
router: new Router(request.url),
|
|
18
27
|
} satisfies AppContext;
|
|
19
28
|
|
|
20
|
-
const
|
|
29
|
+
const isHttps = request.url.startsWith('https://');
|
|
30
|
+
|
|
31
|
+
const response = await renderAppToHTMLResponse(<App context={context} />, {
|
|
21
32
|
request,
|
|
22
33
|
assets,
|
|
34
|
+
headers: {
|
|
35
|
+
// Controls how much information about the current page is included in
|
|
36
|
+
// requests (through the `Referer` header). The default value
|
|
37
|
+
// (strict-origin-when-cross-origin) means that only the origin is included
|
|
38
|
+
// for cross-origin requests, while the origin, path, and querystring
|
|
39
|
+
// are included for same-origin requests.
|
|
40
|
+
//
|
|
41
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
|
|
42
|
+
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
|
43
|
+
|
|
44
|
+
// Disables the cache for this page, which is generally the best option
|
|
45
|
+
// when dealing with authenticated content. If your site doesn't have
|
|
46
|
+
// authentication, or you have a better cache policy that works for your
|
|
47
|
+
// app or deployment, make sure to update this component accordingly!
|
|
48
|
+
//
|
|
49
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
50
|
+
'Cache-Control': cacheControlHeader({
|
|
51
|
+
cache: false,
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
// Sets a strict content security policy for this page. If you load
|
|
55
|
+
// assets from other origins, or want to allow some more dangerous
|
|
56
|
+
// resource loading techniques like `eval`, you can change the
|
|
57
|
+
// `defaultSources` to be less restrictive, or add additional items
|
|
58
|
+
// to the allowlist for more specific directives.
|
|
59
|
+
//
|
|
60
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
|
61
|
+
'Content-Security-Policy': contentSecurityPolicyHeader({
|
|
62
|
+
// By default, only allow sources from the page's origin.
|
|
63
|
+
defaultSources: ["'self'"],
|
|
64
|
+
// In development, allow connections to local websockets for hot reloading.
|
|
65
|
+
connectSources:
|
|
66
|
+
Env.MODE === 'development'
|
|
67
|
+
? ["'self'", `${isHttps ? 'ws' : 'wss'}://localhost:*`]
|
|
68
|
+
: undefined,
|
|
69
|
+
// Includes `'unsafe-inline'` because CSS is often necessary in development,
|
|
70
|
+
// and can be difficult to avoid in production.
|
|
71
|
+
styleSources: ["'self'", "'unsafe-inline'"],
|
|
72
|
+
// Includes `data:` so that an inline image can be used for the favicon.
|
|
73
|
+
// If you do not use the `emoji` or `blank` favicons in your app, and you
|
|
74
|
+
// do not load any other images as data URIs, you can remove this directive.
|
|
75
|
+
imageSources: ["'self'", 'data:'],
|
|
76
|
+
// Don't allow this page to be rendered as a frame from a different origin.
|
|
77
|
+
frameAncestors: false,
|
|
78
|
+
// Ensure that all requests made by this page are made over https, unless
|
|
79
|
+
// it is being served over http (typically, during local development)
|
|
80
|
+
upgradeInsecureRequests: isHttps,
|
|
81
|
+
}),
|
|
82
|
+
|
|
83
|
+
// Sets a strict permissions policy for this page, which limits access
|
|
84
|
+
// to some native browser features.
|
|
85
|
+
//
|
|
86
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
|
|
87
|
+
'Permissions-Policy': permissionsPolicyHeader({
|
|
88
|
+
// Disables Google's Federated Learning of Cohorts ("FLoC") tracking initiative.
|
|
89
|
+
// @see https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea
|
|
90
|
+
interestCohort: false,
|
|
91
|
+
// Don't use synchronous XHRs!
|
|
92
|
+
// @see https://featurepolicy.info/policies/sync-xhr
|
|
93
|
+
syncXhr: false,
|
|
94
|
+
// Disables access to a few device APIs that are infrequently used
|
|
95
|
+
// and prone to abuse. If your application uses these APIs intentionally,
|
|
96
|
+
// feel free to remove the prop, or pass an array containing the origins
|
|
97
|
+
// that should be allowed to use this feature (e.g., `['self']` to allow
|
|
98
|
+
// only the main page's origin).
|
|
99
|
+
camera: false,
|
|
100
|
+
microphone: false,
|
|
101
|
+
geolocation: false,
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
// Instructs browsers to only load this page over HTTPS.
|
|
105
|
+
//
|
|
106
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
|
107
|
+
'Strict-Transport-Security': strictTransportSecurityHeader(),
|
|
108
|
+
},
|
|
23
109
|
});
|
|
24
110
|
|
|
25
111
|
return response;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {createRender} from '@quilted/quilt/testing';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BrowserDetailsContext,
|
|
4
|
+
BrowserTestMock,
|
|
5
|
+
} from '@quilted/quilt/browser/testing';
|
|
3
6
|
import {Navigation, TestRouter} from '@quilted/quilt/navigation/testing';
|
|
4
7
|
import {Localization} from '@quilted/quilt/localize';
|
|
5
8
|
|
|
@@ -29,11 +32,11 @@ export const renderApp = createRender<
|
|
|
29
32
|
|
|
30
33
|
return (
|
|
31
34
|
<AppContextReact.Provider value={context}>
|
|
32
|
-
<
|
|
35
|
+
<BrowserDetailsContext.Provider value={browser}>
|
|
33
36
|
<Localization locale={locale}>
|
|
34
37
|
<Navigation router={router}>{element}</Navigation>
|
|
35
38
|
</Localization>
|
|
36
|
-
</
|
|
39
|
+
</BrowserDetailsContext.Provider>
|
|
37
40
|
</AppContextReact.Provider>
|
|
38
41
|
);
|
|
39
42
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
|
+
|
|
2
3
|
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
3
|
-
import {
|
|
4
|
+
import {renderAppToHTMLResponse} from '@quilted/quilt/server';
|
|
4
5
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
5
6
|
|
|
6
7
|
import {App} from './App.tsx';
|
|
@@ -8,9 +9,9 @@ import {App} from './App.tsx';
|
|
|
8
9
|
const router = new RequestRouter();
|
|
9
10
|
const assets = new BrowserAssets();
|
|
10
11
|
|
|
11
|
-
// For all GET requests, render our
|
|
12
|
+
// For all GET requests, render our Preact application.
|
|
12
13
|
router.get(async (request) => {
|
|
13
|
-
const response = await
|
|
14
|
+
const response = await renderAppToHTMLResponse(<App />, {
|
|
14
15
|
request,
|
|
15
16
|
assets,
|
|
16
17
|
});
|
|
@@ -5,7 +5,7 @@ import {GraphQLContext} from '@quilted/quilt/graphql';
|
|
|
5
5
|
import {Navigation} from '@quilted/quilt/navigation';
|
|
6
6
|
import {Localization} from '@quilted/quilt/localize';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {Head} from './foundation/html.ts';
|
|
9
9
|
import {Frame} from './foundation/frame.ts';
|
|
10
10
|
|
|
11
11
|
import {Home, homeQuery} from './features/home.ts';
|
|
@@ -42,9 +42,8 @@ const routes = [
|
|
|
42
42
|
export function App({context}: AppProps) {
|
|
43
43
|
return (
|
|
44
44
|
<AppContext context={context}>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
</HTML>
|
|
45
|
+
<Head />
|
|
46
|
+
<Navigation router={context.router} routes={routes} context={context} />
|
|
48
47
|
</AppContext>
|
|
49
48
|
);
|
|
50
49
|
}
|
|
@@ -1,22 +1,18 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
2
|
|
|
3
|
-
import {hydrate} from '
|
|
4
|
-
import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
5
|
-
import {Browser, BrowserContext} from '@quilted/quilt/browser';
|
|
3
|
+
import {hydrate} from '@quilted/quilt/browser';
|
|
6
4
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
|
+
import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
7
6
|
|
|
8
7
|
import type {AppContext} from '~/shared/context.ts';
|
|
9
8
|
|
|
10
9
|
import {App} from './App.tsx';
|
|
11
10
|
|
|
12
|
-
const element = document.querySelector('#app')!;
|
|
13
|
-
const browser = new Browser();
|
|
14
|
-
|
|
15
11
|
const graphQLFetch = createGraphQLFetch({url: '/api/graphql'});
|
|
16
12
|
const graphQLCache = new GraphQLCache({fetch: graphQLFetch});
|
|
17
13
|
|
|
18
14
|
const context = {
|
|
19
|
-
router: new Router(
|
|
15
|
+
router: new Router(),
|
|
20
16
|
graphql: {
|
|
21
17
|
fetch: graphQLFetch,
|
|
22
18
|
cache: graphQLCache,
|
|
@@ -24,11 +20,10 @@ const context = {
|
|
|
24
20
|
} satisfies AppContext;
|
|
25
21
|
|
|
26
22
|
// Makes key parts of the app available in the browser console
|
|
27
|
-
Object.
|
|
23
|
+
Object.defineProperty(globalThis, 'app', {
|
|
24
|
+
value: {context},
|
|
25
|
+
enumerable: false,
|
|
26
|
+
configurable: true,
|
|
27
|
+
});
|
|
28
28
|
|
|
29
|
-
hydrate(
|
|
30
|
-
<BrowserContext browser={browser}>
|
|
31
|
-
<App context={context} />
|
|
32
|
-
</BrowserContext>,
|
|
33
|
-
element,
|
|
34
|
-
);
|
|
29
|
+
hydrate(<App context={context} />);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import {Title, Favicon} from '@quilted/quilt/browser';
|
|
3
|
+
import {SearchRobots, Viewport} from '@quilted/quilt/server';
|
|
4
|
+
|
|
5
|
+
import {renderApp} from '~/tests/render.ts';
|
|
6
|
+
|
|
7
|
+
import {Head} from './Head.tsx';
|
|
8
|
+
|
|
9
|
+
describe('<Head />', () => {
|
|
10
|
+
it('sets the default title', async () => {
|
|
11
|
+
const head = await renderApp(<Head />);
|
|
12
|
+
|
|
13
|
+
expect(head).toContainPreactComponent(Title, {
|
|
14
|
+
children: 'App',
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('uses a fun, emoji favicon', async () => {
|
|
19
|
+
const head = await renderApp(<Head />);
|
|
20
|
+
|
|
21
|
+
expect(head).toContainPreactComponent(Favicon, {
|
|
22
|
+
emoji: '🧶',
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('includes a responsive viewport tag', async () => {
|
|
27
|
+
const head = await renderApp(<Head />);
|
|
28
|
+
|
|
29
|
+
expect(head).toContainPreactComponent(Viewport, {
|
|
30
|
+
cover: true,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('prevents search robots from indexing the application', async () => {
|
|
35
|
+
const head = await renderApp(<Head />);
|
|
36
|
+
|
|
37
|
+
expect(head).toContainPreactComponent(SearchRobots, {
|
|
38
|
+
index: false,
|
|
39
|
+
follow: false,
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {Title, Favicon} from '@quilted/quilt/browser';
|
|
2
|
+
import {SearchRobots, Viewport} from '@quilted/quilt/server';
|
|
3
|
+
|
|
4
|
+
// This component sets default `<head>` details of the HTML page. If you need
|
|
5
|
+
// to customize any of these details based on conditions or application data, you
|
|
6
|
+
// can render these same components deeper in the application to override these defaults.
|
|
7
|
+
//
|
|
8
|
+
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/html.md
|
|
9
|
+
export function Head() {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
{/* Sets the default `<title>` for this application. */}
|
|
13
|
+
<Title>App</Title>
|
|
14
|
+
|
|
15
|
+
{/*
|
|
16
|
+
* Sets the default favicon used by the application. You can
|
|
17
|
+
* change this to a different emoji, make it `blank`, or pass
|
|
18
|
+
* a URL with the `source` prop.
|
|
19
|
+
*/}
|
|
20
|
+
<Favicon emoji="🧶" />
|
|
21
|
+
|
|
22
|
+
{/* Adds a responsive-friendly `viewport` `<meta>` tag. */}
|
|
23
|
+
<Viewport cover />
|
|
24
|
+
|
|
25
|
+
{/*
|
|
26
|
+
* Disables all search indexing for this application. If you are
|
|
27
|
+
* building an unauthenticated app, you probably want to remove
|
|
28
|
+
* this component, or update it to control how your site is indexed
|
|
29
|
+
* by search engines.
|
|
30
|
+
*/}
|
|
31
|
+
<SearchRobots index={false} follow={false} />
|
|
32
|
+
</>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {Head} from './html/Head.tsx';
|