@@ -4,11 +4,12 @@ import * as url from 'url';
4
4
import { requireNewCopy } from './RequireNewCopy' ;
5
5
6
6
export interface CreateDevServerCallback {
7
- ( error : any , result : { Port : number , PublicPath : string } ) : void ;
7
+ ( error : any , result : { Port : number , PublicPaths : string [ ] } ) : void ;
8
8
}
9
9
10
10
// These are the options passed by WebpackDevMiddleware.cs
11
11
interface CreateDevServerOptions {
12
+ understandsMultiplePublicPaths : boolean ; // For checking that the NuGet package is recent enough. Can be removed when we no longer need back-compatibility.
12
13
webpackConfigPath : string ;
13
14
suppliedOptions : DevServerOptions ;
14
15
}
@@ -20,11 +21,83 @@ interface DevServerOptions {
20
21
ReactHotModuleReplacement : boolean ;
21
22
}
22
23
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
+
23
82
export function createWebpackDevServer ( callback : CreateDevServerCallback , optionsJson : string ) {
24
83
const 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 ) {
28
101
callback ( 'To use the Webpack dev server, you must specify a value for \'publicPath\' on the \'output\' section of your webpack.config.' , null ) ;
29
102
return ;
30
103
}
@@ -41,69 +114,22 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
41
114
42
115
const app = connect ( ) ;
43
116
const 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 ) ;
64
121
} ) ;
65
122
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 ) ;
100
132
}
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
- } ) ;
107
133
} ) ;
108
134
}
109
135
0 commit comments