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
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default defineConfig({
{ text: "Serialization", link: "/guide/serialization" },
{ text: "Interop Modules", link: "/guide/interop-modules" },
{ text: "Interop Instances", link: "/guide/interop-instances" },
{ text: "Preferences", link: "/guide/preferences" },
{ text: "Renaming", link: "/guide/renaming" },
{ text: "Specialization", link: "/guide/specialization" },
{ text: "Build Configuration", link: "/guide/build-config" },
{ text: "Sideloading", link: "/guide/sideloading" },
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/declarations.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ Class.baz();

:::

You can control how the C#-side namespace path resolves to the generated module path with the `Space` and `Name` options in [preferences](/guide/preferences).
You can control how the C#-side namespace and type names resolve to the generated module and node names with the [rename attributes](/guide/renaming).

## Configuring Type Mappings

You can override which type declaration is generated for associated C# types via `Type` patterns of [emit preferences](/guide/preferences).
You can rename or omit the JavaScript node generated for an associated C# type via the [rename attributes](/guide/renaming).
72 changes: 0 additions & 72 deletions docs/guide/preferences.md

This file was deleted.

68 changes: 68 additions & 0 deletions docs/guide/renaming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Renaming

By default, Bootsharp derives JavaScript names from your C# types: a type's namespace becomes the module path, the type name becomes the node (object) under that module, and members are `camelCased`.

It's possible to customize the behaviour by specifying static methods annotated with `[RenameModule]`, `[RenameNode]` and `[RenameMember]` attributes. The methods receive a CLR type associated with the renamed artifact plus the default name generated by Bootsharp and expected to return the custom name you want to use.

## Module

`[RenameModule]` customizes the module path that groups the generated bindings and declarations. The default is the slugified C# namespace, or `index` for global types. Returning an empty, null or whitespace string falls back to the default `index` module.

```cs
[RenameModule]
public static string RenameModule (Type type, string @default) =>
@default.Replace("/foo/bar", "/foo");
```

In the example above we fold `/foo/bar` modules into the `/foo` module.

## Node

`[RenameNode]` customizes the node — the object representing a C# type under its module. The default is the reflected type name. Returning an empty, null or whitespace string **erases** the type, omitting it from the generated JavaScript.

```cs
[RenameNode]
public static string RenameNode (Type type, string @default)
{
if (type.Name == "Foo") return null;
if (type.IsInterface && @default.EndsWith("UI")) return @default[1..^2];
if (type.IsInterface && @default.StartsWith('I')) return @default[1..];
return @default;
}
```

The example removes `Foo` types from the interop surface, strips the leading `I` from interface names and drops a trailing `UI` suffix when present.

## Member

`[RenameMember]` customizes member names — the methods, properties and events projected on an interop surface. The default is the `camelCased` (and disambiguated) member name. Returning an empty, null or whitespace string **erases** the member, omitting it from the generated JavaScript.

```cs
[RenameMember]
public static string RenameMember (MemberInfo info, string @default)
{
if (info.DeclaringType.Name == "Foo")
if (info is EventInfo) return null;
else return char.ToUpperInvariant(@default[0]) + @default[0..];
return @default;
}
```

Here we drop all events and rename other members declared under `Foo` to `PascalCase`.

## Combining Renamers

Define any combination of the three renamers — each is optional and resolved independently. The example below groups everything under an `api` module, removes the interface prefixes and drops properties from all surfaces:

```cs
[RenameModule]
public static string Module (Type type, string @default) => "api";

[RenameNode]
public static string Node (Type type, string @default) =>
type.IsInterface ? @default[1..] : @default;

[RenameMember]
public static string Member (MemberInfo info, string @default) =>
info is PropertyInfo ? null : @default;
```
8 changes: 6 additions & 2 deletions samples/react/backend/Backend.WASM/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
[assembly: Export(typeof(Backend.IComputer))]
// Generate JavaScript -> C# interop handlers for specified contracts.
[assembly: Import(typeof(Backend.Prime.IPrimeUI))]
// Group all generated JavaScript APIs under "Computer" namespace.
[assembly: Preferences(Space = [".+", "Computer"])]

// Perform dependency injection.
new ServiceCollection()
.AddSingleton<Backend.IComputer, Backend.Prime.Prime>() // use prime computer
.AddBootsharp() // inject generated interop handlers
.BuildServiceProvider()
.RunBootsharp(); // initialize interop services

public static class Prefs
{
[RenameModule] // Group generated JavaScript APIs under the "computer" module.
public static string RenameModule (Type type, string @default) => "computer";
}
1 change: 0 additions & 1 deletion src/cs/Bootsharp.Common.Test/TypesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public void TypesAreAssigned ()
Assert.Equal(typeof(IFrontend), new SpecializeImportAttribute(typeof(IFrontend)).Clr);
Assert.Equal("JS", new SpecializeImportAttribute(typeof(IFrontend), JS: "JS").JS);
Assert.Equal("Decl", new SpecializeImportAttribute(typeof(IFrontend), Decl: "Decl").Decl);
Assert.Equal("Space", new PreferencesAttribute { Space = ["Space"] }.Space[0]);
}

[Fact]
Expand Down
57 changes: 0 additions & 57 deletions src/cs/Bootsharp.Common/Attributes/PreferencesAttribute.cs

This file was deleted.

15 changes: 15 additions & 0 deletions src/cs/Bootsharp.Common/Attributes/RenameMemberAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Bootsharp;

/// <summary>
/// When applied to a static method, designates it as the customizer of JavaScript member names —
/// the methods, properties and events projected on an interop surface.
/// </summary>
/// <remarks>
/// The annotated method has to be static and accept the reflected <see cref="System.Reflection.MemberInfo"/>
/// together with the default member name (camel-cased and disambiguated), returning the desired member name.
/// It is invoked for every inspected member after the metadata is collected. Return the supplied default to
/// keep the member unchanged; returning an empty, null or whitespace string erases the member, omitting it
/// from the generated JavaScript.
/// </remarks>
[AttributeUsage(AttributeTargets.Method)]
public sealed class RenameMemberAttribute : Attribute;
15 changes: 15 additions & 0 deletions src/cs/Bootsharp.Common/Attributes/RenameModuleAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Bootsharp;

/// <summary>
/// When applied to a static method, designates it as the customizer of JavaScript module names —
/// the namespace-derived path that groups generated bindings and declarations.
/// </summary>
/// <remarks>
/// The annotated method has to be static and accept the inspected CLR <see cref="System.Type"/> together
/// with the default module name (the slugified C# namespace, or 'index' for global types), returning the
/// desired module name. It is invoked for every inspected type after the metadata is collected. Return the
/// supplied default to keep the module unchanged; returning an empty, null or whitespace string falls back
/// to the default 'index' module.
/// </remarks>
[AttributeUsage(AttributeTargets.Method)]
public sealed class RenameModuleAttribute : Attribute;
15 changes: 15 additions & 0 deletions src/cs/Bootsharp.Common/Attributes/RenameNodeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Bootsharp;

/// <summary>
/// When applied to a static method, designates it as the customizer of JavaScript node names —
/// the object that represents a CLR type under its module.
/// </summary>
/// <remarks>
/// The annotated method has to be static and accept the inspected CLR <see cref="System.Type"/> together
/// with the default node name (the reflected C# type name), returning the desired node name. It is invoked
/// for every inspected type after the metadata is collected. Return the supplied default to keep the node
/// unchanged; returning an empty, null or whitespace string erases the type, omitting it from the generated
/// JavaScript.
/// </remarks>
[AttributeUsage(AttributeTargets.Method)]
public sealed class RenameNodeAttribute : Attribute;
9 changes: 7 additions & 2 deletions src/cs/Bootsharp.Publish.Test/GenerateCS/CSInteropTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,11 +452,10 @@ public void EscapesReservedArgumentNames ()
}

