diff --git a/src/base-app.js b/src/base-app.js
index 5eaa9175..4c904649 100644
--- a/src/base-app.js
+++ b/src/base-app.js
@@ -12,10 +12,9 @@ import {
ElementToken,
RenderToken,
SSRDeciderToken,
- SSRBodyTemplateToken,
RoutePrefixToken,
} from './tokens';
-import {SSRDecider, SSRBodyTemplate} from './plugins/ssr';
+import {SSRDecider} from './plugins/ssr';
import RoutePrefixPlugin from './plugins/route-prefix';
import type {aliaser, cleanupFn, FusionPlugin, Token} from './types.js';
@@ -29,7 +28,6 @@ class FusionApp {
el && this.register(ElementToken, el);
render && this.register(RenderToken, render);
this.register(SSRDeciderToken, SSRDecider);
- this.register(SSRBodyTemplateToken, SSRBodyTemplate);
this.register(RoutePrefixToken, RoutePrefixPlugin);
}
diff --git a/src/plugins/route-prefix.js b/src/plugins/route-prefix.js
index a10122e1..8ba21f33 100644
--- a/src/plugins/route-prefix.js
+++ b/src/plugins/route-prefix.js
@@ -27,7 +27,7 @@ export default createPlugin({
if (ctx.element) {
ctx.template.head.push(
dangerouslySetHTML(
- ``
)
diff --git a/src/plugins/ssr.js b/src/plugins/ssr.js
index 79967d35..3e3c65c3 100644
--- a/src/plugins/ssr.js
+++ b/src/plugins/ssr.js
@@ -5,6 +5,7 @@
*
* @flow
*/
+/* eslint-env node*/
import {createPlugin} from '../create-plugin';
import {escape, consumeSanitizedHTML} from '../sanitization';
@@ -14,6 +15,11 @@ import type {
SSRBodyTemplate as SSRBodyTemplateService,
} from '../types.js';
+const isTest = Boolean(process.env.NODE_ENV === 'test' || process.env.JEST_ENV);
+
+// Flow workaround: https://github.com/facebook/flow/issues/285#issuecomment-382044301
+const {defineProperty} = Object;
+
const SSRDecider = createPlugin({
provides: () => {
return ctx => {
@@ -31,54 +37,6 @@ const SSRDecider = createPlugin({
});
export {SSRDecider};
-const SSRBodyTemplate = createPlugin({
- provides: () => {
- return ctx => {
- const {htmlAttrs, bodyAttrs, title, head, body} = ctx.template;
- const safeAttrs = Object.keys(htmlAttrs)
- .map(attrKey => {
- return ` ${escape(attrKey)}="${escape(htmlAttrs[attrKey])}"`;
- })
- .join('');
-
- const safeBodyAttrs = Object.keys(bodyAttrs)
- .map(attrKey => {
- return ` ${escape(attrKey)}="${escape(bodyAttrs[attrKey])}"`;
- })
- .join('');
-
- const safeTitle = escape(title);
- // $FlowFixMe
- const safeHead = head.map(consumeSanitizedHTML).join('');
- // $FlowFixMe
- const safeBody = body.map(consumeSanitizedHTML).join('');
-
- const preloadHintLinks = getPreloadHintLinks(ctx);
- const coreGlobals = getCoreGlobals(ctx);
- const chunkScripts = getChunkScripts(ctx);
- const bundleSplittingBootstrap = [
- preloadHintLinks,
- coreGlobals,
- chunkScripts,
- ].join('');
-
- return [
- '',
- ``,
- `
`,
- ``,
- `${safeTitle}`,
- `${bundleSplittingBootstrap}${safeHead}`,
- ``,
- `${ctx.rendered}${safeBody}`,
- '',
- ].join('');
- };
- },
-});
-
-export {SSRBodyTemplate};
-
export default function createSSRPlugin({
element,
ssrDecider,
@@ -86,7 +44,7 @@ export default function createSSRPlugin({
}: {
element: any,
ssrDecider: SSRDeciderService,
- ssrBodyTemplate: SSRBodyTemplateService,
+ ssrBodyTemplate?: SSRBodyTemplateService,
}) {
return async function ssrPlugin(ctx: Context, next: () => Promise) {
if (!ssrDecider(ctx)) return next();
@@ -111,10 +69,86 @@ export default function createSSRPlugin({
return;
}
- ctx.body = ssrBodyTemplate(ctx);
+ if (ssrBodyTemplate) {
+ ctx.body = ssrBodyTemplate(ctx);
+ } else {
+ let legacyBody = legacySSRBodyTemplate(ctx);
+
+ if (!isTest) {
+ if (__DEV__) {
+ // eslint-disable-next-line no-console
+ console.warn([
+ 'Warning: no SSRBodyTemplate token was registered.',
+ 'Upgrading fusion-cli will probably resolve this warning.',
+ ]);
+ }
+ ctx.body = legacyBody;
+ } else {
+ defineProperty(ctx, 'body', {
+ get: () => {
+ // eslint-disable-next-line no-console
+ console.warn([
+ 'In the next major version of fusion-core,',
+ 'if no SSRBodyTemplate token is registered,',
+ 'ctx.body will not be set during SSR.',
+ 'This means simulation tests should assert against ctx.rendered instead of ctx.body',
+ ]);
+ return legacyBody;
+ },
+ set: newBody => {
+ legacyBody = newBody;
+ },
+ writeable: true,
+ enumerable: true,
+ configurable: true,
+ });
+ }
+ }
};
}
+function legacySSRBodyTemplate(ctx) {
+ const {htmlAttrs, bodyAttrs, title, head, body} = ctx.template;
+ const safeAttrs = Object.keys(htmlAttrs)
+ .map(attrKey => {
+ return ` ${escape(attrKey)}="${escape(htmlAttrs[attrKey])}"`;
+ })
+ .join('');
+
+ const safeBodyAttrs = Object.keys(bodyAttrs)
+ .map(attrKey => {
+ return ` ${escape(attrKey)}="${escape(bodyAttrs[attrKey])}"`;
+ })
+ .join('');
+
+ const safeTitle = escape(title);
+ // $FlowFixMe
+ const safeHead = head.map(consumeSanitizedHTML).join('');
+ // $FlowFixMe
+ const safeBody = body.map(consumeSanitizedHTML).join('');
+
+ const preloadHintLinks = getPreloadHintLinks(ctx);
+ const coreGlobals = getCoreGlobals(ctx);
+ const chunkScripts = getChunkScripts(ctx);
+ const bundleSplittingBootstrap = [
+ preloadHintLinks,
+ coreGlobals,
+ chunkScripts,
+ ].join('');
+
+ return [
+ '',
+ ``,
+ ``,
+ ``,
+ `${safeTitle}`,
+ `${bundleSplittingBootstrap}${safeHead}`,
+ ``,
+ `${ctx.rendered}${safeBody}`,
+ '',
+ ].join('');
+}
+
function getCoreGlobals(ctx) {
const {webpackPublicPath, nonce} = ctx;
diff --git a/src/server-app.js b/src/server-app.js
index 48dd76b2..1e26d825 100644
--- a/src/server-app.js
+++ b/src/server-app.js
@@ -33,7 +33,7 @@ export default function(): typeof BaseApp {
{
element: ElementToken,
ssrDecider: SSRDeciderToken,
- ssrBodyTemplate: SSRBodyTemplateToken,
+ ssrBodyTemplate: SSRBodyTemplateToken.optional,
},
ssrPlugin
);