diff --git a/appveyor.yml b/appveyor.yml index 570bca5..e6347c0 100755 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ install: - ps: Install-Product node 6.9.2 x64 # .NET Core SDK binaries # Download .NET Core 2.0 Preview 3 SDK and add to PATH - - ps: $urlCurrent = "https://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0-preview3-006670/dotnet-sdk-2.0.0-preview3-006670-win-x64.zip" + - ps: $urlCurrent = "https://dotnetcli.azureedge.net/dotnet/Sdk/2.0.0-preview3-006729/dotnet-sdk-2.0.0-preview3-006729-win-x64.zip" - ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetsdk" - ps: mkdir $env:DOTNET_INSTALL_DIR -Force | Out-Null - ps: $tempFileCurrent = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()) diff --git a/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js b/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js index c32d9f1..7524fb0 100644 --- a/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js +++ b/src/Microsoft.AspNetCore.NodeServices/Content/Node/entrypoint-http.js @@ -55,10 +55,11 @@ // 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 http = __webpack_require__(3); - var path = __webpack_require__(4); - var ArgsUtil_1 = __webpack_require__(5); - var ExitWhenParentExits_1 = __webpack_require__(6); + __webpack_require__(4); + var http = __webpack_require__(5); + var path = __webpack_require__(3); + var ArgsUtil_1 = __webpack_require__(6); + var ExitWhenParentExits_1 = __webpack_require__(7); // 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'); @@ -142,6 +143,64 @@ /***/ }, /* 2 */ +/***/ function(module, exports, __webpack_require__) { + + "use strict"; + var path = __webpack_require__(3); + var startsWith = function (str, prefix) { return str.substring(0, prefix.length) === prefix; }; + var appRootDir = process.cwd(); + function patchedLStat(pathToStatLong) { + try { + // If the lstat completes without errors, we don't modify its behavior at all + return origLStat.apply(this, arguments); + } + catch (ex) { + var shouldOverrideError = startsWith(ex.message, 'EPERM') // It's a permissions error + && typeof appRootDirLong === 'string' + && startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory + && ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution + if (shouldOverrideError) { + // Fake the result to give the same result as an 'lstat' on the app root dir. + // This stops Node failing to load modules just because it doesn't know whether + // ancestor directories are symlinks or not. If there's a genuine file + // permissions issue, it will still surface later when Node actually + // tries to read the file. + return origLStat.call(this, appRootDir); + } + else { + // In any other case, preserve the original error + throw ex; + } + } + } + ; + // It's only necessary to apply this workaround on Windows + var appRootDirLong = null; + var origLStat = null; + if (/^win/.test(process.platform)) { + try { + // Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir) + appRootDirLong = path._makeLong(appRootDir); + // Actually apply the patch, being as defensive as possible + var bindingFs = process.binding('fs'); + origLStat = bindingFs.lstat; + if (typeof origLStat === 'function') { + bindingFs.lstat = patchedLStat; + } + } + catch (ex) { + } + } + + +/***/ }, +/* 3 */ +/***/ function(module, exports) { + + module.exports = require("path"); + +/***/ }, +/* 4 */ /***/ function(module, exports) { // When Node writes to stdout/strerr, we capture that and convert the lines into calls on the @@ -182,19 +241,13 @@ /***/ }, -/* 3 */ +/* 5 */ /***/ function(module, exports) { module.exports = require("http"); /***/ }, -/* 4 */ -/***/ function(module, exports) { - - module.exports = require("path"); - -/***/ }, -/* 5 */ +/* 6 */ /***/ function(module, exports) { "use strict"; @@ -220,7 +273,7 @@ /***/ }, -/* 6 */ +/* 7 */ /***/ function(module, exports) { /* diff --git a/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts b/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts index 31f6376..b9a8329 100644 --- a/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts +++ b/src/Microsoft.AspNetCore.NodeServices/TypeScript/HttpNodeInstanceEntryPoint.ts @@ -1,5 +1,6 @@ // 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. +import './Util/PatchModuleResolutionLStat'; import './Util/OverrideStdOutputs'; import * as http from 'http'; import * as path from 'path'; diff --git a/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts b/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts new file mode 100644 index 0000000..71dccba --- /dev/null +++ b/src/Microsoft.AspNetCore.NodeServices/TypeScript/Util/PatchModuleResolutionLStat.ts @@ -0,0 +1,48 @@ +import * as path from 'path'; +const startsWith = (str: string, prefix: string) => str.substring(0, prefix.length) === prefix; +const appRootDir = process.cwd(); + +function patchedLStat(pathToStatLong: string) { + try { + // If the lstat completes without errors, we don't modify its behavior at all + return origLStat.apply(this, arguments); + } catch(ex) { + const shouldOverrideError = + startsWith(ex.message, 'EPERM') // It's a permissions error + && typeof appRootDirLong === 'string' + && startsWith(appRootDirLong, pathToStatLong) // ... for an ancestor directory + && ex.stack.indexOf('Object.realpathSync ') >= 0; // ... during symlink resolution + + if (shouldOverrideError) { + // Fake the result to give the same result as an 'lstat' on the app root dir. + // This stops Node failing to load modules just because it doesn't know whether + // ancestor directories are symlinks or not. If there's a genuine file + // permissions issue, it will still surface later when Node actually + // tries to read the file. + return origLStat.call(this, appRootDir); + } else { + // In any other case, preserve the original error + throw ex; + } + } +}; + +// It's only necessary to apply this workaround on Windows +let appRootDirLong: string = null; +let origLStat: Function = null; +if (/^win/.test(process.platform)) { + try { + // Get the app's root dir in Node's internal "long" format (e.g., \\?\C:\dir\subdir) + appRootDirLong = (path as any)._makeLong(appRootDir); + + // Actually apply the patch, being as defensive as possible + const bindingFs = (process as any).binding('fs'); + origLStat = bindingFs.lstat; + if (typeof origLStat === 'function') { + bindingFs.lstat = patchedLStat; + } + } catch(ex) { + // If some future version of Node throws (e.g., to prevent use of process.binding()), + // don't apply the patch, but still let the application run. + } +} diff --git a/templates/AngularSpa/AngularSpa.csproj b/templates/AngularSpa/AngularSpa.csproj index 4ef4c76..c8f88ed 100644 --- a/templates/AngularSpa/AngularSpa.csproj +++ b/templates/AngularSpa/AngularSpa.csproj @@ -9,17 +9,17 @@ - + - - - - + + + + - + diff --git a/templates/AureliaSpa/AureliaSpa.csproj b/templates/AureliaSpa/AureliaSpa.csproj index f99ade1..e36a892 100644 --- a/templates/AureliaSpa/AureliaSpa.csproj +++ b/templates/AureliaSpa/AureliaSpa.csproj @@ -9,13 +9,13 @@ - + - - - - + + + + diff --git a/templates/KnockoutSpa/KnockoutSpa.csproj b/templates/KnockoutSpa/KnockoutSpa.csproj index f99ade1..e36a892 100644 --- a/templates/KnockoutSpa/KnockoutSpa.csproj +++ b/templates/KnockoutSpa/KnockoutSpa.csproj @@ -9,13 +9,13 @@ - + - - - - + + + + diff --git a/templates/ReactReduxSpa/ReactReduxSpa.csproj b/templates/ReactReduxSpa/ReactReduxSpa.csproj index 5e060c0..4708b1f 100644 --- a/templates/ReactReduxSpa/ReactReduxSpa.csproj +++ b/templates/ReactReduxSpa/ReactReduxSpa.csproj @@ -9,13 +9,13 @@ - + - - - - + + + + diff --git a/templates/ReactSpa/ReactSpa.csproj b/templates/ReactSpa/ReactSpa.csproj index f99ade1..e36a892 100644 --- a/templates/ReactSpa/ReactSpa.csproj +++ b/templates/ReactSpa/ReactSpa.csproj @@ -9,13 +9,13 @@ - + - - - - + + + + diff --git a/templates/VueSpa/VueSpa.csproj b/templates/VueSpa/VueSpa.csproj index f99ade1..e36a892 100644 --- a/templates/VueSpa/VueSpa.csproj +++ b/templates/VueSpa/VueSpa.csproj @@ -9,13 +9,13 @@ - + - - - - + + + +