Skip to content

Commit

Permalink
Client: Improve SSR
Browse files Browse the repository at this point in the history
  • Loading branch information
winwiz1 committed Oct 25, 2021
1 parent 1f01a6b commit 8e25bab
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 44 deletions.
3 changes: 1 addition & 2 deletions client/src/components/ComponentB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { Helmet } from "react-helmet";
import { Header, Icon, Container, Menu } from "semantic-ui-react";
import { BaseComponent } from "./BaseComponent";
import { StructuredData } from "./StructuredData";
import * as SPAs from "../../config/spa.config";
import { getTitle, getCanonical } from "../utils/misc";

const cssNav = css({
Expand Down Expand Up @@ -64,7 +63,7 @@ const Navigation: React.FC = _props => {
<Menu.Item>
<Menu.Header>Go back to</Menu.Header>
<Menu.Menu>
<Menu.Item href={`/${SPAs.getNames()[0]}`}>
<Menu.Item href="/">
First SPA
<Icon
name="object group outline"
Expand Down
46 changes: 42 additions & 4 deletions client/src/components/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,73 @@ import { NavLink } from "react-router-dom";
import { Menu, Icon } from "semantic-ui-react";
import * as SPAs from "../../config/spa.config";
import styles from "../css/navigation.module.css";
import { isServer } from "../utils/postprocess/misc";

const cssStyle: Record<string, string> = {
menu: styles["menu"],
};

const pathArray = ["/", "/a", "/lighthouse", "/namelookup"];

const getIndex = () => {
if (isServer()) {
return -1;
}
const path = window.location.pathname;
return pathArray.findIndex((item) => item === path);
}

export const Navigation: React.FC = _props => {
React.useEffect(() => {
const idx = getIndex();
const menuItem = document.querySelector(`div.menu>a:nth-child(${idx + 1})`);
menuItem?.classList.add("active");
if (idx > 0) {
const menuItem = document.querySelector("div.menu>a:nth-child(1)");
menuItem?.classList.remove("active");
}
}, []); // run once upon initial rendering

return (
<nav className={cssStyle.menu}>
<Menu
vertical
size="large"
className="nav_menu"
defaultActiveIndex={0}
>
<Menu.Item>
<Menu.Header>First SPA</Menu.Header>
<Menu.Menu>
<Menu.Item header as={NavLink} exact to="/">
<Menu.Item
header
as={NavLink}
exact to={pathArray[0]}
>
Overview
<Icon name="file alternate outline" size="large" />
</Menu.Item>
<Menu.Item header as={NavLink} to="/a">
<Menu.Item
header
as={NavLink}
to={pathArray[1]}
>
ComponentA
<Icon name="cube" size="large" />
</Menu.Item>
<Menu.Item header as={NavLink} to="/lighthouse">
<Menu.Item
header
as={NavLink}
to={pathArray[2]}
>
Lighthouse
<Icon name="tachometer alternate" size="large" />
</Menu.Item>
<Menu.Item header as={NavLink} to="/namelookup">
<Menu.Item
header
as={NavLink}
to={pathArray[3]}
>
NameLookup
<Icon name="search" size="large" />
</Menu.Item>
Expand Down
4 changes: 0 additions & 4 deletions client/src/entrypoints/first.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Helmet } from "react-helmet";
import { Router, Route, Switch } from "react-router-dom";
import { ComponentA } from "../components/ComponentA";
import { Lighthouse } from "../components/Lighthouse";
Expand Down Expand Up @@ -43,9 +42,6 @@ const First: React.FC = _props => {
<>
<Router history={getHistory()}>
<ErrorBoundary>
<Helmet>
<meta charSet="utf-8" />
</Helmet>
<div className="welcome">
<h2>Welcome to {SPAs.appTitle}</h2>
</div>
Expand Down
4 changes: 0 additions & 4 deletions client/src/entrypoints/second.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/
import * as React from "react";
import * as ReactDOM from "react-dom";
import { Helmet } from "react-helmet";
import { ComponentB } from "../components/ComponentB";
import { ErrorBoundary } from "../components/ErrorBoundary";
// import { renderToString } from "react-dom/server";
Expand All @@ -26,9 +25,6 @@ const Second: React.FC = _props => {
return (
<>
<ErrorBoundary>
<Helmet>
<meta charSet="utf-8" />
</Helmet>
<div className="welcome">
<h2>Welcome to {SPAs.appTitle}</h2>
</div>
Expand Down
47 changes: 17 additions & 30 deletions client/src/utils/postprocess/postProcessSSR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as fs from "fs";
import * as path from "path";
import { promisify } from "util";
import { JSDOM } from "jsdom";
import { CallbackWrapper } from "./misc"
import * as SPAs from "../../../config/spa.config";

export async function postProcess(workDir: string, filePattern: string): Promise<void> {
Expand All @@ -23,39 +22,27 @@ export async function postProcess(workDir: string, filePattern: string): Promise

async function postProcessBody(workDir: string, htmlFile: string, ssrFile: string): Promise<void> {
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);

const htmlFilePath = path.join(workDir, htmlFile);
const ssrFilePath = path.join(workDir, ssrFile);

const dataHtml = await readFile(htmlFilePath);
const jsdom = await JSDOM.fromFile(htmlFilePath);

if (!jsdom) {
throw "JSDOM creation failure";
}

const dataSsr = (await readFile(ssrFilePath)).toString();
const reReact = /^\s*<div\s+id="app-root">/;
const ar: string[] = dataHtml.toString().replace(/\r\n?/g, "\n").split("\n");

const out = ar.map(str => {
if (reReact.test(str)) {
str += "\n";
str += dataSsr;
}
str += "\n";
return str;
});

const stream = fs.createWriteStream(htmlFilePath);
stream.on("error", err => {
console.error(`Failed to write to file ${htmlFilePath}, error: ${err}`)
});
out.forEach(str => { stream.write(str); });

stream.end();

let cbWrapper: CallbackWrapper|undefined;
const waitForBufferFlush = new Promise<void>((resolve) => { cbWrapper = new CallbackWrapper(resolve); });

stream.on("close", function() {
cbWrapper?.invoke();
});

await waitForBufferFlush;
const fragment = JSDOM.fragment(dataSsr);
const appRoot = jsdom.window.document.querySelector("div[id='app-root']");

if (!appRoot) {
throw "JSDOM - 'app-root' not found";
}

appRoot.appendChild(fragment);
await writeFile(htmlFilePath, jsdom.serialize());
}

async function postProcessHeader(workDir: string, htmlFile: string): Promise<void> {
Expand Down

0 comments on commit 8e25bab

Please sign in to comment.