Skip to content

Commit ce10123

Browse files
committed
Add properties_sort_order configuration
1 parent 8c68fe9 commit ce10123

File tree

17 files changed

+349
-4
lines changed

17 files changed

+349
-4
lines changed

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning].
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- New `properties_sort_order` configuration option for consistent property ordering in generated TypeScript interfaces. ([@skryukov])
13+
14+
```ruby
15+
Typelizer.configure do |config|
16+
# Sort properties alphabetically with 'id' first
17+
config.properties_sort_order = :id_first_alphabetical
18+
end
19+
```
20+
21+
Available options:
22+
- `:none` (default) - preserve serializer definition order
23+
- `:alphabetical` - sort properties A-Z (case-insensitive)
24+
- `:id_first_alphabetical` - place `id` first, then sort remaining A-Z
25+
- `Proc` - custom sorting logic
26+
27+
```ruby
28+
# Custom sorting example
29+
config.properties_sort_order = ->(props) {
30+
priority = %w[id uuid type]
31+
props.sort_by { |p| [priority.index(p.name) || 999, p.name] }
32+
}
33+
```
34+
1035
## [0.5.4] - 2025-12-08
1136

1237
### Added

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,13 @@ Typelizer.configure do |config|
486486
# Custom transformation for generated properties
487487
config.properties_transformer = ->(properties) { ... }
488488

489+
# Strategy for ordering properties in generated TypeScript interfaces
490+
# :none - preserve serializer definition order (default)
491+
# :alphabetical - sort properties A-Z (case-insensitive)
492+
# :id_first_alphabetical - place 'id' first, then sort remaining A-Z
493+
# Proc - custom sorting function receiving array of Property objects
494+
config.properties_sort_order = :none
495+
489496
# Plugin for model type inference (default: ModelPlugins::Auto)
490497
config.model_plugin = Typelizer::ModelPlugins::Auto
491498

lib/typelizer.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
require_relative "typelizer/contexts/writer_context"
1313
require_relative "typelizer/contexts/scan_context"
14+
require_relative "typelizer/property_sorter"
1415
require_relative "typelizer/interface"
1516
require_relative "typelizer/renderer"
1617
require_relative "typelizer/writer"

lib/typelizer/config.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module Typelizer
2323
:serializer_name_mapper,
2424
:serializer_model_mapper,
2525
:properties_transformer,
26+
:properties_sort_order,
2627
:model_plugin,
2728
:serializer_plugin,
2829
:plugin_configs,
@@ -77,6 +78,7 @@ def self.defaults
7778
types_import_path: "@/types",
7879
types_global: DEFAULT_TYPES_GLOBAL,
7980
properties_transformer: nil,
81+
properties_sort_order: :none,
8082
verbatim_module_syntax: false
8183
}
8284
end

lib/typelizer/interface.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def meta_fields
4848
props = serializer_plugin.meta_fields || []
4949
props = infer_types(props, :_typelizer_meta_attributes)
5050
props = config.properties_transformer.call(props) if config.properties_transformer
51-
props
51+
PropertySorter.sort(props, config.properties_sort_order)
5252
end
5353
end
5454

@@ -63,7 +63,7 @@ def properties
6363
props = serializer_plugin.properties
6464
props = infer_types(props)
6565
props = config.properties_transformer.call(props) if config.properties_transformer
66-
props
66+
PropertySorter.sort(props, config.properties_sort_order)
6767
end
6868
end
6969

lib/typelizer/property_sorter.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Typelizer
4+
module PropertySorter
5+
def self.sort(props, sort_order)
6+
case sort_order
7+
when :none, nil
8+
props
9+
when :alphabetical
10+
props.sort_by { |p| p.name.to_s.downcase }
11+
when :id_first_alphabetical
12+
props.sort_by { |p| [(p.name.to_s.downcase == "id") ? 0 : 1, p.name.to_s.downcase] }
13+
when Proc
14+
result = sort_order.call(props)
15+
result.is_a?(Array) ? result : props
16+
else
17+
props
18+
end
19+
rescue => e
20+
Typelizer.logger.warn("PropertySorter error: #{e.message}, preserving original order")
21+
props
22+
end
23+
end
24+
end

lib/typelizer/serializer_plugins/alba/trait_interface.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ def name
2424
def properties
2525
@properties ||= begin
2626
props, typelizes = plugin.trait_properties(trait_name)
27-
infer_types(props, typelizes)
27+
props = infer_types(props, typelizes)
28+
PropertySorter.sort(props, config.properties_sort_order)
2829
end
2930
end
3031

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Typelizer digest f8e75a4b9244dcdf1ea5cd6d19a22b4f
2+
//
3+
// DO NOT MODIFY: This file was automatically generated by Typelizer.
4+
5+
type AlbaSorted = {
6+
/** Unique identifier */
7+
id: string;
8+
active: boolean;
9+
created_at: string;
10+
email: string;
11+
name: string;
12+
username: string;
13+
}
14+
15+
export default AlbaSorted;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Typelizer digest a36bbfd40b96d00529cd6cafcf56d333
2+
//
3+
// DO NOT MODIFY: This file was automatically generated by Typelizer.
4+
5+
type AmsSorted = {
6+
/** Unique identifier */
7+
id: string;
8+
active: boolean;
9+
created_at: string;
10+
email: string;
11+
name: string;
12+
username: string;
13+
}
14+
15+
export default AmsSorted;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Typelizer digest d263b621499bd3c11087ff72062a6e6d
2+
//
3+
// DO NOT MODIFY: This file was automatically generated by Typelizer.
4+
5+
type OjSerializersSorted = {
6+
/** Unique identifier */
7+
id: string;
8+
active: boolean;
9+
created_at: string;
10+
email: string;
11+
name: string;
12+
username: string;
13+
}
14+
15+
export default OjSerializersSorted;

0 commit comments

Comments
 (0)