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 @@
-
+
-
-
-
-
+
+
+
+