@@ -4,11 +4,12 @@ import * as url from 'url'
44import { requireNewCopy } from './RequireNewCopy' ;
55
66export interface CreateDevServerCallback {
7- ( error : any , result : { Port : number , PublicPath : string } ) : void ;
7+ ( error : any , result : { Port : number , PublicPaths : string [ ] } ) : void ;
88}
99
1010// These are the options passed by WebpackDevMiddleware.cs
1111interface CreateDevServerOptions {
12+ understandsMultiplePublicPaths : boolean ; // For checking that the NuGet package is recent enough. Can be removed when we no longer need back-compatibility.
1213webpackConfigPath : string ;
1314suppliedOptions : DevServerOptions ;
1415}
@@ -20,11 +21,83 @@ interface DevServerOptions{
2021ReactHotModuleReplacement : boolean ;
2122}
2223
24+ function attachWebpackDevMiddleware ( app : any , webpackConfig : webpack . Configuration , enableHotModuleReplacement : boolean , enableReactHotModuleReplacement : boolean ) {
25+ // Build the final Webpack config based on supplied options
26+ if ( enableHotModuleReplacement ) {
27+ // For this, we only support the key/value config format, not string or string[], since
28+ // those ones don't clearly indicate what the resulting bundle name will be
29+ const entryPoints = webpackConfig . entry ;
30+ const isObjectStyleConfig = entryPoints
31+ && typeof entryPoints === 'object'
32+ && ! ( entryPoints instanceof Array ) ;
33+ if ( ! isObjectStyleConfig ) {
34+ throw new Error ( 'To use HotModuleReplacement, your webpack config must specify an \'entry\' value as a key-value object (e.g., "entry:{main: \'ClientApp/boot-client.ts\'}")' ) ;
35+ }
36+
37+ // Augment all entry points so they support HMR
38+ Object . getOwnPropertyNames ( entryPoints ) . forEach ( entryPointName => {
39+ if ( typeof entryPoints [ entryPointName ] === 'string' ) {
40+ entryPoints [ entryPointName ] = [ 'webpack-hot-middleware/client' , entryPoints [ entryPointName ] ] ;
41+ } else {
42+ entryPoints [ entryPointName ] . unshift ( 'webpack-hot-middleware/client' ) ;
43+ }
44+ } ) ;
45+
46+ webpackConfig . plugins = webpackConfig . plugins || [ ] ;
47+ webpackConfig . plugins . push (
48+ new webpack . HotModuleReplacementPlugin ( )
49+ ) ;
50+
51+ // Set up React HMR support if requested. This requires the 'aspnet-webpack-react' package.
52+ if ( enableReactHotModuleReplacement ) {
53+ let aspNetWebpackReactModule : any ;
54+ try {
55+ aspNetWebpackReactModule = require ( 'aspnet-webpack-react' ) ;
56+ } catch ( ex ) {
57+ throw new Error ( 'ReactHotModuleReplacement failed because of an error while loading \'aspnet-webpack-react\'. Error was: ' + ex . stack ) ;
58+ }
59+
60+ aspNetWebpackReactModule . addReactHotModuleReplacementBabelTransform ( webpackConfig ) ;
61+ }
62+ }
63+
64+ // Attach Webpack dev middleware and optional 'hot' middleware
65+ const compiler = webpack ( webpackConfig ) ;
66+ app . use ( require ( 'webpack-dev-middleware' ) ( compiler , {
67+ noInfo : true ,
68+ publicPath : webpackConfig . output . publicPath
69+ } ) ) ;
70+
71+ if ( enableHotModuleReplacement ) {
72+ let webpackHotMiddlewareModule ;
73+ try {
74+ webpackHotMiddlewareModule = require ( 'webpack-hot-middleware' ) ;
75+ } catch ( ex ) {
76+ throw new Error ( 'HotModuleReplacement failed because of an error while loading \'webpack-hot-middleware\'. Error was: ' + ex . stack ) ;
77+ }
78+ app . use ( webpackHotMiddlewareModule ( compiler ) ) ;
79+ }
80+ }
81+
2382export function createWebpackDevServer ( callback : CreateDevServerCallback , optionsJson : string ) {
2483const options : CreateDevServerOptions = JSON . parse ( optionsJson ) ;
25- const webpackConfig : webpack . Configuration = requireNewCopy ( options . webpackConfigPath ) ;
26- const publicPath = ( webpackConfig . output . publicPath || '' ) . trim ( ) ;
27- if ( ! publicPath ) {
84+
85+ if ( ! options . understandsMultiplePublicPaths ) {
86+ callback ( 'To use Webpack dev server, you must update to a newer version of the Microsoft.AspNetCore.SpaServices package' , null ) ;
87+ return ;
88+ }
89+
90+ // Read the webpack config's export, and normalize it into the more general 'array of configs' format
91+ let webpackConfigArray : webpack . Configuration [ ] = requireNewCopy ( options . webpackConfigPath ) ;
92+ if ( ! ( webpackConfigArray instanceof Array ) ) {
93+ webpackConfigArray = [ webpackConfigArray as webpack . Configuration ] ;
94+ }
95+
96+ // Check that at least one of the configurations specifies a publicPath. Those are the only ones we'll
97+ // enable middleware for, and if there aren't any, you must be making a mistake.
98+ const webpackConfigsWithPublicPath = webpackConfigArray
99+ . filter ( webpackConfig => ( webpackConfig . output . publicPath || '' ) . trim ( ) !== '' ) ;
100+ if ( webpackConfigsWithPublicPath . length === 0 ) {
28101callback ( 'To use the Webpack dev server, you must specify a value for \'publicPath\' on the \'output\' section of your webpack.config.' , null ) ;
29102return ;
30103}
@@ -41,69 +114,22 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
41114
42115const app = connect ( ) ;
43116const listener = app . listen ( suggestedHMRPortOrZero , ( ) => {
44- // Build the final Webpack config based on supplied options
45- if ( enableHotModuleReplacement ) {
46- // For this, we only support the key/value config format, not string or string[], since
47- // those ones don't clearly indicate what the resulting bundle name will be
48- const entryPoints = webpackConfig . entry ;
49- const isObjectStyleConfig = entryPoints
50- && typeof entryPoints === 'object'
51- && ! ( entryPoints instanceof Array ) ;
52- if ( ! isObjectStyleConfig ) {
53- callback ( 'To use HotModuleReplacement, your webpack config must specify an \'entry\' value as a key-value object (e.g., "entry:{main: \'ClientApp/boot-client.ts\'}")' , null ) ;
54- return ;
55- }
56-
57- // Augment all entry points so they support HMR
58- Object . getOwnPropertyNames ( entryPoints ) . forEach ( entryPointName => {
59- if ( typeof entryPoints [ entryPointName ] === 'string' ) {
60- entryPoints [ entryPointName ] = [ 'webpack-hot-middleware/client' , entryPoints [ entryPointName ] ] ;
61- } else {
62- entryPoints [ entryPointName ] . unshift ( 'webpack-hot-middleware/client' ) ;
63- }
117+ try {
118+ // For each webpack config that specifies a public path, add webpack dev middleware for it
119+ webpackConfigsWithPublicPath . forEach ( webpackConfig => {
120+ attachWebpackDevMiddleware ( app , webpackConfig , enableHotModuleReplacement , enableReactHotModuleReplacement ) ;
64121} ) ;
65122
66- webpackConfig . plugins . push (
67- new webpack . HotModuleReplacementPlugin ( )
68- ) ;
69-
70- // Set up React HMR support if requested. This requires the 'aspnet-webpack-react' package.
71- if ( enableReactHotModuleReplacement ) {
72- let aspNetWebpackReactModule : any ;
73- try {
74- aspNetWebpackReactModule = require ( 'aspnet-webpack-react' ) ;
75- } catch ( ex ) {
76- callback ( 'ReactHotModuleReplacement failed because of an error while loading \'aspnet-webpack-react\'. Error was: ' + ex . stack , null ) ;
77- return ;
78- }
79-
80- aspNetWebpackReactModule . addReactHotModuleReplacementBabelTransform ( webpackConfig ) ;
81- }
82- }
83-
84- // Attach Webpack dev middleware and optional 'hot' middleware
85- const compiler = webpack ( webpackConfig ) ;
86- app . use ( require ( 'webpack-dev-middleware' ) ( compiler , {
87- noInfo : true ,
88- publicPath : publicPath
89- } ) ) ;
90-
91- if ( enableHotModuleReplacement ) {
92- let webpackHotMiddlewareModule ;
93- try {
94- webpackHotMiddlewareModule = require ( 'webpack-hot-middleware' ) ;
95- } catch ( ex ) {
96- callback ( 'HotModuleReplacement failed because of an error while loading \'webpack-hot-middleware\'. Error was: ' + ex . stack , null ) ;
97- return ;
98- }
99- app . use ( webpackHotMiddlewareModule ( compiler ) ) ;
123+ // Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
124+ callback ( null , {
125+ Port : listener . address ( ) . port ,
126+ PublicPaths : webpackConfigsWithPublicPath . map ( webpackConfig =>
127+ removeTrailingSlash ( getPath ( webpackConfig . output . publicPath ) )
128+ )
129+ } ) ;
130+ } catch ( ex ) {
131+ callback ( ex . stack , null ) ;
100132}
101-
102- // Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
103- callback ( null , {
104- Port : listener . address ( ) . port ,
105- PublicPath : removeTrailingSlash ( getPath ( publicPath ) )
106- } ) ;
107133} ) ;
108134}
109135
0 commit comments