Skip to content

Conversation

@symball
Copy link
Contributor

@symball symball commented Dec 20, 2025

Add time.Time and uuid.UUID handling to start and demonstrate

Description

Add support for special case type handling in the binding generation. I have included this because in my time using Wails, quite a lot of issues people have tends to do with exporting time.Time.

This is a small simple change that allows for customHandling support within fullyQualifiedName

I noticed there were some other tests failing so, have addressed them be rewriting in the seemingly new test style

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Created a test project with the following struct that previously would have created some warnings

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using wails doctor.

  • Windows
  • macOS
  • Linux
type TestResponse struct {
	Time time.Time `json:"time"`
	UUID uuid.UUID `json:"uuid"`
}

func (a *App) GetTest() TestResponse {
	return TestResponse{
		Time: time.Now(),
		UUID: uuid.New(),
	}
}

Now, on the frontend, a response like the following is returned without any intervention

{time: "2025-12-20T10:11:39.68594+08:00", uuid: "3fe7723d-dab3-4a44-9f45-7124d175498a"}

Test Configuration

# Wails
Version  | v2.11.0                                 
Revision | 8e227e7adc92491e9f96a4027a770f2125f399dd
Modified | true                                    


# System
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| OS           | MacOS                                                                                                                       |
| Version      | 26.2                                                                                                                        |
| ID           | 25C56                                                                                                                       |
| Branding     |                                                                                                                             |
| Go Version   | go1.25.5                                                                                                                    |
| Platform     | darwin                                                                                                                      |
| Architecture | arm64                                                                                                                       |
| CPU 1        | Apple M4 Pro                                                                                                                |
| CPU 2        | Apple M4 Pro                                                                                                                |
| GPU          | Chipset Model: Apple M4 Pro Type: GPU Bus: Built-In Total Number of Cores: 20 Vendor: Apple (0x106b) Metal Support: Metal 4 |
| Memory       | 64GB                                                                                                                        |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Dependencies
┌──────────────────────────────────────────────────────────────────┐
| Dependency                | Package Name | Status    | Version   |
| Xcode command line tools  | N/A          | Installed | 2416      |
| Nodejs                    | N/A          | Installed | 25.2.1    |
| npm                       | N/A          | Installed | 11.6.2    |
| *Xcode                    | N/A          | Available |           |
| *upx                      | N/A          | Installed | upx 5.0.2 |
| *nsis                     | N/A          | Available |           |
|                                                                  |
└──────────────────── * - Optional Dependency ─────────────────────┘

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • Tests

    • Replaced filesystem-based test scaffolding with self-contained, in-memory fixtures and inline expectations; added/expanded tests covering custom types, type aliases, and promise-returning scenarios.
  • Improvements

    • Enhanced type-conversion for generated bindings: map time/UUID/URL to string; improved handling of pointers, slices/arrays, maps and generics for more accurate TypeScript output; removed an unused test dependency.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 20, 2025

Walkthrough

Replaced filesystem-based binding tests with inline in-memory BindingTest fixtures across multiple test files and refactored the generator to centralize Go→TypeScript type resolution: added custom mappings for time.Time, url.URL, uuid.UUID, improved pointer/array/map/generic handling, and updated function signatures and tests.

Changes

Cohort / File(s) Summary
Test fixture refactors
v2/internal/binding/binding_test/binding_conflicting_package_name_test.go, v2/internal/binding/binding_test/binding_custom_type_test.go, v2/internal/binding/binding_test/binding_returned_promises_test.go, v2/internal/binding/binding_test/binding_type_alias_test.go
Removed filesystem I/O and test harness scaffolding; introduced inline BindingTest fixtures and test-only types/methods (e.g., CustomTypeStruct, PromisesTest, AliasTest, MapAlias); embedded expected TypeScript output strings and dropped previous file-read/compare assertions.
Type mapping & generator
v2/internal/binding/generate.go, v2/internal/binding/generate_test.go
Added customTypeMap mapping (time.Time, url.URL, uuid.UUIDstring); introduced genericTypePattern, jsVariableUnsafeChars; centralized type resolution with enhanced handling for pointers, []byte, slices/arrays (Array<T>), maps (Record<K,V>), and legacy generics; changed signatures to goTypeToJSDocType(goType string) and simplified goTypeToTypescriptType; updated tests accordingly and expanded type-mapping cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to v2/internal/binding/generate.go (recursive resolution for arrays/maps, pointer stripping, []byte special-case, generic regex, and ordering/precedence of customTypeMap).
  • Verify goTypeToJSDocType signature change impacts across repo and ensure tests in v2/internal/binding/generate_test.go cover edge cases.
  • Confirm new in-memory BindingTest fixtures accurately reflect expected generation output and replace previous filesystem assertions.

Possibly related PRs

