Skip to content
This repository was archived by the owner on Jan 22, 2020. It is now read-only.

add styled-components support #208

Open
wants to merge 5 commits 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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ Additionally, it can contain the following **optional** properties,

- `staticMarkup`: \<Boolean> - a boolean that indicates if render components without React data attributes and client data. (_Default: `false`_). This is useful if you want to render simple static page, as stripping away the extra React attributes and client data can save lots of bytes.
- `scriptType`: \<String> - a string that can be used as the type for the script (if it is included, which is only if staticMarkup is false). (_Default: `application/json`_).
- `styledComponents`: \<Boolean> - a boolean that indicates if `styled-components` should collect styles on the server and add style tags to the head. (_Default: `false`_). Set this to `true` if you are using `styled-components` in your project.

###### Rendering views on server side
```js
Expand Down Expand Up @@ -231,7 +232,7 @@ var engine = require('react-engine').server.create({
* When Express's `view cache` app property is false (mostly in non-production environments), views are automatically reloaded before render. So there is no need to restart the server for seeing the changes.
* You can use `js` as the engine if you decide not to write your react views in `jsx`.
* [Blog on react-engine](https://www.paypal-engineering.com/2015/04/27/isomorphic-react-apps-with-react-engine/)
* You can add [nonce](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#Unsafe_inline_script) in `_locals`, which will be added in `script` tag that gets injected into the server rendered pages, like `res.locals.nonce = 'nonce value'`
* You can add [nonce](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#Unsafe_inline_script) in `_locals`, which will be added in `script` tag that gets injected into the server rendered pages, like `res.locals.nonce = 'nonce value'`


### License
Expand Down
1 change: 1 addition & 0 deletions lib/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"client": {
"markupId": "react-engine-props"
},
"styledComponents": false,
"staticMarkup": false,
"scriptType": "application/json"
}
100 changes: 57 additions & 43 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ exports.create = function create(createOptions) {
createOptions.docType = isString(createOptions.docType) ? createOptions.docType : Config.docType;
createOptions.renderOptionsKeysToFilter = createOptions.renderOptionsKeysToFilter || [];
createOptions.staticMarkup = createOptions.staticMarkup !== undefined ? createOptions.staticMarkup : Config.staticMarkup;
createOptions.styledComponents = createOptions.styledComponents !== undefined ? createOptions.styledComponents : Config.styledComponents;

assert(Array.isArray(createOptions.renderOptionsKeysToFilter),
'`renderOptionsKeysToFilter` - should be an array');
Expand Down Expand Up @@ -109,53 +110,66 @@ exports.create = function create(createOptions) {
if (createOptions.staticMarkup) {
// render the component to static markup
html += ReactDOMServer.renderToStaticMarkup(component);
} else {
// render the redux wrapped component
if (createOptions.reduxStoreInitiator) {
// add redux provider
var Provider = require('react-redux').Provider;
var initStore;
try {
initStore = require(createOptions.reduxStoreInitiator);
if (initStore.default) {
initStore = initStore.default;
}
var store = initStore(data);
var wrappedComponent = React.createElement(Provider, { store: store }, component);
// render the component
html += ReactDOMServer.renderToString(wrappedComponent);
} catch (err) {
return done(err);
return html;
}

// render the redux wrapped component
if (createOptions.reduxStoreInitiator) {
// add redux provider
var Provider = require('react-redux').Provider;
var initStore;
try {
initStore = require(createOptions.reduxStoreInitiator);
if (initStore.default) {
initStore = initStore.default;
}
} else {
// render the component
html += ReactDOMServer.renderToString(component);
var store = initStore(data);
component = React.createElement(Provider, { store: store }, component);
} catch (err) {
return done(err);
}
}

// the `script` tag that gets injected into the server rendered pages.
// https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.233_-_JavaScript_Escape_Before_Inserting_Untrusted_Data_into_JavaScript_Data_Values
var openScriptTag = `<script id="${Config.client.markupId}" type="${createOptions.scriptType}" ${options.nonce ? `nonce="${options.nonce}"` : ''}>`;
// Escape data for injecting into <script> tag
// https://mathiasbynens.be/notes/etago
var script = openScriptTag + jsesc(data, {
'escapeEtago': true, // old option for escaping in <script> or <style> context
'isScriptContext': true, // soon to be new option
'compact': true, // minifies
'json': true // ensures JSON compatibility
})
+ '</script>';


if (createOptions.docType === '') {
// if the `docType` is empty, the user did not want to add a docType to the rendered component,
// which means they might not be rendering a full page with `html` and `body` tags
// so attach the script tag to just the end of the generated html string
html += script;
}
else {
var htmlTag = createOptions.scriptLocation === 'head' ? '</head>' : '</body>';
html = html.replace(htmlTag, script + htmlTag);
// render the component and get styled-components styles
if (createOptions.styledComponents) {
var ServerStyleSheet = require('styled-components').ServerStyleSheet;
var sheet = new ServerStyleSheet();
try {
html += ReactDOMServer.renderToString(sheet.collectStyles(component));
var styleTags = sheet.getStyleTags();
// add the styles to the end of the head
var htmlTag = '</head>';
html = html.replace(htmlTag, styleTags + htmlTag);
} catch (err) {
return done(err);
}
} else {
html += ReactDOMServer.renderToString(component);
}

// the `script` tag that gets injected into the server rendered pages.
// https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.233_-_JavaScript_Escape_Before_Inserting_Untrusted_Data_into_JavaScript_Data_Values
var openScriptTag = `<script id="${Config.client.markupId}" type="${createOptions.scriptType}" ${options.nonce ? `nonce="${options.nonce}"` : ''}>`;
// Escape data for injecting into <script> tag
// https://mathiasbynens.be/notes/etago
var script = openScriptTag + jsesc(data, {
'escapeEtago': true, // old option for escaping in <script> or <style> context
'isScriptContext': true, // soon to be new option
'compact': true, // minifies
'json': true // ensures JSON compatibility
})
+ '</script>';


if (createOptions.docType === '') {
// if the `docType` is empty, the user did not want to add a docType to the rendered component,
// which means they might not be rendering a full page with `html` and `body` tags
// so attach the script tag to just the end of the generated html string
html += script;
html += styleTags;
} else {
var htmlTag = createOptions.scriptLocation === 'head' ? '</head>' : '</body>';
html = html.replace(htmlTag, script + htmlTag);
}

return html;
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-engine",
"version": "4.5.1",
"name": "@laurenskling/react-engine",
"version": "4.6.1",
"description": "a composite render engine for express apps to render both plain react views and react-router views",
"main": "index.js",
"scripts": {
Expand All @@ -13,10 +13,10 @@
},
"repository": {
"type": "git",
"url": "https://github.com/paypal/react-engine"
"url": "https://github.com/laurenskling/react-engine"
},
"bugs": {
"url": "https://github.com/paypal/react-engine/issues"
"url": "https://github.com/laurenskling/react-engine/issues"
},
"publishConfig": {
"registry": "https://registry.npmjs.org"
Expand Down Expand Up @@ -70,7 +70,8 @@
"Weng Zhi Ping",
"Vincent Orr <[email protected]>",
"skarflacka",
"Mark <[email protected]>"
"Mark <[email protected]>",
"Laurens Kling <[email protected]>"
],
"license": "Apache-2.0"
}