JavaScriptServices 266 B

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. commit 5f6f288056b3263a61a55631257db14314a02795
  2. Author: waterfoul <[email protected]>
  3. Date: Fri Dec 29 15:32:53 2017 -0600
  4. Added support for Thenables
  5. diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
  6. index cb173850a7a..c4204c820fa 100644
  7. --- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
  8. +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack/src/WebpackDevMiddleware.ts
  9. @@ -35,15 +35,25 @@ interface DevServerOptions {
  10. EnvParam: any;
  11. }
  12. -// We support these three kinds of webpack.config.js export. We don't currently support exported promises
  13. -// (though we might be able to add that in the future, if there's a need).
  14. +// Interface as defined in es6-promise
  15. +interface Thenable<T> {
  16. + then<U>(onFulfilled?: (value: T) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
  17. + then<U>(onFulfilled?: (value: T) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
  18. +}
  19. +
  20. +// We support these four kinds of webpack.config.js export
  21. type WebpackConfigOrArray = webpack.Configuration | webpack.Configuration[];
  22. +type WebpackConfigOrArrayOrThenable = WebpackConfigOrArray | Thenable<WebpackConfigOrArray>;
  23. interface WebpackConfigFunc {
  24. - (env?: any): WebpackConfigOrArray;
  25. + (env?: any): WebpackConfigOrArrayOrThenable;
  26. }
  27. -type WebpackConfigExport = WebpackConfigOrArray | WebpackConfigFunc;
  28. +type WebpackConfigExport = WebpackConfigOrArrayOrThenable | WebpackConfigFunc;
  29. type WebpackConfigModuleExports = WebpackConfigExport | EsModuleExports<WebpackConfigExport>;
  30. +function isThenable(obj: any) {
  31. + return obj && typeof (<Thenable<any>>obj).then === 'function';
  32. +}
  33. +
  34. function attachWebpackDevMiddleware(app: any, webpackConfig: webpack.Configuration, enableHotModuleReplacement: boolean, enableReactHotModuleReplacement: boolean, hmrClientOptions: StringMap<string>, hmrServerEndpoint: string) {
  35. // Build the final Webpack config based on supplied options
  36. if (enableHotModuleReplacement) {
  37. @@ -251,76 +261,85 @@ export function createWebpackDevServer(callback: CreateDevServerCallback, option
  38. // your Startup.cs.
  39. webpackConfigExport = webpackConfigExport(options.suppliedOptions.EnvParam);
  40. }
  41. - const webpackConfigArray = webpackConfigExport instanceof Array ? webpackConfigExport : [webpackConfigExport];
  42. - const enableHotModuleReplacement = options.suppliedOptions.HotModuleReplacement;
  43. - const enableReactHotModuleReplacement = options.suppliedOptions.ReactHotModuleReplacement;
  44. - if (enableReactHotModuleReplacement && !enableHotModuleReplacement) {
  45. - callback('To use ReactHotModuleReplacement, you must also enable the HotModuleReplacement option.', null);
  46. - return;
  47. - }
  48. + const webpackConfigThenable = isThenable(webpackConfigExport)
  49. + ? webpackConfigExport
  50. + : { then: callback => callback(webpackConfigExport) };
  51. - // The default value, 0, means 'choose randomly'
  52. - const suggestedHMRPortOrZero = options.suppliedOptions.HotModuleReplacementServerPort || 0;
  53. + webpackConfigThenable.then(webpackConfigResolved => {
  54. + const webpackConfigArray = webpackConfigResolved instanceof Array ? webpackConfigResolved : [webpackConfigResolved];
  55. - const app = connect();
  56. - const listener = app.listen(suggestedHMRPortOrZero, () => {
  57. - try {
  58. - // For each webpack config that specifies a public path, add webpack dev middleware for it
  59. - const normalizedPublicPaths: string[] = [];
  60. - webpackConfigArray.forEach(webpackConfig => {
  61. - if (webpackConfig.target === 'node') {
  62. - // For configs that target Node, it's meaningless to set up an HTTP listener, since
  63. - // Node isn't going to load those modules over HTTP anyway. It just loads them directly
  64. - // from disk. So the most relevant thing we can do with such configs is just write
  65. - // updated builds to disk, just like "webpack --watch".
  66. - beginWebpackWatcher(webpackConfig);
  67. - } else {
  68. - // For configs that target browsers, we can set up an HTTP listener, and dynamically
  69. - // modify the config to enable HMR etc. This just requires that we have a publicPath.
  70. - const publicPath = (webpackConfig.output.publicPath || '').trim();
  71. - if (!publicPath) {
  72. - 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)');
  73. - }
  74. - const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
  75. - normalizedPublicPaths.push(publicPathNoTrailingSlash);
  76. -
  77. - // This is the URL the client will connect to, except that since it's a relative URL
  78. - // (no leading slash), Webpack will resolve it against the runtime <base href> URL
  79. - // plus it also adds the publicPath
  80. - const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
  81. -
  82. - // This is the URL inside the Webpack middleware Node server that we'll proxy to.
  83. - // We have to prefix with the public path because Webpack will add the publicPath
  84. - // when it resolves hmrClientEndpoint as a relative URL.
  85. - const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
  86. -
  87. - // We always overwrite the 'path' option as it needs to match what the .NET side is expecting
  88. - const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || <StringMap<string>>{};
  89. - hmrClientOptions['path'] = hmrClientEndpoint;
  90. -
  91. - const dynamicPublicPathKey = 'dynamicPublicPath';
  92. - if (!(dynamicPublicPathKey in hmrClientOptions)) {
  93. - // dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
  94. - hmrClientOptions[dynamicPublicPathKey] = true;
  95. - } else {
  96. - // ... but you can set it to any other value explicitly if you want (e.g., false)
  97. - hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
  98. - }
  99. + const enableHotModuleReplacement = options.suppliedOptions.HotModuleReplacement;
  100. + const enableReactHotModuleReplacement = options.suppliedOptions.ReactHotModuleReplacement;
  101. + if (enableReactHotModuleReplacement && !enableHotModuleReplacement) {
  102. + callback('To use ReactHotModuleReplacement, you must also enable the HotModuleReplacement option.', null);
  103. + return;
  104. + }
  105. - attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
  106. - }
  107. - });
  108. + // The default value, 0, means 'choose randomly'
  109. + const suggestedHMRPortOrZero = options.suppliedOptions.HotModuleReplacementServerPort || 0;
  110. - // Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
  111. - callback(null, {
  112. - Port: listener.address().port,
  113. - PublicPaths: normalizedPublicPaths
  114. - });
  115. - } catch (ex) {
  116. - callback(ex.stack, null);
  117. - }
  118. - });
  119. + const app = connect();
  120. + const listener = app.listen(suggestedHMRPortOrZero, () => {
  121. + try {
  122. + // For each webpack config that specifies a public path, add webpack dev middleware for it
  123. + const normalizedPublicPaths: string[] = [];
  124. + webpackConfigArray.forEach(webpackConfig => {
  125. + if (webpackConfig.target === 'node') {
  126. + // For configs that target Node, it's meaningless to set up an HTTP listener, since
  127. + // Node isn't going to load those modules over HTTP anyway. It just loads them directly
  128. + // from disk. So the most relevant thing we can do with such configs is just write
  129. + // updated builds to disk, just like "webpack --watch".
  130. + beginWebpackWatcher(webpackConfig);
  131. + } else {
  132. + // For configs that target browsers, we can set up an HTTP listener, and dynamically
  133. + // modify the config to enable HMR etc. This just requires that we have a publicPath.
  134. + const publicPath = (webpackConfig.output.publicPath || '').trim();
  135. + if (!publicPath) {
  136. + 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)');
  137. + }
  138. + const publicPathNoTrailingSlash = removeTrailingSlash(publicPath);
  139. + normalizedPublicPaths.push(publicPathNoTrailingSlash);
  140. +
  141. + // This is the URL the client will connect to, except that since it's a relative URL
  142. + // (no leading slash), Webpack will resolve it against the runtime <base href> URL
  143. + // plus it also adds the publicPath
  144. + const hmrClientEndpoint = removeLeadingSlash(options.hotModuleReplacementEndpointUrl);
  145. +
  146. + // This is the URL inside the Webpack middleware Node server that we'll proxy to.
  147. + // We have to prefix with the public path because Webpack will add the publicPath
  148. + // when it resolves hmrClientEndpoint as a relative URL.
  149. + const hmrServerEndpoint = ensureLeadingSlash(publicPathNoTrailingSlash + options.hotModuleReplacementEndpointUrl);
  150. +
  151. + // We always overwrite the 'path' option as it needs to match what the .NET side is expecting
  152. + const hmrClientOptions = options.suppliedOptions.HotModuleReplacementClientOptions || <StringMap<string>>{};
  153. + hmrClientOptions['path'] = hmrClientEndpoint;
  154. +
  155. + const dynamicPublicPathKey = 'dynamicPublicPath';
  156. + if (!(dynamicPublicPathKey in hmrClientOptions)) {
  157. + // dynamicPublicPath default to true, so we can work with nonempty pathbases (virtual directories)
  158. + hmrClientOptions[dynamicPublicPathKey] = true;
  159. + } else {
  160. + // ... but you can set it to any other value explicitly if you want (e.g., false)
  161. + hmrClientOptions[dynamicPublicPathKey] = JSON.parse(hmrClientOptions[dynamicPublicPathKey]);
  162. + }
  163. +
  164. + attachWebpackDevMiddleware(app, webpackConfig, enableHotModuleReplacement, enableReactHotModuleReplacement, hmrClientOptions, hmrServerEndpoint);
  165. + }
  166. + });
  167. +
  168. + // Tell the ASP.NET app what addresses we're listening on, so that it can proxy requests here
  169. + callback(null, {
  170. + Port: listener.address().port,
  171. + PublicPaths: normalizedPublicPaths
  172. + });
  173. + } catch (ex) {
  174. + callback(ex.stack, null);
  175. + }
  176. + });
  177. + },
  178. + err => callback(err.stack, null)
  179. + );
  180. }
  181. function removeLeadingSlash(str: string) {