diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json
index 010d226..47b3381 100644
--- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json
+++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/package.json
@@ -1,6 +1,6 @@
{
"name": "aspnet-webpack",
- "version": "1.0.29",
+ "version": "2.0.0",
"description": "Helpers for using Webpack in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.",
"main": "index.js",
"scripts": {
diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
index bf31d82..7fba9de 100644
--- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
+++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
@@ -108,7 +108,7 @@ function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configurati
const compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
- publicPath: webpackConfig.output.publicPath,
+ publicPath: ensureLeadingSlash(webpackConfig.output.publicPath),
watchOptions: webpackConfig.watchOptions
}));
@@ -195,6 +195,14 @@ function copyRecursiveToRealFsSync(from: typeof fs, rootDir: string, exclude: Re
});
}
+function ensureLeadingSlash(value: string) {
+ if (value !== null && value.substring(0, 1) !== '/') {
+ value = '/' + value;
+ }
+
+ return value;
+}
+
function pathJoinSafe(rootPath: string, filePath: string) {
// On Windows, MemoryFileSystem's readdirSync output produces directory entries like 'C:'
// which then trigger errors if you call statSync for them. Avoid this by detecting drive
@@ -257,22 +265,32 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
if (!publicPath) {
throw new Error('To use the Webpack dev server, you must specify a value for \'publicPath\' on the \'output\' section of your webpack config (for any configuration that targets browsers)');
}
- normalizedPublicPaths.push(removeTrailingSlash(publicPath));
+ const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
+ normalizedPublicPaths.push(publicPathNoTrailingSlash);
- // Newer versions of Microsoft.AspNetCore.SpaServices will explicitly pass an HMR endpoint URL
- // (because it's relative to the app's URL space root, which the client doesn't otherwise know).
- // For back-compatibility, fall back on connecting directly to the underlying HMR server (though
- // that won't work if the app is hosted on HTTPS because of the mixed-content rule, and we can't
- // run the HMR server itself on HTTPS because in general it has no valid cert).
- const hmrClientEndpoint = options.hotModuleReplacementEndpointUrl // The URL that we'll proxy (e.g., /__asp_webpack_hmr)
- || `http://localhost:${listener.address().port}/__webpack_hmr`; // Fall back on absolute URL to bypass proxying
- const hmrServerEndpoint = options.hotModuleReplacementEndpointUrl
- || '/__webpack_hmr'; // URL is relative to webpack dev server root
+ // This is the URL the client will connect to, except that since it's a relative URL
+ // (no leading slash), Webpack will resolve it against the runtime URL
+ // plus it also adds the publicPath
+ const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
+
+ // This is the URL inside the Webpack middleware Node server that we'll proxy to.
+ // We have to prefix with the public path because Webpack will add the publicPath
+ // when it resolves hmrClientEndpoint as a relative URL.
+ const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
// We always overwrite the 'path' option as it needs to match what the .NET side is expecting
const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || >{};
hmrClientOptions['path'] = hmrClientEndpoint;
+ const dynamicPublicPathKey = 'dynamicPublicPath';
+ if (!(dynamicPublicPathKey in hmrClientOptions)) {
+ // dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
+ hmrClientOptions[dynamicPublicPathKey] = true;
+ } else {
+ // ... but you can set it to any other value explicitly if you want (e.g., false)
+ hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
+ }
+
attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
}
});
@@ -292,6 +310,14 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
});
}
+function removeLeadingSlash(str: string) {
+ if (str.indexOf('/') === 0) {
+ str = str.substring(1);
+ }
+
+ return str;
+}
+
function removeTrailingSlash(str: string) {
if (str.lastIndexOf('/') === str.length - 1) {
str = str.substring(0, str.length - 1);