diff --git a/packages/react-dev-utils/InlineChunkHtmlPlugin.js b/packages/react-dev-utils/InlineChunkHtmlPlugin.js
new file mode 100644
index 00000000000..8f0cd45c717
--- /dev/null
+++ b/packages/react-dev-utils/InlineChunkHtmlPlugin.js
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+class InlineChunkHtmlPlugin {
+ constructor(htmlWebpackPlugin, tests) {
+ this.htmlWebpackPlugin = htmlWebpackPlugin;
+ this.tests = tests;
+ }
+
+ getInlinedTag(publicPath, assets, tag) {
+ if (tag.tagName !== 'script' || !(tag.attributes && tag.attributes.src)) {
+ return tag;
+ }
+ const scriptName = tag.attributes.src.replace(publicPath, '');
+ if (!this.tests.some(test => scriptName.match(test))) {
+ return tag;
+ }
+ const asset = assets[scriptName];
+ if (asset == null) {
+ return tag;
+ }
+ return { tagName: 'script', innerHTML: asset.source(), closeTag: true };
+ }
+
+ apply(compiler) {
+ let publicPath = compiler.options.output.publicPath;
+ if (!publicPath.endsWith('/')) {
+ publicPath += '/';
+ }
+
+ compiler.hooks.compilation.tap('InlineChunkHtmlPlugin', compilation => {
+ const tagFunction = tag =>
+ this.getInlinedTag(publicPath, compilation.assets, tag);
+
+ const hooks = this.htmlWebpackPlugin.getHooks(compilation);
+ hooks.alterAssetTagGroups.tap('InlineChunkHtmlPlugin', assets => {
+ assets.headTags = assets.headTags.map(tagFunction);
+ assets.bodyTags = assets.bodyTags.map(tagFunction);
+ });
+ hooks.afterEmit.tap('InlineChunkHtmlPlugin', () => {
+ Object.keys(compilation.assets).forEach(assetName => {
+ if (this.tests.some(test => assetName.match(test))) {
+ delete compilation.assets[assetName];
+ }
+ });
+ });
+ });
+ }
+}
+
+module.exports = InlineChunkHtmlPlugin;
diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md
index 53c73bfc502..d615f66b5cf 100644
--- a/packages/react-dev-utils/README.md
+++ b/packages/react-dev-utils/README.md
@@ -18,14 +18,14 @@ If you don’t use Create React App, or if you [ejected](https://github.com/face
There is no single entry point. You can only import individual top-level modules.
-#### `new InterpolateHtmlPlugin(replacements: {[key:string]: string})`
+#### `new InterpolateHtmlPlugin(htmlWebpackPlugin: HtmlWebpackPlugin, replacements: {[key:string]: string})`
This Webpack plugin lets us interpolate custom variables into `index.html`.
It works in tandem with [HtmlWebpackPlugin](https://github.com/ampedandwired/html-webpack-plugin) 2.x via its [events](https://github.com/ampedandwired/html-webpack-plugin#events).
```js
var path = require('path');
-var HtmlWebpackPlugin = require('html-dev-plugin');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
var InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
// Webpack config
@@ -56,6 +56,39 @@ module.exports = {
};
```
+#### `new InlineChunkHtmlPlugin(htmlWebpackPlugin: HtmlWebpackPlugin, tests: Regex[])`
+
+This Webpack plugin inlines script chunks into `index.html`.
+It works in tandem with [HtmlWebpackPlugin](https://github.com/ampedandwired/html-webpack-plugin) 4.x.
+
+```js
+var path = require('path');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+var InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
+
+// Webpack config
+var publicUrl = '/my-custom-url';
+
+module.exports = {
+ output: {
+ // ...
+ publicPath: publicUrl + '/',
+ },
+ // ...
+ plugins: [
+ // Generates an `index.html` file with the