Suggested reviewers

  • leaanthony

Poem

🐇 I hopped through code and swapped tests to play,
No more disk chores — fixtures light the way.
I taught Time and UUID to wear string bows,
Tamed maps, pointers, slices — tidy rows.
Hooray — bindings bloom where the green code grows!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding special case type handling in binding generation, which is the core focus of the PR.
Description check ✅ Passed The description covers the main objective, type of change, testing performed, and a test configuration. However, some checklist items lack supporting context and the changelog update is mentioned but not detailed.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
v2/internal/binding/generate.go (1)

247-263: Generic type naming produces trailing underscores.

The sanitization logic correctly creates valid JavaScript identifiers, but produces names with trailing underscores (e.g., ListData[foo.Bar]ListData_foo_Bar_). While functional, this could be cleaned up for better readability.

🔎 Suggested improvement
       // Clean parameter: remove invalid symbols for JS identifiers
       safeParam := strings.NewReplacer(
         "[", "_",
         "]", "_",
         "*", "",
         "/", "_",
         ".", "_",
       ).Replace(param)
+      safeParam = strings.Trim(safeParam, "_")
 
       return base + "_" + safeParam + "_"
+      return base + "_" + safeParam
     }
   }
 }
v2/internal/binding/binding_test/binding_type_alias_test.go (1)

7-48: LGTM! Good coverage of map and array types.

The test validates the new Record<K, V> and Array<T> TypeScript syntax for Go maps and slices, including package-qualified value types. The expected output correctly reflects all method signatures.

Consider adding test coverage for map keys with package-qualified types (e.g., map[foo.Bar]string) to catch potential edge cases in the map parsing logic.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc62140 and fa0a1f5.

📒 Files selected for processing (5)
  • v2/internal/binding/binding_test/binding_conflicting_package_name_test.go (2 hunks)
  • v2/internal/binding/binding_test/binding_custom_type_test.go (1 hunks)
  • v2/internal/binding/binding_test/binding_returned_promises_test.go (2 hunks)
  • v2/internal/binding/binding_test/binding_type_alias_test.go (2 hunks)
  • v2/internal/binding/generate.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
v2/internal/binding/binding_test/binding_custom_type_test.go (1)
v2/internal/binding/binding_test/binding_test.go (1)
  • BindingTest (13-21)
v2/internal/binding/binding_test/binding_type_alias_test.go (1)
v2/internal/binding/binding_test/binding_test.go (1)
  • BindingTest (13-21)
