diff --git a/.eslintrc.json b/.eslintrc.json index 4ffeeca..38b2f49 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": ["@remix-run", "prettier"] + "extends": ["@remix-run", "prettier"] } diff --git a/.prettierrc b/.prettierrc index 8e3601c..bb9ca71 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ # Prettier Configuration # All supported options are listed here: https://prettier.io/docs/en/options.html singleQuote: true +useTabs: true diff --git a/README.md b/README.md index ae07b54..648d68c 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,41 @@ -# remix-worker-template +# remix-cloudflare-template Learn more about [Remix Stacks](https://remix.run/stacks). ``` -npx create-remix --template edmundhung/remix-worker-template +npx create-remix --template edmundhung/remix-cloudflare-template ``` What's included? -- Deploying to [Cloudflare Workers](https://workers.cloudflare.com/) -- Supporting [Durable objects](https://developers.cloudflare.com/workers/learning/using-durable-objects) with [module workers](https://developers.cloudflare.com/workers/learning/migrating-to-module-workers/) -- CI/CD through [Github Actions](https://github.com/features/actions) +- Deploying to [Cloudflare Page](https://workers.cloudflare.com/) +- CI on [Github Actions](https://github.com/features/actions) - Styling with [Tailwind](https://tailwindcss.com/) -- Testing with [Playwright](playwright.dev/) with _undici_ mocking support +- Testing using [Playwright](playwright.dev/) with _undici_ mocking support - Code formatting with [Prettier](https://prettier.io) - Linting with [ESLint](https://eslint.org) - Static Types with [TypeScript](https://typescriptlang.org) ## Node Version -Please make sure the node version is **>= 16.7**. If you are using `nvm`, just run: - -```sh -nvm use -``` - -This allows [miniflare](https://github.com/cloudflare/miniflare) to serve a development environment as close to the actual worker runtime as possibile. +Please make sure the node version is **>= 18**. ## Development -To starts your app in development mode, rebuilding assets on file changes, the recommended approach is: +To starts your app with the vite dev server, run the following command: ```sh npm run dev ``` -This will run your remix app in dev mode using miniflare. - ## Testing -Before running the tests, please ensure the worker is built: +Before running the tests, please ensure the app is built: ```sh npm run build && npm run test ``` -## Deployment - -To deploy your Remix app, simply do it with Wrangler using: - -```sh -npx wrangler publish -``` - ## CI/CD -The template ships a [development workflow](./.github/workflows/development.yml) which is triggered whenever new changes are pushed. - -To allow GitHub deploying the worker for you, following variables are required: - -- CF_API_TOKEN -- CF_ACCOUNT_ID - -These values could be found / created on your Cloudflare Dashboard +The template ships a [CI workflow](./.github/workflows/ci.yml) which is triggered whenever new changes are pushed. diff --git a/app/components.tsx b/app/components.tsx new file mode 100644 index 0000000..3de3d41 --- /dev/null +++ b/app/components.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import markdoc, { type RenderableTreeNodes } from '@markdoc/markdoc'; + +export function RemixLogo(props: React.ComponentPropsWithoutRef<'svg'>) { + return ( + + Remix Logo + + + + + + + ); +} + +export function Markdown({ content }: { content: RenderableTreeNodes }) { + return
{markdoc.renderers.react(content, React)}
; +} diff --git a/app/root.tsx b/app/root.tsx index e52b68d..7118f7a 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,16 +1,20 @@ -import type { MetaFunction, LoaderFunction } from '@remix-run/cloudflare'; +import type { MetaFunction } from '@remix-run/cloudflare'; import * as React from 'react'; import { - Link, - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, - isRouteErrorResponse, - useRouteError, + Link, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, + isRouteErrorResponse, + json, + useLoaderData, + useRouteError, } from '@remix-run/react'; import '~/styles.css'; +import { metadata } from './services/github.server'; +import { RemixLogo } from './components'; // We will rollback to loading CSS through links when `.css?url` is supported // export const links: LinksFunction = () => { @@ -18,153 +22,149 @@ import '~/styles.css'; // }; export const meta: MetaFunction = () => { - return [ - { - charset: 'utf-8', - // title: 'Conform Playground', - viewport: 'width=device-width,initial-scale=1', - }, - ]; + return [ + { + charset: 'utf-8', + title: 'remix-cloudlfare-template', + viewport: 'width=device-width,initial-scale=1', + }, + ]; }; -export const loader: LoaderFunction = async () => { - return { date: new Date() }; -}; +export function loader() { + return json({ + repo: metadata.repo, + owner: metadata.owner, + description: '📜 All-in-one remix starter template for Cloudflare Pages', + }); +} export default function App() { - return ( - - - - - - ); + const { repo, owner, description } = useLoaderData(); + return ( + + + + + + ); } function Document({ - children, - title, + children, + title, }: { - children: React.ReactNode; - title?: string; + children: React.ReactNode; + title?: string; }) { - return ( - - - - {title ? {title} : null} - - - - - {children} - - - - - ); + return ( + + + + {title ? {title} : null} + + + + + {children} + + + + + ); } -function Layout({ children }: React.PropsWithChildren<{}>) { - return ( -
-
- - - -
-
{children}
- -
- ); +function Layout({ + children, + title, + description, + actionText, + actionLink, +}: { + children?: React.ReactNode; + title?: string; + description?: string; + actionText?: string; + actionLink?: string; +}) { + return ( +
+
+
+
+
+
+ + + +
+
+

{title}

+

{description}

+ {actionText ? ( + + {actionText} + + ) : null} +
+ +
+
+
+
+
{children}
+
+
+
+ ); } export function ErrorBoundary() { - const error = useRouteError(); - - // Log the error to the console - console.error(error); + const error = useRouteError(); - if (isRouteErrorResponse(error)) { - let message; - switch (error.status) { - case 401: - message = ( -

- Oops! Looks like you tried to visit a page that you do not have - access to. -

- ); - break; - case 404: - message = ( -

Oops! Looks like you tried to visit a page that does not exist.

- ); - break; + // Log the error to the console + console.error(error); - default: - throw new Error(error.data || error.statusText); - } + if (isRouteErrorResponse(error)) { + const title = `${error.status} ${error.statusText}`; - return ( - - -

