Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/winappsdk/WinAppSdkExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<OutDir>bin</OutDir>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<NodeApiJSModuleType>CommonJs</NodeApiJSModuleType>

<!-- Exclude types from metadata parsing that cause the metadata parser to fail -->
<NodeApiExcludeFromMetadata>ABI.*;WinRT.IInspectable+Vftbl*;WinRT.Interop.IUnknownVftbl*</NodeApiExcludeFromMetadata>

</PropertyGroup>

<ItemGroup>
<!-- We need to add references to these assemblies so we can complete the type closure so that
the generator can find all the types it needs. -->
<UserRuntimeAssembly Include="bin\Microsoft.Windows.SDK.NET.dll" />
<UserRuntimeAssembly Include="bin\WinRT.Runtime.dll" />
</ItemGroup>

<ItemGroup>
<Compile Remove="node_modules\**\*.cs" />
<EmbeddedResource Remove="node_modules\**\*.cs" />
<Compile Include="src\*.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.JavaScript.NodeApi" Version="$(NodeApiDotnetPackageVersion)" PrivateAssets="all" />
<PackageReference Include="Microsoft.JavaScript.NodeApi.Generator" Version="$(NodeApiDotnetPackageVersion)" />

<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6584" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.250916003" />
<PackageReference Include="System.Numerics.Tensors" Version="9.0.10" />
</ItemGroup>
</Project>
20 changes: 20 additions & 0 deletions examples/winappsdk/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@


const dotnet = require('node-api-dotnet');

require('./bin/Microsoft.Windows.SDK.NET.js');
require('./bin/Microsoft.WindowsAppRuntime.Bootstrap.Net.js');
require('./bin/Microsoft.InteractiveExperiences.Projection.js');
require('./bin/Microsoft.Windows.ApplicationModel.WindowsAppRuntime.Projection.js');
require('./bin/Microsoft.Windows.AI.Text.Projection.js');
require('./bin/Microsoft.Windows.AI.ContentSafety.Projection.js');
require('./bin/Microsoft.Windows.AppNotifications.Projection.js');
require('./bin/Microsoft.Windows.AppNotifications.Builder.Projection.js');

const majorVersion = 1;
const minorVersion = 8;

console.log("Attempt to initialize the WindowsAppRuntime Bootstrapper. (This requires the WindowsAppRuntime " + majorVersion + "." + minorVersion + " to be installed on the system.)");
const fullVersion = (majorVersion << 16) | minorVersion;
dotnet.Microsoft.Windows.ApplicationModel.DynamicDependency.Bootstrap.Initialize(fullVersion);
console.log("Initialized Bootstraper. WindowsAppRuntime is now available.");
6 changes: 6 additions & 0 deletions examples/winappsdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "node-api-dotnet-examples-winappsdk",
"dependencies": {
"node-api-dotnet": "file:../../out/pkg/node-api-dotnet"
}
}
2 changes: 2 additions & 0 deletions src/NodeApi.Generator/NodeApi.Generator.targets
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--packs &quot;$(_NodeApiGeneratorTargetingPacks)&quot;" />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--reference &quot;$(_NodeApiGeneratorAssemblyReferences)&quot;" />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--typedefs &quot;$(TargetDir)$(NodeApiTypeDefinitionsFileName)&quot;" />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--exclude &quot;$(NodeApiExcludeFromMetadata.Replace(';', '%3B'))&quot;" Condition=" '$(NodeApiExcludeFromMetadata)' != '' " />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="$(NodeApiTypeDefinitionsGeneratorOptions)" />

<!-- Run the generator using args from the response file. Note the '@' indicates the response file NOT an MSBuild item-list. -->
Expand Down Expand Up @@ -171,6 +172,7 @@
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--packs &quot;$(_NodeApiGeneratorTargetingPacks)&quot;" />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--typedefs &quot;$(_NodeApiGeneratorTypeDefs)&quot;" />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--nowarn" Condition=" '$(NodeApiTypeDefinitionsEnableWarnings)' != 'true' " />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="--exclude &quot;$(NodeApiExcludeFromMetadata.Replace(';', '%3B'))&quot;" Condition=" '$(NodeApiExcludeFromMetadata)' != '' " />
<WriteLinesToFile File="$(NodeApiGeneratorResponseFile)" Lines="$(NodeApiTypeDefinitionsGeneratorOptions)" />

<!-- Run the generator using args from the response file. Note the '@' indicates the response file NOT an MSBuild item-list. -->
Expand Down
10 changes: 9 additions & 1 deletion src/NodeApi.Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static class Program
private static readonly List<string> s_typeDefinitionsPaths = new();
private static readonly HashSet<int> s_systemAssemblyIndexes = new();
private static readonly List<TypeDefinitionsGenerator.ModuleType> s_moduleTypes = new();
private static readonly List<string> s_excludePatterns = new();
private static bool s_suppressWarnings;

