@nativescript/windows 0.1.0-alpha.2 → 0.1.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,16 +19,46 @@ namespace __PROJECT_NAME__
19
19
  this.UnhandledException += OnUnhandledException;
20
20
  }
21
21
 
22
- protected override void OnLaunched(LaunchActivatedEventArgs e)
22
+ protected override async void OnLaunched(LaunchActivatedEventArgs e)
23
23
  {
24
24
  _runtimeHost.Initialize();
25
+
26
+ // Show crash report from the previous run if one exists.
27
+ try
28
+ {
29
+ var panicLogPath = System.IO.Path.Combine(
30
+ ApplicationData.Current.LocalFolder.Path, "nativescript-panic.log");
31
+ if (System.IO.File.Exists(panicLogPath))
32
+ {
33
+ var content = System.IO.File.ReadAllText(panicLogPath);
34
+ System.IO.File.Delete(panicLogPath);
35
+ if (!string.IsNullOrWhiteSpace(content))
36
+ await CrashDiagnostics.ShowCrashDialogAsync("Crash from previous run", content);
37
+ }
38
+ }
39
+ catch { }
40
+
41
+ string jsError = null;
25
42
  try
26
43
  {
27
44
  _runtimeHost.RunMainScript();
45
+ jsError = _runtimeHost.GetLastJsError();
46
+ if (!string.IsNullOrEmpty(jsError))
47
+ {
48
+ CrashDiagnostics.WriteMessage("JS Error", jsError);
49
+ var report = CrashDiagnostics.BuildErrorReport(null, jsError);
50
+ CrashDiagnostics.WriteToTraceLog(report);
51
+ await CrashDiagnostics.ShowCrashDialogAsync("JavaScript Error", report);
52
+ }
28
53
  }
29
54
  catch (Exception scriptEx)
30
55
  {
56
+ jsError = _runtimeHost.GetLastJsError();
31
57
  System.Diagnostics.Debug.WriteLine($"[NativeScript] Script exception: {scriptEx.Message}");
58
+ CrashDiagnostics.WriteExceptionReport("RunMainScript", scriptEx, null);
59
+ var report = CrashDiagnostics.BuildErrorReport(scriptEx, jsError);
60
+ CrashDiagnostics.WriteToTraceLog(report);
61
+ await CrashDiagnostics.ShowCrashDialogAsync("Script Execution Error", report);
32
62
  }
33
63
 
34
64
  #if DEBUG
@@ -66,10 +96,17 @@ namespace __PROJECT_NAME__
66
96
 
67
97
  private void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
68
98
  {
99
+ e.Handled = true;
100
+ var jsError = _runtimeHost.GetLastJsError();
69
101
  CrashDiagnostics.WriteExceptionReport(
70
102
  "Xaml.UnhandledException",
71
103
  e.Exception,
72
- "Message=" + e.Message + "; Handled=" + e.Handled);
104
+ "JsError=" + (jsError ?? "<none>"));
105
+
106
+ var report = CrashDiagnostics.BuildErrorReport(e.Exception, jsError);
107
+ CrashDiagnostics.WriteToTraceLog(report);
108
+ var _ = CrashDiagnostics.ShowCrashDialogAsync(
109
+ e.Message ?? "Unhandled exception", report);
73
110
  }
74
111
 
75
112
  #if DEBUG
@@ -2,7 +2,10 @@ using System;
2
2
  using System.IO;
3
3
  using System.Text;
4
4
  using System.Threading.Tasks;
5
+ using Windows.ApplicationModel.Core;
6
+ using Windows.ApplicationModel.DataTransfer;
5
7
  using Windows.Storage;
8
+ using Windows.UI.Popups;
6
9
 
7
10
  namespace __PROJECT_NAME__
8
11
  {
@@ -53,6 +56,135 @@ namespace __PROJECT_NAME__
53
56
  AppendToLog(sb.ToString());
54
57
  }
55
58
 
