@nativescript/windows 0.1.0-alpha.8 → 0.1.0-alpha.80

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.
Files changed (29) hide show
  1. package/build.ps1 +44 -2
  2. package/framework/__PROJECT_NAME__/App.xaml.cs +67 -22
  3. package/framework/__PROJECT_NAME__/Assets/SplashScreen.png +0 -0
  4. package/framework/__PROJECT_NAME__/Assets/Square150x150Logo.png +0 -0
  5. package/framework/__PROJECT_NAME__/Assets/Square44x44Logo.png +0 -0
  6. package/framework/__PROJECT_NAME__/Assets/StoreLogo.png +0 -0
  7. package/framework/__PROJECT_NAME__/Assets/Wide310x150Logo.png +0 -0
  8. package/framework/__PROJECT_NAME__/CrashDiagnostics.cs +262 -66
  9. package/framework/__PROJECT_NAME__/Directory.Build.props +15 -0
  10. package/framework/__PROJECT_NAME__/Directory.Build.targets +153 -0
  11. package/framework/__PROJECT_NAME__/Package.appxmanifest +2 -5
  12. package/framework/__PROJECT_NAME__/RuntimeHost.cs +97 -10
  13. package/framework/__PROJECT_NAME__/__PROJECT_NAME__.csproj +85 -12
  14. package/framework/dotnet-bridge/BinaryProtocol.cs +145 -0
  15. package/framework/dotnet-bridge/Bridge.BinaryDispatch.cs +216 -0
  16. package/framework/dotnet-bridge/Bridge.Dispatch.cs +468 -0
  17. package/framework/dotnet-bridge/Bridge.JsDelegate.cs +96 -0
  18. package/framework/dotnet-bridge/Bridge.TaskHelper.cs +207 -0
  19. package/framework/dotnet-bridge/Bridge.cs +402 -263
  20. package/framework/dotnet-bridge/DispatchTypes.cs +317 -0
  21. package/framework/dotnet-bridge/DotNetBridge.csproj +7 -1
  22. package/framework/libs/arm64/nativescript.dll +0 -0
  23. package/framework/libs/devtools/arm64/nativescript.dll +0 -0
  24. package/framework/libs/devtools/x64/nativescript.dll +0 -0
  25. package/framework/libs/x64/nativescript.dll +0 -0
  26. package/framework/tools/dotnet-tool-arm64.exe +0 -0
  27. package/framework/tools/dotnet-tool-x64.exe +0 -0
  28. package/framework/tools/dotnet-tool.exe +0 -0
  29. package/package.json +19 -19
