Skip to content

Commit c8d0c3c

Browse files
committed
Misc fixes
1 parent fd1aa67 commit c8d0c3c

File tree

12 files changed

+113
-20
lines changed

12 files changed

+113
-20
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ only need to call `adopt-dev-deps` again if you update
6161
the same uniform manner;
6262
- [**`ScalableRect`**](docs/scalable-rect.md) — Container that keeps
6363
the specified aspect ratio regardless the width you set.
64-
64+
6565
### <a name="utilities">Utilities</a>
66+
- [**Config**](docs/config.md) &mdash; Isomorphic app config;
6667
- [**Global Styles**](docs/global-styles.md) &mdash; Global styles necessary for
6768
a generic application;
6869
- [**Isomorphy**](docs/isomorphy-utils.md) &mdash; Collection of helpers to deal

__tests__/__snapshots__/index.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Object {
88
"NavLink": [Function],
99
"ScalableRect": [Function],
1010
"utils": Object {
11+
"config": Config {},
1112
"isomorphy": Object {
1213
"buildTimestamp": [Function],
1314
"isClientSide": [Function],

__tests__/shared/utils/config.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* eslint-env browser */
2+
3+
import _ from 'lodash';
4+
import { isClientSide, isServerSide } from 'utils/isomorphy';
5+
6+
const CLIENT_SIDE_CONFIG = {
7+
TYPE: 'CLIENT_SIDE_CONFIG',
8+
};
9+
10+
const SERVER_SIDE_CONFIG = {
11+
TYPE: 'SERVER_SIDE_CONFIG',
12+
};
13+
14+
jest.setMock('config', _.clone(SERVER_SIDE_CONFIG));
15+
16+
beforeEach(() => {
17+
jest.resetModules();
18+
window.CONFIG = _.clone(CLIENT_SIDE_CONFIG);
19+
});
20+
21+
afterEach(() => delete global.FRONT_END);
22+
23+
test('Serves injected config at the client side', () => {
24+
global.FRONT_END = true;
25+
expect(isClientSide()).toBe(true);
26+
expect(require('utils/config')).toEqual(CLIENT_SIDE_CONFIG);
27+
});
28+
29+
test('Serves node-config at the server side', () => {
30+
expect(isServerSide()).toBe(true);
31+
expect(require('utils/config')).toEqual(SERVER_SIDE_CONFIG);
32+
});

config/webpack/lib-base.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module.exports = function configFactory(ops) {
3535
entry: ops.entry,
3636
externals: [
3737
'babel-runtime',
38+
'config',
3839
'lodash',
3940
'moment',
4041
'prop-types',

docs/config-utils.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Config
2+
Isomorphic app config.
3+
4+
**Why?** &mdash; we use [node-config](https://github.com/lorenwest/node-config)
5+
for convenient configuration of our apps. `node-config` itself works in NodeJS
6+
environment only. This utility exposes the config in isomorphic way that works
7+
both at server- and client-side.
8+
9+
Use it this way:
10+
```js
11+
import { utils } from 'topcoder-react-utils';
12+
13+
const config = utils.config;
14+
```
15+
16+
Keep in mind that config still can be different at client- and server-side, due
17+
to normalization during injection into the HTML page template:
18+
- Some sensitive config params can be trimmed out of the injected config (by
19+
default we remove `SECRET` key);
20+
- Some extra data can be appended to the config, anything that does not come
21+
from `node-config`, and is not stored in Redux state (although, most probably,
22+
it should be), but still should be transmitted to the client side and be
23+
available inside JS.

docs/server.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,18 @@ props:
5555
logging. Defaults to `console`, otherwise it is expected to provide the same
5656
interface. Note that `console` is not efficient for production use, because
5757
it is not async in NodeJS.
58-
- **`beforeRender`** &mdash; *Function* &mdash; Optional. The hook to be
59-
executed right before the generation of HTML template of the page. If given,
60-
it will receive the HTTP request as its only argument, and
61-
it should return a promise that resolves to an object with the following
62-
fields (all are optional):
63-
- **`config`** &mdash; *Object* &mdash; Config object to inject into the
64-
template.
58+
- **`beforeRender`** &mdash; *Function(req, config)* &mdash; Optional. The hook to be
59+
executed right before the generation of HTML template of the page.
60+
61+
**Arguments:**
62+
- **`req`** &mdash; *Object* &mdash; ExpressJS HTTP request;
63+
- **`config`** &mdash; *Object* &mdash; App config that server wants to inject
64+
into HTML page template;
65+
66+
**Returns:** Promise that resolves to an object with the following fields:
67+
- **`config`** &mdash; *Object* &mdash; Optional. The actual config object
68+
to be injected into the page. If omitted, the one proposed by the server
69+
will be used.
6570
- **`extraScripts`** &mdash; *String[]* &mdash; Additional script tags to be
6671
injected into the page.
6772
- **`store`** &mdash; *Object* &mdash; Redux store which state will be

package-lock.json

Lines changed: 18 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"atob": "^2.0.3",
1010
"babel-runtime": "^6.26.0",
1111
"body-parser": "^1.18.2",
12+
"config": "^1.30.0",
1213
"cookie-parser": "^1.4.3",
1314
"express": "^4.16.2",
1415
"helmet": "^3.11.0",
@@ -105,5 +106,5 @@
105106
"prepublishOnly": "npm run build",
106107
"test": "npm run lint && npm run jest"
107108
},
108-
"version": "0.2.3"
109+
"version": "0.2.4"
109110
}

src/server/index.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
* Standard web servers.
33
*/
44

5-
import atob from 'atob';
65
import _ from 'lodash';
76
import http from 'http';
87

@@ -13,10 +12,6 @@ import 'raf/polyfill';
1312

1413
import serverFactory from './server';
1514

16-
/* Polyfill for codes that rely on atob(..) function present in broswer
17-
* environments. */
18-
global.atob = atob;
19-
2015
/* TODO: Should use the isClientSide(..) method from isomorphy module, once that
2116
* module is moved to topcoder-react-utils. */
2217
if (!isServerSide()) {

src/server/renderer.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import _ from 'lodash';
6+
import config from 'config';
67
import forge from 'node-forge';
78
import fs from 'fs';
89
import path from 'path';
@@ -13,6 +14,8 @@ import { Helmet } from 'react-helmet';
1314
import { Provider } from 'react-redux';
1415
import { StaticRouter } from 'react-router-dom';
1516

17+
const sanitizedConfig = _.omit(config, 'SECRET');
18+
1619
/**
1720
* Reads build-time information about the app. This information is generated
1821
* by our standard Webpack config for apps, and it is written into
@@ -70,14 +73,14 @@ export default async function factory(webpackConfig, options) {
7073
global.BUILD_INFO = buildInfo;
7174

7275
const [{
73-
config,
76+
configToInject,
7477
extraScripts,
7578
store,
7679
}, {
7780
cipher,
7881
iv,
7982
}] = await Promise.all([
80-
ops.beforeRender(req),
83+
ops.beforeRender(req, sanitizedConfig),
8184
prepareCipher(buildInfo.key),
8285
]);
8386

@@ -116,7 +119,7 @@ export default async function factory(webpackConfig, options) {
116119
* Hovewer, for a number of reasons, encryption of injected data is still
117120
* better than injection of a plain text. */
118121
cipher.update(forge.util.createBuffer(JSON.stringify({
119-
CONFIG: config,
122+
CONFIG: configToInject || sanitizedConfig,
120123
ISTATE: store ? store.getState() : null,
121124
}), 'utf8'));
122125
cipher.finish();

src/shared/utils/config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* This module provides isomorphic configuration. At the client-side it serves
3+
* cofiguration object from the global CONFIG variable, injected into the page
4+
* during the server-side rendering; at the server side it is fetched directly
5+
* from node-config.
6+
*/
7+
8+
/* eslint-env browser */
9+
10+
import { isClientSide } from './isomorphy';
11+
12+
/* eslint-disable global-require */
13+
module.exports = isClientSide() ? window.CONFIG : require('config');
14+
/* eslint-enable global-require */

src/shared/utils/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import config from './config';
12
import * as isomorphy from './isomorphy';
23

34
export default {
5+
config,
46
isomorphy,
57
};

0 commit comments

Comments
 (0)