59
+ public static string BuildErrorReport(Exception ex, string jsError = null, string extraDetails = null)
60
+ {
61
+ var sb = new StringBuilder();
62
+
63
+ if (!string.IsNullOrWhiteSpace(jsError))
64
+ {
65
+ sb.AppendLine("── JavaScript Error ──────────────────────────────────────");
66
+ sb.AppendLine(jsError.Trim());
67
+ sb.AppendLine();
68
+ }
69
+
70
+ if (ex != null)
71
+ {
72
+ sb.AppendLine("── Native Exception ──────────────────────────────────────");
73
+ sb.AppendLine(ex.GetType().Name + ": " + ex.Message);
74
+ if (ex.StackTrace != null) sb.AppendLine(ex.StackTrace);
75
+ var inner = ex.InnerException;
76
+ while (inner != null)
77
+ {
78
+ sb.AppendLine("Caused by: " + inner.GetType().Name + ": " + inner.Message);
79
+ if (inner.StackTrace != null) sb.AppendLine(inner.StackTrace);
80
+ inner = inner.InnerException;
81
+ }
82
+ sb.AppendLine();
83
+ }
84
+
85
+ // Include panic log from previous or current run
86
+ try
87
+ {
88
+ var panicLog = Path.Combine(ApplicationData.Current.LocalFolder.Path, "nativescript-panic.log");
89
+ if (File.Exists(panicLog))
90
+ {
91
+ var content = File.ReadAllText(panicLog, Encoding.UTF8).Trim();
92
+ if (!string.IsNullOrEmpty(content))
93
+ {
94
+ sb.AppendLine("── Rust Panic Log ────────────────────────────────────────");
95
+ sb.AppendLine(content);
96
+ sb.AppendLine();
97
+ }
98
+ }
99
+ }
100
+ catch { }
101
+
102
+ if (!string.IsNullOrWhiteSpace(extraDetails))
103
+ {
104
+ sb.AppendLine("── Additional Info ───────────────────────────────────────");
105
+ sb.AppendLine(extraDetails);
106
+ sb.AppendLine();
107
+ }
108
+
109
+ sb.AppendLine("──────────────────────────────────────────────────────────");
110
+ sb.AppendLine("Timestamp: " + DateTimeOffset.UtcNow.ToString("o"));
111
+
112
+ return sb.ToString();
113
+ }
114
+
115
+ public static string CrashLogPath()
116
+ {
117
+ try { return Path.Combine(ApplicationData.Current.LocalFolder.Path, "nativescript-crash.log"); }
118
+ catch { return null; }
119
+ }
120
+
121
+ public static async Task ShowCrashDialogAsync(string heading, string errorReport)
122
+ {
123
+ try
124
+ {
125
+ // Write full details to log so developer can access them even if the
126
+ // dialog summary is truncated.
127
+ var logPath = CrashLogPath();
128
+ if (logPath != null)
129
+ File.WriteAllText(logPath, errorReport, Encoding.UTF8);
130
+
131
+ // Truncate for the dialog (MessageDialog has practical limits).
132
+ const int MaxLen = 800;
133
+ var summary = errorReport.Length > MaxLen
134
+ ? errorReport.Substring(0, MaxLen) + "\n\n[truncated — see log for full details]"
135
+ : errorReport;
136
+
137
+ if (logPath != null)
138
+ summary += "\n\nFull log: " + logPath;
139
+
140
+ var dialog = new MessageDialog(summary, "NativeScript Runtime Error");
141
+
142
+ dialog.Commands.Add(new UICommand("Copy Details", _ =>
143
+ {
144
+ try
145
+ {
146
+ var dp = new DataPackage();
147
+ dp.SetText(errorReport);
148
+ Clipboard.SetContent(dp);
149
+ }
150
+ catch { }
151
+ }));
152
+
153
+ dialog.Commands.Add(new UICommand("Restart App", async _ =>
154
+ {
155
+ try { await CoreApplication.RequestRestartAsync("crash-restart"); }
156
+ catch { }
157
+ }));
158
+
159
+ dialog.Commands.Add(new UICommand("Dismiss"));
160
+ dialog.DefaultCommandIndex = 0;
161
+ dialog.CancelCommandIndex = 2;
162
+
163
+ await dialog.ShowAsync();
164
+ }
165
+ catch (Exception ex)
166
+ {
167
+ System.Diagnostics.Debug.WriteLine("[NativeScript] Failed to show crash dialog: " + ex.Message);
168
+ }
169
+ }
170
+
171
+ /// <summary>
172
+ /// Appends <paramref name="message"/> to ns_trace.log using the Win32 temp path.
173
+ /// Inside UWP, GetTempPath() virtualises to AC\Temp — the same folder Rust's
174
+ /// std::env::temp_dir() uses — so this write appears in the CLI's log stream.
175
+ /// </summary>
176
+ public static void WriteToTraceLog(string message)
177
+ {
178
+ try
179
+ {
180
+ // System.IO.Path.GetTempPath() calls Win32 GetTempPathW() which inside a
181
+ // UWP process returns the container's AC\Temp folder, matching Rust.
182
+ var tracePath = Path.Combine(System.IO.Path.GetTempPath(), "ns_trace.log");
183
+ File.AppendAllText(tracePath, message, Encoding.UTF8);
184
+ }
185
+ catch { }
186
+ }
187
+
56
188
  private static void AppendToLog(string content)
