@quilted/create 0.2.17 → 0.2.19
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/package.json +1 -1
- package/templates/app-basic/App.tsx +18 -22
- package/templates/app-basic/browser.tsx +8 -1
- package/templates/app-basic/features/start.ts +2 -2
- package/templates/app-basic/foundation/html/HTML.test.tsx +46 -0
- package/templates/app-basic/foundation/{http/Headers.tsx → html/HTML.tsx} +50 -10
- package/templates/app-basic/foundation/html.ts +1 -1
- package/templates/app-basic/package.json +1 -1
- package/templates/app-graphql/App.tsx +24 -41
- package/templates/app-graphql/browser.tsx +9 -1
- package/templates/app-graphql/features/start.ts +2 -2
- package/templates/app-graphql/foundation/html/HTML.test.tsx +46 -0
- package/templates/app-graphql/foundation/{http/Headers.tsx → html/HTML.tsx} +50 -10
- package/templates/app-graphql/foundation/html.ts +1 -1
- package/templates/app-graphql/package.json +1 -1
- package/templates/app-graphql/server/graphql.ts +1 -1
- package/templates/app-graphql/server.tsx +8 -2
- package/templates/app-graphql/shared/graphql.ts +11 -0
- package/templates/app-graphql/tests/render/render.tsx +6 -1
- package/templates/app-trpc/App.tsx +30 -46
- package/templates/app-trpc/browser.tsx +10 -1
- package/templates/app-trpc/features/start.ts +2 -2
- package/templates/app-trpc/foundation/html/HTML.test.tsx +46 -0
- package/templates/app-trpc/foundation/{http/Headers.tsx → html/HTML.tsx} +50 -10
- package/templates/app-trpc/foundation/html.ts +1 -1
- package/templates/app-trpc/server.tsx +8 -2
- package/templates/app-trpc/shared/trpc.ts +4 -2
- package/templates/app-basic/foundation/html/Head.test.tsx +0 -25
- package/templates/app-basic/foundation/html/Head.tsx +0 -34
- package/templates/app-basic/foundation/http/Headers.test.tsx +0 -24
- package/templates/app-basic/foundation/http.ts +0 -1
- package/templates/app-graphql/foundation/html/Head.test.tsx +0 -25
- package/templates/app-graphql/foundation/html/Head.tsx +0 -34
- package/templates/app-graphql/foundation/http/Headers.test.tsx +0 -24
- package/templates/app-graphql/foundation/http.ts +0 -1
- package/templates/app-trpc/foundation/html/Head.test.tsx +0 -25
- package/templates/app-trpc/foundation/html/Head.tsx +0 -34
- package/templates/app-trpc/foundation/http/Headers.test.tsx +0 -24
- package/templates/app-trpc/foundation/http.ts +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @quilted/create
|
|
2
2
|
|
|
3
|
+
## 0.2.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#716](https://github.com/lemonmade/quilt/pull/716) [`7daafca`](https://github.com/lemonmade/quilt/commit/7daafca900b3d9ea66be179394eadf7998cc94be) Thanks [@lemonmade](https://github.com/lemonmade)! - Refactor browser APIs
|
|
8
|
+
|
|
9
|
+
## 0.2.18
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#714](https://github.com/lemonmade/quilt/pull/714) [`d4bda43`](https://github.com/lemonmade/quilt/commit/d4bda430900d0e4afd5ccecb04abe9ac81245486) Thanks [@lemonmade](https://github.com/lemonmade)! - Update GraphQL dependencies
|
|
14
|
+
|
|
15
|
+
- [#699](https://github.com/lemonmade/quilt/pull/699) [`8335c47`](https://github.com/lemonmade/quilt/commit/8335c47fa1896ad65d5cd218fe068f22627815d9) Thanks [@lemonmade](https://github.com/lemonmade)! - Update async APIs
|
|
16
|
+
|
|
3
17
|
## 0.2.17
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {type PropsWithChildren} from 'react';
|
|
2
2
|
|
|
3
|
-
import {HTML} from '@quilted/quilt/html';
|
|
4
3
|
import {Routing, useRoutes} from '@quilted/quilt/navigate';
|
|
5
4
|
import {Localization, useLocaleFromEnvironment} from '@quilted/quilt/localize';
|
|
6
5
|
|
|
7
|
-
import {
|
|
8
|
-
import {Headers} from './foundation/http.ts';
|
|
6
|
+
import {HTML} from './foundation/html.ts';
|
|
9
7
|
import {Frame} from './foundation/frame.ts';
|
|
10
8
|
|
|
11
9
|
import {Start} from './features/start.ts';
|
|
@@ -15,27 +13,21 @@ import {
|
|
|
15
13
|
type AppContext as AppContextType,
|
|
16
14
|
} from './shared/context.ts';
|
|
17
15
|
|
|
18
|
-
export interface AppProps
|
|
16
|
+
export interface AppProps {
|
|
17
|
+
context?: AppContextType;
|
|
18
|
+
}
|
|
19
19
|
|
|
20
20
|
// The root component for your application. You will typically render any
|
|
21
21
|
// app-wide context in this component.
|
|
22
|
-
export function App(
|
|
23
|
-
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
24
|
-
|
|
22
|
+
export function App({context}: AppProps) {
|
|
25
23
|
return (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<Routes />
|
|
34
|
-
</Frame>
|
|
35
|
-
</AppContext>
|
|
36
|
-
</Routing>
|
|
37
|
-
</Localization>
|
|
38
|
-
</HTML>
|
|
24
|
+
<AppContext context={context}>
|
|
25
|
+
<HTML>
|
|
26
|
+
<Frame>
|
|
27
|
+
<Routes />
|
|
28
|
+
</Frame>
|
|
29
|
+
</HTML>
|
|
30
|
+
</AppContext>
|
|
39
31
|
);
|
|
40
32
|
}
|
|
41
33
|
|
|
@@ -50,10 +42,14 @@ function Routes() {
|
|
|
50
42
|
}
|
|
51
43
|
|
|
52
44
|
// This component renders any app-wide context.
|
|
53
|
-
function AppContext({children,
|
|
45
|
+
function AppContext({children, context}: PropsWithChildren<AppProps>) {
|
|
46
|
+
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
47
|
+
|
|
54
48
|
return (
|
|
55
49
|
<AppContextReact.Provider value={context}>
|
|
56
|
-
{
|
|
50
|
+
<Localization locale={locale}>
|
|
51
|
+
<Routing>{children}</Routing>
|
|
52
|
+
</Localization>
|
|
57
53
|
</AppContextReact.Provider>
|
|
58
54
|
);
|
|
59
55
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
2
|
import {hydrateRoot} from 'react-dom/client';
|
|
3
|
+
import {Browser, BrowserContext} from '@quilted/quilt/browser';
|
|
3
4
|
|
|
4
5
|
import {App} from './App.tsx';
|
|
5
6
|
|
|
6
7
|
const element = document.querySelector('#app')!;
|
|
8
|
+
const browser = new Browser();
|
|
7
9
|
|
|
8
|
-
hydrateRoot(
|
|
10
|
+
hydrateRoot(
|
|
11
|
+
element,
|
|
12
|
+
<BrowserContext browser={browser}>
|
|
13
|
+
<App />
|
|
14
|
+
</BrowserContext>,
|
|
15
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
CacheControl,
|
|
4
|
+
ContentSecurityPolicy,
|
|
5
|
+
SearchRobots,
|
|
6
|
+
Viewport,
|
|
7
|
+
} from '@quilted/quilt/server';
|
|
8
|
+
|
|
9
|
+
import {renderApp} from '~/tests/render.ts';
|
|
10
|
+
|
|
11
|
+
import {HTML} from './HTML.tsx';
|
|
12
|
+
|
|
13
|
+
describe('<HTML />', () => {
|
|
14
|
+
it('includes a responsive viewport tag', async () => {
|
|
15
|
+
const head = await renderApp(<HTML />);
|
|
16
|
+
|
|
17
|
+
expect(head).toContainReactComponent(Viewport, {
|
|
18
|
+
cover: true,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('prevents search robots from indexing the application', async () => {
|
|
23
|
+
const head = await renderApp(<HTML />);
|
|
24
|
+
|
|
25
|
+
expect(head).toContainReactComponent(SearchRobots, {
|
|
26
|
+
index: false,
|
|
27
|
+
follow: false,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('does not cache the response', async () => {
|
|
32
|
+
const headers = await renderApp(<HTML />);
|
|
33
|
+
|
|
34
|
+
expect(headers).toContainReactComponent(CacheControl, {
|
|
35
|
+
cache: false,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('adds a content security policy with a strict default policy', async () => {
|
|
40
|
+
const headers = await renderApp(<HTML />);
|
|
41
|
+
|
|
42
|
+
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
43
|
+
defaultSources: ["'self'"],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import type {PropsWithChildren} from 'react';
|
|
2
|
+
import {Title, Favicon, useBrowserRequest} from '@quilted/quilt/browser';
|
|
3
3
|
import {
|
|
4
4
|
CacheControl,
|
|
5
5
|
ResponseHeader,
|
|
6
6
|
ContentSecurityPolicy,
|
|
7
7
|
PermissionsPolicy,
|
|
8
|
+
SearchRobots,
|
|
8
9
|
StrictTransportSecurity,
|
|
9
|
-
|
|
10
|
+
Viewport,
|
|
11
|
+
} from '@quilted/quilt/server';
|
|
10
12
|
|
|
11
|
-
// This component sets details
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// This component sets details of the HTML page. If you need to customize
|
|
14
|
+
// any of these details based on conditions like the active route, or some
|
|
15
|
+
// state about the user, you can move these components to wherever in your
|
|
16
|
+
// application you can read that state.
|
|
15
17
|
//
|
|
16
|
-
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/
|
|
17
|
-
export function
|
|
18
|
-
|
|
18
|
+
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/html.md
|
|
19
|
+
export function HTML({children}: PropsWithChildren) {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Headers />
|
|
23
|
+
<Head />
|
|
24
|
+
{children}
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Headers() {
|
|
30
|
+
const {url} = useBrowserRequest();
|
|
31
|
+
const isHttps = new URL(url).protocol === 'https:';
|
|
19
32
|
|
|
20
33
|
return (
|
|
21
34
|
<>
|
|
@@ -116,3 +129,30 @@ export function Headers() {
|
|
|
116
129
|
</>
|
|
117
130
|
);
|
|
118
131
|
}
|
|
132
|
+
|
|
133
|
+
function Head() {
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
{/* Sets the default `<title>` for this application. */}
|
|
137
|
+
<Title>App</Title>
|
|
138
|
+
|
|
139
|
+
{/*
|
|
140
|
+
* Sets the default favicon used by the application. You can
|
|
141
|
+
* change this to a different emoji, make it `blank`, or pass
|
|
142
|
+
* a URL with the `source` prop.
|
|
143
|
+
*/}
|
|
144
|
+
<Favicon emoji="🧶" />
|
|
145
|
+
|
|
146
|
+
{/* Adds a responsive-friendly `viewport` `<meta>` tag. */}
|
|
147
|
+
<Viewport cover />
|
|
148
|
+
|
|
149
|
+
{/*
|
|
150
|
+
* Disables all search indexing for this application. If you are
|
|
151
|
+
* building an unauthenticated app, you probably want to remove
|
|
152
|
+
* this component, or update it to control how your site is indexed
|
|
153
|
+
* by search engines.
|
|
154
|
+
*/}
|
|
155
|
+
<SearchRobots index={false} follow={false} />
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {HTML} from './html/HTML.tsx';
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type PropsWithChildren} from 'react';
|
|
2
2
|
|
|
3
|
-
import {HTML} from '@quilted/quilt/html';
|
|
4
3
|
import {Routing, useRoutes} from '@quilted/quilt/navigate';
|
|
5
4
|
import {Localization, useLocaleFromEnvironment} from '@quilted/quilt/localize';
|
|
6
|
-
import {GraphQLContext
|
|
5
|
+
import {GraphQLContext} from '@quilted/quilt/graphql';
|
|
7
6
|
|
|
8
7
|
import {ReactQueryContext} from '@quilted/react-query';
|
|
9
|
-
import {QueryClient} from '@tanstack/react-query';
|
|
10
8
|
|
|
11
|
-
import {
|
|
12
|
-
import {Headers} from './foundation/http.ts';
|
|
9
|
+
import {HTML} from './foundation/html.ts';
|
|
13
10
|
import {Frame} from './foundation/frame.ts';
|
|
14
11
|
|
|
15
12
|
import {Start} from './features/start.ts';
|
|
@@ -19,29 +16,21 @@ import {
|
|
|
19
16
|
type AppContext as AppContextType,
|
|
20
17
|
} from './shared/context.ts';
|
|
21
18
|
|
|
22
|
-
export interface AppProps
|
|
23
|
-
|
|
19
|
+
export interface AppProps {
|
|
20
|
+
context: AppContextType;
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
// The root component for your application. You will typically render any
|
|
27
24
|
// app-wide context in this component.
|
|
28
|
-
export function App(
|
|
29
|
-
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
30
|
-
|
|
25
|
+
export function App({context}: AppProps) {
|
|
31
26
|
return (
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<Routes />
|
|
40
|
-
</Frame>
|
|
41
|
-
</AppContext>
|
|
42
|
-
</Routing>
|
|
43
|
-
</Localization>
|
|
44
|
-
</HTML>
|
|
27
|
+
<AppContext context={context}>
|
|
28
|
+
<HTML>
|
|
29
|
+
<Frame>
|
|
30
|
+
<Routes />
|
|
31
|
+
</Frame>
|
|
32
|
+
</HTML>
|
|
33
|
+
</AppContext>
|
|
45
34
|
);
|
|
46
35
|
}
|
|
47
36
|
|
|
@@ -56,24 +45,18 @@ function Routes() {
|
|
|
56
45
|
}
|
|
57
46
|
|
|
58
47
|
// This component renders any app-wide context.
|
|
59
|
-
function AppContext({
|
|
60
|
-
|
|
61
|
-
fetchGraphQL,
|
|
62
|
-
...context
|
|
63
|
-
}: PropsWithChildren<AppProps>) {
|
|
64
|
-
const {queryClient} = useMemo(() => {
|
|
65
|
-
return {
|
|
66
|
-
queryClient: new QueryClient(),
|
|
67
|
-
};
|
|
68
|
-
}, []);
|
|
48
|
+
function AppContext({children, context}: PropsWithChildren<AppProps>) {
|
|
49
|
+
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
69
50
|
|
|
70
51
|
return (
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
<
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
52
|
+
<AppContextReact.Provider value={context}>
|
|
53
|
+
<GraphQLContext fetch={context.fetchGraphQL}>
|
|
54
|
+
<ReactQueryContext client={context.queryClient}>
|
|
55
|
+
<Localization locale={locale}>
|
|
56
|
+
<Routing>{children}</Routing>
|
|
57
|
+
</Localization>
|
|
58
|
+
</ReactQueryContext>
|
|
59
|
+
</GraphQLContext>
|
|
60
|
+
</AppContextReact.Provider>
|
|
78
61
|
);
|
|
79
62
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
2
|
|
|
3
3
|
import {hydrateRoot} from 'react-dom/client';
|
|
4
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
4
5
|
import {createGraphQLFetch} from '@quilted/quilt/graphql';
|
|
6
|
+
import {Browser, BrowserContext} from '@quilted/quilt/browser';
|
|
5
7
|
|
|
6
8
|
import {App} from './App.tsx';
|
|
7
9
|
|
|
8
10
|
const element = document.querySelector('#app')!;
|
|
11
|
+
const browser = new Browser();
|
|
12
|
+
|
|
13
|
+
const queryClient = new QueryClient();
|
|
14
|
+
const fetchGraphQL = createGraphQLFetch({url: '/api/graphql'});
|
|
9
15
|
|
|
10
16
|
hydrateRoot(
|
|
11
17
|
element,
|
|
12
|
-
<
|
|
18
|
+
<BrowserContext browser={browser}>
|
|
19
|
+
<App context={{fetchGraphQL, queryClient}} />
|
|
20
|
+
</BrowserContext>,
|
|
13
21
|
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
CacheControl,
|
|
4
|
+
ContentSecurityPolicy,
|
|
5
|
+
SearchRobots,
|
|
6
|
+
Viewport,
|
|
7
|
+
} from '@quilted/quilt/server';
|
|
8
|
+
|
|
9
|
+
import {renderApp} from '~/tests/render.ts';
|
|
10
|
+
|
|
11
|
+
import {HTML} from './HTML.tsx';
|
|
12
|
+
|
|
13
|
+
describe('<HTML />', () => {
|
|
14
|
+
it('includes a responsive viewport tag', async () => {
|
|
15
|
+
const head = await renderApp(<HTML />);
|
|
16
|
+
|
|
17
|
+
expect(head).toContainReactComponent(Viewport, {
|
|
18
|
+
cover: true,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('prevents search robots from indexing the application', async () => {
|
|
23
|
+
const head = await renderApp(<HTML />);
|
|
24
|
+
|
|
25
|
+
expect(head).toContainReactComponent(SearchRobots, {
|
|
26
|
+
index: false,
|
|
27
|
+
follow: false,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('does not cache the response', async () => {
|
|
32
|
+
const headers = await renderApp(<HTML />);
|
|
33
|
+
|
|
34
|
+
expect(headers).toContainReactComponent(CacheControl, {
|
|
35
|
+
cache: false,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('adds a content security policy with a strict default policy', async () => {
|
|
40
|
+
const headers = await renderApp(<HTML />);
|
|
41
|
+
|
|
42
|
+
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
43
|
+
defaultSources: ["'self'"],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import type {PropsWithChildren} from 'react';
|
|
2
|
+
import {Title, Favicon, useBrowserRequest} from '@quilted/quilt/browser';
|
|
3
3
|
import {
|
|
4
4
|
CacheControl,
|
|
5
5
|
ResponseHeader,
|
|
6
6
|
ContentSecurityPolicy,
|
|
7
7
|
PermissionsPolicy,
|
|
8
|
+
SearchRobots,
|
|
8
9
|
StrictTransportSecurity,
|
|
9
|
-
|
|
10
|
+
Viewport,
|
|
11
|
+
} from '@quilted/quilt/server';
|
|
10
12
|
|
|
11
|
-
// This component sets details
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// This component sets details of the HTML page. If you need to customize
|
|
14
|
+
// any of these details based on conditions like the active route, or some
|
|
15
|
+
// state about the user, you can move these components to wherever in your
|
|
16
|
+
// application you can read that state.
|
|
15
17
|
//
|
|
16
|
-
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/
|
|
17
|
-
export function
|
|
18
|
-
|
|
18
|
+
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/html.md
|
|
19
|
+
export function HTML({children}: PropsWithChildren) {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Headers />
|
|
23
|
+
<Head />
|
|
24
|
+
{children}
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Headers() {
|
|
30
|
+
const {url} = useBrowserRequest();
|
|
31
|
+
const isHttps = new URL(url).protocol === 'https:';
|
|
19
32
|
|
|
20
33
|
return (
|
|
21
34
|
<>
|
|
@@ -116,3 +129,30 @@ export function Headers() {
|
|
|
116
129
|
</>
|
|
117
130
|
);
|
|
118
131
|
}
|
|
132
|
+
|
|
133
|
+
function Head() {
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
{/* Sets the default `<title>` for this application. */}
|
|
137
|
+
<Title>App</Title>
|
|
138
|
+
|
|
139
|
+
{/*
|
|
140
|
+
* Sets the default favicon used by the application. You can
|
|
141
|
+
* change this to a different emoji, make it `blank`, or pass
|
|
142
|
+
* a URL with the `source` prop.
|
|
143
|
+
*/}
|
|
144
|
+
<Favicon emoji="🧶" />
|
|
145
|
+
|
|
146
|
+
{/* Adds a responsive-friendly `viewport` `<meta>` tag. */}
|
|
147
|
+
<Viewport cover />
|
|
148
|
+
|
|
149
|
+
{/*
|
|
150
|
+
* Disables all search indexing for this application. If you are
|
|
151
|
+
* building an unauthenticated app, you probably want to remove
|
|
152
|
+
* this component, or update it to control how your site is indexed
|
|
153
|
+
* by search engines.
|
|
154
|
+
*/}
|
|
155
|
+
<SearchRobots index={false} follow={false} />
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {HTML} from './html/HTML.tsx';
|
|
@@ -30,7 +30,7 @@ const Query = createQueryResolver({
|
|
|
30
30
|
|
|
31
31
|
const schema = createGraphQLSchema(schemaSource, {Query, Person});
|
|
32
32
|
|
|
33
|
-
export const performGraphQLOperation: GraphQLRun =
|
|
33
|
+
export const performGraphQLOperation: GraphQLRun<{}> =
|
|
34
34
|
async function performGraphQLOperation(operation, options) {
|
|
35
35
|
const result = await graphql({
|
|
36
36
|
schema,
|
|
@@ -21,15 +21,21 @@ router.post('/api/graphql', async (request) => {
|
|
|
21
21
|
|
|
22
22
|
// For all GET requests, render our React application.
|
|
23
23
|
router.get(async (request) => {
|
|
24
|
-
const [{App}, {performGraphQLOperation}, {renderToResponse}] =
|
|
24
|
+
const [{App}, {performGraphQLOperation}, {renderToResponse}, {QueryClient}] =
|
|
25
25
|
await Promise.all([
|
|
26
26
|
import('./App.tsx'),
|
|
27
27
|
import('./server/graphql.ts'),
|
|
28
28
|
import('@quilted/quilt/server'),
|
|
29
|
+
import('@tanstack/react-query'),
|
|
29
30
|
]);
|
|
30
31
|
|
|
31
32
|
const response = await renderToResponse(
|
|
32
|
-
<App
|
|
33
|
+
<App
|
|
34
|
+
context={{
|
|
35
|
+
fetchGraphQL: performGraphQLOperation,
|
|
36
|
+
queryClient: new QueryClient(),
|
|
37
|
+
}}
|
|
38
|
+
/>,
|
|
33
39
|
{
|
|
34
40
|
request,
|
|
35
41
|
assets,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type {GraphQLFetch} from '@quilted/quilt/graphql';
|
|
2
|
+
import type {QueryClient} from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
declare module '~/shared/context.ts' {
|
|
5
|
+
interface AppContext {
|
|
6
|
+
queryClient: QueryClient;
|
|
7
|
+
fetchGraphQL: GraphQLFetch<{}>;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export {};
|
|
@@ -24,7 +24,12 @@ 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(), graphql = new GraphQLController()}) {
|
|
27
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
router,
|
|
29
|
+
graphql,
|
|
30
|
+
fetchGraphQL: graphql.fetch,
|
|
31
|
+
queryClient: new QueryClient(),
|
|
32
|
+
};
|
|
28
33
|
},
|
|
29
34
|
// Render all of our app-wide context providers around each component under test.
|
|
30
35
|
render(element, context, {locale = 'en'}) {
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {type PropsWithChildren} from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {Routing, useRoutes, useInitialUrl} from '@quilted/quilt/navigate';
|
|
3
|
+
import {Routing, useRoutes} from '@quilted/quilt/navigate';
|
|
5
4
|
import {Localization, useLocaleFromEnvironment} from '@quilted/quilt/localize';
|
|
6
5
|
|
|
7
|
-
import {httpBatchLink} from '@trpc/client';
|
|
8
|
-
import {QueryClient} from '@tanstack/react-query';
|
|
9
6
|
import {ReactQueryContext} from '@quilted/react-query';
|
|
10
7
|
|
|
11
|
-
import {
|
|
12
|
-
import {Headers} from './foundation/http.ts';
|
|
8
|
+
import {HTML} from './foundation/html.ts';
|
|
13
9
|
import {Frame} from './foundation/frame.ts';
|
|
14
10
|
|
|
15
11
|
import {Start} from './features/start.ts';
|
|
@@ -20,27 +16,21 @@ import {
|
|
|
20
16
|
type AppContext as AppContextType,
|
|
21
17
|
} from './shared/context.ts';
|
|
22
18
|
|
|
23
|
-
export interface AppProps
|
|
19
|
+
export interface AppProps {
|
|
20
|
+
context: AppContextType;
|
|
21
|
+
}
|
|
24
22
|
|
|
25
23
|
// The root component for your application. You will typically render any
|
|
26
24
|
// app-wide context in this component.
|
|
27
|
-
export function App(
|
|
28
|
-
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
29
|
-
|
|
25
|
+
export function App({context}: AppProps) {
|
|
30
26
|
return (
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<Routes />
|
|
39
|
-
</Frame>
|
|
40
|
-
</AppContext>
|
|
41
|
-
</Routing>
|
|
42
|
-
</Localization>
|
|
43
|
-
</HTML>
|
|
27
|
+
<AppContext context={context}>
|
|
28
|
+
<HTML>
|
|
29
|
+
<Frame>
|
|
30
|
+
<Routes />
|
|
31
|
+
</Frame>
|
|
32
|
+
</HTML>
|
|
33
|
+
</AppContext>
|
|
44
34
|
);
|
|
45
35
|
}
|
|
46
36
|
|
|
@@ -55,29 +45,23 @@ function Routes() {
|
|
|
55
45
|
}
|
|
56
46
|
|
|
57
47
|
// This component renders any app-wide context.
|
|
58
|
-
function AppContext({children,
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const {queryClient, trpcClient} = useMemo(() => {
|
|
62
|
-
return {
|
|
63
|
-
queryClient: new QueryClient(),
|
|
64
|
-
trpcClient: trpc.createClient({
|
|
65
|
-
links: [
|
|
66
|
-
// We need to use an absolute URL so that queries will
|
|
67
|
-
// work during server-side rendering
|
|
68
|
-
httpBatchLink({url: new URL('/api', initialUrl).href}),
|
|
69
|
-
],
|
|
70
|
-
}),
|
|
71
|
-
};
|
|
72
|
-
}, [initialUrl]);
|
|
48
|
+
function AppContext({children, context}: PropsWithChildren<AppProps>) {
|
|
49
|
+
const locale = useLocaleFromEnvironment() ?? 'en';
|
|
73
50
|
|
|
74
51
|
return (
|
|
75
|
-
<
|
|
76
|
-
<
|
|
77
|
-
<
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
52
|
+
<AppContextReact.Provider value={context}>
|
|
53
|
+
<Localization locale={locale}>
|
|
54
|
+
<Routing>
|
|
55
|
+
<trpc.Provider
|
|
56
|
+
client={context.trpc}
|
|
57
|
+
queryClient={context.queryClient}
|
|
58
|
+
>
|
|
59
|
+
<ReactQueryContext client={context.queryClient}>
|
|
60
|
+
{children}
|
|
61
|
+
</ReactQueryContext>
|
|
62
|
+
</trpc.Provider>
|
|
63
|
+
</Routing>
|
|
64
|
+
</Localization>
|
|
65
|
+
</AppContextReact.Provider>
|
|
82
66
|
);
|
|
83
67
|
}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import '@quilted/quilt/globals';
|
|
2
2
|
import {hydrateRoot} from 'react-dom/client';
|
|
3
3
|
import {httpBatchLink} from '@trpc/client';
|
|
4
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
5
|
+
import {Browser, BrowserContext} from '@quilted/quilt/browser';
|
|
4
6
|
|
|
5
7
|
import {trpc} from '~/shared/trpc.ts';
|
|
6
8
|
|
|
7
9
|
import {App} from './App.tsx';
|
|
8
10
|
|
|
9
11
|
const element = document.querySelector('#app')!;
|
|
12
|
+
const browser = new Browser();
|
|
10
13
|
|
|
14
|
+
const queryClient = new QueryClient();
|
|
11
15
|
const trpcClient = trpc.createClient({
|
|
12
16
|
links: [httpBatchLink({url: new URL('/api', window.location.href).href})],
|
|
13
17
|
});
|
|
14
18
|
|
|
15
|
-
hydrateRoot(
|
|
19
|
+
hydrateRoot(
|
|
20
|
+
element,
|
|
21
|
+
<BrowserContext browser={browser}>
|
|
22
|
+
<App context={{trpc: trpcClient, queryClient}} />
|
|
23
|
+
</BrowserContext>,
|
|
24
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {describe, it, expect} from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
CacheControl,
|
|
4
|
+
ContentSecurityPolicy,
|
|
5
|
+
SearchRobots,
|
|
6
|
+
Viewport,
|
|
7
|
+
} from '@quilted/quilt/server';
|
|
8
|
+
|
|
9
|
+
import {renderApp} from '~/tests/render.ts';
|
|
10
|
+
|
|
11
|
+
import {HTML} from './HTML.tsx';
|
|
12
|
+
|
|
13
|
+
describe('<HTML />', () => {
|
|
14
|
+
it('includes a responsive viewport tag', async () => {
|
|
15
|
+
const head = await renderApp(<HTML />);
|
|
16
|
+
|
|
17
|
+
expect(head).toContainReactComponent(Viewport, {
|
|
18
|
+
cover: true,
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('prevents search robots from indexing the application', async () => {
|
|
23
|
+
const head = await renderApp(<HTML />);
|
|
24
|
+
|
|
25
|
+
expect(head).toContainReactComponent(SearchRobots, {
|
|
26
|
+
index: false,
|
|
27
|
+
follow: false,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('does not cache the response', async () => {
|
|
32
|
+
const headers = await renderApp(<HTML />);
|
|
33
|
+
|
|
34
|
+
expect(headers).toContainReactComponent(CacheControl, {
|
|
35
|
+
cache: false,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('adds a content security policy with a strict default policy', async () => {
|
|
40
|
+
const headers = await renderApp(<HTML />);
|
|
41
|
+
|
|
42
|
+
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
43
|
+
defaultSources: ["'self'"],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import type {PropsWithChildren} from 'react';
|
|
2
|
+
import {Title, Favicon, useBrowserRequest} from '@quilted/quilt/browser';
|
|
3
3
|
import {
|
|
4
4
|
CacheControl,
|
|
5
5
|
ResponseHeader,
|
|
6
6
|
ContentSecurityPolicy,
|
|
7
7
|
PermissionsPolicy,
|
|
8
|
+
SearchRobots,
|
|
8
9
|
StrictTransportSecurity,
|
|
9
|
-
|
|
10
|
+
Viewport,
|
|
11
|
+
} from '@quilted/quilt/server';
|
|
10
12
|
|
|
11
|
-
// This component sets details
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// This component sets details of the HTML page. If you need to customize
|
|
14
|
+
// any of these details based on conditions like the active route, or some
|
|
15
|
+
// state about the user, you can move these components to wherever in your
|
|
16
|
+
// application you can read that state.
|
|
15
17
|
//
|
|
16
|
-
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/
|
|
17
|
-
export function
|
|
18
|
-
|
|
18
|
+
// @see https://github.com/lemonmade/quilt/blob/main/documentation/features/html.md
|
|
19
|
+
export function HTML({children}: PropsWithChildren) {
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<Headers />
|
|
23
|
+
<Head />
|
|
24
|
+
{children}
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function Headers() {
|
|
30
|
+
const {url} = useBrowserRequest();
|
|
31
|
+
const isHttps = new URL(url).protocol === 'https:';
|
|
19
32
|
|
|
20
33
|
return (
|
|
21
34
|
<>
|
|
@@ -116,3 +129,30 @@ export function Headers() {
|
|
|
116
129
|
</>
|
|
117
130
|
);
|
|
118
131
|
}
|
|
132
|
+
|
|
133
|
+
function Head() {
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
{/* Sets the default `<title>` for this application. */}
|
|
137
|
+
<Title>App</Title>
|
|
138
|
+
|
|
139
|
+
{/*
|
|
140
|
+
* Sets the default favicon used by the application. You can
|
|
141
|
+
* change this to a different emoji, make it `blank`, or pass
|
|
142
|
+
* a URL with the `source` prop.
|
|
143
|
+
*/}
|
|
144
|
+
<Favicon emoji="🧶" />
|
|
145
|
+
|
|
146
|
+
{/* Adds a responsive-friendly `viewport` `<meta>` tag. */}
|
|
147
|
+
<Viewport cover />
|
|
148
|
+
|
|
149
|
+
{/*
|
|
150
|
+
* Disables all search indexing for this application. If you are
|
|
151
|
+
* building an unauthenticated app, you probably want to remove
|
|
152
|
+
* this component, or update it to control how your site is indexed
|
|
153
|
+
* by search engines.
|
|
154
|
+
*/}
|
|
155
|
+
<SearchRobots index={false} follow={false} />
|
|
156
|
+
</>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {HTML} from './html/HTML.tsx';
|
|
@@ -25,13 +25,19 @@ router.any(
|
|
|
25
25
|
|
|
26
26
|
// For all GET requests, render our React application.
|
|
27
27
|
router.get(async (request) => {
|
|
28
|
-
const [{App}, {renderToResponse}] = await Promise.all([
|
|
28
|
+
const [{App}, {renderToResponse}, {QueryClient}] = await Promise.all([
|
|
29
29
|
import('./App.tsx'),
|
|
30
30
|
import('@quilted/quilt/server'),
|
|
31
|
+
import('@tanstack/react-query'),
|
|
31
32
|
]);
|
|
32
33
|
|
|
33
34
|
const response = await renderToResponse(
|
|
34
|
-
<App
|
|
35
|
+
<App
|
|
36
|
+
context={{
|
|
37
|
+
trpc: createDirectClient(appRouter),
|
|
38
|
+
queryClient: new QueryClient(),
|
|
39
|
+
}}
|
|
40
|
+
/>,
|
|
35
41
|
{
|
|
36
42
|
request,
|
|
37
43
|
assets,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type {TRPCClient} from '@trpc/client';
|
|
2
2
|
import {createTRPCReact, type CreateTRPCReact} from '@trpc/react-query';
|
|
3
|
+
import type {QueryClient} from '@tanstack/react-query';
|
|
3
4
|
|
|
4
5
|
// Get access to our app’s router type signature, which will
|
|
5
6
|
// provide strong typing on the queries and mutations we can
|
|
6
7
|
// perform.
|
|
7
|
-
import
|
|
8
|
+
import type {AppRouter} from '../trpc.ts';
|
|
8
9
|
|
|
9
10
|
export const trpc: CreateTRPCReact<AppRouter, unknown, null> =
|
|
10
11
|
createTRPCReact<AppRouter>();
|
|
@@ -12,5 +13,6 @@ export const trpc: CreateTRPCReact<AppRouter, unknown, null> =
|
|
|
12
13
|
declare module '~/shared/context.ts' {
|
|
13
14
|
interface AppContext {
|
|
14
15
|
trpc: TRPCClient<AppRouter>;
|
|
16
|
+
queryClient: QueryClient;
|
|
15
17
|
}
|
|
16
18
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {Viewport, SearchRobots} from '@quilted/quilt/html';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Head} from './Head.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Head />', () => {
|
|
9
|
-
it('includes a responsive viewport tag', async () => {
|
|
10
|
-
const head = await renderApp(<Head />);
|
|
11
|
-
|
|
12
|
-
expect(head).toContainReactComponent(Viewport, {
|
|
13
|
-
cover: true,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('prevents search robots from indexing the application', async () => {
|
|
18
|
-
const head = await renderApp(<Head />);
|
|
19
|
-
|
|
20
|
-
expect(head).toContainReactComponent(SearchRobots, {
|
|
21
|
-
index: false,
|
|
22
|
-
follow: false,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {Title, Viewport, Favicon, SearchRobots} from '@quilted/quilt/html';
|
|
2
|
-
|
|
3
|
-
// This component sets details of the HTML page. If you need to customize
|
|
4
|
-
// any of these details based on conditions like the active route, or some
|
|
5
|
-
// state about the user, you can move these components to wherever in your
|
|
6
|
-
// application you can read that state.
|
|
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,24 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {CacheControl, ContentSecurityPolicy} from '@quilted/quilt/http';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Headers} from './Headers.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Headers />', () => {
|
|
9
|
-
it('does not cache the response', async () => {
|
|
10
|
-
const headers = await renderApp(<Headers />);
|
|
11
|
-
|
|
12
|
-
expect(headers).toContainReactComponent(CacheControl, {
|
|
13
|
-
cache: false,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('adds a content security policy with a strict default policy', async () => {
|
|
18
|
-
const headers = await renderApp(<Headers />);
|
|
19
|
-
|
|
20
|
-
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
21
|
-
defaultSources: ["'self'"],
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {Headers} from './http/Headers.tsx';
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {Viewport, SearchRobots} from '@quilted/quilt/html';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Head} from './Head.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Head />', () => {
|
|
9
|
-
it('includes a responsive viewport tag', async () => {
|
|
10
|
-
const head = await renderApp(<Head />);
|
|
11
|
-
|
|
12
|
-
expect(head).toContainReactComponent(Viewport, {
|
|
13
|
-
cover: true,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('prevents search robots from indexing the application', async () => {
|
|
18
|
-
const head = await renderApp(<Head />);
|
|
19
|
-
|
|
20
|
-
expect(head).toContainReactComponent(SearchRobots, {
|
|
21
|
-
index: false,
|
|
22
|
-
follow: false,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {Title, Viewport, Favicon, SearchRobots} from '@quilted/quilt/html';
|
|
2
|
-
|
|
3
|
-
// This component sets details of the HTML page. If you need to customize
|
|
4
|
-
// any of these details based on conditions like the active route, or some
|
|
5
|
-
// state about the user, you can move these components to wherever in your
|
|
6
|
-
// application you can read that state.
|
|
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,24 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {CacheControl, ContentSecurityPolicy} from '@quilted/quilt/http';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Headers} from './Headers.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Headers />', () => {
|
|
9
|
-
it('does not cache the response', async () => {
|
|
10
|
-
const headers = await renderApp(<Headers />);
|
|
11
|
-
|
|
12
|
-
expect(headers).toContainReactComponent(CacheControl, {
|
|
13
|
-
cache: false,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('adds a content security policy with a strict default policy', async () => {
|
|
18
|
-
const headers = await renderApp(<Headers />);
|
|
19
|
-
|
|
20
|
-
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
21
|
-
defaultSources: ["'self'"],
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {Headers} from './http/Headers.tsx';
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {Viewport, SearchRobots} from '@quilted/quilt/html';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Head} from './Head.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Head />', () => {
|
|
9
|
-
it('includes a responsive viewport tag', async () => {
|
|
10
|
-
const head = await renderApp(<Head />);
|
|
11
|
-
|
|
12
|
-
expect(head).toContainReactComponent(Viewport, {
|
|
13
|
-
cover: true,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('prevents search robots from indexing the application', async () => {
|
|
18
|
-
const head = await renderApp(<Head />);
|
|
19
|
-
|
|
20
|
-
expect(head).toContainReactComponent(SearchRobots, {
|
|
21
|
-
index: false,
|
|
22
|
-
follow: false,
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import {Title, Viewport, Favicon, SearchRobots} from '@quilted/quilt/html';
|
|
2
|
-
|
|
3
|
-
// This component sets details of the HTML page. If you need to customize
|
|
4
|
-
// any of these details based on conditions like the active route, or some
|
|
5
|
-
// state about the user, you can move these components to wherever in your
|
|
6
|
-
// application you can read that state.
|
|
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,24 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {CacheControl, ContentSecurityPolicy} from '@quilted/quilt/http';
|
|
3
|
-
|
|
4
|
-
import {renderApp} from '~/tests/render.ts';
|
|
5
|
-
|
|
6
|
-
import {Headers} from './Headers.tsx';
|
|
7
|
-
|
|
8
|
-
describe('<Headers />', () => {
|
|
9
|
-
it('does not cache the response', async () => {
|
|
10
|
-
const headers = await renderApp(<Headers />);
|
|
11
|
-
|
|
12
|
-
expect(headers).toContainReactComponent(CacheControl, {
|
|
13
|
-
cache: false,
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('adds a content security policy with a strict default policy', async () => {
|
|
18
|
-
const headers = await renderApp(<Headers />);
|
|
19
|
-
|
|
20
|
-
expect(headers).toContainReactComponent(ContentSecurityPolicy, {
|
|
21
|
-
defaultSources: ["'self'"],
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {Headers} from './http/Headers.tsx';
|