Skip to content

Commit

Permalink
feat(og-gen): Implement middleware and hooks (#10469)
Browse files Browse the repository at this point in the history
Co-authored-by: Rob Cameron <cannikin@fastmail.com>
  • Loading branch information
dac09 and cannikin committed Apr 26, 2024
1 parent a2a4a02 commit e5f32b6
Show file tree
Hide file tree
Showing 32 changed files with 1,135 additions and 63 deletions.
45 changes: 45 additions & 0 deletions .changesets/10469.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
- feat(og-gen): Implement middleware and hooks (#10469) by @dac09

The OG Gen saga continues with @cannikin and @dac09 ⚔️

This PR:
- adds OgImageMiddleware and Hooks to `@redwoodjs/og-gen`, complete with tests

⚠️ Template changes:
- updates entry.client template to pass in Routes to App
- updates App to take children (i.e. Routes)

This is so that we can pass the OG component to be rendered _with_ your App's CSS setup.


**How to use this?**

1. **Registering the middleware:**
```ts
import OgImageMiddleware from '@redwoodjs/ogimage-gen/middleware'

export const registerMiddleware = () => {
const ogMw = new OgImageMiddleware({
App,
Document,
})

return [ogMw]
}
```

2. Configure your `vite.config.ts`
```ts
import vitePluginOgImageGen from '@redwoodjs/ogimage-gen/plugin'
const viteConfig: UserConfig = {
// 👇 so it builds your OG components
plugins: [redwood(), vitePluginOgImageGen()],
}
export default defineConfig(viteConfig)
```
3. Add your OG Image component next to the page it's for
e.g. web/src/pages/AboutPage/AboutPage.png.tsx

4. Use hooks on AboutPage to generate the ogURL
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"Flightcontrol",
"graphiql",
"memfs",
"OGIMAGE",
"opentelemetry",
"pino",
"Pistorius",
Expand Down
6 changes: 3 additions & 3 deletions __fixtures__/test-project/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import { AuthProvider, useAuth } from './auth'

import './scaffold.css'
import './index.css'
import './scaffold.css'

interface AppProps {
children?: ReactNode
}
Expand All @@ -19,7 +19,7 @@ const App = ({ children }: AppProps) => (
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<AuthProvider>
<RedwoodApolloProvider useAuth={useAuth}>
{children ? children : <Routes />}
{children}
</RedwoodApolloProvider>
</AuthProvider>
</RedwoodProvider>
Expand Down
15 changes: 13 additions & 2 deletions __fixtures__/test-project/web/src/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { hydrateRoot, createRoot } from 'react-dom/client'

import App from './App'
import Routes from './Routes'

/**
* When `#redwood-app` isn't empty then it's very likely that you're using
* prerendering. So React attaches event listeners to the existing markup
Expand All @@ -16,8 +18,17 @@ if (!redwoodAppElement) {
}

if (redwoodAppElement.children?.length > 0) {
hydrateRoot(redwoodAppElement, <App />)
hydrateRoot(
redwoodAppElement,
<App>
<Routes />
</App>
)
} else {
const root = createRoot(redwoodAppElement)
root.render(<App />)
root.render(
<App>
<Routes />
</App>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { hydrateRoot, createRoot } from 'react-dom/client'

import App from './App'
import { Document } from './Document'
import Routes from './Routes'

/**
* When `#redwood-app` isn't empty then it's very likely that you're using
Expand All @@ -15,14 +16,18 @@ if (redwoodAppElement.children?.length > 0) {
hydrateRoot(
document,
<Document css={window.__assetMap?.()?.css}>
<App />
<App>
<Routes />
</App>
</Document>
)
} else {
const root = createRoot(document)
root.render(
<Document css={window.__assetMap?.()?.css}>
<App />
<App>
<Routes />
</App>
</Document>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { TagDescriptor } from '@redwoodjs/web'

import App from './App'
import { Document } from './Document'
import Routes from './Routes'

interface Props {
css: string[]
Expand All @@ -11,7 +12,9 @@ interface Props {
export const ServerEntry: React.FC<Props> = ({ css, meta }) => {
return (
<Document css={css} meta={meta}>
<App />
<App>
<Routes />
</App>
</Document>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ describe('fragments graphQLClientConfig', () => {
import { RedwoodApolloProvider } from \"@redwoodjs/web/apollo\";
import FatalErrorPage from \"src/pages/FatalErrorPage\";
import Routes from \"src/Routes\";
import { AuthProvider, useAuth } from \"./auth\";
import \"./scaffold.css\";
import \"./index.css\";
import \"./scaffold.css\";
interface AppProps {
children?: ReactNode;
}
Expand All @@ -94,7 +94,7 @@ describe('fragments graphQLClientConfig', () => {
useAuth={useAuth}
graphQLClientConfig={graphQLClientConfig}
>
{children ? children : <Routes />}
{children}
</RedwoodApolloProvider>
</AuthProvider>
</RedwoodProvider>
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,8 +441,8 @@ export const addScaffoldImport = () => {
}

appJsContents = appJsContents.replace(
"import Routes from 'src/Routes'\n",
"import Routes from 'src/Routes'\n\nimport './scaffold.css'",
"import './index.css'",
"import './index.css'\nimport './scaffold.css'\n",
)
writeFile(appJsPath, appJsContents, { overwriteExisting: true })

Expand Down
1 change: 1 addition & 0 deletions packages/core/config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ module.exports = (webpackEnv) => {
'styled-components',
),
'~redwood-app-root': path.resolve(redwoodPaths.web.app),
'~redwood-app-routes': path.resolve(redwoodPaths.web.routes),
react: path.resolve(redwoodPaths.base, 'node_modules', 'react'),
'react-hook-form': path.resolve(
redwoodPaths.base,
Expand Down
5 changes: 1 addition & 4 deletions packages/create-redwood-app/templates/js/web/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'

const App = ({ children }) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
{children ? children : <Routes />}
</RedwoodApolloProvider>
<RedwoodApolloProvider>{children}</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
)
Expand Down
15 changes: 13 additions & 2 deletions packages/create-redwood-app/templates/js/web/src/entry.client.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { hydrateRoot, createRoot } from 'react-dom/client'

import App from './App'
import Routes from './Routes'

/**
* When `#redwood-app` isn't empty then it's very likely that you're using
* prerendering. So React attaches event listeners to the existing markup
Expand All @@ -16,8 +18,17 @@ if (!redwoodAppElement) {
}

if (redwoodAppElement.children?.length > 0) {
hydrateRoot(redwoodAppElement, <App />)
hydrateRoot(
redwoodAppElement,
<App>
<Routes />
</App>
)
} else {
const root = createRoot(redwoodAppElement)
root.render(<App />)
root.render(
<App>
<Routes />
</App>
)
}
5 changes: 1 addition & 4 deletions packages/create-redwood-app/templates/ts/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'
interface AppProps {
Expand All @@ -14,9 +13,7 @@ interface AppProps {
const App = ({ children }: AppProps) => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
{children ? children : <Routes />}
</RedwoodApolloProvider>
<RedwoodApolloProvider>{children}</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
)
Expand Down
15 changes: 13 additions & 2 deletions packages/create-redwood-app/templates/ts/web/src/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { hydrateRoot, createRoot } from 'react-dom/client'

import App from './App'
import Routes from './Routes'

/**
* When `#redwood-app` isn't empty then it's very likely that you're using
* prerendering. So React attaches event listeners to the existing markup
Expand All @@ -16,8 +18,17 @@ if (!redwoodAppElement) {
}

if (redwoodAppElement.children?.length > 0) {
hydrateRoot(redwoodAppElement, <App />)
hydrateRoot(
redwoodAppElement,
<App>
<Routes />
</App>
)
} else {
const root = createRoot(redwoodAppElement)
root.render(<App />)
root.render(
<App>
<Routes />
</App>
)
}
4 changes: 2 additions & 2 deletions packages/internal/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export interface RWRouteManifestItem {
routeHooks: string | null
bundle: string | null
hasParams: boolean
relativeFilePath: string | undefined
relativeFilePath: string
redirect: { to: string; permanent: boolean } | null
// Probably want isNotFound here, so we can attach a separate 404 handler
}
Expand All @@ -80,7 +80,7 @@ export interface RouteSpec extends RWRouteManifestItem {
id: string
isNotFound: boolean
filePath: string | undefined
relativeFilePath: string | undefined
relativeFilePath: string
}

export const getProjectRoutes = (): RouteSpec[] => {
Expand Down
3 changes: 3 additions & 0 deletions packages/ogimage-gen/cjsWrappers/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* eslint-env node */

module.exports = require('../dist/hooks.js').default
3 changes: 3 additions & 0 deletions packages/ogimage-gen/cjsWrappers/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* eslint-env node */

module.exports = require('../dist/OgImageMiddleware.js').default
18 changes: 14 additions & 4 deletions packages/ogimage-gen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@
},
"license": "MIT",
"exports": {
".": {
"default": "./dist/index.js"
},
"./plugin": {
"import": "./dist/vite-plugin-ogimage-gen.js",
"default": "./cjsWrappers/plugin.js"
"default": "./cjsWrappers/plugin.js",
"types": "./dist/vite-plugin-ogimage-gen.d.ts"
},
"./middleware": {
"import": "./dist/OgImageMiddleware.js",
"default": "./cjsWrappers/middleware.js",
"types": "./dist/OgImageMiddleware.d.ts"
},
"./hooks": {
"import": "./dist/hooks.js",
"default": "./cjsWrappers/hooks.js",
"types": "./dist/hooks.d.ts"
}
},
"files": [
Expand All @@ -34,10 +42,12 @@
"@redwoodjs/router": "workspace:*",
"@redwoodjs/vite": "workspace:*",
"fast-glob": "3.3.2",
"lodash": "4.17.21",
"react": "19.0.0-canary-cb151849e1-20240424",
"react-dom": "19.0.0-canary-cb151849e1-20240424"
},
"devDependencies": {
"@playwright/test": "1.42.1",
"@redwoodjs/framework-tools": "workspace:*",
"ts-toolbelt": "9.6.0",
"tsx": "4.7.1",
Expand Down
Loading

0 comments on commit e5f32b6

Please sign in to comment.