Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep prerender component tree the same as regular render tree #7760

Merged
merged 36 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f10c14c
Keep prerender component tree the same as regular render tree
Tobbe Mar 5, 2023
7637e66
Comment loader() type cast
Tobbe Mar 5, 2023
aea1e20
Update test project
Tobbe Mar 5, 2023
6156553
Merge branch 'main' into tobbe-prerender-react18
Tobbe Mar 5, 2023
5aad591
Merge branch 'main' of https://github.com/redwoodjs/redwood into tobb…
Tobbe Mar 5, 2023
e162c67
Merge branch 'tobbe-prerender-react18' of https://github.com/Tobbe/re…
Tobbe Mar 5, 2023
8857aff
renderedLoadingState
Tobbe Mar 7, 2023
08e820c
Merge branch 'main' into tobbe-prerender-react18
Tobbe Mar 18, 2023
52b6166
syncLoader
Tobbe Mar 18, 2023
72c8c03
No renderLoadingState
Tobbe Mar 18, 2023
92c510c
set state depending on prerender
Tobbe Mar 18, 2023
4ca028f
Merge remote-tracking branch 'upstream/main' into tobbe-prerender-rea…
Tobbe Mar 19, 2023
500c38e
Fix unprerendered pages
Tobbe Mar 19, 2023
2f7dfcd
Use require.resolveWeak
Tobbe Mar 20, 2023
df90de2
save progress
jtoar Mar 20, 2023
ff6d583
working changes
jtoar Mar 21, 2023
7810557
clean up from late-night pairing session
Tobbe Mar 21, 2023
cc15e43
set with prerender prop
Tobbe Mar 21, 2023
3a0b331
rename syncLoader to prerenderLoader
jtoar Mar 21, 2023
6074c6d
update comment
jtoar Mar 21, 2023
384ca95
add wip codemod
jtoar Mar 22, 2023
a6f701c
finish codemod
jtoar Mar 22, 2023
ff9a874
add codemod test
jtoar Mar 22, 2023
1905796
Merge branch 'main' into tobbe-prerender-react18
Tobbe Mar 25, 2023
6e3b7eb
Update unit tests to match new page loaders
Tobbe Mar 25, 2023
cb3b4e1
Update index.html in test project fixture
Tobbe Mar 25, 2023
25c23ac
active-route-loader: fix initial load
Tobbe Mar 25, 2023
d5e3287
Only use prerenderLoader in production
Tobbe Mar 25, 2023
4aae773
Detect prerendered pages
Tobbe Mar 25, 2023
215d898
Merge branch 'main' into tobbe-prerender-react18
Tobbe Mar 25, 2023
14e9af8
revert changes to analyzeRouterTree
Tobbe Mar 26, 2023
0c63eee
Don't try to load separate chunk for /
Tobbe Mar 26, 2023
18187b0
Merge branch 'tobbe-prerender-react18' of https://github.com/Tobbe/re…
Tobbe Mar 26, 2023
bd518b6
createCell: Suspense null fallback
Tobbe Mar 27, 2023
37d7acb
Merge branch 'main' into tobbe-prerender-react18
jtoar Mar 27, 2023
a33042e
Merge branch 'main' into tobbe-prerender-react18
jtoar Mar 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions packages/core/config/webpack.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,6 @@ module.exports = (webpackEnv) => {
!isEnvProduction && new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: path.resolve(redwoodPaths.base, 'web/src/index.html'),
templateParameters: {
prerenderPlaceholder: isEnvProduction
? '<server-markup></server-markup>'
Tobbe marked this conversation as resolved.
Show resolved Hide resolved
: '<!-- Prerender placeholder -->',
},
scriptLoading: 'defer',
inject: true,
chunks: 'all',
Expand Down
6 changes: 2 additions & 4 deletions packages/create-redwood-app/template/web/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
</head>

<body>
<div id="redwood-app">
<!-- Please keep the line below for prerender support. -->
<%= prerenderPlaceholder %>
</div>
<!-- Please keep this div empty -->
<div id="redwood-app"></div>
</body>

</html>
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ describe('page auto loader correctly imports pages', () => {
delete process.env.RWJS_CWD
})

test('Pages are automatically imported', () => {
test('Pages get both a loader and a prerenderLoader', () => {
expect(result?.code).toContain(`const HomePage = {
name: "HomePage",
loader: () => import("`)
loader: () => import( /* webpackChunkName: "HomePage" */"./pages/HomePage/HomePage"),
prerenderLoader: () => __webpack_require__(require.resolveWeak("./pages/HomePage/HomePage"))`)
})

test('Already imported pages are left alone.', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,16 @@ export default function (
)[0]

// Remove Page imports in prerender mode (see babel-preset)
// This is to make sure that all the imported "Page modules" are normal imports
// and not asynchronous ones.
// But note that jest in a user's project does not enter this block, but our tests do
// The removed imports will be replaced further down in this file
// with declarations like these:
// const HomePage = {
// name: "HomePage",
// loader: () => import("./pages/HomePage/HomePage")
// prerenderLoader: () => require("./pages/HomePage/HomePage")
// };
// This is to make sure that all the imported "Page modules" are normal
// imports and not asynchronous ones.
// Note that jest in a user's project does not enter this block, but our tests do
if (useStaticImports) {
// Match import paths, const name could be different

Expand Down Expand Up @@ -108,7 +115,23 @@ export default function (
const nodes = []
// Prepend all imports to the top of the file
for (const { importName, relativeImport } of pages) {
// + const <importName> = { name: <importName>, loader: () => import(<relativeImportPath>) }
// + const <importName> = {
// name: <importName>,
// loader: () => import(/* webpackChunkName: "<importName>" */ <relativeImportPath>)
// // prerender
// prerenderLoader: () => require(<relativeImportPath>)
// // crs
// prerenderLoader: () => __webpack_require__(require.resolveWeak(<relativeImportPath>))
// }

const importArgument = t.stringLiteral(relativeImport)

importArgument.leadingComments = [
{
type: 'CommentBlock',
value: ` webpackChunkName: "${importName}" `,
},
]

nodes.push(
t.variableDeclaration('const', [
Expand All @@ -119,18 +142,39 @@ export default function (
t.identifier('name'),
t.stringLiteral(importName)
),
// loader for dynamic imports (browser)
t.objectProperty(
t.identifier('loader'),
t.arrowFunctionExpression(
[],
t.callExpression(
// If useStaticImports, do a synchronous import with require (ssr/prerender)
// otherwise do a dynamic import (browser)
useStaticImports
? t.identifier('require')
: t.identifier('import'),
[t.stringLiteral(relativeImport)]
)
t.callExpression(t.identifier('import'), [
importArgument,
])
)
),
// prerenderLoader for ssr/prerender and first load of
// prerendered pages in browser (csr)
t.objectProperty(
t.identifier('prerenderLoader'),
t.arrowFunctionExpression(
[],
useStaticImports
? t.callExpression(t.identifier('require'), [
t.stringLiteral(relativeImport),
])
: t.callExpression(
t.identifier(
// Use __webpack_require__ otherwise all pages will
// be bundled
'__webpack_require__'
),
[
t.callExpression(
t.identifier('require.resolveWeak'),
[t.stringLiteral(relativeImport)]
),
]
)
)
),
])
Expand Down
1 change: 1 addition & 0 deletions packages/prerender/src/detection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const detectPrerenderRoutes = () => {
id: route.id,
isNotFound: route.isNotFound,
filePath: route.page?.filePath,
pageIdentifier: route.page_identifier_str,
}))

return prerenderRoutes
Expand Down
37 changes: 34 additions & 3 deletions packages/prerender/src/runPrerender.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import path from 'path'

import React from 'react'

import { load as loadHtml } from 'cheerio'
import { CheerioAPI, load as loadHtml } from 'cheerio'
import ReactDOMServer from 'react-dom/server'

import { registerApiSideBabelHook } from '@redwoodjs/internal/dist/build/babel/api'
import { registerWebSideBabelHook } from '@redwoodjs/internal/dist/build/babel/web'
import { getPaths } from '@redwoodjs/internal/dist/paths'
import { LocationProvider } from '@redwoodjs/router'
import { matchPath } from '@redwoodjs/router/dist/util'
import type { QueryInfo } from '@redwoodjs/web'

import mediaImportsPlugin from './babelPlugins/babel-plugin-redwood-prerender-media-imports'
import { detectPrerenderRoutes } from './detection'
import {
GqlHandlerImportError,
JSONParseError,
Expand Down Expand Up @@ -131,6 +133,34 @@ async function recursivelyRender(
}
}

function insertChunkLoadingScript(
indexHtmlTree: CheerioAPI,
renderPath: string
) {
const prerenderRoutes = detectPrerenderRoutes()

const route = prerenderRoutes.find((route: any) => {
return matchPath(route.routePath, renderPath).match
})

if (!route) {
throw new Error('Could not find a Route matching ' + renderPath)
}

const buildManifest = JSON.parse(
fs.readFileSync(
path.join(getPaths().web.dist, 'build-manifest.json'),
'utf-8'
)
)

const chunkPath = buildManifest[`${route?.pageIdentifier}.js`]

indexHtmlTree('head').prepend(
`<script defer="defer" src="${chunkPath}"></script>`
)
}

interface PrerenderParams {
queryCache: Record<string, QueryInfo>
renderPath: string // The path (url) to render e.g. /about, /dashboard/me, /blog-post/3
Expand Down Expand Up @@ -229,8 +259,9 @@ export const runPrerender = async ({
}
}

// This is set by webpack by the html plugin
indexHtmlTree('server-markup').replaceWith(componentAsHtml)
insertChunkLoadingScript(indexHtmlTree, renderPath)

indexHtmlTree('#redwood-app').append(componentAsHtml)

const renderOutput = indexHtmlTree.html()

Expand Down
Loading