57
189
  {
58
190
  try
@@ -2,17 +2,14 @@
2
2
 
3
3
  <Package
4
4
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
5
- xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
6
5
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
7
- IgnorableNamespaces="uap mp">
6
+ IgnorableNamespaces="uap">
8
7
 
9
8
  <Identity
10
9
  Name="__APP_IDENTIFIER__"
11
10
  Publisher="CN=__PROJECT_NAME__"
12
11
  Version="1.0.0.0" />
13
12
 
14
- <mp:PhoneIdentity PhoneProductId="__APP_IDENTIFIER__" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
15
-
16
13
  <Properties>
17
14
  <DisplayName>__PROJECT_NAME__</DisplayName>
18
15
  <PublisherDisplayName>__PROJECT_NAME__</PublisherDisplayName>
@@ -24,7 +21,7 @@
24
21
  </Dependencies>
25
22
 
26
23
  <Resources>
27
- <Resource Language="x-generate"/>
24
+ <Resource Language="en-US"/>
28
25
  </Resources>
29
26
 
30
27
  <Applications>
@@ -1,5 +1,7 @@
1
1
  using System;
2
+ using System.Collections.Generic;
2
3
  using System.IO;
4
+ using System.Linq;
3
5
  using System.Runtime.InteropServices;
4
6
  using System.Text.Json;
5
7
 
@@ -26,6 +28,18 @@ namespace __PROJECT_NAME__
26
28
  [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_install_ctrlc_handler))]
27
29
  private static extern void runtime_install_ctrlc_handler(int exitCode);
28
30
 
31
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_set_local_folder))]
32
+ private static extern void runtime_set_local_folder([MarshalAs(UnmanagedType.LPUTF8Str)] string localFolder);
33
+
34
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_get_last_js_error))]
35
+ private static extern IntPtr runtime_get_last_js_error();
36
+
37
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_free_js_error))]
38
+ private static extern void runtime_free_js_error(IntPtr ptr);
39
+
40
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_has_devtools))]
41
+ private static extern bool runtime_has_devtools();
42
+
29
43
  #if DEBUG
30
44
  [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_devtools_start))]
31
45
  private static extern IntPtr runtime_devtools_start(long runtime, ushort port);
@@ -38,9 +52,11 @@ namespace __PROJECT_NAME__
38
52
 
39
53
  public string DevtoolsFrontendUrl { get; private set; }
40
54
 
55
+ private bool _devtoolsAvailable;
56
+
41
57
  public void PumpDevtools()
