Niedzielski has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/395039 )

Change subject: Fix: show original server errors
......................................................................

Fix: show original server errors

When the server encounters an error, show the original error encountered
on the server instead of allowing a client side render which may cause a
different error.

Change-Id: I3603e26ffdbb8f0313c71c8c598055639b085a54
---
M src/client/index.tsx
A src/common/models/ssr-data.ts
M src/common/router/router.ts
M src/server/components/html-page.tsx
M src/server/index.tsx
M test/server/components/html-page.test.ts
6 files changed, 37 insertions(+), 4 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/marvin refs/changes/39/395039/1

diff --git a/src/client/index.tsx b/src/client/index.tsx
index 0386958..3ded82a 100644
--- a/src/client/index.tsx
+++ b/src/client/index.tsx
@@ -3,6 +3,7 @@
 import "wikimedia-ui-base/wikimedia-ui-base.css";
 import "./index.css";
 import { RouteResponse, newRouter } from "../common/router/router";
+import { SSRData } from "../common/models/ssr-data";
 import { WithContext } from "../common/components/with-context";
 import { routes } from "../common/router/routes";
 
@@ -13,6 +14,7 @@
   require("preact/debug");
 }
 
+const ssrData: SSRData = (window as any).__SSR_DATA__;
 const history = newHistory();
 const router = newRouter(routes);
 const pageRoot = (_ => {
@@ -43,4 +45,6 @@
 // Replace the server rendered root, which does not include CSS, with a styled
 // page that manages navigation with History. This enables the single page app
 // experience.
-route(location.pathname);
+if (!ssrData.forceSSR) {
+  route(location.pathname);
+}
diff --git a/src/common/models/ssr-data.ts b/src/common/models/ssr-data.ts
new file mode 100644
index 0000000..a881d5b
--- /dev/null
+++ b/src/common/models/ssr-data.ts
@@ -0,0 +1,9 @@
+/**
+ * Server-side rendered data. This data is serialized by the server and
+ * deserialized by the client through the `window.__SSR_DATA__` global which is
+ * set prior to any client script execution.
+ */
+export interface SSRData {
+  /** Forbid client-side rendering of subsequently requested pages. */
+  forceSSR: boolean;
+}
diff --git a/src/common/router/router.ts b/src/common/router/router.ts
index c4b8f45..f35deff 100644
--- a/src/common/router/router.ts
+++ b/src/common/router/router.ts
@@ -6,7 +6,6 @@
   Route
 } from "../../common/router/route";
 import HttpResponse from "../http/http-response";
-
 import notFoundPage from "../pages/not-found";
 import errorPage from "../pages/error";
 import { RedirectError } from "../http/fetch";
diff --git a/src/server/components/html-page.tsx 
b/src/server/components/html-page.tsx
index 61603ff..12fcdb6 100644
--- a/src/server/components/html-page.tsx
+++ b/src/server/components/html-page.tsx
@@ -2,6 +2,7 @@
 import { PRODUCTION } from "../../common/assets/config";
 import { asset } from "../../common/assets/manifest";
 import { ChildrenProps } from "../../common/components/preact-utils";
+import { SSRData } from "../../common/models/ssr-data";
 declare function __non_webpack_require__(name: string): any; // 
eslint-disable-line camelcase
 
 export interface Props extends ChildrenProps {
@@ -10,6 +11,7 @@
   // Chunk to preload on the HTML. May not be needed if the chunks are already
   // included, like the error pages
   chunkName?: string;
+  ssrData: SSRData;
 }
 
 // The production asset manifest from the public build products or
@@ -23,6 +25,7 @@
 export default function HTMLPage({
   title = "",
   chunkName,
+  ssrData,
   children
 }: Props): JSX.Element {
   // Asset order matters.
@@ -36,6 +39,10 @@
   const style = asset("index", "css", manifest);
   const favicon = asset("favicon/wikipedia", "ico", manifest);
 
+  // Serialize the SSR data safely. See:
+  // 
https://github.com/reactjs/redux/blob/cda8699/docs/recipes/ServerRendering.md#inject-initial-component-html-and-state
+  const ssrDataString = JSON.stringify(ssrData).replace(/</g, "\\u003c");
+
   return (
     <html lang="en">
       <head>
@@ -46,6 +53,13 @@
         {/* Preload the stylesheet before the scripts */}
         <link rel="preload" href={style} as="style" />
         <link rel="stylesheet" href={style} />
+        {/* Set the global server-side rendered data before scripts execute. 
*/}
+        <script
+          type="text/javascript"
+          dangerouslySetInnerHTML={{
+            __html: `window.__SSR_DATA__ = ${ssrDataString}`
+          }}
+        />
         {assets.map(path => <link rel="preload" href={path} as="script" />)}
         <link rel="shortcut icon" href={favicon} />
       </head>
diff --git a/src/server/index.tsx b/src/server/index.tsx
index fb30274..a4b2c0f 100644
--- a/src/server/index.tsx
+++ b/src/server/index.tsx
@@ -6,6 +6,7 @@
 import { RedirectError } from "../common/http/fetch";
 import { routes } from "../common/router/routes";
 import { SERVER_PORT, SERVER_URL } from "../common/assets/config";
+import ErrorPage from "../common/pages/error";
 import HTMLPage from "./components/html-page";
 
 const server = express();
@@ -15,10 +16,11 @@
 server.use("/public", express.static("dist/public"));
 
 function render({ chunkName, Component, props }: RouteResponse<any>) {
+  const ssrData = { forceSSR: Component === ErrorPage.Component };
   return (
     "<!doctype html>" + // eslint-disable-line prefer-template
     renderToString(
-      <HTMLPage title="" chunkName={chunkName}>
+      <HTMLPage title="" chunkName={chunkName} ssrData={ssrData}>
         <Component {...props} />
       </HTMLPage>
     )
diff --git a/test/server/components/html-page.test.ts 
b/test/server/components/html-page.test.ts
index a9b1243..ef48709 100644
--- a/test/server/components/html-page.test.ts
+++ b/test/server/components/html-page.test.ts
@@ -7,6 +7,7 @@
     const vNode = HTMLPage({
       title: "Test",
       chunkName: "",
+      ssrData: { forceSSR: false },
       children: ["body"]
     });
     const html = render(vNode);
@@ -18,7 +19,11 @@
   });
 
   it("contains a <title/> when rendered", () => {
-    const vNode = HTMLPage({ title: "Test", chunkName: "" });
+    const vNode = HTMLPage({
+      title: "Test",
+      chunkName: "",
+      ssrData: { forceSSR: false }
+    });
     const html = render(vNode);
     const expected = "<title>Test - Marvin</title>";
     assert.ok(

-- 
To view, visit https://gerrit.wikimedia.org/r/395039
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3603e26ffdbb8f0313c71c8c598055639b085a54
Gerrit-PatchSet: 1
Gerrit-Project: marvin
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <[email protected]>
Gerrit-Reviewer: Sniedzielski <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to