Migrate ts-rs to but-ts (schemars) for SDK type generation#13117
Migrate ts-rs to but-ts (schemars) for SDK type generation#13117
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR switches several backend-facing TypeScript types from hand-written interfaces to generated definitions produced from Rust structs/enums via ts-rs, and updates desktop/web consumers to import the generated types from @gitbutler/core/api.
Changes:
- Added
ts-rsexport support (export-tsfeature + derives/field overrides) to multiple Rust crates and core ID types. - Added generated TS type files under
packages/core/src/generated/and re-exported them via the generated index barrel. - Updated desktop frontend code to consume generated types (and removed redundant hand-written TS type files).
Reviewed changes
Copilot reviewed 30 out of 57 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/generate-ts-definitions-from-rust.sh | Minor formatting change in the TS generation script. |
| packages/ui/src/lib/components/select/Select.svelte | Adjusts generic typing/defaults for Select item typing. |
| packages/core/src/generated/Author.ts | Generated TS type for backend author model. |
| packages/core/src/generated/BaseBranch.ts | Generated TS type for base-branch payload. |
| packages/core/src/generated/BaseBranchResolution.ts | Generated TS type for upstream base-branch resolution request. |
| packages/core/src/generated/BaseBranchResolutionApproach.ts | Generated TS discriminated-union for base-branch resolution approach. |
| packages/core/src/generated/BranchAuthor.ts | Generated TS type for branch listing “author/signature” (renamed). |
| packages/core/src/generated/BranchListing.ts | Generated TS type for branch listings. |
| packages/core/src/generated/BranchListingDetails.ts | Generated TS type for detailed branch listing payload. |
| packages/core/src/generated/BranchStatus.ts | Generated TS discriminated-union for upstream branch status. |
| packages/core/src/generated/ConflictEntryPresence.ts | Generated TS type for conflict entry presence flags. |
| packages/core/src/generated/EditModeMetadata.ts | Generated TS type for edit-mode metadata payload. |
| packages/core/src/generated/HeadAndMode.ts | Generated TS type for head + operating mode payload. |
| packages/core/src/generated/HeadSha.ts | Generated TS type for head SHA payload. |
| packages/core/src/generated/IntegrationOutcome.ts | Generated TS type for integrate-upstream outcome payload. |
| packages/core/src/generated/NameAndStatus.ts | Generated TS helper type for name + status pairs. |
| packages/core/src/generated/OperatingMode.ts | Generated TS discriminated-union for operating mode. |
| packages/core/src/generated/OutsideWorkspaceMetadata.ts | Generated TS type for outside-workspace metadata. |
| packages/core/src/generated/RemoteCommit.ts | Generated TS type for remote commit payload. |
| packages/core/src/generated/Resolution.ts | Generated TS type for per-stack upstream resolution. |
| packages/core/src/generated/ResolutionApproach.ts | Generated TS discriminated-union for per-stack resolution approach. |
| packages/core/src/generated/SeriesOrder.ts | Generated TS type for series commit-order payload. |
| packages/core/src/generated/StackOrder.ts | Generated TS type for stack reorder payload. |
| packages/core/src/generated/StackReference.ts | Generated TS type for stack references in listings. |
| packages/core/src/generated/StackStatus.ts | Generated TS type for stack status payload. |
| packages/core/src/generated/StackStatuses.ts | Generated TS discriminated-union for upstream integration statuses response. |
| packages/core/src/generated/TreeStatus.ts | Generated TS discriminated-union for tree status. |
| packages/core/src/generated/index.ts | Re-exports generated types from a single barrel. |
| crates/gitbutler-operating-modes/src/lib.rs | Adds ts-rs derives/field TS overrides for mode types. |
| crates/gitbutler-operating-modes/Cargo.toml | Adds optional ts-rs dep + export-ts feature. |
| crates/gitbutler-edit-mode/src/lib.rs | Adds ts-rs derive for conflict presence type. |
| crates/gitbutler-edit-mode/Cargo.toml | Adds optional ts-rs dep + export-ts feature. |
| crates/gitbutler-branch-actions/src/upstream_integration.rs | Adds ts-rs derives + TS field overrides for upstream integration types. |
| crates/gitbutler-branch-actions/src/reorder.rs | Adds ts-rs derives + TS type override for commit ID arrays. |
| crates/gitbutler-branch-actions/src/remote.rs | Adds ts-rs derives + TS type overrides for commit metadata. |
| crates/gitbutler-branch-actions/src/branch.rs | Adds ts-rs derives + TS type overrides/skips for listing payloads. |
| crates/gitbutler-branch-actions/src/base.rs | Adds ts-rs derives + TS type overrides for base branch payload. |
| crates/gitbutler-branch-actions/src/author.rs | Adds ts-rs derives + TS type override for URL field. |
| crates/gitbutler-branch-actions/Cargo.toml | Adds optional ts-rs dep + export-ts feature wiring. |
| crates/but-core/src/id.rs | Implements ts_rs::TS for Id<KIND> under export-ts. |
| crates/but-api/src/legacy/modes.rs | Adds ts-rs derives for legacy mode payload types. |
| crates/but-api/Cargo.toml | Adds optional ts-rs dep + export-ts feature wiring across crates. |
| Cargo.lock | Records ts-rs dependency additions. |
| apps/desktop/src/routes/[projectId]/edit/+page.svelte | Switches to importing EditModeMetadata from @gitbutler/core/api. |
| apps/desktop/src/lib/upstream/types.ts | Replaces local TS types with generated imports; keeps helper functions. |
| apps/desktop/src/lib/stacks/stackEndpoints.ts | Switches StackOrder to generated import. |
| apps/desktop/src/lib/mode/modeService.ts | Re-exports mode-related types from generated API. |
| apps/desktop/src/lib/mode/modeEndpoints.ts | Switches mode endpoint types to generated API types. |
| apps/desktop/src/lib/files/conflicts.ts | Switches ConflictEntryPresence type import to generated API. |
| apps/desktop/src/lib/files/conflictEntryPresence.ts | Re-exports ConflictEntryPresence from generated API. |
| apps/desktop/src/lib/dragging/stackingReorderDropzoneManager.ts | Switches StackOrder to generated import. |
| apps/desktop/src/lib/branches/branchListing.ts | Switches BranchListing type import to generated API. |
| apps/desktop/src/lib/branches/branchEndpoints.ts | Switches upstream integration endpoint types to generated API types. |
| apps/desktop/src/lib/branches/branch.ts | Removes hand-written StackOrder type (now generated). |
| apps/desktop/src/components/workspace/EditCommitPanel.svelte | Switches edit/conflict presence types to generated imports. |
| apps/desktop/src/components/upstream/IntegrateUpstreamModal.svelte | Switches upstream integration types to generated imports and aligns payload shapes. |
| apps/desktop/src/components/branchesPage/BranchListCard.svelte | Switches branch listing types to generated imports. |
estib-vega
left a comment
There was a problem hiding this comment.
Looks good!
We should start looking a using the generated types from the but-sdk and slowly transition away from the ts-rs types.
We already have a bunch of duplicate generated types
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 57 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
packages/ui/src/lib/components/select/Select.svelte:22
SelectPropsis not generic, but it references a module-scopedT(currently effectivelyany). This breaks the component’sgenerics="T extends string"contract and prevents consumers from getting strongly-typedvalue/options/onselectvalues. Consider removing the module-leveltype T = ...entirely and makingSelectPropsgeneric (e.g.,SelectProps<T extends string = string>), then type$props()asSelectProps<T>in the instance script so inference flows fromoptions/valuetoonselect.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 29 out of 30 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
packages/ui/src/lib/components/select/Select.svelte:21
SelectPropsis not generic, but it usesTfrom the module script. ThatTis unrelated to the component generic declared ingenerics="T extends string = string", sovalue,options, andonselectwon’t be typed based on the actual generic argument.
Define SelectProps as SelectProps<T extends string = string> and annotate $props() as SelectProps<T> to connect the prop types to the component generic.
export type SelectProps = {
id?: string;
label?: string;
disabled?: boolean;
loading?: boolean;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 206 out of 227 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
packages/but-sdk/package.json:69
- The
exports.require.defaulttarget points to./src/generated/index.js, but this package is marked"type": "module"and the NAPI build is--esm, soindex.jswill be treated as an ES module. Noderequire()callers selecting therequirecondition will fail to load an ES module fromrequire. Consider either removing therequirecondition (if CJS isn’t supported) or emitting/providing a separate CommonJS entry (e.g.,index.cjs) for therequirecondition.
|
@estib-vega @krlvi if you could both just have a quick look at this, everything should be accounted for. |
|
@mtsgrd hard to parse the whole thing. Thinking about it, I'd say maybe worth waiting for a release before merging this. Just so that we can test it in a nightly for a day or so |
|
@estib-vega thanks for taking the time. Yes, I'm pretty sure this works, but let's not include it in the release. |
|
@krlvi let's merge this? |
Rust side: - Register all frontend-facing types with schemars JsonSchema + register_sdk_type!() across gitbutler-branch-actions, but-api, but-workspace, but-settings, but-core, but-github, but-gitlab, gitbutler-edit-mode, and gitbutler-operating-modes - Add export-schema Cargo feature to gitbutler-branch-actions and gitbutler-edit-mode with conditional schemars guards - Add rename_all = "camelCase" to but_core::ui::WorktreeChanges (fixes ignored_changes field being snake_case in JSON) - Remove skip_serializing_if from SDK-registered types so nullable fields are always serialized (as null), matching the but-ts assumption - Remove ts-rs derives and Cargo feature flags from all crates - Delete generate-ts-definitions-from-rust.sh script - Enhance but-ts: --source-crates filter, nullable-but-required field handling, append-to-existing output mode TypeScript side: - Delete all ts-rs generated files from @gitbutler/core - Remove hand-written type duplicates from the desktop app (~40 files) and replace with direct imports from @gitbutler/but-sdk - Add browser export condition to @gitbutler/but-sdk package.json with empty stub so Vite serves it instead of the napi index.js - Add @gitbutler/but-sdk to Vite optimizeDeps.exclude
| build: { | ||
| rollupOptions: { output: { manualChunks: {} } }, | ||
| rollupOptions: { | ||
| output: { | ||
| manualChunks: {}, | ||
| // When building for the embedded server, merge small chunks to | ||
| // reduce the number of files embedded in the binary. | ||
| ...(process.env.VITE_EMBEDDED_BUILD ? { experimentalMinChunkSize: 500_000 } : {}), | ||
| }, | ||
| }, | ||
| // Tauri supports es2021 | ||
| target: "modules", | ||
| // minify production builds | ||
| minify: !process.env.TAURI_ENV_DEBUG ? "esbuild" : false, | ||
| // ship sourcemaps for better sentry error reports | ||
| sourcemap: true, | ||
| // Sourcemaps are useful for Sentry in Tauri builds but wasteful when | ||
| // the frontend is embedded in the server binary. | ||
| sourcemap: process.env.VITE_EMBEDDED_BUILD ? false : true, |
Replace ts-rs (export-ts) with schemars-based but-ts for all SDK types,
and introduce where missing, removing hand-written TypeScript duplicates
and wiring everything through @gitbutler/but-sdk.
Types migrated to export-schema / register_sdk_type!:
StackReference, BranchListingDetails, StackOrder, SeriesOrder, and all
upstream integration types (NameAndStatus, StackStatus, Resolution,
etc.)
skip_serializing_if so nullable fields are always present in JSON)
ignored_changes serializes as ignoredChanges)
Infrastructure:
gitbutler-edit-mode with conditional schemars guards
errors
fields; remove skip_serializing_if from SDK-registered types instead
e2e test failure (node:module externalized error in Vite dev server)
directly from declaration sites (no re-exports)