diff --git a/fixtures/smoke/relative-paths/index.test.js b/fixtures/smoke/relative-paths/index.test.js
new file mode 100644
index 00000000000..1f367f64350
--- /dev/null
+++ b/fixtures/smoke/relative-paths/index.test.js
@@ -0,0 +1,37 @@
+const fs = require('fs-extra');
+const globby = require('globby');
+const path = require('path');
+const {
+ bootstrap,
+ isSuccessfulDevelopment,
+ isSuccessfulProduction,
+} = require('../../utils');
+beforeEach(async () => {
+ await bootstrap({ directory: global.testDirectory, template: __dirname });
+});
+
+describe('relative paths', () => {
+ // TODO: enable when development relative paths are supported
+ xit('builds in development', async () => {
+ await isSuccessfulDevelopment({ directory: global.testDirectory });
+ });
+ it('builds in production', async () => {
+ await isSuccessfulProduction({ directory: global.testDirectory });
+
+ const buildDir = path.join(global.testDirectory, 'build');
+ const cssFile = path.join(
+ buildDir,
+ globby.sync('**/*.css', { cwd: buildDir }).pop()
+ );
+ const svgFile = path.join(
+ buildDir,
+ globby.sync('**/*.svg', { cwd: buildDir }).pop()
+ );
+ const desiredPath = /url\((.+?)\)/
+ .exec(fs.readFileSync(cssFile, 'utf8'))
+ .pop();
+ expect(path.resolve(path.join(path.dirname(cssFile), desiredPath))).toBe(
+ path.resolve(svgFile)
+ );
+ });
+});
diff --git a/fixtures/smoke/relative-paths/package.json b/fixtures/smoke/relative-paths/package.json
new file mode 100644
index 00000000000..3ed0e3e4eea
--- /dev/null
+++ b/fixtures/smoke/relative-paths/package.json
@@ -0,0 +1,6 @@
+{
+ "dependencies": {
+ "react-scripts": "latest"
+ },
+ "homepage": "."
+}
diff --git a/fixtures/smoke/relative-paths/public/index.html b/fixtures/smoke/relative-paths/public/index.html
new file mode 100644
index 00000000000..86010b24067
--- /dev/null
+++ b/fixtures/smoke/relative-paths/public/index.html
@@ -0,0 +1,9 @@
+
+
+
+ React App
+
+
+
+
+
diff --git a/fixtures/smoke/relative-paths/src/index.css b/fixtures/smoke/relative-paths/src/index.css
new file mode 100644
index 00000000000..244889b10aa
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/index.css
@@ -0,0 +1,8 @@
+.RootSvg:before {
+ display: block;
+ content: ' ';
+ background-image: url(./logo.svg);
+ background-size: 28px 28px;
+ height: 28px;
+ width: 28px;
+}
diff --git a/fixtures/smoke/relative-paths/src/index.js b/fixtures/smoke/relative-paths/src/index.js
new file mode 100644
index 00000000000..6a9a4b13285
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/index.js
@@ -0,0 +1 @@
+import './index.css';
diff --git a/fixtures/smoke/relative-paths/src/logo.svg b/fixtures/smoke/relative-paths/src/logo.svg
new file mode 100644
index 00000000000..5e53156653f
--- /dev/null
+++ b/fixtures/smoke/relative-paths/src/logo.svg
@@ -0,0 +1,8 @@
+
diff --git a/package.json b/package.json
index 5acfc3a2563..96d63b4f224 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
"execa": "1.0.0",
"fs-extra": "^7.0.0",
"get-port": "^4.0.0",
+ "globby": "^8.0.1",
"husky": "1.0.0-rc.15",
"jest": "^23.6.0",
"lerna": "2.9.1",
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 0defc5f44ab..2c830fd86c8 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -28,6 +28,9 @@ const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
const publicPath = paths.servedPath;
+// Some apps do not use client-side routing with pushState.
+// For these, "homepage" can be set to "." to enable relative asset paths.
+const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
@@ -52,7 +55,13 @@ const sassModuleRegex = /\.module\.(scss|sass)$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
- MiniCssExtractPlugin.loader,
+ {
+ loader: MiniCssExtractPlugin.loader,
+ options: Object.assign(
+ {},
+ shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
+ ),
+ },
{
loader: require.resolve('css-loader'),
options: cssOptions,