Skip to content
This repository has been archived by the owner on May 17, 2019. It is now read-only.

Deprecate old context properties and env vars #299

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
10 changes: 2 additions & 8 deletions src/base-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/get-env.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down
95 changes: 80 additions & 15 deletions src/plugins/server-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>) {
// 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;
Expand Down
144 changes: 92 additions & 52 deletions src/plugins/ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -31,62 +39,14 @@ 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 [
'<!doctype html>',
`<html${safeAttrs}>`,
`<head>`,
`<meta charset="utf-8" />`,
`<title>${safeTitle}</title>`,
`${bundleSplittingBootstrap}${safeHead}`,
`</head>`,
`<body${safeBodyAttrs}>${ctx.rendered}${safeBody}</body>`,
'</html>',
].join('');
};
},
});

export {SSRBodyTemplate};

export default function createSSRPlugin({
element,
ssrDecider,
ssrBodyTemplate,
}: {
element: any,
ssrDecider: SSRDeciderService,
ssrBodyTemplate: SSRBodyTemplateService,
ssrBodyTemplate?: SSRBodyTemplateService,
}) {
return async function ssrPlugin(ctx: Context, next: () => Promise<void>) {
if (!ssrDecider(ctx)) return next();
Expand All @@ -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 [
'<!doctype html>',
`<html${safeAttrs}>`,
`<head>`,
`<meta charset="utf-8" />`,
`<title>${safeTitle}</title>`,
`${bundleSplittingBootstrap}${safeHead}`,
`</head>`,
`<body${safeBodyAttrs}>${ctx.rendered}${safeBody}</body>`,
'</html>',
].join('');
}
function getCoreGlobals(ctx) {
const {webpackPublicPath, nonce} = ctx;

Expand Down
2 changes: 1 addition & 1 deletion src/server-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function(): typeof BaseApp {
{
element: ElementToken,
ssrDecider: SSRDeciderToken,
ssrBodyTemplate: SSRBodyTemplateToken,
ssrBodyTemplate: SSRBodyTemplateToken.optional,
},
ssrPlugin
);
Expand Down