- {error.status}: {error.statusText} -

- {message} -
-
- ); - } + let message; + switch (error.status) { + case 401: + message = + 'Oops! Looks like you tried to visit a page that you do not have access to.'; + break; + case 404: + message = + 'Oops! Looks like you tried to visit a page that does not exist.'; + break; + default: + throw new Error(error.data || error.statusText); + } - return ( - - -
-

There was an error

-

{`${error}`}

-
-

- Hey, developer, you should replace this with what you want your - users to see. -

-
-
-
- ); -} + return ( + + + + ); + } -function RemixLogo(props: React.ComponentPropsWithoutRef<'svg'>) { - return ( - - Remix Logo - - - - - - - ); + return ( + + + + ); } diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 42ac1ea..f0e477e 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,39 +1,26 @@ -import type { MetaFunction, LoaderFunctionArgs } from '@remix-run/cloudflare'; -import { useLoaderData } from '@remix-run/react'; +import type { LoaderFunctionArgs } from '@remix-run/cloudflare'; +import { json, useLoaderData } from '@remix-run/react'; +import { Markdown } from '~/components'; +import { getFileContentWithCache } from '~/services/github.server'; +import { parse } from '~/services/markdoc.server'; -export const meta: MetaFunction = () => { - return [ - { title: 'remix-worker-template' }, - { description: 'All-in-one remix starter template for Cloudflare Workers' }, - ]; -}; +export async function loader({ context }: LoaderFunctionArgs) { + const content = await getFileContentWithCache(context, 'README.md'); -export function loader({ request }: LoaderFunctionArgs) { - return { - title: 'remix-worker-template', - }; + return json( + { + content: parse(content), + }, + { + headers: { + 'Cache-Control': 'public, max-age=3600', + }, + }, + ); } export default function Index() { - const { title } = useLoaderData(); + const { content } = useLoaderData(); - return ( -
-
-

{title}

-

- All-in-one remix starter template for Cloudflare Workers -

- - - Github Repository - -
-
- ); + return ; } diff --git a/app/services/github.server.ts b/app/services/github.server.ts new file mode 100644 index 0000000..51d87b5 --- /dev/null +++ b/app/services/github.server.ts @@ -0,0 +1,77 @@ +import type { Endpoints } from '@octokit/types'; +import type { AppLoadContext } from '@remix-run/cloudflare'; + +export const metadata = { + repo: 'remix-cloudflare-template', + owner: 'edmundhung', +}; + +export function getHeaders(auth: string | undefined) { + const headers = new Headers({ + Accept: 'application/vnd.github+json', + 'User-Agent': 'Conform Guide', + }); + + if (auth) { + headers.set('Authorization', `Bearer ${auth}`); + } + + return headers; +} + +export async function getFileContent(options: { + auth?: string; + ref?: string; + path: string; + owner: string; + repo: string; +}): Promise { + const searchParams = new URLSearchParams(); + + if (options.ref) { + searchParams.set('ref', options.ref); + } + + const url = `https://api.github.com/repos/${options.owner}/${options.repo}/contents/${options.path}?${searchParams}`; + const resposne = await fetch(url, { + headers: getHeaders(options.auth), + }); + + if (resposne.status === 404) { + throw resposne; + } + + const file: Endpoints['GET /repos/{owner}/{repo}/contents/{path}']['response']['data'] = + await resposne.json(); + + if (Array.isArray(file) || file.type !== 'file') { + throw new Response('Not found', { status: 404 }); + } + + return atob(file.content); +} + +export async function getFileContentWithCache( + context: AppLoadContext, + path: string, +): Promise { + const key = `github/${path}`; + const cache = await context.env.CACHE.get(key); + + if (cache) { + return cache; + } + + const content = await getFileContent({ + auth: context.env.GITHUB_TOKEN, + owner: metadata.owner, + repo: metadata.repo, + path, + }); + + // Update the cache + // TODO: Use `waitUntil` to update the cache in the background + await context.env.CACHE.put(key, content, { expirationTtl: 60 * 60 }); + + return content; +} diff --git a/app/services/markdoc.server.tsx b/app/services/markdoc.server.tsx new file mode 100644 index 0000000..85a6526 --- /dev/null +++ b/app/services/markdoc.server.tsx @@ -0,0 +1,8 @@ +import markdoc from '@markdoc/markdoc'; + +export function parse(markdown: string) { + const ast = markdoc.parse(markdown); + const node = markdoc.transform(ast); + + return node; +} diff --git a/env.d.ts b/env.d.ts index 009c0ed..973e98e 100644 --- a/env.d.ts +++ b/env.d.ts @@ -3,12 +3,14 @@ import '@remix-run/cloudflare'; import '@cloudflare/workers-types'; interface Env { - ENVIRONMENT?: 'development'; + ENVIRONMENT?: 'development'; + GITHUB_TOKEN?: string; + CACHE: KVNamespace; } declare module '@remix-run/cloudflare' { - export interface AppLoadContext { - env: Env; - waitUntil(promise: Promise): void; - } + export interface AppLoadContext { + env: Env; + waitUntil(promise: Promise): void; + } } diff --git a/functions/[[path]].ts b/functions/[[path]].ts index d7db3b9..38c4358 100644 --- a/functions/[[path]].ts +++ b/functions/[[path]].ts @@ -6,11 +6,11 @@ import { createPagesFunctionHandler } from '@remix-run/cloudflare-pages'; import * as build from '../build/server'; export const onRequest = createPagesFunctionHandler({ - build, - getLoadContext: (context) => ({ - env: context.env, - waitUntil(promise: Promise) { - context.waitUntil(promise); - }, - }), + build, + getLoadContext: (context) => ({ + env: context.env, + waitUntil(promise: Promise) { + context.waitUntil(promise); + }, + }), }); diff --git a/package.json b/package.json index 6b00570..91d0dbc 100644 --- a/package.json +++ b/package.json @@ -1,55 +1,58 @@ { - "private": true, - "name": "remix-worker-template", - "description": "All-in-one remix starter template for Cloudflare Pages", - "module": "./dist/worker.mjs", - "scripts": { - "dev": "remix vite:dev", - "test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" playwright test", - "start": "wrangler pages dev ./build/client", - "build": "remix vite:build", - "cleanup": "rimraf .cache ./build ./public/build", - "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", - "typecheck": "tsc", - "prepare": "husky install" - }, - "dependencies": { - "@remix-run/cloudflare": "*", - "@remix-run/cloudflare-pages": "*", - "@remix-run/react": "*", - "isbot": "^3.6.5", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20240208.0", - "@playwright/test": "^1.41.2", - "@remix-run/dev": "*", - "@remix-run/eslint-config": "*", - "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.19", - "autoprefixer": "^10.4.17", - "concurrently": "^8.2.2", - "cross-env": "^7.0.3", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "husky": "^9.0.10", - "lint-staged": "^15.2.2", - "miniflare": "^3.20240129.1", - "postcss": "^8.4.35", - "prettier": "^3.2.5", - "rimraf": "^5.0.5", - "tailwindcss": "^3.4.1", - "typescript": "^5.3.3", - "vite": "^5.1.1", - "vite-tsconfig-paths": "^4.3.1", - "wrangler": "^3.28.1" - }, - "engines": { - "node": ">=18" - }, - "sideEffects": false, - "lint-staged": { - "*.{js,mjs,ts,tsx,css,md,yml}": "prettier --write" - } + "private": true, + "name": "remix-worker-template", + "description": "All-in-one remix starter template for Cloudflare Pages", + "module": "./dist/worker.mjs", + "scripts": { + "dev": "remix vite:dev", + "test": "NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" playwright test", + "start": "wrangler pages dev ./build/client", + "build": "remix vite:build", + "cleanup": "rimraf .cache ./build ./public/build", + "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .", + "typecheck": "tsc", + "prepare": "husky install" + }, + "dependencies": { + "@markdoc/markdoc": "^0.4.0", + "@remix-run/cloudflare": "*", + "@remix-run/cloudflare-pages": "*", + "@remix-run/react": "*", + "isbot": "^3.6.5", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20240208.0", + "@octokit/types": "^12.4.0", + "@playwright/test": "^1.41.2", + "@remix-run/dev": "*", + "@remix-run/eslint-config": "*", + "@tailwindcss/typography": "^0.5.10", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "autoprefixer": "^10.4.17", + "concurrently": "^8.2.2", + "cross-env": "^7.0.3", + "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "husky": "^9.0.10", + "lint-staged": "^15.2.2", + "miniflare": "^3.20240129.1", + "postcss": "^8.4.35", + "prettier": "^3.2.5", + "rimraf": "^5.0.5", + "tailwindcss": "^3.4.1", + "typescript": "^5.3.3", + "vite": "^5.1.1", + "vite-tsconfig-paths": "^4.3.1", + "wrangler": "^3.28.1" + }, + "engines": { + "node": ">=18" + }, + "sideEffects": false, + "lint-staged": { + "*.{js,mjs,ts,tsx,css,md,yml}": "prettier --write" + } } diff --git a/playwright.config.ts b/playwright.config.ts index 03f685b..a74c062 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,24 +1,24 @@ import { type PlaywrightTestConfig, devices } from '@playwright/test'; const config: PlaywrightTestConfig = { - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - use: { - trace: 'on-first-retry', - }, - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - ], + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + use: { + trace: 'on-first-retry', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], }; export default config; diff --git a/postcss.config.js b/postcss.config.js index 12a703d..e873f1a 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, }; diff --git a/scripts/build.mjs b/scripts/build.mjs deleted file mode 100644 index b146989..0000000 --- a/scripts/build.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import * as esbuild from 'esbuild'; - -async function build() { - // eslint-disable-next-line no-undef - const mode = process.env.NODE_ENV?.toLowerCase() ?? 'development'; - - console.log(`Building Worker in ${mode} mode`); - - const outfile = './dist/worker.mjs'; - const startTime = Date.now(); - const result = await esbuild.build({ - entryPoints: ['./worker/index.ts'], - bundle: true, - minify: mode === 'production', - sourcemap: mode !== 'production', - format: 'esm', - metafile: true, - external: ['__STATIC_CONTENT_MANIFEST'], - define: { - 'process.env.NODE_ENV': `"${mode}"`, - 'process.env.REMIX_DEV_SERVER_WS_PORT': `"8002"`, - }, - outfile, - }); - const endTime = Date.now(); - - console.log(`Built in ${endTime - startTime}ms`); - - if (mode === 'production') { - console.log(await esbuild.analyzeMetafile(result.metafile)); - } -} - -build().catch((e) => console.error('Unknown error caught during build:', e)); diff --git a/tailwind.config.js b/tailwind.config.js index e2cd1f2..91f1831 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,5 @@ module.exports = { - content: ['./app/**/*.tsx', './app/**/*.ts'], - theme: {}, - plugins: [], + content: ['./app/**/*.tsx', './app/**/*.ts'], + theme: {}, + plugins: [require('@tailwindcss/typography')], }; diff --git a/tests/index.spec.ts b/tests/index.spec.ts index 1c46169..30e90a3 100644 --- a/tests/index.spec.ts +++ b/tests/index.spec.ts @@ -1,18 +1,18 @@ import { test, expect } from './setup'; test.beforeEach(async ({ page }) => { - await page.goto('/'); + await page.goto('/'); }); /** * You can interact with the page or browser through the page / queries */ test('if the page shows the package name', async ({ page }) => { - const title = page.getByText('remix-worker-template', { - exact: false, - }); + const title = page.getByText('remix-worker-template', { + exact: false, + }); - await expect(title).toBeVisible(); + await expect(title).toBeVisible(); }); /** @@ -21,12 +21,12 @@ test('if the page shows the package name', async ({ page }) => { * Or even interacting with the DO by `mf.getDurableObjectNamespace(...)` */ test('if the binding are set properly', async ({ mf }) => { - const bindings = await mf.getBindings(); + const bindings = await mf.getBindings(); - expect(bindings).toEqual({ - __STATIC_CONTENT: expect.anything(), - __STATIC_CONTENT_MANIFEST: expect.anything(), - }); + expect(bindings).toEqual({ + __STATIC_CONTENT: expect.anything(), + __STATIC_CONTENT_MANIFEST: expect.anything(), + }); }); /** @@ -34,16 +34,16 @@ test('if the binding are set properly', async ({ mf }) => { * @see https://github.com/nodejs/undici/blob/main/docs/api/MockAgent.md */ test('if the request is sent', async ({ mockAgent }) => { - const client = mockAgent.get('http://example.com'); - - client - .intercept({ - method: 'GET', - path: '/hello-world', - }) - .reply(200, { - foo: 'bar', - }); - - // expect() something happens + const client = mockAgent.get('http://example.com'); + + client + .intercept({ + method: 'GET', + path: '/hello-world', + }) + .reply(200, { + foo: 'bar', + }); + + // expect() something happens }); diff --git a/tests/setup.ts b/tests/setup.ts index 842b295..e0dd087 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -4,66 +4,66 @@ import { MockAgent, setGlobalDispatcher } from 'undici'; import packageJson from '../package.json'; interface TestFixtures { - mockAgent: MockAgent; + mockAgent: MockAgent; } interface WorkerFixtures { - mf: Miniflare; - port: number; + mf: Miniflare; + port: number; } export { expect }; export const test = base.extend({ - // Assign a unique "port" for each worker process - port: [ - // eslint-disable-next-line no-empty-pattern - async ({}, use, workerInfo) => { - await use(3001 + workerInfo.workerIndex); - }, - { scope: 'worker' }, - ], + // Assign a unique "port" for each worker process + port: [ + // eslint-disable-next-line no-empty-pattern + async ({}, use, workerInfo) => { + await use(3001 + workerInfo.workerIndex); + }, + { scope: 'worker' }, + ], - // Ensure visits works with relative path - baseURL: ({ port }, use) => { - use(`http://localhost:${port}`); - }, + // Ensure visits works with relative path + baseURL: ({ port }, use) => { + use(`http://localhost:${port}`); + }, - // Setup mock client for requests initiated by the Worker - mockAgent: - // eslint-disable-next-line no-empty-pattern - async ({}, use) => { - const mockAgent = new MockAgent(); + // Setup mock client for requests initiated by the Worker + mockAgent: + // eslint-disable-next-line no-empty-pattern + async ({}, use) => { + const mockAgent = new MockAgent(); - // Optional: This makes all the request fails if no matching mock is found - // mockAgent.disableNetConnect(); + // Optional: This makes all the request fails if no matching mock is found + // mockAgent.disableNetConnect(); - setGlobalDispatcher(mockAgent); + setGlobalDispatcher(mockAgent); - await use(mockAgent); - }, + await use(mockAgent); + }, - // Miniflare instance - mf: [ - async ({ port }, use) => { - const mf = new Miniflare({ - scriptPath: packageJson.module, - modules: true, - wranglerConfigPath: true, - port, - }); + // Miniflare instance + mf: [ + async ({ port }, use) => { + const mf = new Miniflare({ + scriptPath: packageJson.module, + modules: true, + wranglerConfigPath: true, + port, + }); - // Start the server. - let server = await mf.startServer(); + // Start the server. + let server = await mf.startServer(); - // Use the server in the tests. - await use(mf); + // Use the server in the tests. + await use(mf); - // Cleanup. - await new Promise((resolve, reject) => { - server.close((error) => (error ? reject(error) : resolve())); - }); - }, - { scope: 'worker', auto: true }, - ], + // Cleanup. + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); + }, + { scope: 'worker', auto: true }, + ], }); diff --git a/vite.config.ts b/vite.config.ts index 68aa6c4..c79c48f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,26 +1,26 @@ import { - unstable_vitePlugin as remix, - unstable_cloudflarePreset as cloudflare, + unstable_vitePlugin as remix, + unstable_cloudflarePreset as cloudflare, } from '@remix-run/dev'; import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ - plugins: [ - remix({ - presets: [ - cloudflare({ - getRemixDevLoadContext(context) { - return context; - }, - }), - ], - }), - tsconfigPaths(), - ], - ssr: { - resolve: { - externalConditions: ['workerd', 'worker'], - }, - }, + plugins: [ + remix({ + presets: [ + cloudflare({ + getRemixDevLoadContext(context) { + return context; + }, + }), + ], + }), + tsconfigPaths(), + ], + ssr: { + resolve: { + externalConditions: ['workerd', 'worker'], + }, + }, }); diff --git a/wrangler.toml b/wrangler.toml index 7ebe8ae..d829a1f 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,8 +1,4 @@ -name = "template" -main = "./dist/worker.mjs" -# route = "example.com/*" -compatibility_date = "2022-11-25" -compatibility_flags = ["streams_enable_constructors"] - -[site] -bucket = "./public" \ No newline at end of file +name = "remix-cloudflare-template" +kv_namespaces = [ + { id = "CACHE", binding="CACHE" } +] \ No newline at end of file