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

Externalize prepare template #100

Merged
merged 4 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 4 additions & 17 deletions docs/02.vite-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,12 @@ This one supports the dev-server, HMR and transformation & compilation.
Vite's configuration is managed by two main files:

- [vite.config.ts](vite.config.ts): contains the whole vite config [(vite config documentation)](https://vitejs.dev/config/)
- [vite.scripts.config.ts](vite.scripts.config.ts): contains the whole vite scripts config. It built scripts files relative to the SSR and SSG part.
- [config/config.js](config/config.js): is the internal paths and tasks config file.

### <a name="Entrypoints"></a>Entry points

By default, the single application entrypoint is [src/index.tsx](src/index.tsx). It initializes a react App.
But this file can be changed as `index.ts` or `index.js`. In case this filename or type change, [config/config.js](config/config.js)
`input` array need to be modified.
Two entry points are set:

```js
input: [
"src/index.tsx", // -> ex: modified as 'src/main.js'
]
```

Being an array, it is possible to define several application entry points.

```js
input: ["src/main.ts", "src/second.ts"]
```

The default [base.twig](dist/front/www/views/layouts/base.twig) template,
is configured to add as script each input entrypoint automatically.
- server side [src/index-server.tsx](src/index-server.tsx)
- client side [src/index-client.tsx](src/index.tsx)
2 changes: 1 addition & 1 deletion docs/09.php-service-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- Add `./composer-install.sh` script in the project root.
- Add docker apache volume in `docker-compose.yml`
- Remove ssr ssg files
- [server.js](../server.js)
- [server.js](../server/server.js)
- [vite.scripts.config.js](../vite.scripts.config.js)
- [index.html](index.html)
- Update [config/config.js](../config/config.js)
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="<!--lang-->">
<head>
<meta charset="UTF-8" />
<meta http-equiv="imagetoolbar" content="no" />
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"main": "src/index.tsx",
"type": "module",
"scripts": {
"dev": "node server.js",
"dev": "node server/server.js",
"build:client": "vite build --outDir dist/front/client",
"build:server": "vite build --ssr src/index-server.tsx --outDir dist/front/server",
"build:scripts": "vite build -c vite.scripts.config.ts",
Expand Down
22 changes: 9 additions & 13 deletions prerender/prerender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { render } from "~/index-server"
import config from "../config/config.js"
import palette from "../config/helpers/palette.js"
import { isRouteIndex } from "./helpers/isRouteIndex"
import { prepareTemplate } from "../server/prepareTemplate.js"

export const prerender = async (urls: string[], outDirStatic = config.outDirStatic) => {
console.log("URLs to generate", urls)
Expand All @@ -22,26 +23,21 @@ export const prerender = async (urls: string[], outDirStatic = config.outDirStat

try {
// Request information from render method
const { renderToString, ssrStaticProps, globalData, meta } = await render(
const { renderToString, ssrStaticProps, globalData, meta, lang } = await render(
preparedUrl,
true
)

// Case url is index of root or of index of a group
if (isRouteIndex(preparedUrl, urls)) preparedUrl = `${preparedUrl}/index`

// include it in the template
const template = layout
? layout
.replaceAll(`<!--meta-title-->`, meta?.title ?? "")
.replaceAll(`<!--meta-description-->`, meta?.description ?? "")
.replaceAll(`<!--meta-imageUrl-->`, meta?.imageUrl ?? "")
.replaceAll(`<!--meta-url-->`, meta?.url ?? "")
.replaceAll(`<!--meta-siteName-->`, meta?.siteName ?? "")
.replace(`<!--app-html-->`, renderToString)
.replace(`"<!--ssr-static-props-->"`, JSON.stringify(ssrStaticProps))
.replace(`"<!--ssr-global-data-->"`, JSON.stringify(globalData))
: ""
const template = prepareTemplate(layout, {
app: renderToString,
ssrStaticProps,
globalData,
meta,
lang,
})

// prepare sub folder templates if exist
const routePath = path.resolve(`${outDirStatic}/${preparedUrl}`)
Expand Down
26 changes: 26 additions & 0 deletions server/prepareTemplate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Replace strings
* @param layout
* @param meta
* @param app
* @param ssrStaticProps
* @param globalData
* @param lang
* @return {string}
*/
export const prepareTemplate = (
layout,
{ meta, app, ssrStaticProps, globalData, lang }
) => {
if (!layout) return ""
return layout
.replaceAll(`<!--meta-title-->`, meta?.title ?? "")
.replaceAll(`<!--meta-description-->`, meta?.description ?? "")
.replaceAll(`<!--meta-imageUrl-->`, meta?.imageUrl ?? "")
.replaceAll(`<!--meta-url-->`, meta?.url ?? "")
.replaceAll(`<!--meta-siteName-->`, meta?.siteName ?? "")
.replace(`<!--lang-->`, lang)
.replace(`<!--app-html-->`, app)
.replace(`"<!--ssr-static-props-->"`, JSON.stringify(ssrStaticProps))
.replace(`"<!--ssr-global-data-->"`, JSON.stringify(globalData))
}
66 changes: 33 additions & 33 deletions server.js → server/server.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import * as mfs from "./config/helpers/mfs.js"
import * as mfs from "../config/helpers/mfs.js"
import { resolve } from "path"
import express from "express"
import { createServer as createViteServer } from "vite"
import compression from "compression"
import portFinderSync from "portfinder-sync"
import config from "./config/config.js"
import config from "../config/config.js"
import { prepareTemplate } from "./prepareTemplate.js"
import debug from "@wbe/debug"
const log = debug("server:server")

/**
* Create development server
*
*
*
*/

async function createDevServer() {
const app = express()

Expand All @@ -33,36 +36,31 @@ async function createDevServer() {

try {
// 1. Read index.html
let template = await mfs.readFile(resolve("index.html"))
//console.log('template', template)
let layout = await mfs.readFile(resolve("index.html"))

// 2. Apply Vite HTML transforms. This injects the Vite HMR client, and
// also applies HTML transforms from Vite plugins, e.g. global preambles
// from @vitejs/plugin-react
template = await vite.transformIndexHtml(url, template)
layout = await vite.transformIndexHtml(url, layout)

// 3. Load the server entry. vite.ssrLoadModule automatically transforms
// your ESM source code to be usable in Node.js! There is no bundling
// required, and provides efficient invalidation similar to HMR.
const { render } = await vite.ssrLoadModule(`${config.srcDir}/index-server.tsx`)

// 4. render the app HTML. This assumes entry-server.js's exported `render`
// function calls appropriate framework SSR APIs,
// e.g. ReactDOMServer.renderToString()
const { meta, renderToString, ssrStaticProps, globalData } = await render(url)

log({ url, renderToString, ssrStaticProps, globalData })
// function calls appropriate framework SSR APIs
const { meta, renderToString, ssrStaticProps, globalData, lang } = await render(url)
log({ url, renderToString, ssrStaticProps, globalData, lang })

// 5. Inject the app-rendered HTML into the template.
let html = template
.replaceAll(`<!--meta-title-->`, meta?.title ?? "")
.replaceAll(`<!--meta-description-->`, meta?.description ?? "")
.replaceAll(`<!--meta-imageUrl-->`, meta?.imageUrl ?? "")
.replaceAll(`<!--meta-url-->`, meta?.url ?? "")
.replaceAll(`<!--meta-siteName-->`, meta?.siteName ?? "")
.replace(`<!--app-html-->`, renderToString)
.replace(`"<!--ssr-static-props-->"`, JSON.stringify(ssrStaticProps))
.replace(`"<!--ssr-global-data-->"`, JSON.stringify(globalData))
const html = prepareTemplate(layout, {
app: renderToString,
ssrStaticProps,
globalData,
meta,
lang,
})

// 6. Send the rendered HTML back.
res.status(200).set({ "Content-Type": "text/html" }).end(html)
Expand All @@ -79,6 +77,8 @@ async function createDevServer() {

/**
* Create production server
*
*
*/
async function createProdServer() {
console.log("prod server")
Expand All @@ -90,17 +90,19 @@ async function createProdServer() {
appType: "custom", // don't include Vite's default HTML handling middlewares
})

app.use(compression())
// @ts-ignore
app.use(serveStatic(config.outDirClient), { index: false })

app.use((await import("compression")).default())
app.use("*", async (req, res) => {
try {
const url = req.originalUrl
const template = await mfs.readFile(`${config.outDirClient}/index.html`)
const { render } = require(`${config.outDirServer}/index-server.js`)
const appHtml = render(url)
const html = template.replace(`<!--app-html-->`, appHtml)
const layout = await mfs.readFile(`${config.outDirClient}/index.html`)
const { render } = await import(`${config.outDirServer}/index-server.js`)
const { meta, renderToString, ssrStaticProps, globalData } = await render(url)
const html = prepareTemplate(layout, {
app: renderToString,
ssrStaticProps,
globalData,
meta,
})
res.status(200).set({ "Content-Type": "text/html" }).end(html)
} catch (e) {
console.log(e.stack)
Expand All @@ -117,8 +119,6 @@ async function createProdServer() {
const isProd = process.env.NODE_ENV === "production"
const port = process.env.DOCKER_NODE_PORT ?? portFinderSync.getPort(3000)

if (!isProd) {
createDevServer().then(({ app }) => app.listen(port))
} else {
createProdServer().then(({ app }) => app.listen(port))
}
isProd
? createProdServer().then(({ app }) => app.listen(port))
: createDevServer().then(({ app }) => app.listen(port))
1 change: 1 addition & 0 deletions src/index-server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ export async function render(url: string, isPrerender = false) {
ssrStaticProps,
globalData,
languages: langService.languages,
lang: langService.currentLang?.key,
}
}
8 changes: 4 additions & 4 deletions src/routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import fetch from "cross-fetch"
import { TRoute } from "@cher-ami/router"
import { TLanguage, TRoute } from "@cher-ami/router"
import { TMetaTags } from "./managers/MetaManager"
import HomePage from "./pages/homePage/HomePage"
import WorkPage from "./pages/workPage/WorkPage"
Expand All @@ -16,7 +16,7 @@ export const routes: TRoute[] = [
path: "/",
component: HomePage,
name: EPages.HOME,
getStaticProps: async (props) => {
getStaticProps: async (props, currentLang: TLanguage) => {
const res = await fetch("https://worldtimeapi.org/api/ip")
const time = await res.json()
const meta: TMetaTags = {
Expand All @@ -31,7 +31,7 @@ export const routes: TRoute[] = [
path: "/work/:slug?",
name: EPages.WORK,
component: WorkPage,
getStaticProps: async (props) => {
getStaticProps: async (props, currentLang: TLanguage) => {
const meta: TMetaTags = {
title: `Work - ${props.params.slug}`,
description: "Work description",
Expand All @@ -44,7 +44,7 @@ export const routes: TRoute[] = [
path: "/:rest",
name: EPages.NOT_FOUND,
component: NotFoundPage,
getStaticProps: async (props) => {
getStaticProps: async (props, currentLang: TLanguage) => {
const meta = {
title: `404`,
description: "Not found",
Expand Down
2 changes: 1 addition & 1 deletion vite.scripts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default defineConfig(({ command, mode }) => {
},
rollupOptions: {
input: [
resolve("server.js"),
resolve("server/server.js"),
resolve("prerender/prerender.ts"),
resolve("prerender/exe-prerender-server.ts"),
resolve("prerender/exe-prerender.ts"),
Expand Down