[Fact]
public void RespectsSpacePref ()
public void RespectsPref ()
{
AddAssembly(With(
"""
[assembly:Preferences(Space = [@"Space", "Foo"])]
[assembly:Export(typeof(Space.IExported))]
[assembly:Import(typeof(Space.IImported))]

Expand All @@ -471,6 +470,12 @@ public class Class
[Export] public static void Inv () {}
[Import] public static void Fun () => Proxies.Get<Action>("Class.Fun")();
}

public static class Prefs
{
[RenameModule]
public static string Module (Type type, string @default) => @default.Replace("space", "foo");
}
"""));
Execute();
Contains("[JSExport] internal static void JS_Export_Space_IExported_Inv () => global::Bootsharp.Generated.JS_Export_Space_IExported.Inv();");
Expand Down
3 changes: 0 additions & 3 deletions src/cs/Bootsharp.Publish.Test/GenerateCS/GenerateCSTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,11 @@ public GenerateCSTest ()

public override void Execute ()
{
if (LastAddedAssemblyName is not null)
Task.EntryAssemblyName = LastAddedAssemblyName;
Task.Execute();
}

private GenerateCS CreateTask () => new() {
InspectedDirectory = Project.Root,
EntryAssemblyName = "System.Runtime.dll",
SerializerFilePath = serializerPath,
InstancesFilePath = instancesPath,
ModulesFilePath = modulesPath,
Expand Down
Loading
Loading