Skip to content
This repository was archived by the owner on Apr 8, 2020. It is now read-only.

Commit d308fae

Browse files
authored
Added support for Thenables
1 parent 4af2e86 commit d308fae

File tree

1 file changed

+87
-68
lines changed

1 file changed

+87
-68
lines changed

src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts

Lines changed: 87 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@ interface DevServerOptions {
3434
ReactHotModuleReplacement: boolean;
3535
}
3636

37-
// We support these three kinds of webpack.config.js export. We don't currently support exported promises
38-
// (though we might be able to add that in the future, if there's a need).
39-
type WebpackConfigOrArray = webpack.Configuration | webpack.Configuration[];
37+
// We support these four kinds of webpack.config.js export
38+
type WebpackConfigOrArray = webpack.Configuration | webpack.Configuration[] | WebpackConfigThenable;
39+
// Copied from DefinitelyTyped/types/es6-promise/index.d.ts, We only need this interface so avoiding pulling another dependancy
40+
interface Thenable<T> {
41+
then<U>(onFulfilled?: (value: T) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
42+
then<U>(onFulfilled?: (value: T) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
43+
}
44+
type WebpackConfigOrArrayOrThenable = WebpackConfigOrArray | Thenable<WebpackConfigOrArray>;
4045
interface WebpackConfigFunc {
41-
(env?: any): WebpackConfigOrArray;
46+
(env?: any): WebpackConfigOrArrayOrThenable;
4247
}
43-
type WebpackConfigExport = WebpackConfigOrArray | WebpackConfigFunc;
48+
type WebpackConfigExport = WebpackConfigOrArrayOrThenable | WebpackConfigFunc;
4449
type WebpackConfigModuleExports = WebpackConfigExport | EsModuleExports<WebpackConfigExport>;
4550

4651
function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configuration, enableHotModuleReplacement: boolean, enableReactHotModuleReplacement: boolean, hmrClientOptions: StringMap<string>, hmrServerEndpoint: string) {
@@ -250,74 +255,88 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
250255
// default env values in their webpack.config.js).
251256
webpackConfigExport = webpackConfigExport();
252257
}
253-
const webpackConfigArray = webpackConfigExport instanceof Array ? webpackConfigExport : [webpackConfigExport];
254-
255-
const enableHotModuleReplacement = options.suppliedOptions.HotModuleReplacement;
256-
const enableReactHotModuleReplacement = options.suppliedOptions.ReactHotModuleReplacement;
257-
if (enableReactHotModuleReplacement && !enableHotModuleReplacement) {
258-
callback('To use ReactHotModuleReplacement, you must also enable the HotModuleReplacement option.', null);
259-
return;
258+
259+
var webpackConfigThenable: Thenable<WebpackConfigOrArray>;
260+
if (!webpackConfigExport.then) {
261+
webpackConfigThenable = {
262+
then: function (cb) {
263+
cb(webpackConfigExport);
264+
}
265+
}
266+
} else {
267+
webpackConfigThenable = webpackConfigExport
260268
}
269+
270+
webpackConfigThenable.then(function(webpackConfigResolved) {
271+
const webpackConfigArray = webpackConfigResolved instanceof Array ? webpackConfigResolved : [webpackConfigResolved];
272+
273+
const enableHotModuleReplacement = options.suppliedOptions.HotModuleReplacement;
274+
const enableReactHotModuleReplacement = options.suppliedOptions.ReactHotModuleReplacement;
275+
if (enableReactHotModuleReplacement && !enableHotModuleReplacement) {
276+
callback('To use ReactHotModuleReplacement, you must also enable the HotModuleReplacement option.', null);
277+
return;
278+
}
261279

262-
// The default value, 0, means 'choose randomly'
263-
const suggestedHMRPortOrZero = options.suppliedOptions.HotModuleReplacementServerPort || 0;
280+
// The default value, 0, means 'choose randomly'
281+
const suggestedHMRPortOrZero = options.suppliedOptions.HotModuleReplacementServerPort || 0;
264282

265-
const app = connect();
266-
const listener = app.listen(suggestedHMRPortOrZero, () => {
267-
try {
268-
// For each webpack config that specifies a public path, add webpack dev middleware for it
269-
const normalizedPublicPaths: string[] = [];
270-
webpackConfigArray.forEach(webpackConfig => {
271-
if (webpackConfig.target === 'node') {
272-
// For configs that target Node, it's meaningless to set up an HTTP listener, since
273-
// Node isn't going to load those modules over HTTP anyway. It just loads them directly
274-
// from disk. So the most relevant thing we can do with such configs is just write
275-
// updated builds to disk, just like "webpack --watch".
276-
beginWebpackWatcher(webpackConfig);
277-
} else {
278-
// For configs that target browsers, we can set up an HTTP listener, and dynamically
279-
// modify the config to enable HMR etc. This just requires that we have a publicPath.
280-
const publicPath = (webpackConfig.output.publicPath || '').trim();
281-
if (!publicPath) {
282-
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)');
283-
}
284-
const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
285-
normalizedPublicPaths.push(publicPathNoTrailingSlash);
286-
287-
// This is the URL the client will connect to, except that since it's a relative URL
288-
// (no leading slash), Webpack will resolve it against the runtime <base href> URL
289-
// plus it also adds the publicPath
290-
const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
291-
292-
// This is the URL inside the Webpack middleware Node server that we'll proxy to.
293-
// We have to prefix with the public path because Webpack will add the publicPath
294-
// when it resolves hmrClientEndpoint as a relative URL.
295-
const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
296-
297-
// We always overwrite the 'path' option as it needs to match what the .NET side is expecting
298-
const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || <StringMap<string>>{};
299-
hmrClientOptions['path'] = hmrClientEndpoint;
300-
301-
const dynamicPublicPathKey = 'dynamicPublicPath';
302-
if (!(dynamicPublicPathKey in hmrClientOptions)) {
303-
// dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
304-
hmrClientOptions[dynamicPublicPathKey] = true;
283+
const app = connect();
284+
const listener = app.listen(suggestedHMRPortOrZero, () => {
285+
try {
286+
// For each webpack config that specifies a public path, add webpack dev middleware for it
287+
const normalizedPublicPaths: string[] = [];
288+
webpackConfigArray.forEach(webpackConfig => {
289+
if (webpackConfig.target === 'node') {
290+
// For configs that target Node, it's meaningless to set up an HTTP listener, since
291+
// Node isn't going to load those modules over HTTP anyway. It just loads them directly
292+
// from disk. So the most relevant thing we can do with such configs is just write
293+
// updated builds to disk, just like "webpack --watch".
294+
beginWebpackWatcher(webpackConfig);
305295
} else {
306-
// ... but you can set it to any other value explicitly if you want (e.g., false)
307-
hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
296+
// For configs that target browsers, we can set up an HTTP listener, and dynamically
297+
// modify the config to enable HMR etc. This just requires that we have a publicPath.
298+
const publicPath = (webpackConfig.output.publicPath || '').trim();
299+
if (!publicPath) {
300+
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)');
301+
}
302+
const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
303+
normalizedPublicPaths.push(publicPathNoTrailingSlash);
304+
305+
// This is the URL the client will connect to, except that since it's a relative URL
306+
// (no leading slash), Webpack will resolve it against the runtime <base href> URL
307+
// plus it also adds the publicPath
308+
const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
309+
310+
// This is the URL inside the Webpack middleware Node server that we'll proxy to.
311+
// We have to prefix with the public path because Webpack will add the publicPath
312+
// when it resolves hmrClientEndpoint as a relative URL.
313+
const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
314+
315+
// We always overwrite the 'path' option as it needs to match what the .NET side is expecting
316+
const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || <StringMap<string>>{};
317+
hmrClientOptions['path'] = hmrClientEndpoint;
318+
319+
const dynamicPublicPathKey = 'dynamicPublicPath';
320+
if (!(dynamicPublicPathKey in hmrClientOptions)) {
321+
// dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
322+
hmrClientOptions[dynamicPublicPathKey] = true;
323+
} else {
324+
// ... but you can set it to any other value explicitly if you want (e.g., false)
325+
hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
326+
}
327+
328+
attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
308329
}
309-
310-
attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
311-
}
312-
});
313-
314-
// Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
315-
callback(null, {
316-
Port: listener.address().port,
317-
PublicPaths: normalizedPublicPaths
318-
});
319-
} catch (ex) {
320-
callback(ex.stack, null);
330+
});
331+
332+
// Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
333+
callback(null, {
334+
Port: listener.address().port,
335+
PublicPaths: normalizedPublicPaths
336+
});
337+
} catch (ex) {
338+
callback(ex.stack, null);
339+
}
321340
}
322341
});
323342
}

0 commit comments

Comments
 (0)