From 981ba9a5689cbe708c09f195996c85b67fff02f5 Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Thu, 20 Sep 2018 11:06:58 -0700 Subject: [PATCH] Deprecate old context properties and env vars --- src/base-app.js | 10 +-- src/get-env.js | 2 + src/plugins/server-context.js | 95 ++++++++++++++++++---- src/plugins/ssr.js | 144 ++++++++++++++++++++++------------ src/server-app.js | 2 +- 5 files changed, 177 insertions(+), 76 deletions(-) diff --git a/src/base-app.js b/src/base-app.js index 95de201f..513a1d36 100644 --- a/src/base-app.js +++ b/src/base-app.js @@ -8,13 +8,8 @@ import {createPlugin} from './create-plugin'; import {createToken, TokenType, TokenImpl} from './create-token'; -import { - ElementToken, - RenderToken, - SSRDeciderToken, - SSRBodyTemplateToken, -} from './tokens'; -import {SSRDecider, SSRBodyTemplate} from './plugins/ssr'; +import {ElementToken, RenderToken, SSRDeciderToken} from './tokens'; +import {SSRDecider} from './plugins/ssr'; import type {aliaser, cleanupFn, FusionPlugin, Token} from './types.js'; @@ -27,7 +22,6 @@ class FusionApp { el && this.register(ElementToken, el); render && this.register(RenderToken, render); this.register(SSRDeciderToken, SSRDecider); - this.register(SSRBodyTemplateToken, SSRBodyTemplate); } // eslint-disable-next-line diff --git a/src/get-env.js b/src/get-env.js index 459e8162..c75bb137 100644 --- a/src/get-env.js +++ b/src/get-env.js @@ -15,6 +15,8 @@ function load(key, value) { } export function loadEnv() { + console.warn('Warning: getEnv from fusion-core is deprecated.'); // eslint-disable-line no-console + const rootDir = load('ROOT_DIR', '.'); const env = load('NODE_ENV', 'development'); if (!(env === 'development' || env === 'production' || env === 'test')) { diff --git a/src/plugins/server-context.js b/src/plugins/server-context.js index 5019ec5a..933727d7 100644 --- a/src/plugins/server-context.js +++ b/src/plugins/server-context.js @@ -11,28 +11,93 @@ import UAParser from 'ua-parser-js'; import getEnv from '../get-env.js'; import type {Context} from '../types.js'; +// Flow workaround: https://github.com/facebook/flow/issues/285#issuecomment-382044301 +const {defineProperty} = Object; -const envVars = getEnv(); +let envVars = null; +const lazyEnv = new Proxy( + {}, + { + get(_, prop) { + if (!envVars) { + envVars = getEnv(); + } + return envVars[prop]; + }, + } +); export default function middleware(ctx: Context, next: () => Promise) { - // env vars - ctx.rootDir = envVars.rootDir; - ctx.env = envVars.env; - ctx.prefix = envVars.prefix; - ctx.assetPath = envVars.assetPath; - ctx.cdnUrl = envVars.cdnUrl; - - // webpack-related things - ctx.preloadChunks = []; - ctx.webpackPublicPath = - ctx.webpackPublicPath || envVars.cdnUrl || envVars.assetPath; + let deprecated = (property, value, details) => { + defineProperty(ctx, property, { + enumerable: true, + configurable: true, + set(newValue) { + value = newValue; + }, + get() { + // eslint-disable-next-line no-console + console.warn( + `Warning: ctx.${property} is deprecated and may be incorrect or inaccurate. ctx.${property} should no longer be used.` + ); + if (details) { + // eslint-disable-next-line no-console + console.warn(details); + } + return value; + }, + }); + }; + + // env vars (deprecated) + deprecated('rootDir', lazyEnv.rootDir); + deprecated('env', lazyEnv.env); + deprecated( + 'prefix', + lazyEnv.prefix, + [ + 'To retrieve the route prefix, depend on the RoutePrefixToken instead.', + 'You may be able to resolve this warning by upgrading fusion-plugin-react-router, and/or fusion-plugin-error-handling.', + ].join(' ') + ); + deprecated('assetPath', lazyEnv.assetPath); + deprecated('cdnUrl', lazyEnv.cdnUrl); + + // webpack-related things (deprecated) + deprecated( + 'preloadChunks', + [], + 'You may be able to resolve this warning by upgrading fusion-react, fusion-react-async, and/or fusion-cli.' + ); + deprecated( + 'webpackPublicPath', + ctx.webpackPublicPath || lazyEnv.cdnUrl || lazyEnv.assetPath, + [ + 'Use __webpack_public_path__ instead.', + 'You may be able to resolve this warning by upgrading fusion-cli.', + ].join(' ') + ); // these are set by fusion-cli, however since fusion-cli plugins are not added when // running simulation tests, it is good to default them here - ctx.syncChunks = ctx.syncChunks || []; - ctx.chunkUrlMap = ctx.chunkUrlMap || new Map(); + // (deprecated) + deprecated( + 'syncChunks', + ctx.syncChunks || [], + [ + 'To retrieve the route prefix, depend on the RoutePrefixToken instead.', + 'You may be able to resolve this warning by upgrading fusion-plugin-i18n and/or fusion-cli.', + ].join(' ') + ); + deprecated( + 'chunkUrlMap', + ctx.chunkUrlMap || new Map(), + [ + 'To retrieve the route prefix, depend on the RoutePrefixToken instead.', + 'You may be able to resolve this warning by upgrading fusion-cli.', + ].join(' ') + ); - // fusion-specific things ctx.nonce = uuidv4(); ctx.useragent = new UAParser(ctx.headers['user-agent']).getResult(); ctx.element = null; diff --git a/src/plugins/ssr.js b/src/plugins/ssr.js index 57414a8a..876913d9 100644 --- a/src/plugins/ssr.js +++ b/src/plugins/ssr.js @@ -6,14 +6,22 @@ * @flow */ -import {createPlugin} from '../create-plugin'; -import {escape, consumeSanitizedHTML} from '../sanitization'; +/* eslint-env node */ + import type { Context, SSRDecider as SSRDeciderService, SSRBodyTemplate as SSRBodyTemplateService, } from '../types.js'; +import {createPlugin} from '../create-plugin'; +import {escape, consumeSanitizedHTML} from '../sanitization'; + +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 +39,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 +46,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 +71,90 @@ export default function createSSRPlugin({ return; } - ctx.body = ssrBodyTemplate(ctx); + if (ssrBodyTemplate) { + ctx.body = ssrBodyTemplate(ctx); + } else { + let legacyBody = legacySSRTemplate(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, + }); + } + } }; } +/** + * This template should be deleted + * + */ + +function legacySSRTemplate(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 );