Skip to content

Commit 9559d6c

Browse files
committed
feat(remix): Add support for Hydrogen
1 parent a0be1a5 commit 9559d6c

33 files changed

+1475
-255
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* This is intended to be a basic starting point for linting in your app.
3+
* It relies on recommended configs out of the box for simplicity, but you can
4+
* and should modify this configuration to best suit your team's needs.
5+
*/
6+
7+
/** @type {import('eslint').Linter.Config} */
8+
module.exports = {
9+
root: true,
10+
parserOptions: {
11+
ecmaVersion: 'latest',
12+
sourceType: 'module',
13+
ecmaFeatures: {
14+
jsx: true,
15+
},
16+
},
17+
env: {
18+
browser: true,
19+
commonjs: true,
20+
es6: true,
21+
},
22+
23+
// Base config
24+
extends: ['eslint:recommended'],
25+
26+
overrides: [
27+
// React
28+
{
29+
files: ['**/*.{js,jsx,ts,tsx}'],
30+
plugins: ['react', 'jsx-a11y'],
31+
extends: [
32+
'plugin:react/recommended',
33+
'plugin:react/jsx-runtime',
34+
'plugin:react-hooks/recommended',
35+
'plugin:jsx-a11y/recommended',
36+
],
37+
settings: {
38+
react: {
39+
version: 'detect',
40+
},
41+
formComponents: ['Form'],
42+
linkComponents: [
43+
{ name: 'Link', linkAttribute: 'to' },
44+
{ name: 'NavLink', linkAttribute: 'to' },
45+
],
46+
'import/resolver': {
47+
typescript: {},
48+
},
49+
},
50+
},
51+
52+
// Typescript
53+
{
54+
files: ['**/*.{ts,tsx}'],
55+
plugins: ['@typescript-eslint', 'import'],
56+
parser: '@typescript-eslint/parser',
57+
settings: {
58+
'import/internal-regex': '^~/',
59+
'import/resolver': {
60+
node: {
61+
extensions: ['.ts', '.tsx'],
62+
},
63+
typescript: {
64+
alwaysTryTypes: true,
65+
},
66+
},
67+
},
68+
extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/recommended', 'plugin:import/typescript'],
69+
},
70+
71+
// Node
72+
{
73+
files: ['.eslintrc.cjs', 'server.ts'],
74+
env: {
75+
node: true,
76+
},
77+
},
78+
],
79+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { RemixBrowser, useLocation, useMatches } from '@remix-run/react';
2+
import * as Sentry from '@sentry/remix/cloudflare';
3+
import { StrictMode, startTransition } from 'react';
4+
import { useEffect } from 'react';
5+
import { hydrateRoot } from 'react-dom/client';
6+
7+
Sentry.init({
8+
environment: 'qa', // dynamic sampling bias to keep transactions
9+
// Could not find a working way to set the DSN in the browser side from the environment variables
10+
dsn: 'https://[email protected]/1337',
11+
debug: true,
12+
integrations: [
13+
Sentry.browserTracingIntegration({
14+
useEffect,
15+
useLocation,
16+
useMatches,
17+
}),
18+
Sentry.replayIntegration({
19+
maskAllText: true,
20+
blockAllMedia: true,
21+
}),
22+
],
23+
24+
tracesSampleRate: 1.0,
25+
replaysSessionSampleRate: 0.1,
26+
replaysOnErrorSampleRate: 1.0,
27+
tunnel: 'http://localhost:3031/', // proxy server
28+
});
29+
30+
startTransition(() => {
31+
hydrateRoot(
32+
document,
33+
<StrictMode>
34+
<RemixBrowser />
35+
</StrictMode>,
36+
);
37+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { RemixServer } from '@remix-run/react';
2+
import { createContentSecurityPolicy } from '@shopify/hydrogen';
3+
import type { EntryContext } from '@shopify/remix-oxygen';
4+
import isbot from 'isbot';
5+
import { renderToReadableStream } from 'react-dom/server';
6+
7+
export default async function handleRequest(
8+
request: Request,
9+
responseStatusCode: number,
10+
responseHeaders: Headers,
11+
remixContext: EntryContext,
12+
) {
13+
const { nonce, header, NonceProvider } = createContentSecurityPolicy({
14+
connectSrc: [
15+
// Need to allow the proxy server to fetch the data
16+
'http://localhost:3031/',
17+
],
18+
});
19+
20+
const body = await renderToReadableStream(
21+
<NonceProvider>
22+
<RemixServer context={remixContext} url={request.url} />
23+
</NonceProvider>,
24+
{
25+
nonce,
26+
signal: request.signal,
27+
onError(error) {
28+
// eslint-disable-next-line no-console
29+
console.error(error);
30+
responseStatusCode = 500;
31+
},
32+
},
33+
);
34+
35+
if (isbot(request.headers.get('user-agent'))) {
36+
await body.allReady;
37+
}
38+
39+
responseHeaders.set('Content-Type', 'text/html');
40+
responseHeaders.set('Content-Security-Policy', header);
41+
42+
// Add the document policy header to enable JS profiling
43+
// This is required for Sentry's profiling integration
44+
responseHeaders.set('Document-Policy', 'js-profiling');
45+
46+
return new Response(body, {
47+
headers: responseHeaders,
48+
status: responseStatusCode,
49+
});
50+
}

0 commit comments

Comments
 (0)