public static int Main(string[] args)
Expand All @@ -56,6 +57,7 @@ public static int Main(string[] args)
-t --typedefs Path to output type definitions file (required)
-m --module Generate JS module(s) alongside typedefs (optional, multiple)
Value: 'commonjs', 'esm', or path to package.json with "type"
-e --exclude Exclude types matching wildcard patterns from metadata parsing (optional)
--nowarn Suppress warnings
-? -h --help Show this help message
@<file> Read response file for more options
Expand Down Expand Up @@ -108,7 +110,8 @@ public static int Main(string[] args)
modulePaths,
s_targetFramework,
isSystemAssembly: s_systemAssemblyIndexes.Contains(i),
s_suppressWarnings);
s_suppressWarnings,
s_excludePatterns);
}

return 0;
Expand Down Expand Up @@ -206,6 +209,11 @@ void AddItems(List<string> list, string items)
}
break;

case "-e":
case "--exclude":
AddItems(s_excludePatterns, args[++i]);
break;

case "--nowarn":
s_suppressWarnings = true;
break;
Expand Down
73 changes: 64 additions & 9 deletions src/NodeApi.Generator/TypeDefinitionsGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ public static void GenerateTypeDefinitions(
IDictionary<ModuleType, string> modulePaths,
string? targetFramework = null,
bool isSystemAssembly = false,
bool suppressWarnings = false)
bool suppressWarnings = false,
IEnumerable<string>? excludePatterns = null)
{
if (string.IsNullOrEmpty(assemblyPath))
{
Expand Down Expand Up @@ -282,6 +283,7 @@ public static void GenerateTypeDefinitions(
SuppressWarnings = suppressWarnings,
ExportAll = assemblyExportAttribute != null &&
GetExportAttributeValue(assemblyExportAttribute),
ExcludePatterns = excludePatterns?.ToList() ?? new List<string>(),
};

generator.LoadAssemblyDocs();
Expand Down Expand Up @@ -380,6 +382,8 @@ public TypeDefinitionsGenerator(

public bool SuppressWarnings { get; set; }

public List<string> ExcludePatterns { get; set; } = new();

public override void ReportDiagnostic(Diagnostic diagnostic)
{
if (SuppressWarnings && diagnostic.Severity == DiagnosticSeverity.Warning)
Expand Down Expand Up @@ -1260,8 +1264,23 @@ private void EndNamespace(ref SourceBuilder s, Type type)
}
}

private static bool IsExcluded(MemberInfo member)
private bool IsExcluded(MemberInfo member)
{
Type type = member as Type ?? member.DeclaringType!;

// Check user-provided exclude patterns first
if (ExcludePatterns.Count > 0)
{
string typeFullName = type.FullName ?? type.Name;
foreach (string pattern in ExcludePatterns)
{
if (IsWildcardMatch(typeFullName, pattern))
{
return true;
}
}
}

if (member is PropertyInfo property)
{
return IsExcluded(property);
Expand All @@ -1271,8 +1290,6 @@ private static bool IsExcluded(MemberInfo member)
return IsExcluded(method);
}

Type type = member as Type ?? member.DeclaringType!;

if (type.BaseType != null && IsExcluded(type.BaseType))
{
return true;
Expand Down Expand Up @@ -1301,14 +1318,41 @@ private static bool IsExcluded(MemberInfo member)
};
}

private static bool IsExcluded(PropertyInfo property)
/// <summary>
/// Checks if a string matches a wildcard pattern. Supports * (any characters) and ? (single character).
/// </summary>
private static bool IsWildcardMatch(string input, string pattern)
{
if (property.PropertyType.IsPointer)
if (string.IsNullOrEmpty(pattern)) return false;
if (string.IsNullOrEmpty(input)) return false;

// Convert wildcard pattern to regex pattern
string regexPattern = "^" + Regex.Escape(pattern)
.Replace(@"\*", ".*")
.Replace(@"\?", ".") + "$";

return Regex.IsMatch(input, regexPattern, RegexOptions.IgnoreCase);
}

private bool IsExcluded(PropertyInfo property)
{
Type propertyType;
try
{
propertyType = property.PropertyType;
}
catch (System.NotSupportedException e)
{
Console.WriteLine($"Error: Property {property.DeclaringType!.FullName}.{property.Name} could not be analyzed. ({e.GetType().Name})");
throw;
}

if (propertyType.IsPointer)
{
return true;
}

if (IsExcluded(property.PropertyType))
if (IsExcluded(propertyType))
{
return true;
}
Expand All @@ -1324,11 +1368,22 @@ private bool IsExcluded(MethodBase method)
return true;
}

System.Reflection.ParameterInfo[] methodParams;
try
{
methodParams = method.GetParameters();
}
catch (System.NotSupportedException e)
{
Console.WriteLine($"Error: Method {method.DeclaringType!.FullName}.{method.Name}() could not be analyzed. ({e.GetType().Name})");
throw;
}

// Exclude old style Begin/End async methods, as they always have Task-based alternatives.
if ((method.Name.StartsWith("Begin") &&
(method as MethodInfo)?.ReturnType.FullName == typeof(IAsyncResult).FullName) ||
(method.Name.StartsWith("End") && method.GetParameters().Length == 1 &&
method.GetParameters()[0].ParameterType.FullName == typeof(IAsyncResult).FullName))
(method.Name.StartsWith("End") && methodParams.Length == 1 &&
methodParams[0].ParameterType.FullName == typeof(IAsyncResult).FullName))
{
return true;
}
Expand Down