@@ -0,0 +1,153 @@
1
+ <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2
+ <!-- Directory.Build.targets for the Windows project template.
3
+ This file applies manifest overrides at build time using XmlPoke.
4
+ Place in the template root so generated projects inherit it. -->
5
+ <!-- Default properties are provided in Directory.Build.props so they can be
6
+ easily overridden per-app or via MSBuild /p: properties. -->
7
+
8
+ <!-- Inline merge task: will merge App_Resources/Windows/Package.appxmanifest or Package.appxmanifest.partial.xml into the generated Package.appxmanifest -->
9
+ <UsingTask TaskName="MergeAppxManifest" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll" Condition="'$(MSBuildRuntimeType)' != 'Core'">
10
+ <ParameterGroup>
11
+ <SourceManifest ParameterType="System.String" Required="true" />
12
+ <TargetManifest ParameterType="System.String" Required="true" />
13
+ </ParameterGroup>
14
+ <Task>
15
+ <Reference Include="System.Core" />
16
+ <Reference Include="System.Xml" />
17
+ <Reference Include="System.Xml.Linq" />
18
+ <Using Namespace="System" />
19
+ <Using Namespace="System.IO" />
20
+ <Using Namespace="System.Linq" />
21
+ <Using Namespace="System.Xml.Linq" />
22
+ <Code Type="Fragment" Language="cs"><![CDATA[
23
+ try {
24
+ if (!File.Exists(SourceManifest)) { Log.LogMessage(MessageImportance.Low, "No source manifest found: " + SourceManifest); return true; }
25
+ if (!File.Exists(TargetManifest)) { Log.LogMessage(MessageImportance.Low, "No target manifest found: " + TargetManifest); return true; }
26
+ XDocument src = XDocument.Load(SourceManifest);
27
+ XDocument dest = XDocument.Load(TargetManifest);
28
+
29
+ Func<XElement, XElement, bool> mergeElement = null;
30
+ mergeElement = (srcElem, destParent) => {
31
+ XElement match = destParent.Elements().FirstOrDefault(c => c.Name.LocalName == srcElem.Name.LocalName);
32
+ if (match == null) {
33
+ destParent.Add(new XElement(srcElem));
34
+ return true;
35
+ }
36
+ // copy/overwrite attributes
37
+ foreach (var a in srcElem.Attributes()) { match.SetAttributeValue(a.Name, a.Value); }
38
+ // merge children
39
+ foreach (var child in srcElem.Elements()) {
40
+ // skip if identical child exists
41
+ bool exists = match.Elements().Any(mc => mc.Name.LocalName == child.Name.LocalName && mc.ToString() == child.ToString());
42
+ if (exists) { continue; }
43
+ // try to find same-named child to merge into
44
+ XElement matchChild = match.Elements().FirstOrDefault(mc => mc.Name.LocalName == child.Name.LocalName);
45
+ if (matchChild != null) { mergeElement(child, matchChild); } else { match.Add(new XElement(child)); }
46
+ }
47
+ return true;
48
+ };
49
+
50
+ // Merge VisualElements (common case) into the Application node
51
+ var srcVisuals = src.Descendants().Where(e => e.Name.LocalName == "VisualElements");
52
+ foreach (var ve in srcVisuals) {
53
+ var destApp = dest.Descendants().FirstOrDefault(e => e.Name.LocalName == "Application");
54
+ if (destApp == null) { dest.Root.Add(new XElement(ve)); }
55
+ else { mergeElement(ve, destApp); }
56
+ }
57
+
58
+ // Merge Capabilities (if present)
59
+ var srcCaps = src.Descendants().Where(e => e.Name.LocalName == "Capabilities");
60
+ foreach (var cap in srcCaps) {
61
+ var destCaps = dest.Descendants().FirstOrDefault(e => e.Name.LocalName == "Capabilities");
62
+ if (destCaps == null) { dest.Root.Add(new XElement(cap)); }
63
+ else { mergeElement(cap, destCaps); }
64
+ }
65
+
66
+ // Merge other top-level sections present in src (e.g., Extensions) by local-name
67
+ foreach (var top in src.Root.Elements()) {
68
+ if (top.Name.LocalName == "Dependencies" || top.Name.LocalName == "Applications") { continue; }
69
+ var existing = dest.Root.Elements().FirstOrDefault(e => e.Name.LocalName == top.Name.LocalName);
70
+ if (existing == null) { dest.Root.Add(new XElement(top)); }
71
+ else {
72
+ // merge children of this section
73
+ foreach (var child in top.Elements()) {
74
+ bool exists = existing.Elements().Any(ec => ec.Name.LocalName == child.Name.LocalName && ec.ToString() == child.ToString());
75
+ if (!exists) { existing.Add(new XElement(child)); }
76
+ }
77
+ }
78
+ }
79
+
80
+ dest.Save(TargetManifest);
81
+ Log.LogMessage(MessageImportance.High, $"Merged manifest from {SourceManifest} into {TargetManifest}");
82
+ return true;
83
+ } catch (Exception ex) { Log.LogErrorFromException(ex); return false; }
84
+ ]]></Code>
85
+ </Task>
86
+ </UsingTask>
87
+
88
+ <Target Name="MergeAppResourcesManifest" AfterTargets="ApplyManifestOverrides" Condition="Exists('$(MSBuildProjectDirectory)\Package.appxmanifest') and '$(MSBuildRuntimeType)' != 'Core'">
89
+ <PropertyGroup>
90
+ <_AppResourcesManifest>$(MSBuildProjectDirectory)\App_Resources\Windows\Package.appxmanifest</_AppResourcesManifest>
91
+ <!-- Allow CLI or callers to override the source manifest via MSBuild property 'AppResourcesManifestPath' -->
92
+ <_AppResourcesManifest Condition="'$(AppResourcesManifestPath)' != ''">$(AppResourcesManifestPath)</_AppResourcesManifest>
93
+ <!-- Fallback template manifest name (no '.partial' suffix) -->
94
+ <_PartialManifest>$(MSBuildProjectDirectory)\Package.appxmanifest</_PartialManifest>
95
+ </PropertyGroup>
96
+ <Message Text="Merging app manifest (if present) into generated Package.appxmanifest (app wins)" Importance="Low" />
97
+ <MergeAppxManifest SourceManifest="$([System.IO.File]::Exists('$(_AppResourcesManifest)') ? '$(_AppResourcesManifest)' : '$(_PartialManifest)')" TargetManifest="$(MSBuildProjectDirectory)\Package.appxmanifest" Condition=" $([System.IO.File]::Exists('$(_AppResourcesManifest)')) Or $([System.IO.File]::Exists('$(_PartialManifest)')) " />
98
+ </Target>
99
+
100
+ <Target Name="ApplyManifestOverrides" BeforeTargets="BeforeBuild" Condition="Exists('$(MSBuildProjectDirectory)\Package.appxmanifest')">
101
+ <Message Text="Applying manifest overrides (VisualElements, logos, splash) via XmlPoke..." Importance="High" />
102
+
103
+ <!-- DisplayName -->
104
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
105
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@DisplayName"
106
+ Value="$(AppDisplayName)"
107
+ Condition="'$(AppDisplayName)' != ''" />
108
+
109
+ <!-- Description -->
110
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
111
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@Description"
112
+ Value="$(AppDescription)"
113
+ Condition="'$(AppDescription)' != ''" />
114
+
115
+ <!-- Logos -->
116
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
117
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@Square150x150Logo"
118
+ Value="$(Square150x150Logo)"
119
+ Condition="'$(Square150x150Logo)' != ''" />
120
+
121
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
122
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@Square44x44Logo"
123
+ Value="$(Square44x44Logo)"
124
+ Condition="'$(Square44x44Logo)' != ''" />
125
+
126
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
127
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/*[local-name()='DefaultTile']/@Wide310x150Logo"
128
+ Value="$(Wide310x150Logo)"
129
+ Condition="'$(Wide310x150Logo)' != ''" />
130
+
131
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
132
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@StoreLogo"
133
+ Value="$(StoreLogo)"
134
+ Condition="'$(StoreLogo)' != ''" />
135
+
136
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
137
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@LockScreenLogo"
138
+ Value="$(LockScreenLogo)"
139
+ Condition="'$(LockScreenLogo)' != ''" />
140
+
141
+ <!-- BackgroundColor and SplashScreen image -->
142
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
143
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/@BackgroundColor"
144
+ Value="$(SplashBackgroundColor)"
145
+ Condition="'$(SplashBackgroundColor)' != ''" />
146
+
147
+ <XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\Package.appxmanifest"
148
+ Query="/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='VisualElements']/*[local-name()='SplashScreen']/@Image"
149
+ Value="$(SplashImage)"
150
+ Condition="'$(SplashImage)' != ''" />
151
+
152
+ </Target>
153
+ </Project>
@@ -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,41 @@ 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
+
43
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_pump_timers))]
44
+ private static extern void runtime_pump_timers();
45
+
46
+ #if DEBUG
47
+ [DllImport(NativeScriptLibrary, EntryPoint = nameof(ns_set_log_to_console))]
48
+ private static extern int ns_set_log_to_console(int enabled);
49
+
50
+ public static bool TrySetLogToConsole(bool enabled)
51
+ {
52
+ try { return ns_set_log_to_console(enabled ? 1 : 0) == 1; }
53
+ catch { return false; }
54
+ }
55
+ #else
56
+ public static bool TrySetLogToConsole(bool enabled) => false;
57
+ #endif
58
+
59
+ public void PumpTimers()
60
+ {
61
+ if (!_initialized) return;
62
+ try { runtime_pump_timers(); }
63
+ catch { }
64
+ }
65
+
29
66
  #if DEBUG
30
67
  [DllImport(NativeScriptLibrary, EntryPoint = nameof(runtime_devtools_start))]
31
68
  private static extern IntPtr runtime_devtools_start(long runtime, ushort port);
@@ -38,9 +75,11 @@ namespace __PROJECT_NAME__
38
75
 
39
76
  public string DevtoolsFrontendUrl { get; private set; }
40
77
 
78
+ private bool _devtoolsAvailable;
79
+
41
80
  public void PumpDevtools()
42
81
  {
43
- if (!_initialized) return;
82
+ if (!_initialized || !_devtoolsAvailable) return;
44
83
  try { runtime_devtools_pump(_runtime); }
45
84
  catch (Exception ex)
46
85
  {
@@ -50,6 +89,7 @@ namespace __PROJECT_NAME__
50
89
 
51
90
  private void StartDevtoolsSafely()
52
91
  {
92
+ if (!runtime_has_devtools()) return;
53
93
  IntPtr urlPtr = IntPtr.Zero;
54
94
  try
55
95
  {
@@ -60,7 +100,10 @@ namespace __PROJECT_NAME__
60
100
  ? $"devtools://devtools/bundled/inspector.html?ws={wsUrl.Replace("ws://", "")}"
61
101
  : null;
62
102
  if (DevtoolsFrontendUrl != null)
103
+ {
104
+ _devtoolsAvailable = true;
63
105
  System.Diagnostics.Debug.WriteLine($"[NativeScript DevTools] {DevtoolsFrontendUrl}");
106
+ }
64
107
  }
65
108
  catch (Exception ex)
66
109
  {
@@ -82,8 +125,10 @@ namespace __PROJECT_NAME__
82
125
  if (_initialized) return;
83
126
  AttachConsole(ATTACH_PARENT_PROCESS);
84
127
  runtime_install_ctrlc_handler(0);
128
+ runtime_set_local_folder(Windows.Storage.ApplicationData.Current.LocalFolder.Path);
85
129
  _runtime = runtime_init(AppContext.BaseDirectory);
86
130
  #if DEBUG
131
+ TrySetLogToConsole(true);
87
132
  if (ConsumeDebugBreakMarker())
88
133
  StartDevtoolsSafely();
89
134
  #endif
@@ -117,16 +162,38 @@ namespace __PROJECT_NAME__
117
162
  throw new InvalidOperationException("Runtime must be initialized before running scripts.");
118
163
 
119
164
  var entryPath = ResolveEntryScriptPath();
120
- var script = File.ReadAllText(Path.GetFullPath(entryPath));
121
- try
165
+ if (entryPath == null)
122
166
  {
123
- runtime_runscript(_runtime, script, Path.GetFileName(entryPath));
167
+ System.Diagnostics.Debug.WriteLine("[NativeScript Runtime] No entry script found — bundle missing from app directory.");
168
+ return;
124
169
  }
125
- catch (Exception ex)
170
+
171
+ var dir = Path.GetDirectoryName(Path.GetFullPath(entryPath));
172
+ var chunks = new List<string>();
173
+ foreach (var chunkName in new[] { "runtime.js", "vendor.js" })
174
+ {
175
+ var chunkPath = Path.Combine(dir, chunkName);
176
+ if (File.Exists(chunkPath) &&
177
+ !string.Equals(chunkPath, Path.GetFullPath(entryPath), StringComparison.OrdinalIgnoreCase))
178
+ {
179
+ chunks.Add(chunkPath);
180
+ }
181
+ }
182
+ chunks.Add(entryPath);
183
+
184
+ foreach (var scriptPath in chunks)
126
185
  {
127
- CrashDiagnostics.WriteExceptionReport("RuntimeHost.RunMainScript", ex, "EntryScript=" + entryPath);
128
- System.Diagnostics.Debug.WriteLine($"[NativeScript Runtime] Script execution failed ({entryPath}): {ex}");
129
- throw;
186
+ var script = File.ReadAllText(Path.GetFullPath(scriptPath));
187
+ try
188
+ {
189
+ runtime_runscript(_runtime, script, Path.GetFileName(scriptPath));
190
+ }
191
+ catch (Exception ex)
192
+ {
193
+ CrashDiagnostics.WriteExceptionReport("RuntimeHost.RunMainScript", ex, "Script=" + scriptPath);
194
+ System.Diagnostics.Debug.WriteLine($"[NativeScript Runtime] Script execution failed ({scriptPath}): {ex}");
195
+ throw;
196
+ }
130
197
  }
131
198
  }
132
199
 
@@ -174,7 +241,9 @@ namespace __PROJECT_NAME__
174
241
  }
175
242
 
176
243
  string Fallback() =>
177
- appDirCandidates.Select(d => Path.Combine(d, "bundle.js")).FirstOrDefault(File.Exists);
244
+ appDirCandidates
245
+ .SelectMany(d => new[] { Path.Combine(d, "bundle.js"), Path.Combine(d, "bundle.mjs") })
246
+ .FirstOrDefault(File.Exists);
178
247
 
179
248
  if (packageJsonPath == null)
180
249
  return Fallback();
@@ -214,7 +283,7 @@ namespace __PROJECT_NAME__
214
283
  {
215
284
  if (string.IsNullOrWhiteSpace(scriptPath)) return null;
216
285
  var normalized = scriptPath.Replace('/', Path.DirectorySeparatorChar);
217
- foreach (var candidate in new[] { normalized, normalized + ".js" })
286
+ foreach (var candidate in new[] { normalized, normalized + ".js", normalized + ".mjs" })
218
287
  {
219
288
  var direct = Path.IsPathRooted(candidate) ? candidate : Path.Combine(baseDir, candidate);
220
289
  if (File.Exists(direct)) return direct;
@@ -226,6 +295,24 @@ namespace __PROJECT_NAME__
226
295
  return null;
227
296
  }
228
297
 
298
+ /// Returns the last JavaScript error (message + stack trace) captured during
299
+ /// script execution, or null if no error was recorded since the last call.
300
+ public string GetLastJsError()
301
+ {
302
+ if (!_initialized) return null;
303
+ IntPtr ptr = IntPtr.Zero;
304
+ try
305
+ {
306
+ ptr = runtime_get_last_js_error();
307
+ return ptr == IntPtr.Zero ? null : Marshal.PtrToStringUTF8(ptr);
308
+ }
309
+ catch { return null; }
310
+ finally
311
+ {
312
+ if (ptr != IntPtr.Zero) runtime_free_js_error(ptr);
313
+ }
314
+ }
315
+
229
316
  public void Dispose()
230
317
  {
231
318
  if (!_initialized) return;
@@ -25,6 +25,10 @@
25
25
  <NSLibPlatform Condition="'$(Platform)' != ''">$(Platform)</NSLibPlatform>
26
26
  <NSLibPlatform Condition="'$(NSLibPlatform)' == '' and '$(RuntimeIdentifier)' != ''">$([System.Text.RegularExpressions.Regex]::Replace('$(RuntimeIdentifier)','^win-',''))</NSLibPlatform>
27
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>
28
32
  </PropertyGroup>
29
33
 
30
34
  <ItemGroup>
@@ -41,6 +45,29 @@
41
45
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
42
46
  </Content>
43
47
 
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>
68
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
69
+ </Content>
70
+
44
71
  <!-- Debug: devtools-enabled runtime DLL (platform fallback via NSLibPlatform) -->
45
72
  <Content Include="$(NSWindowsRoot)\libs\devtools\$(NSLibPlatform)\nativescript.dll"
46
73
  Condition="'$(Configuration)' == 'Debug' and Exists('$(NSWindowsRoot)\libs\devtools\$(NSLibPlatform)\nativescript.dll')">
@@ -55,21 +82,12 @@
55
82
  <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
56
83
  </Content>
57
84
 
58
- <!-- .NET bridge — published on first build by the target below; enables ns.dotnet.*
59
- APIs from JavaScript. Must land at dotnet-bridge\publish\ relative to the
60
- executable so the Rust runtime can locate DotNetBridge.dll via
61
- AppContext.BaseDirectory. Condition guards against a missing source tree. -->
62
- <Content Include="$(NSWindowsRoot)\dotnet-bridge\publish\**\*"
63
- Condition="Exists('$(NSWindowsRoot)\dotnet-bridge\publish')">
64
- <Link>dotnet-bridge\publish\%(RecursiveDir)%(Filename)%(Extension)</Link>
65
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
66
- </Content>
67
85
  </ItemGroup>
68
86
 
69
87
  <!--
70
- Publishes DotNetBridge.dll before the app build so the Content glob above has
71
- something to pick up. Inputs/Outputs make this incremental: MSBuild skips the
72
- 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.
73
91
  -->
74
92
  <Target Name="PublishDotNetBridge"
75
93
  BeforeTargets="Build"
@@ -81,6 +99,61 @@
81
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" />
82
100
  </Target>
83
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\%(RecursiveDir)%(Filename)%(Extension)')"
118
+ SkipUnchangedFiles="true" />
119
+ </Target>
120
+
121
+ <!-- Ensure bridge files are included in the final `dotnet publish` output. -->
122
+ <Target Name="CopyDotNetBridgeToPublishDir"
123
+ AfterTargets="Publish"
124
+ Condition="Exists('$(NSWindowsRoot)\dotnet-bridge\publish')">
125
+ <ItemGroup>
126
+ <_DotNetBridgePublishFiles Include="$(NSWindowsRoot)\dotnet-bridge\publish\**\*" />
127
+ </ItemGroup>
128
+ <Copy SourceFiles="@(_DotNetBridgePublishFiles)"
129
+ DestinationFiles="@(_DotNetBridgePublishFiles->'$(PublishDir)dotnet-bridge\%(RecursiveDir)%(Filename)%(Extension)')"
130
+ SkipUnchangedFiles="true" />
131
+ </Target>
132
+
133
+ <!-- Inject the bridge publish files as Content before Publish so `dotnet publish` includes them automatically -->
134
+ <Target Name="AddDotNetBridgeContent" BeforeTargets="Publish" Condition="Exists('$(NSWindowsRoot)\dotnet-bridge\publish')">
135
+ <ItemGroup>
136
+ <Content Include="$(NSWindowsRoot)\dotnet-bridge\publish\**\*">
137
+ <Link>dotnet-bridge\%(RecursiveDir)%(Filename)%(Extension)</Link>
138
+ <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
139
+ </Content>
140
+ </ItemGroup>
141
+ </Target>
142
+
143
+ <!--
144
+ Wait for the `dotnet-tool` sentinel marker so Build/Publish don't race with
145
+ the external tool that publishes/copies DotNetBridge into the repo.
146
+ This polls for `dotnet-bridge/publish/.dotnet_tool_done` with a short timeout.
147
+ -->
148
+ <Target Name="WaitForDotNetToolDone" BeforeTargets="Build;Publish" Condition="Exists('$(NSWindowsRoot)\dotnet-bridge')">
149
+ <PropertyGroup>
150
+ <DotNetToolMarker>$(NSWindowsRoot)\dotnet-bridge\publish\.dotnet_tool_done</DotNetToolMarker>
151
+ <DotNetWaitTimeout>30</DotNetWaitTimeout>
152
+ </PropertyGroup>
153
+ <Message Text="[NativeScript] Waiting for dotnet-tool marker at $(DotNetToolMarker) (timeout $(DotNetWaitTimeout)s)..." Importance="high" />
154
+ <Exec Command="powershell -NoProfile -Command &quot;$t=0; while(-not(Test-Path '$(DotNetToolMarker)') -and $t -lt $(DotNetWaitTimeout)) { Start-Sleep -Seconds 1; $t++; }; if(-not(Test-Path '$(DotNetToolMarker)')) { Write-Error 'dotnet-tool marker not found after timeout'; exit 1 };&quot;" />
155
+ </Target>
156
+
84
157
  <!-- Allow project-level Windows overrides: place App_Resources\Windows\app.csproj to extend/override generated settings -->
85
158
  <Import Project="App_Resources\Windows\app.csproj" Condition="Exists('App_Resources\Windows\app.csproj')" />
86
159
 
@@ -0,0 +1,145 @@
1
+ using System;
2
+ using System.Buffers;
3
+ using System.Buffers.Binary;
4
+ using System.Text;
5
+
6
+ namespace NativeScriptBridge;
7
+
8
+ internal readonly struct HandleRef(int id)
9
+ {
10
+ public readonly int Id = id;
11
+ }
12
+
13
+ // Carries a raw IUnknown/IInspectable pointer from a WinRT proxy (tag 0x0A).
14
+ // CoerceBin calls Marshal.GetObjectForIUnknown to create a managed RCW.
15
+ internal readonly struct WinRtRef(long ptr)
16
+ {
17
+ public readonly long Ptr = ptr;
18
+ }
19
+
20
+ internal ref struct BinReader(ReadOnlySpan<byte> buf)
21
+ {
22
+ private readonly ReadOnlySpan<byte> _buf = buf;
23
+ private int _pos = 0;
24
+
25
+ public byte ReadByte() => _buf[_pos++];
26
+
27
+ public int ReadI32()
28
+ {
29
+ var v = BinaryPrimitives.ReadInt32LittleEndian(_buf[_pos..]);
30
+ _pos += 4;
31
+ return v;
32
+ }
33
+
34
+ public long ReadI64()
35
+ {
36
+ var v = BinaryPrimitives.ReadInt64LittleEndian(_buf[_pos..]);
37
+ _pos += 8;
38
+ return v;
39
+ }
40
+
41
+ public ushort ReadU16()
42
+ {
43
+ var v = BinaryPrimitives.ReadUInt16LittleEndian(_buf[_pos..]);
44
+ _pos += 2;
45
+ return v;
46
+ }
47
+
48
+ public double ReadF64()
49
+ {
50
+ var v = BinaryPrimitives.ReadDoubleLittleEndian(_buf[_pos..]);
51
+ _pos += 8;
52
+ return v;
53
+ }
54
+
55
+ public string ReadString16()
56
+ {
57
+ var len = ReadU16();
58
+ var s = Encoding.UTF8.GetString(_buf.Slice(_pos, len));
59
+ _pos += len;
60
+ // Intern so repeated method/type names reuse the same heap string.
61
+ // Eliminates the allocation on every subsequent warm-path call.
62
+ return string.Intern(s);
63
+ }
64
+
65
+ public object?[] ReadArgs()
66
+ {
67
+ var count = ReadByte();
68
+ if (count == 0) return [];
69
+ var args = new object?[count];
70
+ for (int i = 0; i < count; i++)
71
+ {
72
+ var tag = ReadByte();
73
+ args[i] = tag switch
74
+ {
75
+ 0x00 => null,
76
+ 0x01 => (object)false,
77
+ 0x02 => (object)true,
78
+ 0x03 => (object)ReadI32(),
79
+ 0x04 => (object)ReadF64(),
80
+ 0x05 => (object)ReadString16(),
81
+ 0x06 => (object)new HandleRef(ReadI32()),
82
+ 0x0A => (object)new WinRtRef(ReadI64()),
83
+ _ => null,
84
+ };
85
+ }
86
+ return args;
87
+ }
88
+ }
89
+
90
+ internal ref struct BinWriter(ArrayBufferWriter<byte> buf)
91
+ {
92
+ private readonly ArrayBufferWriter<byte> _buf = buf;
93
+
94
+ public void WriteByte(byte b)
95
+ {
96
+ _buf.GetSpan(1)[0] = b;
97
+ _buf.Advance(1);
98
+ }
99
+
100
+ public void WriteI32(int v)
101
+ {
102
+ BinaryPrimitives.WriteInt32LittleEndian(_buf.GetSpan(4), v);
103
+ _buf.Advance(4);
104
+ }
105
+
106
+ public void WriteU16(ushort v)
107
+ {
108
+ BinaryPrimitives.WriteUInt16LittleEndian(_buf.GetSpan(2), v);
109
+ _buf.Advance(2);
110
+ }
111
+
112
+ public void WriteU32(uint v)
113
+ {
114
+ BinaryPrimitives.WriteUInt32LittleEndian(_buf.GetSpan(4), v);
115
+ _buf.Advance(4);
116
+ }
117
+
118
+ public void WriteI64(long v)
119
+ {
120
+ BinaryPrimitives.WriteInt64LittleEndian(_buf.GetSpan(8), v);
121
+ _buf.Advance(8);
122
+ }
123
+
124
+ public void WriteF64(double v)
125
+ {
126
+ BinaryPrimitives.WriteDoubleLittleEndian(_buf.GetSpan(8), v);
127
+ _buf.Advance(8);
128
+ }
129
+
130
+ public void WriteString32(ReadOnlySpan<char> s)
131
+ {
132
+ var byteCount = Encoding.UTF8.GetByteCount(s);
133
+ WriteU32((uint)byteCount);
134
+ Encoding.UTF8.GetBytes(s, _buf.GetSpan(byteCount));
135
+ _buf.Advance(byteCount);
136
+ }
137
+
138
+ public void WriteString16(ReadOnlySpan<char> s)
139
+ {
140
+ var byteCount = Encoding.UTF8.GetByteCount(s);
141
+ WriteU16((ushort)byteCount);
142
+ Encoding.UTF8.GetBytes(s, _buf.GetSpan(byteCount));
143
+ _buf.Advance(byteCount);
144
+ }
145
+ }