42
58
  {
43
- if (!_initialized) return;
59
+ if (!_initialized || !_devtoolsAvailable) return;
44
60
  try { runtime_devtools_pump(_runtime); }
45
61
  catch (Exception ex)
46
62
  {
@@ -50,6 +66,7 @@ namespace __PROJECT_NAME__
50
66
 
51
67
  private void StartDevtoolsSafely()
52
68
  {
69
+ if (!runtime_has_devtools()) return;
53
70
  IntPtr urlPtr = IntPtr.Zero;
54
71
  try
55
72
  {
@@ -60,7 +77,10 @@ namespace __PROJECT_NAME__
60
77
  ? $"devtools://devtools/bundled/inspector.html?ws={wsUrl.Replace("ws://", "")}"
61
78
  : null;
62
79
  if (DevtoolsFrontendUrl != null)
80
+ {
81
+ _devtoolsAvailable = true;
63
82
  System.Diagnostics.Debug.WriteLine($"[NativeScript DevTools] {DevtoolsFrontendUrl}");
83
+ }
64
84
  }
65
85
  catch (Exception ex)
66
86
  {
@@ -82,6 +102,7 @@ namespace __PROJECT_NAME__
82
102
  if (_initialized) return;
83
103
  AttachConsole(ATTACH_PARENT_PROCESS);
84
104
  runtime_install_ctrlc_handler(0);
105
+ runtime_set_local_folder(Windows.Storage.ApplicationData.Current.LocalFolder.Path);
85
106
  _runtime = runtime_init(AppContext.BaseDirectory);
86
107
  #if DEBUG
87
108
  if (ConsumeDebugBreakMarker())
@@ -117,16 +138,38 @@ namespace __PROJECT_NAME__
117
138
  throw new InvalidOperationException("Runtime must be initialized before running scripts.");
118
139
 
119
140
  var entryPath = ResolveEntryScriptPath();
120
- var script = File.ReadAllText(Path.GetFullPath(entryPath));
121
- try
141
+ if (entryPath == null)
122
142
  {
123
- runtime_runscript(_runtime, script, Path.GetFileName(entryPath));
143
+ System.Diagnostics.Debug.WriteLine("[NativeScript Runtime] No entry script found — bundle missing from app directory.");
144
+ return;
124
145
  }
125
- catch (Exception ex)
146
+
147
+ var dir = Path.GetDirectoryName(Path.GetFullPath(entryPath));
148
+ var chunks = new List<string>();
149
+ foreach (var chunkName in new[] { "runtime.js", "vendor.js" })
126
150
  {
127
- CrashDiagnostics.WriteExceptionReport("RuntimeHost.RunMainScript", ex, "EntryScript=" + entryPath);
128
- System.Diagnostics.Debug.WriteLine($"[NativeScript Runtime] Script execution failed ({entryPath}): {ex}");
129
- throw;
151
+ var chunkPath = Path.Combine(dir, chunkName);
152
+ if (File.Exists(chunkPath) &&
153
+ !string.Equals(chunkPath, Path.GetFullPath(entryPath), StringComparison.OrdinalIgnoreCase))
154
+ {
155
+ chunks.Add(chunkPath);
156
+ }
157
+ }
158
+ chunks.Add(entryPath);
159
+
160
+ foreach (var scriptPath in chunks)
161
+ {
162
+ var script = File.ReadAllText(Path.GetFullPath(scriptPath));
163
+ try
164
+ {
165
+ runtime_runscript(_runtime, script, Path.GetFileName(scriptPath));
166
+ }
167
+ catch (Exception ex)
168
+ {
169
+ CrashDiagnostics.WriteExceptionReport("RuntimeHost.RunMainScript", ex, "Script=" + scriptPath);
170
+ System.Diagnostics.Debug.WriteLine($"[NativeScript Runtime] Script execution failed ({scriptPath}): {ex}");
171
+ throw;
172
+ }
130
173
  }
131
174
  }
132
175
 
@@ -139,28 +182,65 @@ namespace __PROJECT_NAME__
139
182
  private static string ResolveEntryScriptPath()
140
183
  {
141
184
  var baseDir = AppContext.BaseDirectory;
142
- var defaultPath = Path.Combine(baseDir, "App", "main.js");
143
- var packageJsonPath = Path.Combine(baseDir, "package.json");
185
+ // EXE lives in <project>/bin/; webpack bundle lives in <project>/app/.
186
+ var parentDir = Path.GetDirectoryName(
187
+ baseDir.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar))
188
+ ?? baseDir;
189
+
190
+ // Candidate app directories: sibling of bin/ first, then directly under baseDir.
191
+ var appDirCandidates = new[]
192
+ {
193
+ Path.Combine(parentDir, "app"),
194
+ Path.Combine(parentDir, "App"),
195
+ Path.Combine(baseDir, "app"),
196
+ Path.Combine(baseDir, "App"),
197
+ };
144
198
 
145
- if (!File.Exists(packageJsonPath)) return defaultPath;
199
+ string packageJsonPath = null;
200
+ string resolvedBaseDir = null;
201
+ foreach (var dir in appDirCandidates)
202
+ {
203
+ var candidate = Path.Combine(dir, "package.json");
204
+ if (File.Exists(candidate))
205
+ {
206
+ packageJsonPath = candidate;
207
+ resolvedBaseDir = dir;
208
+ break;
209
+ }
210
+ }
211
+
212
+ // Also accept package.json at the project root (parent of bin/).
213
+ if (packageJsonPath == null && File.Exists(Path.Combine(parentDir, "package.json")))
214
+ {
215
+ packageJsonPath = Path.Combine(parentDir, "package.json");
216
+ resolvedBaseDir = parentDir;
217
+ }
218
+
219
+ string Fallback() =>
220
+ appDirCandidates
221
+ .SelectMany(d => new[] { Path.Combine(d, "bundle.js"), Path.Combine(d, "bundle.mjs") })
222
+ .FirstOrDefault(File.Exists);
223
+
224
+ if (packageJsonPath == null)
225
+ return Fallback();
146
226
 
147
227
  try
148
228
  {
149
229
  var config = ParsePackageConfig(packageJsonPath);
150
230
  if (!string.IsNullOrWhiteSpace(config.WindowsMain))
151
231
  {
152
- var p = ResolveScriptPath(baseDir, config.WindowsMain);
232
+ var p = ResolveScriptPath(resolvedBaseDir, config.WindowsMain);
153
233
  if (p != null) return p;
154
234
  }
155
235
  if (!string.IsNullOrWhiteSpace(config.Main))
156
236
  {
157
- var p = ResolveScriptPath(baseDir, config.Main);
237
+ var p = ResolveScriptPath(resolvedBaseDir, config.Main);
158
238
  if (p != null) return p;
159
239
  }
160
240
  }
161
241
  catch { }
162
242
 
163
- return defaultPath;
243
+ return Fallback();
164
244
  }
165
245
 
166
246
  private static RuntimePackageConfig ParsePackageConfig(string packageJsonPath)
@@ -179,10 +259,34 @@ namespace __PROJECT_NAME__
179
259
  {
180
260
  if (string.IsNullOrWhiteSpace(scriptPath)) return null;
181
261
  var normalized = scriptPath.Replace('/', Path.DirectorySeparatorChar);
182
- var direct = Path.IsPathRooted(normalized) ? normalized : Path.Combine(baseDir, normalized);
183
- if (File.Exists(direct)) return direct;
184
- var appPath = Path.Combine(baseDir, "App", normalized);
185
- return File.Exists(appPath) ? appPath : null;
262
+ foreach (var candidate in new[] { normalized, normalized + ".js", normalized + ".mjs" })
263
+ {
264
+ var direct = Path.IsPathRooted(candidate) ? candidate : Path.Combine(baseDir, candidate);
265
+ if (File.Exists(direct)) return direct;
266
+ var appLower = Path.Combine(baseDir, "app", candidate);
267
+ if (File.Exists(appLower)) return appLower;
268
+ var appUpper = Path.Combine(baseDir, "App", candidate);
269
+ if (File.Exists(appUpper)) return appUpper;
270
+ }
271
+ return null;
272
+ }
273
+
274
+ /// Returns the last JavaScript error (message + stack trace) captured during
275
+ /// script execution, or null if no error was recorded since the last call.
276
+ public string GetLastJsError()
277
+ {
278
+ if (!_initialized) return null;
279
+ IntPtr ptr = IntPtr.Zero;
280
+ try
281
+ {
282
+ ptr = runtime_get_last_js_error();
283
+ return ptr == IntPtr.Zero ? null : Marshal.PtrToStringUTF8(ptr);
284
+ }
285
+ catch { return null; }
286
+ finally
287
+ {
288
+ if (ptr != IntPtr.Zero) runtime_free_js_error(ptr);
289
+ }
186
290
  }
187
291
 
188
292
  public void Dispose()
@@ -19,12 +19,24 @@
19
19
  <NSWindowsRoot>$(MSBuildProjectDirectory)\..</NSWindowsRoot>
20
20
  </PropertyGroup>
21
21
 
22
+ <PropertyGroup>
23
+ <!-- Derived library platform for selecting prebuilt native libs.
24
+ Prefer $(Platform); fall back to trimming 'win-' from $(RuntimeIdentifier); default to x64. -->
25
+ <NSLibPlatform Condition="'$(Platform)' != ''">$(Platform)</NSLibPlatform>
26
+ <NSLibPlatform Condition="'$(NSLibPlatform)' == '' and '$(RuntimeIdentifier)' != ''">$([System.Text.RegularExpressions.Regex]::Replace('$(RuntimeIdentifier)','^win-',''))</NSLibPlatform>
27
+ <NSLibPlatform Condition="'$(NSLibPlatform)' == ''">x64</NSLibPlatform>
28
+ <!-- Windows SDK .NET runtime pack location (resolved from NuGet cache) -->
29
+ <_WinSdkNetRefPkg>$(NuGetPackageRoot)microsoft.windows.sdk.net.ref</_WinSdkNetRefPkg>
30
+ <_WinSdkNetRefVer Condition="Exists('$(_WinSdkNetRefPkg)\10.0.26100.57')">10.0.26100.57</_WinSdkNetRefVer>
31
+ <_WinSdkNetRefLib Condition="'$(_WinSdkNetRefVer)' != ''">$(_WinSdkNetRefPkg)\$(_WinSdkNetRefVer)\lib\net8.0</_WinSdkNetRefLib>
32
+ </PropertyGroup>
33
+
22
34
  <ItemGroup>
23
35
  <AppxManifest Include="Package.appxmanifest" />
24
36
  <Content Include="Properties\Default.rd.xml" />
25
37
 
26
38
  <!-- App JS/resource files — copied to the output directory on every build -->
27
- <Content Include="App\**\*">
39
+ <Content Include="app\**\*">
28
40
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
29
41
  </Content>
30
42
 
@@ -33,35 +45,49 @@
33
45
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
34
46
  </Content>
35
47
 
36
- <!-- Debug: devtools-enabled runtime DLL -->
37
- <Content Include="$(NSWindowsRoot)\libs\devtools\$(Platform)\nativescript.dll"
38
- Condition="'$(Configuration)' == 'Debug'">
39
- <Link>nativescript.dll</Link>
48
+ <!-- UWP logo/splash assets required for Add-AppxPackage -Register -->
49
+ <Content Include="Assets\**\*" Condition="Exists('Assets')">
50
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
51
+ </Content>
52
+
53
+ <!-- Windows SDK .NET runtime pack assemblies — required when the dotnet workload
54
+ is not installed; copied from the NuGet package cache. -->
55
+ <Content Include="$(_WinSdkNetRefLib)\Microsoft.Windows.UI.Xaml.dll"
56
+ Condition="'$(_WinSdkNetRefLib)' != '' and Exists('$(_WinSdkNetRefLib)\Microsoft.Windows.UI.Xaml.dll')">
57
+ <Link>Microsoft.Windows.UI.Xaml.dll</Link>
58
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
59
+ </Content>
60
+ <Content Include="$(_WinSdkNetRefLib)\WinRT.Runtime.dll"
61
+ Condition="'$(_WinSdkNetRefLib)' != '' and Exists('$(_WinSdkNetRefLib)\WinRT.Runtime.dll')">
62
+ <Link>WinRT.Runtime.dll</Link>
63
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
64
+ </Content>
65
+ <Content Include="$(_WinSdkNetRefLib)\Microsoft.Windows.SDK.NET.dll"
66
+ Condition="'$(_WinSdkNetRefLib)' != '' and Exists('$(_WinSdkNetRefLib)\Microsoft.Windows.SDK.NET.dll')">
67
+ <Link>Microsoft.Windows.SDK.NET.dll</Link>
40
68
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
41
69
  </Content>
42
70
 
43
- <!-- Release: production runtime DLL -->
44
- <Content Include="$(NSWindowsRoot)\libs\$(Platform)\nativescript.dll"
45
- Condition="'$(Configuration)' != 'Debug'">
71
+ <!-- Debug: devtools-enabled runtime DLL (platform fallback via NSLibPlatform) -->
72
+ <Content Include="$(NSWindowsRoot)\libs\devtools\$(NSLibPlatform)\nativescript.dll"
73
+ Condition="'$(Configuration)' == 'Debug' and Exists('$(NSWindowsRoot)\libs\devtools\$(NSLibPlatform)\nativescript.dll')">
46
74
  <Link>nativescript.dll</Link>
47
75
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
48
76
  </Content>
49
77
 
50
- <!-- .NET bridge published on first build by the target below; enables ns.dotnet.*
51
- APIs from JavaScript. Must land at dotnet-bridge\publish\ relative to the
52
- executable so the Rust runtime can locate DotNetBridge.dll via
53
- AppContext.BaseDirectory. Condition guards against a missing source tree. -->
54
- <Content Include="$(NSWindowsRoot)\dotnet-bridge\publish\**\*"
55
- Condition="Exists('$(NSWindowsRoot)\dotnet-bridge\publish')">
56
- <Link>dotnet-bridge\publish\%(RecursiveDir)%(Filename)%(Extension)</Link>
78
+ <!-- Release: production runtime DLL (platform fallback via NSLibPlatform) -->
79
+ <Content Include="$(NSWindowsRoot)\libs\$(NSLibPlatform)\nativescript.dll"
80
+ Condition="'$(Configuration)' != 'Debug' and Exists('$(NSWindowsRoot)\libs\$(NSLibPlatform)\nativescript.dll')">
81
+ <Link>nativescript.dll</Link>
57
82
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
58
83
  </Content>
84
+
59
85
  </ItemGroup>
60
86
 
61
87
  <!--
62
- Publishes DotNetBridge.dll before the app build so the Content glob above has
63
- something to pick up. Inputs/Outputs make this incremental: MSBuild skips the
64
- Exec entirely when DotNetBridge.dll is newer than both source files.
88
+ Publishes DotNetBridge.dll before the app build.
89
+ Inputs/Outputs make this incremental: MSBuild skips the Exec when DotNetBridge.dll
90
+ is already newer than both source files.
65
91
  -->
66
92
  <Target Name="PublishDotNetBridge"
67
93
  BeforeTargets="Build"
@@ -72,4 +98,30 @@
72
98
  <Message Text="[NativeScript] Publishing DotNetBridge..." Importance="high" />
73
99
  <Exec Command="dotnet publish &quot;$(NSWindowsRoot)\dotnet-bridge\DotNetBridge.csproj&quot; -c Release -o &quot;$(NSWindowsRoot)\dotnet-bridge\publish&quot; --no-self-contained /nologo" />
74
100
  </Target>
101
+
102
+ <!--
103
+ Copies the published bridge files to the output directory AFTER PublishDotNetBridge
104
+ runs, avoiding the MSBuild evaluation-order problem where Condition="Exists(...)"
105
+ on a static Content item is checked before the publish target creates the directory.
106
+ Must land at dotnet-bridge\publish\ relative to the EXE so the Rust runtime can
107
+ locate DotNetBridge.dll via AppContext.BaseDirectory.
108
+ -->
109
+ <Target Name="CopyDotNetBridge"
110
+ AfterTargets="PublishDotNetBridge"
111
+ BeforeTargets="Build"
112
+ Condition="Exists('$(NSWindowsRoot)\dotnet-bridge\publish')">
113
+ <ItemGroup>
114
+ <_DotNetBridgeFiles Include="$(NSWindowsRoot)\dotnet-bridge\publish\**\*" />
115
+ </ItemGroup>
116
+ <Copy SourceFiles="@(_DotNetBridgeFiles)"
117
+ DestinationFiles="@(_DotNetBridgeFiles->'$(OutDir)dotnet-bridge\publish\%(RecursiveDir)%(Filename)%(Extension)')"
118
+ SkipUnchangedFiles="true" />
119
+ </Target>
120
+
121
+ <!-- Allow project-level Windows overrides: place App_Resources\Windows\app.csproj to extend/override generated settings -->
122
+ <Import Project="App_Resources\Windows\app.csproj" Condition="Exists('App_Resources\Windows\app.csproj')" />
123
+
124
+ <!-- CLI-generated plugin imports (managed by the CLI during prepare) -->
125
+ <Import Project="plugins\Plugins.props" Condition="Exists('plugins\Plugins.props')" />
126
+ <Import Project="plugins\Plugins.targets" Condition="Exists('plugins\Plugins.targets')" />
75
127
  </Project>