From a1f9d3cbccf191ffba6e76c383f4d4fb59fb059b Mon Sep 17 00:00:00 2001 From: Nicolas Merget Date: Thu, 18 Dec 2025 13:53:05 +0100 Subject: [PATCH 1/4] fix: issue with codegen for ClassBinder (onready, help, experimental, deprecated) --- scripts/jsb.editor/src/jsb.editor.codegen.ts | 101 ++++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/scripts/jsb.editor/src/jsb.editor.codegen.ts b/scripts/jsb.editor/src/jsb.editor.codegen.ts index 0a5eb934..6d742910 100644 --- a/scripts/jsb.editor/src/jsb.editor.codegen.ts +++ b/scripts/jsb.editor/src/jsb.editor.codegen.ts @@ -1882,7 +1882,7 @@ const annotation_types = { type: DescriptorType.Godot, name: "string", }, - { type: DescriptorType.Godot, name: "ClassMethodDecoratorContext" }, + { type: DescriptorType.Godot, name: "ClassFieldDecoratorContext" }, ], }, }, @@ -1895,23 +1895,24 @@ const annotation_types = { { name: "message", type: { type: DescriptorType.Godot, name: "string" }, optional: true }, ], returns: { - type: DescriptorType.Godot, - name: "Decorator", - arguments: [ + type: DescriptorType.FunctionLiteral, + parameters: [ + { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, { - type: DescriptorType.Union, - types: [ - { - type: DescriptorType.Godot, - name: "ClassDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - { - type: DescriptorType.Godot, - name: "ClassValueMemberDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - ], + name: "context", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "ClassDecoratorContext", + }, + { + type: DescriptorType.Godot, + name: "ClassValueMemberDecoratorContext", + }, + ], + }, }, ], }, @@ -1922,23 +1923,24 @@ const annotation_types = { { name: "message", type: { type: DescriptorType.Godot, name: "string" }, optional: true }, ], returns: { - type: DescriptorType.Godot, - name: "Decorator", - arguments: [ + type: DescriptorType.FunctionLiteral, + parameters: [ + { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, { - type: DescriptorType.Union, - types: [ - { - type: DescriptorType.Godot, - name: "ClassDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - { - type: DescriptorType.Godot, - name: "ClassValueMemberDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - ], + name: "context", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "ClassDecoratorContext", + }, + { + type: DescriptorType.Godot, + name: "ClassValueMemberDecoratorContext", + }, + ], + }, }, ], }, @@ -1949,23 +1951,24 @@ const annotation_types = { { name: "message", type: { type: DescriptorType.Godot, name: "string" }, optional: true }, ], returns: { - type: DescriptorType.Godot, - name: "Decorator", - arguments: [ + type: DescriptorType.FunctionLiteral, + parameters: [ + { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, { - type: DescriptorType.Union, - types: [ - { - type: DescriptorType.Godot, - name: "ClassDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - { - type: DescriptorType.Godot, - name: "ClassValueMemberDecoratorContext", - arguments: [{ type: DescriptorType.Godot, name: "GObjectConstructor" }], - }, - ], + name: "context", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "ClassDecoratorContext", + }, + { + type: DescriptorType.Godot, + name: "ClassValueMemberDecoratorContext", + }, + ], + }, }, ], }, From 3650d699de3bb21c9e1690693a6eb1fa3bf1b415 Mon Sep 17 00:00:00 2001 From: Nicolas Merget Date: Fri, 19 Dec 2025 00:25:10 +0100 Subject: [PATCH 2/4] fix: issue with @help, @experimental, @deprecated and @icon chore: add annotation test to project --- .changeset/big-hoops-clap.md | 5 ++ bridge/jsb_class_info.cpp | 10 ++- bridge/jsb_class_info.h | 3 + scripts/jsb.editor/src/jsb.editor.codegen.ts | 63 +++++++++++++- scripts/jsb.runtime/src/godot.annotations.ts | 12 +-- scripts/typings/godot.generated.d.ts | 15 +++- tests/project/assets/icon.svg | 1 + tests/project/assets/icon.svg.import | 43 ++++++++++ .../project/gen/godot/assets/icon.svg.gen.ts | 5 ++ .../annotations/Annotations.nodes.gen.ts | 5 ++ .../tests/annotations/Annotations.tscn.gen.ts | 6 ++ .../tests/annotations/annotations.ts.gen.ts | 5 ++ .../tests/annotations/Annotations.tscn | 11 +++ .../project/tests/annotations/annotations.ts | 86 +++++++++++++++++++ tests/project/tests/start.ts | 1 + tests/project/typings/jsb.runtime.gen.d.ts | 35 +++++--- weaver/jsb_script.cpp | 30 ++++++- weaver/jsb_script_language.cpp | 9 ++ weaver/jsb_script_language.h | 2 + 19 files changed, 320 insertions(+), 27 deletions(-) create mode 100644 .changeset/big-hoops-clap.md create mode 100644 tests/project/assets/icon.svg create mode 100644 tests/project/assets/icon.svg.import create mode 100644 tests/project/gen/godot/assets/icon.svg.gen.ts create mode 100644 tests/project/gen/godot/tests/annotations/Annotations.nodes.gen.ts create mode 100644 tests/project/gen/godot/tests/annotations/Annotations.tscn.gen.ts create mode 100644 tests/project/gen/godot/tests/annotations/annotations.ts.gen.ts create mode 100644 tests/project/tests/annotations/Annotations.tscn create mode 100644 tests/project/tests/annotations/annotations.ts diff --git a/.changeset/big-hoops-clap.md b/.changeset/big-hoops-clap.md new file mode 100644 index 00000000..3dc1de1b --- /dev/null +++ b/.changeset/big-hoops-clap.md @@ -0,0 +1,5 @@ +--- +"@godot-js/editor": patch +--- + +fix: issues for annotations (@icon, @help, @deprecated, @experimental) diff --git a/bridge/jsb_class_info.cpp b/bridge/jsb_class_info.cpp index 7d0265a2..a4889680 100644 --- a/bridge/jsb_class_info.cpp +++ b/bridge/jsb_class_info.cpp @@ -190,7 +190,15 @@ namespace jsb v8::Local signal_name_js = collection->Get(p_context, index).ToLocalChecked(); jsb_check(signal_name_js->IsString()); const StringName signal_name = environment->get_string_name_cache().get_string_name(isolate, signal_name_js.As()); - p_class_info->signals.insert(signal_name, {}); + + ScriptSignalInfo signal_info = {}; +#ifdef TOOLS_ENABLED + if (v8::Local val; !doc_map.IsEmpty() && doc_map->Get(p_context, signal_name_js).ToLocal(&val) && val->IsObject()) + { + _parse_script_doc(isolate, p_context, val, signal_info.doc); + } +#endif // TOOLS_ENABLED + p_class_info->signals.insert(signal_name, signal_info); // instantiate a fake Signal property //NOTE: we use JS string representation of signal name for info.Data() to avoid persistent StringNameID requirement. diff --git a/bridge/jsb_class_info.h b/bridge/jsb_class_info.h index f5b0b6a6..22c32b31 100644 --- a/bridge/jsb_class_info.h +++ b/bridge/jsb_class_info.h @@ -109,6 +109,7 @@ namespace jsb struct ScriptClassDoc : ScriptBaseDoc {}; struct ScriptMethodDoc : ScriptBaseDoc {}; struct ScriptPropertyDoc : ScriptBaseDoc {}; + struct ScriptSignalDoc : ScriptBaseDoc {}; #else struct ScriptClassDoc {}; struct ScriptMethodDoc {}; @@ -126,6 +127,8 @@ namespace jsb struct ScriptSignalInfo { + // only valid with TOOLS_ENABLED + ScriptSignalDoc doc; }; struct ScriptMethodInfo diff --git a/scripts/jsb.editor/src/jsb.editor.codegen.ts b/scripts/jsb.editor/src/jsb.editor.codegen.ts index 6d742910..67bcb717 100644 --- a/scripts/jsb.editor/src/jsb.editor.codegen.ts +++ b/scripts/jsb.editor/src/jsb.editor.codegen.ts @@ -1897,7 +1897,22 @@ const annotation_types = { returns: { type: DescriptorType.FunctionLiteral, parameters: [ - { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, + { + name: "target", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "GObject", + }, + { + type: DescriptorType.Godot, + name: "GObjectConstructor", + }, + ], + }, + }, { name: "context", type: { @@ -1911,6 +1926,10 @@ const annotation_types = { type: DescriptorType.Godot, name: "ClassValueMemberDecoratorContext", }, + { + type: DescriptorType.Godot, + name: "ClassMethodDecoratorContext", + }, ], }, }, @@ -1925,7 +1944,22 @@ const annotation_types = { returns: { type: DescriptorType.FunctionLiteral, parameters: [ - { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, + { + name: "target", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "GObject", + }, + { + type: DescriptorType.Godot, + name: "GObjectConstructor", + }, + ], + }, + }, { name: "context", type: { @@ -1939,6 +1973,10 @@ const annotation_types = { type: DescriptorType.Godot, name: "ClassValueMemberDecoratorContext", }, + { + type: DescriptorType.Godot, + name: "ClassMethodDecoratorContext", + }, ], }, }, @@ -1953,7 +1991,22 @@ const annotation_types = { returns: { type: DescriptorType.FunctionLiteral, parameters: [ - { name: "target", type: { type: DescriptorType.Godot, name: "GObjectConstructor" } }, + { + name: "target", + type: { + type: DescriptorType.Union, + types: [ + { + type: DescriptorType.Godot, + name: "GObject", + }, + { + type: DescriptorType.Godot, + name: "GObjectConstructor", + }, + ], + }, + }, { name: "context", type: { @@ -1967,6 +2020,10 @@ const annotation_types = { type: DescriptorType.Godot, name: "ClassValueMemberDecoratorContext", }, + { + type: DescriptorType.Godot, + name: "ClassMethodDecoratorContext", + }, ], }, }, diff --git a/scripts/jsb.runtime/src/godot.annotations.ts b/scripts/jsb.runtime/src/godot.annotations.ts index 1221f4e2..9d4e1ded 100644 --- a/scripts/jsb.runtime/src/godot.annotations.ts +++ b/scripts/jsb.runtime/src/godot.annotations.ts @@ -1107,8 +1107,8 @@ export function createClassBinder(): ClassBinder { deprecated(message?: string) { return function ( - target: GObjectConstructor, - context: ClassDecoratorContext | ClassValueMemberDecoratorContext, + target: Godot.Object | GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, ) { if (typeof context !== "object") { throw new Error( @@ -1132,8 +1132,8 @@ export function createClassBinder(): ClassBinder { }, experimental(message?: string) { return function ( - target: GObjectConstructor, - context: ClassDecoratorContext | ClassValueMemberDecoratorContext, + target: Godot.Object | GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, ) { if (typeof context !== "object") { throw new Error( @@ -1157,8 +1157,8 @@ export function createClassBinder(): ClassBinder { }, help(message?: string) { return function ( - target: GObjectConstructor, - context: ClassDecoratorContext | ClassValueMemberDecoratorContext, + target: Godot.Object | GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, ) { if (typeof context !== "object") { throw new Error( diff --git a/scripts/typings/godot.generated.d.ts b/scripts/typings/godot.generated.d.ts index dde2ce37..2c879163 100644 --- a/scripts/typings/godot.generated.d.ts +++ b/scripts/typings/godot.generated.d.ts @@ -1227,13 +1227,22 @@ declare module "godot.annotations" { ) => (_target: undefined, context: string | ClassFieldDecoratorContext) => void; deprecated: ( message?: string, - ) => (target: GObjectConstructor, context: ClassDecoratorContext | ClassValueMemberDecoratorContext) => void; + ) => ( + target: GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, + ) => void; experimental: ( message?: string, - ) => (target: GObjectConstructor, context: ClassDecoratorContext | ClassValueMemberDecoratorContext) => void; + ) => ( + target: GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, + ) => void; help: ( message?: string, - ) => (target: GObjectConstructor, context: ClassDecoratorContext | ClassValueMemberDecoratorContext) => void; + ) => ( + target: GObjectConstructor, + context: ClassDecoratorContext | ClassValueMemberDecoratorContext | ClassMethodDecoratorContext, + ) => void; }; type ExportOptions = { diff --git a/tests/project/assets/icon.svg b/tests/project/assets/icon.svg new file mode 100644 index 00000000..c6bbb7d8 --- /dev/null +++ b/tests/project/assets/icon.svg @@ -0,0 +1 @@ + diff --git a/tests/project/assets/icon.svg.import b/tests/project/assets/icon.svg.import new file mode 100644 index 00000000..433ad17d --- /dev/null +++ b/tests/project/assets/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0312tiuxwg17" +path="res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icon.svg" +dest_files=["res://.godot/imported/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/tests/project/gen/godot/assets/icon.svg.gen.ts b/tests/project/gen/godot/assets/icon.svg.gen.ts new file mode 100644 index 00000000..9eca69fe --- /dev/null +++ b/tests/project/gen/godot/assets/icon.svg.gen.ts @@ -0,0 +1,5 @@ +declare module "godot" { + interface ResourceTypes { + "res://assets/icon.svg": CompressedTexture2D; + } +} diff --git a/tests/project/gen/godot/tests/annotations/Annotations.nodes.gen.ts b/tests/project/gen/godot/tests/annotations/Annotations.nodes.gen.ts new file mode 100644 index 00000000..30a1ef32 --- /dev/null +++ b/tests/project/gen/godot/tests/annotations/Annotations.nodes.gen.ts @@ -0,0 +1,5 @@ +declare module "godot" { + interface SceneNodes { + "tests/annotations/Annotations.tscn": { Label: Label<{}>; }; + } +} diff --git a/tests/project/gen/godot/tests/annotations/Annotations.tscn.gen.ts b/tests/project/gen/godot/tests/annotations/Annotations.tscn.gen.ts new file mode 100644 index 00000000..c71c5685 --- /dev/null +++ b/tests/project/gen/godot/tests/annotations/Annotations.tscn.gen.ts @@ -0,0 +1,6 @@ +import Annotations from "../../../../tests/annotations/annotations"; +declare module "godot" { + interface ResourceTypes { + "res://tests/annotations/Annotations.tscn": PackedScene; + } +} diff --git a/tests/project/gen/godot/tests/annotations/annotations.ts.gen.ts b/tests/project/gen/godot/tests/annotations/annotations.ts.gen.ts new file mode 100644 index 00000000..4870d048 --- /dev/null +++ b/tests/project/gen/godot/tests/annotations/annotations.ts.gen.ts @@ -0,0 +1,5 @@ +declare module "godot" { + interface ResourceTypes { + "res://tests/annotations/annotations.ts": Script; + } +} diff --git a/tests/project/tests/annotations/Annotations.tscn b/tests/project/tests/annotations/Annotations.tscn new file mode 100644 index 00000000..ea65c23c --- /dev/null +++ b/tests/project/tests/annotations/Annotations.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://dg678hkmjdk8q"] + +[ext_resource type="Script" uid="uid://crxgru6txypvq" path="res://tests/annotations/annotations.ts" id="1_wypiw"] + +[node name="Annotations" type="Node2D"] +script = ExtResource("1_wypiw") + +[node name="Label" type="Label" parent="."] +offset_right = 40.0 +offset_bottom = 23.0 +text = "Annotations" diff --git a/tests/project/tests/annotations/annotations.ts b/tests/project/tests/annotations/annotations.ts new file mode 100644 index 00000000..23d8ff9d --- /dev/null +++ b/tests/project/tests/annotations/annotations.ts @@ -0,0 +1,86 @@ +import { Label, Node, Signal, Variant } from "godot"; +import { createClassBinder } from "godot.annotations"; + +const bind = createClassBinder(); + +enum MyColor { + White, + Black, + Red, + Blue, + Green, +} + +enum MyTags { + None = 0, + Cold = 1, + Hot = 2, + Soft = 4, + Hard = 8, + Opaque = 16, +} + +@bind() +@bind.icon("res://assets/icon.svg") +@bind.help("Just a test!") +export default class Annotations extends Node { + @bind.onready("Label") + ready_node!: Label; + + @bind.help("Just a test xxx!") + @bind.export.flags(MyTags) + accessor tags: MyTags = MyTags.None; + + @bind.export.enum(MyColor) + accessor color: MyColor = MyColor.White; + + @bind.export(Variant.Type.TYPE_STRING) + accessor hello = "hello"; + + @bind.export.multiline() + accessor ml_text = "hello\nworld"; + + @bind.export(Variant.Type.TYPE_INT) + accessor int_value = 0; + + @bind.export.range(0, 100, 0.1) + accessor float_range = 0; + + @bind.export.range_int(0, 100, 1) + accessor int_range = 0; + + @bind.export.range_int(0, 100, 1, "suffix:px") + accessor px_range = 0; + + @bind.export.file("*.svg") + accessor svg_path = ""; + + @bind.export.global_file("*.txt") + accessor global_txt_path = ""; + + @bind.export.exp_easing("positive_only") + accessor ev_val = 0; + + @bind.signal() + @bind.help("Just a test!") + accessor test_signal!: Signal<() => void>; + + @bind.experimental("Alternative to [method Annotations.doNewStuff].") + public doNewStuff() { + // ... + } + + doStuff() { + // ... + } + + @bind.deprecated("Use [method Annotations.doNewStuff] instead.") + doOldStuff() { + // ... + } + + _ready() { + console.log("onready works:", this.ready_node.text); + console.assert(this.ready_node.text === "Annotations"); + } +} diff --git a/tests/project/tests/start.ts b/tests/project/tests/start.ts index 7c89ec0b..4451b66c 100644 --- a/tests/project/tests/start.ts +++ b/tests/project/tests/start.ts @@ -8,6 +8,7 @@ export default class Start extends Node { "res://tests/extend/Extend.tscn", "res://tests/papaparse/Papaparse.tscn", "res://tests/os-executor/OSExecutor.tscn", + "res://tests/annotations/Annotations.tscn", ]; for (const scene of scenes) { diff --git a/tests/project/typings/jsb.runtime.gen.d.ts b/tests/project/typings/jsb.runtime.gen.d.ts index 0f5555df..3f64a8e8 100644 --- a/tests/project/typings/jsb.runtime.gen.d.ts +++ b/tests/project/typings/jsb.runtime.gen.d.ts @@ -110,23 +110,32 @@ declare module "godot.annotations" { (( _target: undefined, context: string - | ClassMethodDecoratorContext + | ClassFieldDecoratorContext ) => void); deprecated: (message?: string) => - Decorator< - ClassDecoratorContext - | ClassValueMemberDecoratorContext - >; + (( + target: GObject + | GObjectConstructor, + context: ClassDecoratorContext + | ClassValueMemberDecoratorContext + | ClassMethodDecoratorContext + ) => void); experimental: (message?: string) => - Decorator< - ClassDecoratorContext - | ClassValueMemberDecoratorContext - >; + (( + target: GObject + | GObjectConstructor, + context: ClassDecoratorContext + | ClassValueMemberDecoratorContext + | ClassMethodDecoratorContext + ) => void); help: (message?: string) => - Decorator< - ClassDecoratorContext - | ClassValueMemberDecoratorContext - >; + (( + target: GObject + | GObjectConstructor, + context: ClassDecoratorContext + | ClassValueMemberDecoratorContext + | ClassMethodDecoratorContext + ) => void); } type ExportOptions = { "class"?: any; diff --git a/weaver/jsb_script.cpp b/weaver/jsb_script.cpp index 5b3cd3c7..382db79a 100644 --- a/weaver/jsb_script.cpp +++ b/weaver/jsb_script.cpp @@ -268,6 +268,9 @@ Vector GodotJSScript::get_documentation() const class_doc_data.name = class_name; class_doc_data.inherits = base_type.is_empty() ? "Object" : base_type; class_doc_data.is_script_doc = true; + class_doc_data.script_path = get_path(); + + /* TODO: Can we read the source code and use JSDoc and ClassDoc.from_dict({...}) instead of annotations */ class_doc_data.brief_description = script_class_info_.doc.brief_description; class_doc_data.is_deprecated = script_class_info_.doc.is_deprecated; class_doc_data.is_experimental = script_class_info_.doc.is_experimental; @@ -275,7 +278,6 @@ Vector GodotJSScript::get_documentation() const class_doc_data.deprecated_message = script_class_info_.doc.deprecated_message; class_doc_data.experimental_message = script_class_info_.doc.experimental_message; #endif - class_doc_data.script_path = get_path(); for (const auto& item : script_class_info_.properties) { DocData::PropertyDoc property_doc_data; @@ -289,6 +291,32 @@ Vector GodotJSScript::get_documentation() const #endif class_doc_data.properties.append(property_doc_data); } + for (const auto& item : script_class_info_.methods) + { + DocData::MethodDoc method_doc_data; + method_doc_data.name = item.key; + method_doc_data.description = item.value.doc.brief_description; + method_doc_data.is_deprecated = item.value.doc.is_deprecated; + method_doc_data.is_experimental = item.value.doc.is_experimental; +#if GODOT_4_3_OR_NEWER + method_doc_data.deprecated_message = item.value.doc.deprecated_message; + method_doc_data.experimental_message = item.value.doc.experimental_message; +#endif + class_doc_data.methods.append(method_doc_data); + } + for (const auto& item : script_class_info_.signals) + { + DocData::MethodDoc signals_doc_data; + signals_doc_data.name = item.key; + signals_doc_data.description = item.value.doc.brief_description; + signals_doc_data.is_deprecated = item.value.doc.is_deprecated; + signals_doc_data.is_experimental = item.value.doc.is_experimental; +#if GODOT_4_3_OR_NEWER + signals_doc_data.deprecated_message = item.value.doc.deprecated_message; + signals_doc_data.experimental_message = item.value.doc.experimental_message; +#endif + class_doc_data.signals.append(signals_doc_data); + } return { class_doc_data }; } diff --git a/weaver/jsb_script_language.cpp b/weaver/jsb_script_language.cpp index b8ab5b42..784c5a51 100644 --- a/weaver/jsb_script_language.cpp +++ b/weaver/jsb_script_language.cpp @@ -56,6 +56,8 @@ GodotJSScriptLanguage::GodotJSScriptLanguage() js_class_name_matcher1_ = RegEx::create_from_string(R"(\s*exports.default\s*=\s*class\s*(\w+)\s+extends\s+(\w+))"); js_class_name_matcher2_ = RegEx::create_from_string(R"(\s*exports.default\s*=\s*(\w+)\s*;?)"); ts_class_name_matcher_ = RegEx::create_from_string(R"(\s*(@[tT]ool\s*\(\s*\)\s*\n*\s*)?export\s+default\s+class\s+(\w+)(\s*<)?[^\n]*(?:>|\s+)extends\s+(\w+))"); + ts_icon_matcher_ = RegEx::create_from_string(R"(@(?:\w+\.)?[iI]con\([\"']([^\"']+)[\"']\))"); + jsb::internal::StringNames::create(); } @@ -381,6 +383,13 @@ String GodotJSScriptLanguage::get_global_class_name(const String& p_path, String // hope it's a typescript file jsb_check(!ts_class_name_matcher_.is_null()); + Ref icon_match = ts_icon_matcher_->search(source); + if (icon_match.is_valid()) { + if (r_icon_path) { + *r_icon_path = icon_match->get_string(1); + } + } + Ref match = ts_class_name_matcher_->search(source); if (match.is_valid() && match->get_group_count() == 4) { diff --git a/weaver/jsb_script_language.h b/weaver/jsb_script_language.h index 0822917f..0dc54643 100644 --- a/weaver/jsb_script_language.h +++ b/weaver/jsb_script_language.h @@ -108,6 +108,8 @@ class GodotJSScriptLanguage : public ScriptLanguage Ref ts_class_name_matcher_; + Ref ts_icon_matcher_; + // [JS] export & declare in two lines, matches 'class ClassName extends BaseName' + 'exports.default = ClassName' Ref js_class_name_matcher2_; From 94c689449c3244b14f10e850b89d77387558cee8 Mon Sep 17 00:00:00 2001 From: Nicolas Merget Date: Fri, 19 Dec 2025 00:38:59 +0100 Subject: [PATCH 3/4] fix: issue with missing ScriptSignalDoc for template builds --- bridge/jsb_class_info.h | 1 + 1 file changed, 1 insertion(+) diff --git a/bridge/jsb_class_info.h b/bridge/jsb_class_info.h index 22c32b31..05bebac5 100644 --- a/bridge/jsb_class_info.h +++ b/bridge/jsb_class_info.h @@ -114,6 +114,7 @@ namespace jsb struct ScriptClassDoc {}; struct ScriptMethodDoc {}; struct ScriptPropertyDoc {}; + struct ScriptSignalDoc {}; #endif namespace ScriptMethodFlags From 148fa17b8c626bf0f76f8518315e6ea3dd33c583 Mon Sep 17 00:00:00 2001 From: Nicolas Merget Date: Sun, 28 Dec 2025 21:07:10 +0100 Subject: [PATCH 4/4] chore: use @icon matching only for editor --- weaver/jsb_script_language.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/weaver/jsb_script_language.cpp b/weaver/jsb_script_language.cpp index 784c5a51..5702efa1 100644 --- a/weaver/jsb_script_language.cpp +++ b/weaver/jsb_script_language.cpp @@ -383,12 +383,16 @@ String GodotJSScriptLanguage::get_global_class_name(const String& p_path, String // hope it's a typescript file jsb_check(!ts_class_name_matcher_.is_null()); +#if TOOLS_ENABLED + // Custom icons only appear in editor Ref icon_match = ts_icon_matcher_->search(source); if (icon_match.is_valid()) { if (r_icon_path) { *r_icon_path = icon_match->get_string(1); } } +#endif + Ref match = ts_class_name_matcher_->search(source); if (match.is_valid() && match->get_group_count() == 4)