Skip to content

Commit

Permalink
Allow to customize Babel/Webpack configs (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
koistya committed Feb 22, 2018
1 parent ec31ea7 commit aef70e1
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 112 deletions.
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ entry point for the server-side application bundle as demonstrated below:
},
{
- "react-scripts": "^1.1.1"
+ "react-app-tools": "^2.0.0-beta.6"
+ "react-app-tools": "^2.0.0-beta.10"
},
"scripts": {
- "test": "react-scripts test --env=jsdom",
Expand Down Expand Up @@ -69,13 +69,19 @@ import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from './components/App';
import assets from './assets.json';

const app = express();

app.use(express.static(path.join(__dirname, 'public')));

app.get('*', (req, res) => {
res.send(ReactDOMServer.renderToString(<App />));
res.send(`
<html>
<body>
<div id="root">${ReactDOMServer.renderToString(<App />)}</div>
${assets.map(src => `<script src="${src}"></script>`)}
</body>
</html>
`);
});

export default app;
Expand Down Expand Up @@ -110,6 +116,30 @@ Join our Telegram chat for support and feature requests - https://t.me/reactapp

<p align="center"><a href="https://www.youtube.com/watch?v=GH3kJwQ7mxM"><img src="http://img.youtube.com/vi/GH3kJwQ7mxM/maxresdefault.jpg" width="1187" alt="Server-side rendering with React.js" /><br /><sup>How fast is React SSR?</sup></a></p>

### How to Customize

Create `override.js` file in the root of your project containing configuration overrides.
For example:

```js
module.exports = {
babel(config, { target }) {
return {
...config,
plugins: config.plugins.concact(require.resolve('babel-relay-plugin')),
};
},
webpack(config, { target }) {
return {
...config,
plugins: target === 'node'
? config.plugins.concat(new LimitChunkCountPlugin({ maxChunks: 1 })),
: config.plugins
};
}
};
```

### Contribute

Help shape the future of **React App SDK** by joining our [community](https://t.me/reactapp)
Expand Down
4 changes: 4 additions & 0 deletions packages/react-app-tools/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
bin/react-app.js
config/env.js
config/webpack.config.dev.js
config/webpack.config.prod.js
scripts/build.js
scripts/start.js
scripts/test.js
README.md
WebpackDevServerUtils.js
18 changes: 11 additions & 7 deletions packages/react-app-tools/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
const path = require('path');
const paths = require('react-scripts/config/paths');

const { appPath, appSrc } = paths;
const serverBuild = paths.serverBuild || paths.appBuild;

module.exports = Object.assign(paths, {
serverBuild: paths.appBuild,
serverBuildAppJs: path.join(paths.appBuild, 'app.js'),
appBuild: path.join(paths.appBuild, 'public'),
appIndexJs: `${paths.appIndexJs.slice(0, -8)}app.browser.js`,
appEntry: `${paths.appIndexJs.slice(0, -8)}app.node.js`,
serverEntry: `${paths.appIndexJs.slice(0, -8)}server.js`,
assets: path.join(paths.appBuild, 'assets.json'),
serverBuild,
serverBuildAppJs: path.join(serverBuild, 'app.js'),
appBuild: path.join(serverBuild, 'public'),
appIndexJs: path.join(appSrc, 'app.browser.js'),
override: path.join(appPath, 'override.js'),
appEntry: path.join(appSrc, 'app.node.js'),
serverEntry: path.join(appSrc, 'server.js'),
assets: path.join(serverBuild, 'assets.json'),
});
15 changes: 0 additions & 15 deletions packages/react-app-tools/config/webpack.config.dev.js

This file was deleted.

15 changes: 0 additions & 15 deletions packages/react-app-tools/config/webpack.config.prod.js

This file was deleted.

114 changes: 49 additions & 65 deletions packages/react-app-tools/config/webpack.config.server.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,81 +13,65 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const WriteFilePlugin = require('write-file-webpack-plugin');
const customize = require('../customize');
const paths = require('./paths');

module.exports = config =>
Object.assign({}, config, {
name: 'server',
target: 'node',
node: false,
customize(
'webpack',
Object.assign({}, config, {
name: 'server',
target: 'node',
node: false,

entry: {
app: paths.appEntry,
server: paths.serverEntry,
},
entry: {
app: paths.appEntry,
server: paths.serverEntry,
},

output: Object.assign({}, config.output, {
path: paths.serverBuild,
filename: '[name].js',
libraryTarget: 'commonjs2',
}),
output: Object.assign({}, config.output, {
path: paths.serverBuild,
filename: '[name].js',
libraryTarget: 'commonjs2',
}),

// Modify babel-preset-react-app settings
module: Object.assign({}, config.module, {
rules: config.module.rules.map(x => {
if (x.oneOf) {
return Object.assign({}, x, {
oneOf: x.oneOf.map(y => {
if (y.use) {
if (y.use[0] === require.resolve('style-loader')) {
module: Object.assign({}, config.module, {
rules: config.module.rules.map(x => {
if (x.oneOf) {
return Object.assign({}, x, {
oneOf: x.oneOf.map(y => {
if (y.use && y.use[0] === require.resolve('style-loader')) {
return Object.assign({}, y, {
use: y.use.slice(1),
});
}
return Object.assign({}, y, {
use: y.use.map(z => {
if (
z.options &&
z.options.presets &&
z.options.presets[0] ===
require.resolve('babel-preset-react-app')
) {
return Object.assign({}, z, {
options: Object.assign({}, z.options, {
// presets: [path.join(__dirname, 'babel-preset.js')],
compact: false,
}),
});
}
return z;
}),
});
}
return y;
}),
});
}
return x;
return y;
}),
});
}
return x;
}),
}),
}),

// Remove plugins that are not needed in the server-side bundle
plugins: config.plugins
.filter(
x =>
!(
x instanceof InterpolateHtmlPlugin ||
x instanceof HtmlWebpackPlugin ||
x instanceof UglifyJsPlugin ||
x instanceof SWPrecacheWebpackPlugin
)
)
.concat([
new WriteFilePlugin({
output: paths.serverBuild,
test: /^(.(?!hot-update))*$/,
}),
]),
// Remove plugins that are not needed in the server-side bundle
plugins: config.plugins
.filter(
x =>
!(
x instanceof InterpolateHtmlPlugin ||
x instanceof HtmlWebpackPlugin ||
x instanceof UglifyJsPlugin ||
x instanceof SWPrecacheWebpackPlugin
)
)
.concat([
new WriteFilePlugin({
output: paths.serverBuild,
test: /^(.(?!hot-update))*$/,
}),
]),

externals: ['./assets.json', nodeExternals()],
});
externals: ['./assets.json', nodeExternals()],
}),
{ target: 'node' }
);
2 changes: 1 addition & 1 deletion packages/react-app-tools/config/webpackDevServer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = function(proxy, allowedHost) {
});
},
after(app) {
app.get('*', (req, res, next) => {
app.use((req, res, next) => {
global.appPromise
.then(() => {
const app = require(paths.serverBuildAppJs).default;
Expand Down
69 changes: 69 additions & 0 deletions packages/react-app-tools/customize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* Copyright © 2016-present Kriasoft. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE.txt file in the root directory of this source tree.
*/

'use strict';

const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const paths = require('./config/paths');

// Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve('react-scripts/config/paths')];
delete require.cache[require.resolve('./config/paths')];

const override = fs.existsSync(paths.override) ? require(paths.override) : null;

function overrideBabel(config, options) {
const newConfig = Object.assign({}, config);

if (options.target === 'node') {
newConfig.compact = false;
}

return override && typeof override.babel === 'function'
? override.babel(newConfig, options)
: newConfig;
}

module.exports = function customize(name, config, options) {
const newConfig = Object.assign({}, config, {
// Remove HtmlWebpackPlugin
plugins: config.plugins.filter(x => !(x instanceof HtmlWebpackPlugin)),

// Find Babel config
module: Object.assign({}, config.module, {
rules: config.module.rules.map(
x =>
x.oneOf
? Object.assign({}, x, {
oneOf: x.oneOf.map(
y =>
y.use
? Object.assign({}, y, {
use: y.use.map(
z =>
z.loader === require.resolve('babel-loader') &&
z.options.presets[0] ===
require.resolve('babel-preset-react-app')
? Object.assign({}, z, {
options: overrideBabel(z.options, options),
})
: z
),
})
: y
),
})
: x
),
}),
});

return override && typeof override[name] === 'function'
? override[name](newConfig, options)
: newConfig;
};
3 changes: 2 additions & 1 deletion packages/react-app-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-app-tools",
"version": "2.0.0-beta.6",
"version": "2.0.0-beta.10",
"description": "Build and test tooling for front-end application development with Node.js and React.",
"repository": "kriasoft/react-app",
"bugs": {
Expand Down Expand Up @@ -30,6 +30,7 @@
"bin",
"config",
"scripts",
"customize.js",
"README.md",
"WebpackDevServerUtils.js"
],
Expand Down
Loading

0 comments on commit aef70e1

Please sign in to comment.