diff --git a/Directory.Build.props b/Directory.Build.props index 7d0c6585..cec8e3da 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,9 +10,6 @@ $(MSBuildThisFileDirectory) $(MSBuildThisFileDirectory)build\Key.snk true - Microsoft - MicrosoftNuGet - true true diff --git a/Directory.Build.targets b/Directory.Build.targets index 78626b77..7e3f8df9 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,10 +1,6 @@ - $(MicrosoftNETCoreApp20PackageVersion) - $(MicrosoftNETCoreApp21PackageVersion) - $(MicrosoftNETCoreApp22PackageVersion) + $(MicrosoftNETCoreAppPackageVersion) $(NETStandardLibrary20PackageVersion) - - 99.9 diff --git a/LICENSE.txt b/LICENSE.txt index 7b2956ec..b3b180cd 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,14 +1,201 @@ -Copyright (c) .NET Foundation and Contributors + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -All rights reserved. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at + 1. Definitions. - http://www.apache.org/licenses/LICENSE-2.0 + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) .NET Foundation and Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index e70dfdb9..dfa67285 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# JavaScriptServices +# JavaScriptServices [Archived] -AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/gprilrckx116vc9m/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/javascriptservices/branch/dev) +## IMPORTANT + +The features described in this article are obsolete as of ASP.NET Core 3.0. A simpler SPA frameworks integration mechanism is available in the [Microsoft.AspNetCore.SpaServices.Extensions](https://www.nuget.org/packages/Microsoft.AspNetCore.SpaServices.Extensions) NuGet package. For more information, see [[Announcement] Obsoleting Microsoft.AspNetCore.SpaServices and Microsoft.AspNetCore.NodeServices](https://github.com/dotnet/AspNetCore/issues/12890). -This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. ## What is this? @@ -88,9 +89,3 @@ The [`samples` directory](/samples) contains examples of: * If you're trying to run the Angular "Music Store" sample, then also run `gulp` (which you need to have installed globally). None of the other samples require this. * Run the application (`dotnet run`) * Browse to [http://localhost:5000](http://localhost:5000) - -## Contributing - -If you're interested in contributing to the various packages, samples, and project templates in this repo, that's great! - -Before working on a pull request, especially if it's more than a trivial fix (for example, for a typo), it's usually a good idea first to file an issue describing what you're proposing to do and how it will work. Then you can find out if it's likely that such a pull request will be accepted, and how it fits into wider ongoing plans. diff --git a/build/dependencies.props b/build/dependencies.props index 967ed8b0..40e13eba 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -3,28 +3,28 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 3.0.0-alpha1-20180821.3 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 3.0.0-alpha1-10393 - 2.0.9 - 2.1.2 - 2.2.0-preview1-26618-02 + 3.0.0-build-20181114.5 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview1-26907-05 + 3.0.0-alpha1-10742 2.0.3 11.0.2 - 4.10.0-preview1-26829-04 + 4.10.0-preview1-26907-04 - + diff --git a/build/repo.props b/build/repo.props index 17a98ac7..4402da1d 100644 --- a/build/repo.props +++ b/build/repo.props @@ -8,8 +8,6 @@ - - - + diff --git a/korebuild-lock.txt b/korebuild-lock.txt index 767a4717..73613543 100644 --- a/korebuild-lock.txt +++ b/korebuild-lock.txt @@ -1,2 +1,2 @@ -version:3.0.0-alpha1-20180821.3 -commithash:0939a90812deb1c604eb9a4768869687495fc1dd +version:3.0.0-build-20181114.5 +commithash:880e9a204d4ee4a18dfd83c9fb05a192a28bca60 diff --git a/run.sh b/run.sh index 61f7a533..4c1fed56 100755 --- a/run.sh +++ b/run.sh @@ -220,7 +220,7 @@ if [ -f "$config_file" ]; then config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" else - _error "$config_file contains invalid JSON." + __error "$config_file contains invalid JSON." exit 1 fi elif __machine_has python ; then @@ -228,7 +228,7 @@ if [ -f "$config_file" ]; then config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" else - _error "$config_file contains invalid JSON." + __error "$config_file contains invalid JSON." exit 1 fi elif __machine_has python3 ; then @@ -236,11 +236,11 @@ if [ -f "$config_file" ]; then config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" else - _error "$config_file contains invalid JSON." + __error "$config_file contains invalid JSON." exit 1 fi else - _error 'Missing required command: jq or python. Could not parse the JSON file.' + __error 'Missing required command: jq or python. Could not parse the JSON file.' exit 1 fi diff --git a/samples/misc/LatencyTest/LatencyTest.csproj b/samples/misc/LatencyTest/LatencyTest.csproj index c29edd19..c146eb6a 100644 --- a/samples/misc/LatencyTest/LatencyTest.csproj +++ b/samples/misc/LatencyTest/LatencyTest.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2;net461 + netcoreapp3.0 false exe @@ -13,6 +13,7 @@ + diff --git a/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj b/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj index b80ffecf..874807b9 100644 --- a/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj +++ b/samples/misc/NodeServicesExamples/NodeServicesExamples.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2;net461 + netcoreapp3.0 true false @@ -18,6 +18,8 @@ + + diff --git a/samples/misc/Webpack/Webpack.csproj b/samples/misc/Webpack/Webpack.csproj index b80ffecf..874807b9 100644 --- a/samples/misc/Webpack/Webpack.csproj +++ b/samples/misc/Webpack/Webpack.csproj @@ -1,7 +1,7 @@ - netcoreapp2.2;net461 + netcoreapp3.0 true false @@ -18,6 +18,8 @@ + + diff --git a/scripts/Regenerate-JSFiles.ps1 b/scripts/Regenerate-JSFiles.ps1 new file mode 100644 index 00000000..ef3336c8 --- /dev/null +++ b/scripts/Regenerate-JSFiles.ps1 @@ -0,0 +1,28 @@ +[cmdletbinding(SupportsShouldProcess = $true)] +param( +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2 + +Push-Location "src" +try { + $dirs = Get-ChildItem -Directory + foreach($dir in $dirs) + { + Push-Location $dir + try{ + if(Test-Path -Path "package.json") + { + npm install + npm run build + } + } + finally{ + Pop-Location + } + } +} +finally { + Pop-Location +} diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js b/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js index 89dafef6..b9550ff8 100644 --- a/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js +++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/Content/Node/entrypoint-socket.js @@ -1,4 +1,4 @@ -(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap +(function (e, a) { for (var i in a) e[i] = a[i]; }(exports, /******/(function (modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ @@ -6,15 +6,17 @@ /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { +/******/ if (installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; -/******/ } + /******/ + } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} -/******/ }; + /******/ + }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); @@ -24,7 +26,8 @@ /******/ /******/ // Return the exports of the module /******/ return module.exports; -/******/ } + /******/ + } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) @@ -34,47 +37,53 @@ /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { +/******/ __webpack_require__.d = function (exports, name, getter) { +/******/ if (!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); -/******/ } -/******/ }; + /******/ + } + /******/ + }; /******/ /******/ // define __esModule on exports -/******/ __webpack_require__.r = function(exports) { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ __webpack_require__.r = function (exports) { +/******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } + /******/ + } /******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; + /******/ + }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = __webpack_require__(value); -/******/ if(mode & 8) return value; -/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ __webpack_require__.t = function (value, mode) { +/******/ if (mode & 1) value = __webpack_require__(value); +/******/ if (mode & 8) return value; +/******/ if ((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); -/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ if (mode & 2 && typeof value != 'string') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key)); /******/ return ns; -/******/ }; + /******/ + }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { +/******/ __webpack_require__.n = function (module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; -/******/ }; + /******/ + }; /******/ /******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ __webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; @@ -82,523 +91,536 @@ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); -/******/ }) + /******/ +}) /************************************************************************/ -/******/ ([ +/******/([ /* 0 */ -/***/ (function(module, exports, __webpack_require__) { +/***/ (function (module, exports, __webpack_require__) { -module.exports = __webpack_require__(1); + module.exports = __webpack_require__(1); -/***/ }), + /***/ + }), /* 1 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -exports.__esModule = true; -// Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, -// but simplifies things for the consumer of this module. -__webpack_require__(2); -var net = __webpack_require__(3); -var path = __webpack_require__(4); -var readline = __webpack_require__(5); -var ArgsUtil_1 = __webpack_require__(6); -var ExitWhenParentExits_1 = __webpack_require__(7); -var virtualConnectionServer = __webpack_require__(8); -// Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct -// reference to Node's runtime 'require' function. -var dynamicRequire = eval('require'); -// Signal to the .NET side when we're ready to accept invocations -var server = net.createServer().on('listening', function () { - console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); -}); -// Each virtual connection represents a separate invocation -virtualConnectionServer.createInterface(server).on('connection', function (connection) { - readline.createInterface(connection, null).on('line', function (line) { - try { - // Get a reference to the function to invoke - var invocation = JSON.parse(line); - var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName)); - var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule; - // Prepare a callback for accepting non-streamed JSON responses - var hasInvokedCallback_1 = false; - var invocationCallback = function (errorValue, successValue) { - if (hasInvokedCallback_1) { - throw new Error('Cannot supply more than one result. The callback has already been invoked,' - + ' or the result stream has already been accessed'); - } - hasInvokedCallback_1 = true; - connection.end(JSON.stringify({ - result: successValue, - errorMessage: errorValue && (errorValue.message || errorValue), - errorDetails: errorValue && (errorValue.stack || null) - })); - }; - // Also support streamed binary responses - Object.defineProperty(invocationCallback, 'stream', { - enumerable: true, - get: function () { - hasInvokedCallback_1 = true; - return connection; - } +/***/ (function (module, exports, __webpack_require__) { + + "use strict"; + + exports.__esModule = true; + // Limit dependencies to core Node modules. This means the code in this file has to be very low-level and unattractive, + // but simplifies things for the consumer of this module. + __webpack_require__(2); + var net = __webpack_require__(3); + var path = __webpack_require__(4); + var readline = __webpack_require__(5); + var ArgsUtil_1 = __webpack_require__(6); + var ExitWhenParentExits_1 = __webpack_require__(7); + var virtualConnectionServer = __webpack_require__(8); + // Webpack doesn't support dynamic requires for files not present at compile time, so grab a direct + // reference to Node's runtime 'require' function. + var dynamicRequire = eval('require'); + // Signal to the .NET side when we're ready to accept invocations + var server = net.createServer().on('listening', function () { + console.log('[Microsoft.AspNetCore.NodeServices:Listening]'); }); - // Actually invoke it, passing through any supplied args - invokedFunction.apply(null, [invocationCallback].concat(invocation.args)); - } - catch (ex) { - connection.end(JSON.stringify({ - errorMessage: ex.message, - errorDetails: ex.stack - })); - } - }); -}); -// Begin listening now. The underlying transport varies according to the runtime platform. -// On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets. -var useWindowsNamedPipes = /^win/.test(process.platform); -var parsedArgs = ArgsUtil_1.parseArgs(process.argv); -var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress; -server.listen(listenAddress); -ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); - - -/***/ }), + // Each virtual connection represents a separate invocation + virtualConnectionServer.createInterface(server).on('connection', function (connection) { + readline.createInterface(connection, null).on('line', function (line) { + try { + // Get a reference to the function to invoke + var invocation = JSON.parse(line); + var invokedModule = dynamicRequire(path.resolve(process.cwd(), invocation.moduleName)); + var invokedFunction = invocation.exportedFunctionName ? invokedModule[invocation.exportedFunctionName] : invokedModule; + // Prepare a callback for accepting non-streamed JSON responses + var hasInvokedCallback_1 = false; + var invocationCallback = function (errorValue, successValue) { + if (hasInvokedCallback_1) { + throw new Error('Cannot supply more than one result. The callback has already been invoked,' + + ' or the result stream has already been accessed'); + } + hasInvokedCallback_1 = true; + connection.end(JSON.stringify({ + result: successValue, + errorMessage: errorValue && (errorValue.message || errorValue), + errorDetails: errorValue && (errorValue.stack || null) + })); + }; + // Also support streamed binary responses + Object.defineProperty(invocationCallback, 'stream', { + enumerable: true, + get: function () { + hasInvokedCallback_1 = true; + return connection; + } + }); + // Actually invoke it, passing through any supplied args + invokedFunction.apply(null, [invocationCallback].concat(invocation.args)); + } + catch (ex) { + connection.end(JSON.stringify({ + errorMessage: ex.message, + errorDetails: ex.stack + })); + } + }); + }); + // Begin listening now. The underlying transport varies according to the runtime platform. + // On Windows it's Named Pipes; on Linux/OSX it's Domain Sockets. + var useWindowsNamedPipes = /^win/.test(process.platform); + var parsedArgs = ArgsUtil_1.parseArgs(process.argv); + var listenAddress = (useWindowsNamedPipes ? '\\\\.\\pipe\\' : '/tmp/') + parsedArgs.listenAddress; + server.listen(listenAddress); + ExitWhenParentExits_1.exitWhenParentExits(parseInt(parsedArgs.parentPid), /* ignoreSigint */ true); + + + /***/ + }), /* 2 */ -/***/ (function(module, exports) { - -// When Node writes to stdout/strerr, we capture that and convert the lines into calls on the -// active .NET ILogger. But by default, stdout/stderr don't have any way of distinguishing -// linebreaks inside log messages from the linebreaks that delimit separate log messages, -// so multiline strings will end up being written to the ILogger as multiple independent -// log messages. This makes them very hard to make sense of, especially when they represent -// something like stack traces. -// -// To fix this, we intercept stdout/stderr writes, and replace internal linebreaks with a -// marker token. When .NET receives the lines, it converts the marker tokens back to regular -// linebreaks within the logged messages. -// -// Note that it's better to do the interception at the stdout/stderr level, rather than at -// the console.log/console.error (etc.) level, because this takes place after any native -// message formatting has taken place (e.g., inserting values for % placeholders). -var findInternalNewlinesRegex = /\n(?!$)/g; -var encodedNewline = '__ns_newline__'; -encodeNewlinesWrittenToStream(process.stdout); -encodeNewlinesWrittenToStream(process.stderr); -function encodeNewlinesWrittenToStream(outputStream) { - var origWriteFunction = outputStream.write; - outputStream.write = function (value) { - // Only interfere with the write if it's definitely a string - if (typeof value === 'string') { - var argsClone = Array.prototype.slice.call(arguments, 0); - argsClone[0] = encodeNewlinesInString(value); - origWriteFunction.apply(this, argsClone); - } - else { - origWriteFunction.apply(this, arguments); - } - }; -} -function encodeNewlinesInString(str) { - return str.replace(findInternalNewlinesRegex, encodedNewline); -} +/***/ (function (module, exports) { + + // When Node writes to stdout/strerr, we capture that and convert the lines into calls on the + // active .NET ILogger. But by default, stdout/stderr don't have any way of distinguishing + // linebreaks inside log messages from the linebreaks that delimit separate log messages, + // so multiline strings will end up being written to the ILogger as multiple independent + // log messages. This makes them very hard to make sense of, especially when they represent + // something like stack traces. + // + // To fix this, we intercept stdout/stderr writes, and replace internal linebreaks with a + // marker token. When .NET receives the lines, it converts the marker tokens back to regular + // linebreaks within the logged messages. + // + // Note that it's better to do the interception at the stdout/stderr level, rather than at + // the console.log/console.error (etc.) level, because this takes place after any native + // message formatting has taken place (e.g., inserting values for % placeholders). + var findInternalNewlinesRegex = /\n(?!$)/g; + var encodedNewline = '__ns_newline__'; + encodeNewlinesWrittenToStream(process.stdout); + encodeNewlinesWrittenToStream(process.stderr); + function encodeNewlinesWrittenToStream(outputStream) { + var origWriteFunction = outputStream.write; + outputStream.write = function (value) { + // Only interfere with the write if it's definitely a string + if (typeof value === 'string') { + var argsClone = Array.prototype.slice.call(arguments, 0); + argsClone[0] = encodeNewlinesInString(value); + origWriteFunction.apply(this, argsClone); + } + else { + origWriteFunction.apply(this, arguments); + } + }; + } + function encodeNewlinesInString(str) { + return str.replace(findInternalNewlinesRegex, encodedNewline); + } -/***/ }), + /***/ + }), /* 3 */ -/***/ (function(module, exports) { +/***/ (function (module, exports) { -module.exports = require("net"); + module.exports = require("net"); -/***/ }), + /***/ + }), /* 4 */ -/***/ (function(module, exports) { +/***/ (function (module, exports) { -module.exports = require("path"); + module.exports = require("path"); -/***/ }), + /***/ + }), /* 5 */ -/***/ (function(module, exports) { +/***/ (function (module, exports) { -module.exports = require("readline"); + module.exports = require("readline"); -/***/ }), + /***/ + }), /* 6 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -exports.__esModule = true; -function parseArgs(args) { - // Very simplistic parsing which is sufficient for the cases needed. We don't want to bring in any external - // dependencies (such as an args-parsing library) to this file. - var result = {}; - var currentKey = null; - args.forEach(function (arg) { - if (arg.indexOf('--') === 0) { - var argName = arg.substring(2); - result[argName] = undefined; - currentKey = argName; - } - else if (currentKey) { - result[currentKey] = arg; - currentKey = null; - } - }); - return result; -} -exports.parseArgs = parseArgs; +/***/ (function (module, exports, __webpack_require__) { + + "use strict"; + + exports.__esModule = true; + function parseArgs(args) { + // Very simplistic parsing which is sufficient for the cases needed. We don't want to bring in any external + // dependencies (such as an args-parsing library) to this file. + var result = {}; + var currentKey = null; + args.forEach(function (arg) { + if (arg.indexOf('--') === 0) { + var argName = arg.substring(2); + result[argName] = undefined; + currentKey = argName; + } + else if (currentKey) { + result[currentKey] = arg; + currentKey = null; + } + }); + return result; + } + exports.parseArgs = parseArgs; -/***/ }), + /***/ + }), /* 7 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -/* -In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, -because we have no further use for them. If the .NET process shuts down gracefully, it will run its -finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. - -But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have -any opportunity to shut down its child processes, and by default they will keep running. In this case, it's -up to the child process to detect this has happened and terminate itself. - -There are many possible approaches to detecting when a parent process has exited, most of which behave -differently between Windows and Linux/OS X: - - - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when - the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. - - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). - But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). - - The child Node process can get a callback when its stdin/stdout are disconnected, as described at - http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows - causes the process to terminate prematurely. - - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes - the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. - - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone - - You can directly pass a parent process PID to the child, and then have the child poll to see if it's - still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, - as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) - - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. - However I don't see this documented anywhere. It would be nice if you could just poll for whether or not - process.stdout is still connected (without actually writing to it) but I haven't found any property whose - value changes until you actually try to write to it. - -Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling -to check whether the parent PID is still running. So that's what we do here. -*/ -exports.__esModule = true; -var pollIntervalMs = 1000; -function exitWhenParentExits(parentPid, ignoreSigint) { - setInterval(function () { - if (!processExists(parentPid)) { - // Can't log anything at this point, because out stdout was connected to the parent, - // but the parent is gone. - process.exit(); - } - }, pollIntervalMs); - if (ignoreSigint) { - // Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree. - // By default, the Node process would then exit before the .NET process, because ASP.NET implements - // a delayed shutdown to allow ongoing requests to complete. - // - // This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware - // will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is - // already set up to shut itself down if it detects the .NET process is terminated, all we have to do is - // ignore the SIGINT. The Node process will then terminate automatically after the .NET process does. - // - // A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any - // ongoing EventSource connections before letting the Node process exit, independently of the .NET - // process exiting. However, doing this well in general is very nontrivial (see all the discussion at - // https://github.com/nodejs/node/issues/2642). - process.on('SIGINT', function () { - console.log('Received SIGINT. Waiting for .NET process to exit...'); - }); - } -} -exports.exitWhenParentExits = exitWhenParentExits; -function processExists(pid) { - try { - // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't - // throw, that means it does exist. - process.kill(pid, 0); - return true; - } - catch (ex) { - // If the reason for the error is that we don't have permission to ask about this process, - // report that as a separate problem. - if (ex.code === 'EPERM') { - throw new Error("Attempted to check whether process " + pid + " was running, but got a permissions error."); - } - return false; - } -} - - -/***/ }), -/* 8 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -exports.__esModule = true; -var events_1 = __webpack_require__(9); -var VirtualConnection_1 = __webpack_require__(10); -// Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length, -// and both will reject longer frames. -var MaxFrameBodyLength = 16 * 1024; -/** - * Accepts connections to a net.Server and adapts them to behave as multiplexed connections. That is, for each physical socket connection, - * we track a list of 'virtual connections' whose API is a Duplex stream. The remote clients may open and close as many virtual connections - * as they wish, reading and writing to them independently, without the overhead of establishing new physical connections each time. - */ -function createInterface(server) { - var emitter = new events_1.EventEmitter(); - server.on('connection', function (socket) { - // For each physical socket connection, maintain a set of virtual connections. Issue a notification whenever - // a new virtual connections is opened. - var childSockets = new VirtualConnectionsCollection(socket, function (virtualConnection) { - emitter.emit('connection', virtualConnection); - }); - }); - return emitter; -} -exports.createInterface = createInterface; -/** - * Tracks the 'virtual connections' associated with a single physical socket connection. - */ -var VirtualConnectionsCollection = /** @class */ (function () { - function VirtualConnectionsCollection(_socket, _onVirtualConnectionCallback) { - var _this = this; - this._socket = _socket; - this._onVirtualConnectionCallback = _onVirtualConnectionCallback; - this._currentFrameHeader = null; - this._virtualConnections = {}; - // If the remote end closes the physical socket, treat all the virtual connections as being closed remotely too - this._socket.on('close', function () { - Object.getOwnPropertyNames(_this._virtualConnections).forEach(function (id) { - // A 'null' frame signals that the connection was closed remotely - _this._virtualConnections[id].onReceivedData(null); - }); - }); - this._socket.on('readable', this._onIncomingDataAvailable.bind(this)); - } - /** - * This is called whenever the underlying socket signals that it may have some data available to read. It will synchronously read as many - * message frames as it can from the underlying socket, opens virtual connections as needed, and dispatches data to them. - */ - VirtualConnectionsCollection.prototype._onIncomingDataAvailable = function () { - var exhaustedAllData = false; - while (!exhaustedAllData) { - // We might already have a pending frame header from the previous time this method ran, but if not, that's the next thing we need to read - if (this._currentFrameHeader === null) { - this._currentFrameHeader = this._readNextFrameHeader(); - } - if (this._currentFrameHeader === null) { - // There's not enough data to fill a frameheader, so wait until more arrives later - // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) - exhaustedAllData = true; +/***/ (function (module, exports, __webpack_require__) { + + "use strict"; + + /* + In general, we want the Node child processes to be terminated as soon as the parent .NET processes exit, + because we have no further use for them. If the .NET process shuts down gracefully, it will run its + finalizers, one of which (in OutOfProcessNodeInstance.cs) will kill its associated Node process immediately. + + But if the .NET process is terminated forcefully (e.g., on Linux/OSX with 'kill -9'), then it won't have + any opportunity to shut down its child processes, and by default they will keep running. In this case, it's + up to the child process to detect this has happened and terminate itself. + + There are many possible approaches to detecting when a parent process has exited, most of which behave + differently between Windows and Linux/OS X: + + - On Windows, the parent process can mark its child as being a 'job' that should auto-terminate when + the parent does (http://stackoverflow.com/a/4657392). Not cross-platform. + - The child Node process can get a callback when the parent disconnects (process.on('disconnect', ...)). + But despite http://stackoverflow.com/a/16487966, no callback fires in any case I've tested (Windows / OS X). + - The child Node process can get a callback when its stdin/stdout are disconnected, as described at + http://stackoverflow.com/a/15693934. This works well on OS X, but calling stdout.resume() on Windows + causes the process to terminate prematurely. + - I don't know why, but on Windows, it's enough to invoke process.stdin.resume(). For some reason this causes + the child Node process to exit as soon as the parent one does, but I don't see this documented anywhere. + - You can poll to see if the parent process, or your stdin/stdout connection to it, is gone + - You can directly pass a parent process PID to the child, and then have the child poll to see if it's + still running (e.g., using process.kill(pid, 0), which doesn't kill it but just tests whether it exists, + as per https://nodejs.org/api/process.html#process_process_kill_pid_signal) + - Or, on each poll, you can try writing to process.stdout. If the parent has died, then this will throw. + However I don't see this documented anywhere. It would be nice if you could just poll for whether or not + process.stdout is still connected (without actually writing to it) but I haven't found any property whose + value changes until you actually try to write to it. + + Of these, the only cross-platform approach that is actually documented as a valid strategy is simply polling + to check whether the parent PID is still running. So that's what we do here. + */ + exports.__esModule = true; + var pollIntervalMs = 1000; + function exitWhenParentExits(parentPid, ignoreSigint) { + setInterval(function () { + if (!processExists(parentPid)) { + // Can't log anything at this point, because out stdout was connected to the parent, + // but the parent is gone. + process.exit(); + } + }, pollIntervalMs); + if (ignoreSigint) { + // Pressing ctrl+c in the terminal sends a SIGINT to all processes in the foreground process tree. + // By default, the Node process would then exit before the .NET process, because ASP.NET implements + // a delayed shutdown to allow ongoing requests to complete. + // + // This is problematic, because if Node exits first, the CopyToAsync code in ConditionalProxyMiddleware + // will experience a read fault, and logs a huge load of errors. Fortunately, since the Node process is + // already set up to shut itself down if it detects the .NET process is terminated, all we have to do is + // ignore the SIGINT. The Node process will then terminate automatically after the .NET process does. + // + // A better solution would be to have WebpackDevMiddleware listen for SIGINT and gracefully close any + // ongoing EventSource connections before letting the Node process exit, independently of the .NET + // process exiting. However, doing this well in general is very nontrivial (see all the discussion at + // https://github.com/nodejs/node/issues/2642). + process.on('SIGINT', function () { + console.log('Received SIGINT. Waiting for .NET process to exit...'); + }); + } } - else { - var frameBodyLength = this._currentFrameHeader.bodyLength; - var frameBodyOrNull = frameBodyLength > 0 ? this._socket.read(this._currentFrameHeader.bodyLength) : null; - if (frameBodyOrNull !== null || frameBodyLength === 0) { - // We have a complete frame header+body pair, so we can now dispatch this to a virtual connection. We set _currentFrameHeader back to null - // so that the next thing we try to read is the next frame header. - var headerCopy = this._currentFrameHeader; - this._currentFrameHeader = null; - this._onReceivedCompleteFrame(headerCopy, frameBodyOrNull); + exports.exitWhenParentExits = exitWhenParentExits; + function processExists(pid) { + try { + // Sending signal 0 - on all platforms - tests whether the process exists. As long as it doesn't + // throw, that means it does exist. + process.kill(pid, 0); + return true; } - else { - // There's not enough data to fill the pending frame body, so wait until more arrives later - // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) - exhaustedAllData = true; + catch (ex) { + // If the reason for the error is that we don't have permission to ask about this process, + // report that as a separate problem. + if (ex.code === 'EPERM') { + throw new Error("Attempted to check whether process " + pid + " was running, but got a permissions error."); + } + return false; } } - } - }; - VirtualConnectionsCollection.prototype._onReceivedCompleteFrame = function (header, bodyIfNotEmpty) { - // An incoming zero-length frame signals that there's no more data to read. - // Signal this to the Node stream APIs by pushing a 'null' chunk to it. - var virtualConnection = this._getOrOpenVirtualConnection(header); - virtualConnection.onReceivedData(header.bodyLength > 0 ? bodyIfNotEmpty : null); - }; - VirtualConnectionsCollection.prototype._getOrOpenVirtualConnection = function (header) { - if (this._virtualConnections.hasOwnProperty(header.connectionIdString)) { - // It's an existing virtual connection - return this._virtualConnections[header.connectionIdString]; - } - else { - // It's a new one - return this._openVirtualConnection(header); - } - }; - VirtualConnectionsCollection.prototype._openVirtualConnection = function (header) { - var _this = this; - var beginWriteCallback = function (data, writeCompletedCallback) { - // Only send nonempty frames, since empty ones are a signal to close the virtual connection - if (data.length > 0) { - _this._sendFrame(header.connectionIdBinary, data, writeCompletedCallback); - } - }; - var newVirtualConnection = new VirtualConnection_1.VirtualConnection(beginWriteCallback); - newVirtualConnection.on('end', function () { - // The virtual connection was closed remotely. Clean up locally. - _this._onVirtualConnectionWasClosed(header.connectionIdString); - }); - newVirtualConnection.on('finish', function () { - // The virtual connection was closed locally. Clean up locally, and notify the remote that we're done. - _this._onVirtualConnectionWasClosed(header.connectionIdString); - _this._sendFrame(header.connectionIdBinary, new Buffer(0)); - }); - this._virtualConnections[header.connectionIdString] = newVirtualConnection; - this._onVirtualConnectionCallback(newVirtualConnection); - return newVirtualConnection; - }; - /** - * Attempts to read a complete frame header, synchronously, from the underlying socket. - * If not enough data is available synchronously, returns null without consuming any data from the socket. - */ - VirtualConnectionsCollection.prototype._readNextFrameHeader = function () { - var headerBuf = this._socket.read(12); - if (headerBuf !== null) { - // We have enough data synchronously - var connectionIdBinary = headerBuf.slice(0, 8); - var connectionIdString = connectionIdBinary.toString('hex'); - var bodyLength = headerBuf.readInt32LE(8); - if (bodyLength < 0 || bodyLength > MaxFrameBodyLength) { - // Throwing here is going to bring down the whole process, so this cannot be allowed to happen in real use. - // But it won't happen in real use, because this is only used with our .NET client, which doesn't violate this rule. - throw new Error('Illegal frame body length: ' + bodyLength); - } - return { connectionIdBinary: connectionIdBinary, connectionIdString: connectionIdString, bodyLength: bodyLength }; - } - else { - // Not enough bytes are available synchronously, so none were consumed - return null; - } - }; - VirtualConnectionsCollection.prototype._sendFrame = function (connectionIdBinary, data, callback) { - // For all sends other than the last one, only invoke the callback if it failed. - // Also, only invoke the callback at most once. - var hasInvokedCallback = false; - var finalCallback = callback && (function (error) { - if (!hasInvokedCallback) { - hasInvokedCallback = true; - callback(error); - } - }); - var notFinalCallback = callback && (function (error) { - if (error) { - finalCallback(error); - } - }); - // The amount of data we're writing might exceed MaxFrameBodyLength, so split into frames as needed. - // Note that we always send at least one frame, even if it's empty (because that's the close-virtual-connection signal). - // If needed, this could be changed to send frames asynchronously, so that large sends could proceed in parallel - // (though that would involve making a clone of 'data', to avoid the risk of it being mutated during the send). - var bytesSent = 0; - do { - var nextFrameBodyLength = Math.min(MaxFrameBodyLength, data.length - bytesSent); - var isFinalChunk = (bytesSent + nextFrameBodyLength) === data.length; - this._socket.write(connectionIdBinary, notFinalCallback); - this._sendInt32LE(nextFrameBodyLength, notFinalCallback); - this._socket.write(data.slice(bytesSent, bytesSent + nextFrameBodyLength), isFinalChunk ? finalCallback : notFinalCallback); - bytesSent += nextFrameBodyLength; - } while (bytesSent < data.length); - }; - /** - * Sends a number serialized in the correct format for .NET to receive as a System.Int32 - */ - VirtualConnectionsCollection.prototype._sendInt32LE = function (value, callback) { - var buf = new Buffer(4); - buf.writeInt32LE(value, 0); - this._socket.write(buf, callback); - }; - VirtualConnectionsCollection.prototype._onVirtualConnectionWasClosed = function (id) { - if (this._virtualConnections.hasOwnProperty(id)) { - delete this._virtualConnections[id]; - } - }; - return VirtualConnectionsCollection; -}()); -/***/ }), + /***/ + }), +/* 8 */ +/***/ (function (module, exports, __webpack_require__) { + + "use strict"; + + exports.__esModule = true; + var events_1 = __webpack_require__(9); + var VirtualConnection_1 = __webpack_require__(10); + // Keep this in sync with the equivalent constant in the .NET code. Both sides split up their transmissions into frames with this max length, + // and both will reject longer frames. + var MaxFrameBodyLength = 16 * 1024; + /** + * Accepts connections to a net.Server and adapts them to behave as multiplexed connections. That is, for each physical socket connection, + * we track a list of 'virtual connections' whose API is a Duplex stream. The remote clients may open and close as many virtual connections + * as they wish, reading and writing to them independently, without the overhead of establishing new physical connections each time. + */ + function createInterface(server) { + var emitter = new events_1.EventEmitter(); + server.on('connection', function (socket) { + // For each physical socket connection, maintain a set of virtual connections. Issue a notification whenever + // a new virtual connections is opened. + var childSockets = new VirtualConnectionsCollection(socket, function (virtualConnection) { + emitter.emit('connection', virtualConnection); + }); + }); + return emitter; + } + exports.createInterface = createInterface; + /** + * Tracks the 'virtual connections' associated with a single physical socket connection. + */ + var VirtualConnectionsCollection = /** @class */ (function () { + function VirtualConnectionsCollection(_socket, _onVirtualConnectionCallback) { + var _this = this; + this._socket = _socket; + this._onVirtualConnectionCallback = _onVirtualConnectionCallback; + this._currentFrameHeader = null; + this._virtualConnections = {}; + // If the remote end closes the physical socket, treat all the virtual connections as being closed remotely too + this._socket.on('close', function () { + Object.getOwnPropertyNames(_this._virtualConnections).forEach(function (id) { + // A 'null' frame signals that the connection was closed remotely + _this._virtualConnections[id].onReceivedData(null); + }); + }); + this._socket.on('readable', this._onIncomingDataAvailable.bind(this)); + } + /** + * This is called whenever the underlying socket signals that it may have some data available to read. It will synchronously read as many + * message frames as it can from the underlying socket, opens virtual connections as needed, and dispatches data to them. + */ + VirtualConnectionsCollection.prototype._onIncomingDataAvailable = function () { + var exhaustedAllData = false; + while (!exhaustedAllData) { + // We might already have a pending frame header from the previous time this method ran, but if not, that's the next thing we need to read + if (this._currentFrameHeader === null) { + this._currentFrameHeader = this._readNextFrameHeader(); + } + if (this._currentFrameHeader === null) { + // There's not enough data to fill a frameheader, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } + else { + var frameBodyLength = this._currentFrameHeader.bodyLength; + var frameBodyOrNull = frameBodyLength > 0 ? this._socket.read(this._currentFrameHeader.bodyLength) : null; + if (frameBodyOrNull !== null || frameBodyLength === 0) { + // We have a complete frame header+body pair, so we can now dispatch this to a virtual connection. We set _currentFrameHeader back to null + // so that the next thing we try to read is the next frame header. + var headerCopy = this._currentFrameHeader; + this._currentFrameHeader = null; + this._onReceivedCompleteFrame(headerCopy, frameBodyOrNull); + } + else { + // There's not enough data to fill the pending frame body, so wait until more arrives later + // The next attempt to read from the socket will start from the same place this one did (incomplete reads don't consume any data) + exhaustedAllData = true; + } + } + } + }; + VirtualConnectionsCollection.prototype._onReceivedCompleteFrame = function (header, bodyIfNotEmpty) { + // An incoming zero-length frame signals that there's no more data to read. + // Signal this to the Node stream APIs by pushing a 'null' chunk to it. + var virtualConnection = this._getOrOpenVirtualConnection(header); + virtualConnection.onReceivedData(header.bodyLength > 0 ? bodyIfNotEmpty : null); + }; + VirtualConnectionsCollection.prototype._getOrOpenVirtualConnection = function (header) { + if (this._virtualConnections.hasOwnProperty(header.connectionIdString)) { + // It's an existing virtual connection + return this._virtualConnections[header.connectionIdString]; + } + else { + // It's a new one + return this._openVirtualConnection(header); + } + }; + VirtualConnectionsCollection.prototype._openVirtualConnection = function (header) { + var _this = this; + var beginWriteCallback = function (data, writeCompletedCallback) { + // Only send nonempty frames, since empty ones are a signal to close the virtual connection + if (data.length > 0) { + _this._sendFrame(header.connectionIdBinary, data, writeCompletedCallback); + } + }; + var newVirtualConnection = new VirtualConnection_1.VirtualConnection(beginWriteCallback); + newVirtualConnection.on('end', function () { + // The virtual connection was closed remotely. Clean up locally. + _this._onVirtualConnectionWasClosed(header.connectionIdString); + }); + newVirtualConnection.on('finish', function () { + // The virtual connection was closed locally. Clean up locally, and notify the remote that we're done. + _this._onVirtualConnectionWasClosed(header.connectionIdString); + _this._sendFrame(header.connectionIdBinary, Buffer.alloc(0)); + }); + this._virtualConnections[header.connectionIdString] = newVirtualConnection; + this._onVirtualConnectionCallback(newVirtualConnection); + return newVirtualConnection; + }; + /** + * Attempts to read a complete frame header, synchronously, from the underlying socket. + * If not enough data is available synchronously, returns null without consuming any data from the socket. + */ + VirtualConnectionsCollection.prototype._readNextFrameHeader = function () { + var headerBuf = this._socket.read(12); + if (headerBuf !== null) { + // We have enough data synchronously + var connectionIdBinary = headerBuf.slice(0, 8); + var connectionIdString = connectionIdBinary.toString('hex'); + var bodyLength = headerBuf.readInt32LE(8); + if (bodyLength < 0 || bodyLength > MaxFrameBodyLength) { + // Throwing here is going to bring down the whole process, so this cannot be allowed to happen in real use. + // But it won't happen in real use, because this is only used with our .NET client, which doesn't violate this rule. + throw new Error('Illegal frame body length: ' + bodyLength); + } + return { connectionIdBinary: connectionIdBinary, connectionIdString: connectionIdString, bodyLength: bodyLength }; + } + else { + // Not enough bytes are available synchronously, so none were consumed + return null; + } + }; + VirtualConnectionsCollection.prototype._sendFrame = function (connectionIdBinary, data, callback) { + // For all sends other than the last one, only invoke the callback if it failed. + // Also, only invoke the callback at most once. + var hasInvokedCallback = false; + var finalCallback = callback && (function (error) { + if (!hasInvokedCallback) { + hasInvokedCallback = true; + callback(error); + } + }); + var notFinalCallback = callback && (function (error) { + if (error) { + finalCallback(error); + } + }); + // The amount of data we're writing might exceed MaxFrameBodyLength, so split into frames as needed. + // Note that we always send at least one frame, even if it's empty (because that's the close-virtual-connection signal). + // If needed, this could be changed to send frames asynchronously, so that large sends could proceed in parallel + // (though that would involve making a clone of 'data', to avoid the risk of it being mutated during the send). + var bytesSent = 0; + do { + var nextFrameBodyLength = Math.min(MaxFrameBodyLength, data.length - bytesSent); + var isFinalChunk = (bytesSent + nextFrameBodyLength) === data.length; + this._socket.write(connectionIdBinary, notFinalCallback); + this._sendInt32LE(nextFrameBodyLength, notFinalCallback); + this._socket.write(data.slice(bytesSent, bytesSent + nextFrameBodyLength), isFinalChunk ? finalCallback : notFinalCallback); + bytesSent += nextFrameBodyLength; + } while (bytesSent < data.length); + }; + /** + * Sends a number serialized in the correct format for .NET to receive as a System.Int32 + */ + VirtualConnectionsCollection.prototype._sendInt32LE = function (value, callback) { + var buf = Buffer.alloc(4); + buf.writeInt32LE(value, 0); + this._socket.write(buf, callback); + }; + VirtualConnectionsCollection.prototype._onVirtualConnectionWasClosed = function (id) { + if (this._virtualConnections.hasOwnProperty(id)) { + delete this._virtualConnections[id]; + } + }; + return VirtualConnectionsCollection; + }()); + + + /***/ + }), /* 9 */ -/***/ (function(module, exports) { +/***/ (function (module, exports) { -module.exports = require("events"); + module.exports = require("events"); -/***/ }), + /***/ + }), /* 10 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - } - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -exports.__esModule = true; -var stream_1 = __webpack_require__(11); -/** - * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection. - */ -var VirtualConnection = /** @class */ (function (_super) { - __extends(VirtualConnection, _super); - function VirtualConnection(_beginWriteCallback) { - var _this = _super.call(this) || this; - _this._beginWriteCallback = _beginWriteCallback; - _this._flowing = false; - _this._receivedDataQueue = []; - return _this; - } - VirtualConnection.prototype._read = function () { - this._flowing = true; - // Keep pushing data until we run out, or the underlying framework asks us to stop. - // When we finish, the 'flowing' state is detemined by whether more data is still being requested. - while (this._flowing && this._receivedDataQueue.length > 0) { - var nextChunk = this._receivedDataQueue.shift(); - this._flowing = this.push(nextChunk); - } - }; - VirtualConnection.prototype._write = function (chunk, encodingIfString, callback) { - if (typeof chunk === 'string') { - chunk = new Buffer(chunk, encodingIfString); - } - this._beginWriteCallback(chunk, callback); - }; - VirtualConnection.prototype.onReceivedData = function (dataOrNullToSignalEOF) { - if (this._flowing) { - this._flowing = this.push(dataOrNullToSignalEOF); - } - else { - this._receivedDataQueue.push(dataOrNullToSignalEOF); - } - }; - return VirtualConnection; -}(stream_1.Duplex)); -exports.VirtualConnection = VirtualConnection; +/***/ (function (module, exports, __webpack_require__) { + "use strict"; -/***/ }), + var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + } + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + exports.__esModule = true; + var stream_1 = __webpack_require__(11); + /** + * Represents a virtual connection. Multiple virtual connections may be multiplexed over a single physical socket connection. + */ + var VirtualConnection = /** @class */ (function (_super) { + __extends(VirtualConnection, _super); + function VirtualConnection(_beginWriteCallback) { + var _this = _super.call(this) || this; + _this._beginWriteCallback = _beginWriteCallback; + _this._flowing = false; + _this._receivedDataQueue = []; + return _this; + } + VirtualConnection.prototype._read = function () { + this._flowing = true; + // Keep pushing data until we run out, or the underlying framework asks us to stop. + // When we finish, the 'flowing' state is detemined by whether more data is still being requested. + while (this._flowing && this._receivedDataQueue.length > 0) { + var nextChunk = this._receivedDataQueue.shift(); + this._flowing = this.push(nextChunk); + } + }; + VirtualConnection.prototype._write = function (chunk, encodingIfString, callback) { + if (typeof chunk === 'string') { + chunk = Buffer.from(chunk, encodingIfString); + } + this._beginWriteCallback(chunk, callback); + }; + VirtualConnection.prototype.onReceivedData = function (dataOrNullToSignalEOF) { + if (this._flowing) { + this._flowing = this.push(dataOrNullToSignalEOF); + } + else { + this._receivedDataQueue.push(dataOrNullToSignalEOF); + } + }; + return VirtualConnection; + }(stream_1.Duplex)); + exports.VirtualConnection = VirtualConnection; + + + /***/ + }), /* 11 */ -/***/ (function(module, exports) { +/***/ (function (module, exports) { -module.exports = require("stream"); + module.exports = require("stream"); -/***/ }) -/******/ ]))); \ No newline at end of file + /***/ + }) +/******/]))); diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj b/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj index 9dab54a9..453eb629 100644 --- a/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj +++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/Microsoft.AspNetCore.NodeServices.Sockets.csproj @@ -2,7 +2,7 @@ Socket-based RPC for Microsoft.AspNetCore.NodeServices. - netstandard2.0 + netcoreapp3.0 diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts b/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts index de71f607..76394409 100644 --- a/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts +++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnection.ts @@ -27,7 +27,7 @@ export class VirtualConnection extends Duplex { public _write(chunk: Buffer | string, encodingIfString: string, callback: EndWriteCallback) { if (typeof chunk === 'string') { - chunk = new Buffer(chunk as string, encodingIfString); + chunk = Buffer.from(chunk as string, encodingIfString); } this._beginWriteCallback(chunk as Buffer, callback); diff --git a/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts b/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts index 76b833d4..5dbf9698 100644 --- a/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts +++ b/src/Microsoft.AspNetCore.NodeServices.Sockets/TypeScript/VirtualConnections/VirtualConnectionServer.ts @@ -113,7 +113,7 @@ class VirtualConnectionsCollection { newVirtualConnection.on('finish', () => { // The virtual connection was closed locally. Clean up locally, and notify the remote that we're done. this._onVirtualConnectionWasClosed(header.connectionIdString); - this._sendFrame(header.connectionIdBinary, new Buffer(0)); + this._sendFrame(header.connectionIdBinary, Buffer.alloc(0)); }); this._virtualConnections[header.connectionIdString] = newVirtualConnection; @@ -180,7 +180,7 @@ class VirtualConnectionsCollection { * Sends a number serialized in the correct format for .NET to receive as a System.Int32 */ private _sendInt32LE(value: number, callback?: EndWriteCallback) { - const buf = new Buffer(4); + const buf = Buffer.alloc(4); buf.writeInt32LE(value, 0); this._socket.write(buf, callback); } diff --git a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs index 384abad0..6a72b002 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs +++ b/src/Microsoft.AspNetCore.NodeServices/Configuration/NodeServicesOptions.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.NodeServices { @@ -58,8 +58,7 @@ public NodeServicesOptions(IServiceProvider serviceProvider) var loggerFactory = serviceProvider.GetService(); NodeInstanceOutputLogger = loggerFactory != null ? loggerFactory.CreateLogger(LogCategoryName) - : new ConsoleLogger(LogCategoryName, null, false); - + : NullLogger.Instance; // By default, we use this package's built-in out-of-process-via-HTTP hosting/transport this.UseHttpHosting(); } diff --git a/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj b/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj index 6d64797f..742aa3fa 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj +++ b/src/Microsoft.AspNetCore.NodeServices/Microsoft.AspNetCore.NodeServices.csproj @@ -2,7 +2,7 @@ Invoke Node.js modules at runtime in ASP.NET Core applications. - netstandard2.0 + netcoreapp3.0 @@ -12,7 +12,7 @@ - + diff --git a/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj b/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj index 4472217f..e263efb9 100644 --- a/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj +++ b/src/Microsoft.AspNetCore.SpaServices.Extensions/Microsoft.AspNetCore.SpaServices.Extensions.csproj @@ -2,7 +2,7 @@ Helpers for building single-page applications on ASP.NET MVC Core. - netstandard2.0 + netcoreapp3.0 diff --git a/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs b/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs index 54e57dea..d49b60c0 100644 --- a/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs +++ b/src/Microsoft.AspNetCore.SpaServices.Extensions/Util/LoggerFinder.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Console; +using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.SpaServices.Util { @@ -14,11 +14,11 @@ public static ILogger GetOrCreateLogger( IApplicationBuilder appBuilder, string logCategoryName) { - // If the DI system gives us a logger, use it. Otherwise, set up a default one. + // If the DI system gives us a logger, use it. Otherwise, set up a default one var loggerFactory = appBuilder.ApplicationServices.GetService(); var logger = loggerFactory != null ? loggerFactory.CreateLogger(logCategoryName) - : new ConsoleLogger(logCategoryName, null, false); + : NullLogger.Instance; return logger; } } diff --git a/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj b/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj index 15cf8129..0b0cfe2f 100644 --- a/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj +++ b/src/Microsoft.AspNetCore.SpaServices/Microsoft.AspNetCore.SpaServices.csproj @@ -2,7 +2,7 @@ Helpers for building single-page applications on ASP.NET MVC Core. - netstandard2.0 + netcoreapp3.0 diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json index 19a2a1b7..0e469c78 100644 --- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/package.json @@ -1,6 +1,6 @@ { "name": "aspnet-webpack-react", - "version": "3.0.0", + "version": "4.0.0", "description": "Helpers for using Webpack with React in ASP.NET Core projects. Works in conjunction with the Microsoft.AspNetCore.SpaServices NuGet package.", "main": "index.js", "scripts": { @@ -17,12 +17,12 @@ "url": "https://github.com/aspnet/JavaScriptServices.git" }, "devDependencies": { - "@types/webpack": "^2.2.0", + "@types/webpack": "^4.4.0", "rimraf": "^2.5.4", "typescript": "^2.0.0", - "webpack": "^2.2.0" + "webpack": "^4.16.0" }, "peerDependencies": { - "webpack": "^2.2.0" + "webpack": "^4.0.0" } } diff --git a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts index ca1bad63..4f63516c 100644 --- a/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts +++ b/src/Microsoft.AspNetCore.SpaServices/npm/aspnet-webpack-react/src/HotModuleReplacement.ts @@ -1,11 +1,9 @@ import * as webpack from 'webpack'; -const reactHotLoaderWebpackLoader = 'react-hot-loader/webpack'; -const reactHotLoaderPatch = 'react-hot-loader/patch'; const supportedTypeScriptLoaders = ['ts-loader', 'awesome-typescript-loader']; export function addReactHotModuleReplacementConfig(webpackConfig: webpack.Configuration) { - const moduleConfig = webpackConfig.module as webpack.NewModule; + const moduleConfig = webpackConfig.module as webpack.Module; const moduleRules = moduleConfig.rules; if (!moduleRules) { return; // Unknown rules list format. Might be Webpack 1.x, which is not supported. @@ -15,24 +13,18 @@ export function addReactHotModuleReplacementConfig(webpackConfig: webpack.Config // to its array of loaders for (let ruleIndex = 0; ruleIndex < moduleRules.length; ruleIndex++) { // We only support NewUseRule (i.e., { use: ... }) because OldUseRule doesn't accept array values - const rule = moduleRules[ruleIndex] as webpack.NewUseRule; + const rule = moduleRules[ruleIndex] as webpack.RuleSetRule; if (!rule.use) { continue; } // We're looking for the first 'use' value that's a TypeScript loader - const loadersArray = rule.use instanceof Array ? rule.use : [rule.use]; + const loadersArray: webpack.RuleSetUseItem[] = rule.use instanceof Array ? rule.use : [rule.use as webpack.RuleSetUseItem]; const isTypescriptLoader = supportedTypeScriptLoaders.some(typeScriptLoaderName => containsLoader(loadersArray, typeScriptLoaderName)); if (!isTypescriptLoader) { continue; } - // This is the one - prefix it with the react-hot-loader loader - // (unless it's already in there somewhere) - if (!containsLoader(loadersArray, reactHotLoaderWebpackLoader)) { - loadersArray.unshift(reactHotLoaderWebpackLoader); - rule.use = loadersArray; // In case we normalised it to an array - } break; } @@ -48,19 +40,14 @@ export function addReactHotModuleReplacementConfig(webpackConfig: webpack.Config // Normalise to array entryConfig[entrypointName] = [entryConfig[entrypointName] as string]; } - - let entryValueArray = entryConfig[entrypointName] as string[]; - if (entryValueArray.indexOf(reactHotLoaderPatch) < 0) { - entryValueArray.unshift(reactHotLoaderPatch); - } }); } -function containsLoader(loadersArray: webpack.Loader[], loaderName: string) { +function containsLoader(loadersArray: webpack.RuleSetUseItem[], loaderName: string) { return loadersArray.some(loader => { // Allow 'use' values to be either { loader: 'name' } or 'name' // No need to support legacy webpack.OldLoader - const actualLoaderName = (loader as webpack.NewLoader).loader || (loader as string); + const actualLoaderName = (loader as webpack.RuleSetLoader).loader || (loader as string); return actualLoaderName && new RegExp(`\\b${ loaderName }\\b`).test(actualLoaderName); }); }