v2/internal/binding/binding_test/binding_returned_promises_test.go (1)
v2/internal/binding/binding_test/binding_test.go (1)
  • BindingTest (13-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
  • GitHub Check: Run Go Tests (ubuntu-22.04, 1.23)
  • GitHub Check: Run Go Tests (ubuntu-24.04, 1.23)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
v2/internal/binding/binding_test/binding_conflicting_package_name_test.go (1)

10-46: LGTM! Clean refactor to inline test fixtures.

The test successfully migrates from filesystem-based verification to a declarative inline fixture, improving maintainability and execution speed. The expected TypeScript output correctly reflects the method signatures with properly namespaced imported types.

v2/internal/binding/binding_test/binding_custom_type_test.go (1)

9-41: LGTM! Test validates the PR's core objective.

The test correctly demonstrates that uuid.UUID and time.Time fields are mapped to TypeScript string types in generated models. The expected output aligns with the customTypeMap introduced in generate.go. This provides good coverage for the special-case type handling feature.

Note: This tests struct field mapping. Additional test coverage for methods that directly return time.Time or uuid.UUID (not as struct fields) would strengthen validation of the binding generation path.

v2/internal/binding/binding_test/binding_returned_promises_test.go (1)

1-60: LGTM! Comprehensive promise handling test.

The refactored test covers various return type scenarios including void, single values, errors, structs, pointers, slices, and combinations. The expected TypeScript bindings correctly represent each case with appropriate Promise<T> types and array syntax.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
v2/internal/binding/generate.go (1)

294-296: Consider removing the unused parameter in a follow-up.

The _ *slicer.StringSlicer parameter is now unused. This maintains compatibility with existing call sites (lines 95, 111), but could be cleaned up if those callers are updated.

v2/internal/binding/generate_test.go (1)

140-164: Good test coverage for custom type mappings.

The new test cases comprehensively cover:

  • Direct type mapping (time.Time, uuid.UUIDstring)
  • Slice composition ([]time.TimeArray<string>)
  • Map composition (map[string]uuid.UUIDRecord<string, string>)
  • Pointer handling (*time.Timestring)

Consider adding a test case for generics containing custom types to verify the behavior:

🔎 Suggested additional test case
 		{
 			name:  "pointer to time.Time",
 			input: "*time.Time",
 			want:  "string",
 		},
+		{
+			name:  "generic containing time.Time",
+			input: "main.ListData[time.Time]",
+			want:  "main.ListData_time_Time_",
+		},
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa0a1f5 and 3cb1191.

📒 Files selected for processing (2)
  • v2/internal/binding/generate.go (3 hunks)
  • v2/internal/binding/generate_test.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
🔇 Additional comments (5)
v2/internal/binding/generate.go (4)

148-151: LGTM! Custom type mappings for time.Time and uuid.UUID.

This correctly maps standard library types that serialize to strings in JSON. Previous review concerns about this map not being checked in goTypeToJSDocType have been addressed at lines 263-267 and 270-273.


208-212: LGTM! Pointer handling now correctly strips leading * characters.

This addresses the previous review concern where types like *time.Time would fall through to the default any case.


226-252: LGTM! Map parsing now correctly handles nested brackets.

The bracket-depth tracking logic properly handles cases like map[[]int]string and map[map[int]string]bool. This addresses the previous review concern.


262-273: LGTM! customTypeMap is now properly checked for fully-qualified types.

This addresses the previous critical review concern where time.Time would return the literal type name instead of "string". The dual lookup pattern (lines 263-267 for qualified names, 270-273 for base names) ensures both forms are handled.

v2/internal/binding/generate_test.go (1)

166-172: LGTM! Test runner correctly updated for new function signature.

The call site properly reflects the simplified goTypeToJSDocType(input string) signature.

Add time.Time and uuid.UUID handling to start and demonstrate

Ad# dress goTypeToJSDocType CodeRabbit issues
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
v2/internal/binding/generate.go (1)

254-260: Nested generics produce unexpected results with greedy regex.

As noted in a previous review, the pattern ^(.*)\[(.*)\]$ uses greedy matching. For nested generics like main.Outer[main.Inner[string]], this produces base="main.Outer[main.Inner" instead of base="main.Outer". If nested generics are not a current use case, consider adding a clarifying comment.

🧹 Nitpick comments (3)
v2/internal/binding/generate_test.go (1)

140-164: Good test coverage for the new custom type mappings.

The test cases comprehensively cover the new time.Time and uuid.UUID mappings across different type constructs (direct, slice, map, pointer). Consider adding a test case for custom types within generics to ensure complete coverage:

{
    name:  "generic with custom type",
    input: "main.ListData[time.Time]",
    want:  "main.ListData_time_Time_",
},
v2/internal/binding/binding_test/binding_returned_promises_test.go (1)

26-27: Unused Get() method adds noise.

The Get() method returns the receiver's value but isn't referenced in the expected bindings output. If it's truly optional and unused, consider removing it to keep the test fixture minimal.

v2/internal/binding/generate.go (1)

299-301: Consider removing the unused importNamespaces parameter.

The _ parameter indicates importNamespaces is no longer used. While keeping it maintains backward compatibility, if there are no external callers outside this package, consider removing it entirely to simplify the API.

#!/bin/bash
# Check if goTypeToTypescriptType is called from outside this package
rg -n "goTypeToTypescriptType" --type go -g '!*_test.go' | grep -v "v2/internal/binding/generate.go"
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb1191 and 503f9c6.

📒 Files selected for processing (6)
  • v2/internal/binding/binding_test/binding_conflicting_package_name_test.go (2 hunks)
  • v2/internal/binding/binding_test/binding_custom_type_test.go (1 hunks)
  • v2/internal/binding/binding_test/binding_returned_promises_test.go (2 hunks)
  • v2/internal/binding/binding_test/binding_type_alias_test.go (2 hunks)
  • v2/internal/binding/generate.go (3 hunks)
  • v2/internal/binding/generate_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • v2/internal/binding/binding_test/binding_conflicting_package_name_test.go
  • v2/internal/binding/binding_test/binding_custom_type_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
v2/internal/binding/binding_test/binding_returned_promises_test.go (1)
v2/internal/binding/binding_test/binding_test.go (1)
  • BindingTest (13-21)
v2/internal/binding/binding_test/binding_type_alias_test.go (1)
v2/internal/binding/binding_test/binding_test.go (1)
  • BindingTest (13-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Run Go Tests (ubuntu-24.04, 1.23)
  • GitHub Check: Run Go Tests (ubuntu-22.04, 1.23)
  • GitHub Check: Run Go Tests (windows-latest, 1.23)
  • GitHub Check: Run Go Tests (macos-latest, 1.23)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (8)
v2/internal/binding/generate_test.go (1)

166-172: LGTM!

The test loop correctly uses the updated single-parameter goTypeToJSDocType signature.

v2/internal/binding/binding_test/binding_returned_promises_test.go (1)

30-59: LGTM!

The test fixture correctly defines expected TypeScript bindings for various promise return scenarios. The expected output properly reflects the Go-to-TypeScript type mappings.

v2/internal/binding/binding_test/binding_type_alias_test.go (2)

34-48: LGTM!

The expected TypeScript output correctly maps the various return types including the imported int_package.SomeStruct type.


27-31: The code is correct. &MapAlias{} is intentional here—this test file explicitly verifies that type aliases, including map type aliases, are properly handled by the binding generator. The expected output confirms MapAlias is recognized as a named type. No changes needed.

v2/internal/binding/generate.go (4)

148-151: LGTM!

The customTypeMap correctly maps time.Time and uuid.UUID to "string" for TypeScript generation, addressing the PR objective.


153-182: LGTM!

The fullyQualifiedName function correctly checks customTypeMap before falling back to primitive type handling.


275-278: Defensive lookup for future extensibility.

This customTypeMap lookup handles non-fully-qualified type names, which is currently not used but provides extensibility. The code is correct.


303-310: LGTM!

The entityFullReturnType function correctly handles namespace extraction for import statements.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
11.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
v2/internal/binding/generate.go (2)

154-183: Function correctly checks customTypeMap for fully-qualified names.

The updated logic properly constructs the fully-qualified type name and checks customTypeMap before falling through to basic type mappings. This ensures custom types like time.Time are resolved to "string".

💡 Optional: Minor clarity improvement

Line 155 assigns fullType := typeName, then line 157 reassigns it. Consider simplifying:

 func fullyQualifiedName(packageName string, typeName string) string {
-  fullType := typeName
+  var fullType string
   if len(packageName) > 0 {
     fullType = packageName + "." + typeName
+  } else {
+    fullType = typeName
   }

278-300: Comprehensive type mappings with safe fallback.

The function includes a second customTypeMap check for base type names (line 279) and comprehensive primitive mappings for all Go numeric types. The fallback to "any" on line 299 safely handles unknown types.

The customTypeMap is checked in three places: fullyQualifiedName (line 160), goTypeToJSDocType for fully-qualified names (line 268), and for base names (line 279). While this ensures complete coverage, it could be consolidated if maintainability becomes a concern in the future.

v2/internal/binding/generate_test.go (1)

140-169: Good test coverage for custom type mappings.

The six new test cases effectively validate the core functionality:

  • Direct custom type mappings (time.Time, url.URL, uuid.UUID → string)
  • Custom types in slices ([]time.Time → Array<string>)
  • Custom types in maps (map[string]uuid.UUID → Record<string, string>)
  • Pointer to custom type (*time.Time → string)
💡 Consider additional edge-case tests

The existing tests cover the common scenarios well. For more thorough coverage, consider adding:

{
  name:  "nested slice of time.Time",
  input: "[][]time.Time",
  want:  "Array<Array<string>>",
},
{
  name:  "map with time.Time key",
  input: "map[time.Time]string",
  want:  "Record<string, string>",
},
{
  name:  "nested map with custom types",
  input: "map[string]map[string]uuid.UUID",
  want:  "Record<string, Record<string, string>>",
},

These would validate the recursive type resolution for more complex compositions.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07bc375 and 67c3f3a.

📒 Files selected for processing (2)
  • v2/internal/binding/generate.go (3 hunks)
  • v2/internal/binding/generate_test.go (1 hunks)
🔇 Additional comments (6)
v2/internal/binding/generate.go (5)

148-152: LGTM! Custom type mappings correctly defined.

The customTypeMap properly maps time.Time, url.URL, and uuid.UUID to "string" for TypeScript/JavaScript binding generation, aligning with the PR objective to serialize these types as frontend strings.


227-253: Map parsing with bracket-depth tracking correctly implemented.

The bracket-depth logic properly handles nested brackets in map keys (e.g., map[[]int]string or map[map[int]string]bool). The defensive check on line 247 ensures malformed inputs safely fall back to "any".


255-263: Generic type handling with documented limitation.

The regex-based generic type flattening correctly transforms types like main.ListData[string] to main.ListData_string_. The comment on lines 256-257 appropriately documents that nested generics are not currently supported, which is acceptable for the current use case.


265-276: Fully-qualified type handling with customTypeMap check.

The code correctly checks customTypeMap for fully-qualified types (line 268) before returning them unchanged. This ensures that method return types like time.Time are properly mapped to "string" instead of emitting non-existent TypeScript types.


302-304: Function simplified to delegate to centralized type resolution.

The signature retains the importNamespaces parameter (as _) for backward compatibility with calling code (lines 95, 111), while delegating all type conversion logic to goTypeToJSDocType. This centralization improves maintainability.

v2/internal/binding/generate_test.go (1)

173-173: Function call correctly updated for new signature.

The test properly uses the simplified single-parameter goTypeToJSDocType signature, aligning with the centralized type resolution approach in the implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant