typelizer 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +79 -7
- data/README.md +16 -2
- data/lib/typelizer/config.rb +4 -0
- data/lib/typelizer/interface.rb +42 -12
- data/lib/typelizer/model_plugins/active_record.rb +24 -0
- data/lib/typelizer/model_plugins/poro.rb +1 -1
- data/lib/typelizer/property.rb +12 -1
- data/lib/typelizer/renderer.rb +8 -0
- data/lib/typelizer/serializer_plugins/alba.rb +38 -8
- data/lib/typelizer/serializer_plugins/ams.rb +3 -1
- data/lib/typelizer/serializer_plugins/auto.rb +2 -0
- data/lib/typelizer/serializer_plugins/oj_serializers.rb +5 -1
- data/lib/typelizer/serializer_plugins/panko.rb +63 -0
- data/lib/typelizer/templates/comment.ts.erb +8 -0
- data/lib/typelizer/templates/inheritance.ts.erb +1 -0
- data/lib/typelizer/templates/interface.ts.erb +18 -21
- data/lib/typelizer/version.rb +1 -1
- data/lib/typelizer/writer.rb +1 -1
- data/lib/typelizer.rb +1 -0
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed3e7b6339e4a79168c10afd541bba9ea8c509b00f689465df037501327ac6e2
|
4
|
+
data.tar.gz: a7f079a7ad377d1f2754cc1efcb98a7bc0e8af11c3910e8f56b648090ddb7f46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7687ff8e061c46e11f56421684a5629a823debc4d76ac3018e3ab7ac960416a05088662372b38bb4029a650ba6d81c74fbccb49d9821d7864f86023fdddd819e
|
7
|
+
data.tar.gz: 860d8dc24a04302c2e70d30a4ef6811f595ee4e55788ab47d6966c10a405370dc756419b06e15b343b00fc38d495897e706d5be5a5fb1816b9a0da3315c009ce
|
data/CHANGELOG.md
CHANGED
@@ -5,23 +5,93 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog],
|
6
6
|
and this project adheres to [Semantic Versioning].
|
7
7
|
|
8
|
-
## [
|
8
|
+
## [0.4.0] - 2025-05-03
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Support for `panko_serializer` gem ([@PedroAugustoRamalhoDuarte], [@skryukov])
|
13
|
+
- Mark `has_one` and `belongs_to` association as nullable. ([@skryukov])
|
14
|
+
|
15
|
+
By default, `has_one` associations are marked as nullable in TypeScript interfaces.
|
16
|
+
`belongs_to` associations are marked as nullable if the database column is nullable.
|
17
|
+
Use the new `config.associations_strategy = :active_record` configuration option to mark associations as nullable based on the `required`/`optional` options.
|
18
|
+
You can also use the type hint `typelize latest_post: {nullable: false}` in the serializer to override the defaults.
|
19
|
+
|
20
|
+
- Support inherited typelization. ([@skryukov])
|
21
|
+
|
22
|
+
Set `config.inheritance_strategy = :inheritance` to make Typelizer respect the inheritance hierarchy of serializers:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
class AdminSerializer < UserSerializer
|
26
|
+
attributes :admin_level
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
```typescript
|
31
|
+
// app/javascript/types/serializers/Admin.ts
|
32
|
+
import { User } from "@/types";
|
33
|
+
|
34
|
+
export type Admin = User & {
|
35
|
+
admin_level: number;
|
36
|
+
}
|
37
|
+
```
|
38
|
+
|
39
|
+
### Fixed
|
40
|
+
|
41
|
+
- Alba: always use strings for keys in properties. ([@skryukov])
|
42
|
+
This change will fire update of all hashes for Alba serializers, but it's necessary to support inheritance strategy.
|
43
|
+
|
44
|
+
## [0.3.0] - 2025-02-28
|
45
|
+
|
46
|
+
### Added
|
47
|
+
|
48
|
+
- Support transform keys. ([@patvice], [@skryukov])
|
49
|
+
|
50
|
+
Typelizer now respects `transform_keys`/`key_transform` configurations for all plugins.
|
51
|
+
|
52
|
+
- Support typing method def in Alba. ([@patvice])
|
53
|
+
|
54
|
+
The `typelize` helper now can be used before a method definition:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class UserResource < ApplicationResource
|
58
|
+
attributes :id, :name, :email, :chars_in_name
|
59
|
+
|
60
|
+
typelize :number
|
61
|
+
def chars_in_name(obj)
|
62
|
+
obj.name.chars.count
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
- Support for deprecated attributes. ([@Envek])
|
68
|
+
|
69
|
+
They will be marked as deprecated using JSDoc [`@deprecated` tag](https://jsdoc.app/tags-deprecated) in TypeScript interface comments.
|
70
|
+
|
71
|
+
In ActiveModel::Serializer attributes `deprecated` option is recognized.
|
72
|
+
|
73
|
+
For other serializers, you can use `deprecated` option of `typelize` method.
|
74
|
+
|
75
|
+
### Fixed
|
76
|
+
|
77
|
+
- Ignore `nil` values on fingerprint calculation. ([@Envek])
|
9
78
|
|
10
79
|
## [0.2.0] - 2024-11-26
|
11
80
|
|
12
81
|
## Added
|
13
82
|
|
14
|
-
- Add support for enum attributes declared using `ActiveRecord::Enum` or explicitly in serializers ([@
|
15
|
-
- Add support for comments in generated TypeScript interfaces ([@
|
83
|
+
- Add support for enum attributes declared using `ActiveRecord::Enum` or explicitly in serializers ([@Envek])
|
84
|
+
- Add support for comments in generated TypeScript interfaces ([@Envek])
|
16
85
|
- Add TypeScript verbatim module syntax support through `verbatim_module_syntax` config option ([@patvice])
|
17
86
|
- Add `typelizer:generate:refresh` command to clean output directory and regenerate all interfaces ([@patvice])
|
18
87
|
- Allow disabling Typelizer in Rails development with `DISABLE_TYPELIZER` environment variable to `true` ([@okuramasafumi])
|
88
|
+
- Allow to get interfaces without generating TypeScript files ([@Envek])
|
19
89
|
|
20
|
-
##
|
90
|
+
## Fixed
|
21
91
|
|
22
92
|
- Do not override `Typelizer.dirs` in the railtie initializer ([@patvice])
|
23
93
|
- Do not raise on empty nested serializers ([@skryukov])
|
24
|
-
- Attribute options merging in inherited serializers ([@
|
94
|
+
- Attribute options merging in inherited serializers ([@Envek])
|
25
95
|
- Allow recursive type definition ([@okuramasafumi])
|
26
96
|
|
27
97
|
## [0.1.5] - 2024-10-07
|
@@ -75,12 +145,14 @@ and this project adheres to [Semantic Versioning].
|
|
75
145
|
- Initial release ([@skryukov])
|
76
146
|
|
77
147
|
[@davidrunger]: https://github.com/davidrunger
|
78
|
-
[@
|
148
|
+
[@Envek]: https://github.com/Envek
|
79
149
|
[@okuramasafumi]: https://github.com/okuramasafumi
|
80
150
|
[@patvice]: https://github.com/patvice
|
81
151
|
[@skryukov]: https://github.com/skryukov
|
82
152
|
|
83
|
-
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.
|
153
|
+
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.4.0...HEAD
|
154
|
+
[0.4.0]: https://github.com/skryukov/typelizer/compare/v0.3.0...v0.4.0
|
155
|
+
[0.3.0]: https://github.com/skryukov/typelizer/compare/v0.2.0...v0.3.0
|
84
156
|
[0.2.0]: https://github.com/skryukov/typelizer/compare/v0.1.5...v0.2.0
|
85
157
|
[0.1.5]: https://github.com/skryukov/typelizer/compare/v0.1.4...v0.1.5
|
86
158
|
[0.1.4]: https://github.com/skryukov/typelizer/compare/v0.1.3...v0.1.4
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Typelizer is a Ruby gem that automatically generates TypeScript interfaces from
|
|
29
29
|
## Features
|
30
30
|
|
31
31
|
- Automatic TypeScript interface generation
|
32
|
-
- Support for multiple serializer libraries (`Alba`, `ActiveModel::Serializer`, `Oj::Serializer`)
|
32
|
+
- Support for multiple serializer libraries (`Alba`, `ActiveModel::Serializer`, `Oj::Serializer`, `Panko::Serializer`)
|
33
33
|
- File watching and automatic regeneration in development
|
34
34
|
|
35
35
|
## Installation
|
@@ -50,6 +50,10 @@ Include the Typelizer DSL in your serializers:
|
|
50
50
|
class ApplicationResource
|
51
51
|
include Alba::Resource
|
52
52
|
include Typelizer::DSL
|
53
|
+
|
54
|
+
# For Alba, we recommend using the `helper` method instead of `include`.
|
55
|
+
# See the documentation: https://github.com/okuramasafumi/alba/blob/main/README.md#helper
|
56
|
+
# helper Typelizer::DSL
|
53
57
|
end
|
54
58
|
|
55
59
|
class PostResource < ApplicationResource
|
@@ -105,7 +109,7 @@ end
|
|
105
109
|
You can also specify more complex type definitions using a lower-level API:
|
106
110
|
|
107
111
|
```ruby
|
108
|
-
typelize attribute_name: ["string", "Date", optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description"]
|
112
|
+
typelize attribute_name: ["string", "Date", optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description", deprecated: "Use `another_attribute` instead"]
|
109
113
|
```
|
110
114
|
|
111
115
|
### TypeScript Integration
|
@@ -252,6 +256,16 @@ Typelizer.configure do |config|
|
|
252
256
|
# Strategy for handling null values (:nullable, :optional, or :nullable_and_optional)
|
253
257
|
config.null_strategy = :nullable
|
254
258
|
|
259
|
+
# Strategy for handling serializer inheritance (:none, :inheritance)
|
260
|
+
# :none - lists all attributes of the serializer in the type
|
261
|
+
# :inheritance - extends the type from the parent serializer
|
262
|
+
config.inheritance_strategy = :none
|
263
|
+
|
264
|
+
# Strategy for handling `has_one` and `belongs_to` associations nullability (:database, :active_record)
|
265
|
+
# :database - uses the database column nullability
|
266
|
+
# :active_record - uses the `required` / `optional` association options
|
267
|
+
config.associations_strategy = :database
|
268
|
+
|
255
269
|
# Directory where TypeScript interfaces will be generated
|
256
270
|
config.output_dir = Rails.root.join("app/javascript/types/serializers")
|
257
271
|
|
data/lib/typelizer/config.rb
CHANGED
@@ -25,6 +25,8 @@ module Typelizer
|
|
25
25
|
:types_import_path,
|
26
26
|
:types_global,
|
27
27
|
:verbatim_module_syntax,
|
28
|
+
:inheritance_strategy,
|
29
|
+
:associations_strategy,
|
28
30
|
:comments,
|
29
31
|
keyword_init: true
|
30
32
|
) do
|
@@ -47,6 +49,8 @@ module Typelizer
|
|
47
49
|
|
48
50
|
type_mapping: TYPE_MAPPING,
|
49
51
|
null_strategy: :nullable,
|
52
|
+
inheritance_strategy: :none,
|
53
|
+
associations_strategy: :database,
|
50
54
|
comments: false,
|
51
55
|
|
52
56
|
output_dir: js_root.join("types/serializers"),
|
data/lib/typelizer/interface.rb
CHANGED
@@ -17,7 +17,7 @@ module Typelizer
|
|
17
17
|
|
18
18
|
def name
|
19
19
|
if inline?
|
20
|
-
Renderer.
|
20
|
+
Renderer.call("inline_type.ts.erb", properties: properties).strip
|
21
21
|
else
|
22
22
|
config.serializer_name_mapper.call(serializer).tr_s(":", "")
|
23
23
|
end
|
@@ -53,26 +53,56 @@ module Typelizer
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
+
def overwritten_properties
|
57
|
+
return [] unless parent_interface
|
58
|
+
|
59
|
+
@overwritten_properties ||= parent_interface.properties - properties
|
60
|
+
end
|
61
|
+
|
62
|
+
def own_properties
|
63
|
+
@own_properties ||= properties - (parent_interface&.properties || [])
|
64
|
+
end
|
65
|
+
|
66
|
+
def properties_to_print
|
67
|
+
parent_interface ? own_properties : properties
|
68
|
+
end
|
69
|
+
|
70
|
+
def parent_interface
|
71
|
+
return if config.inheritance_strategy == :none
|
72
|
+
return unless serializer.superclass.respond_to?(:typelizer_interface)
|
73
|
+
|
74
|
+
interface = serializer.superclass.typelizer_interface
|
75
|
+
return if interface.empty?
|
76
|
+
|
77
|
+
interface
|
78
|
+
end
|
79
|
+
|
56
80
|
def imports
|
57
|
-
|
58
|
-
.
|
59
|
-
|
81
|
+
@imports ||= begin
|
82
|
+
association_serializers, attribute_types = properties_to_print.filter_map(&:type)
|
83
|
+
.uniq
|
84
|
+
.partition { |type| type.is_a?(Interface) }
|
60
85
|
|
61
|
-
|
62
|
-
|
86
|
+
serializer_types = association_serializers
|
87
|
+
.filter_map { |interface| interface.name if interface.name != name && !interface.inline? }
|
63
88
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
89
|
+
custom_type_imports = attribute_types
|
90
|
+
.flat_map { |type| extract_typescript_types(type.to_s) }
|
91
|
+
.uniq
|
92
|
+
.reject { |type| global_type?(type) }
|
68
93
|
|
69
|
-
|
94
|
+
(custom_type_imports + serializer_types + Array(parent_interface&.name)).uniq - Array(self_type_name)
|
95
|
+
end
|
70
96
|
end
|
71
97
|
|
72
98
|
def inspect
|
73
99
|
"<#{self.class.name} #{name} properties=#{properties.inspect}>"
|
74
100
|
end
|
75
101
|
|
102
|
+
def fingerprint
|
103
|
+
"<#{self.class.name} #{name} properties=[#{properties_to_print.map(&:fingerprint).join(", ")}]>"
|
104
|
+
end
|
105
|
+
|
76
106
|
private
|
77
107
|
|
78
108
|
def self_type_name
|
@@ -90,7 +120,7 @@ module Typelizer
|
|
90
120
|
def infer_types(props, hash_name = :_typelizer_attributes)
|
91
121
|
props.map do |prop|
|
92
122
|
if serializer.respond_to?(hash_name)
|
93
|
-
dsl_type = serializer.public_send(hash_name)[prop.
|
123
|
+
dsl_type = serializer.public_send(hash_name)[prop.column_name.to_sym]
|
94
124
|
if dsl_type&.any?
|
95
125
|
next Property.new(prop.to_h.merge(dsl_type)).tap do |property|
|
96
126
|
property.comment ||= model_plugin.comment_for(property) if config.comments && property.comment != false
|
@@ -9,6 +9,30 @@ module Typelizer
|
|
9
9
|
attr_reader :model_class, :config
|
10
10
|
|
11
11
|
def infer_types(prop)
|
12
|
+
if (association = model_class&.reflect_on_association(prop.column_name.to_sym))
|
13
|
+
case association.macro
|
14
|
+
when :belongs_to
|
15
|
+
foreign_key = association.foreign_key
|
16
|
+
column = model_class&.columns_hash&.dig(foreign_key.to_s)
|
17
|
+
if config.associations_strategy == :database
|
18
|
+
prop.nullable = column.null if column
|
19
|
+
elsif config.associations_strategy == :active_record
|
20
|
+
prop.nullable = !association.options[:required] || association.options[:optional]
|
21
|
+
else
|
22
|
+
raise "Unknown associations strategy: #{config.associations_strategy}"
|
23
|
+
end
|
24
|
+
when :has_one
|
25
|
+
if config.associations_strategy == :database
|
26
|
+
prop.nullable = true
|
27
|
+
elsif config.associations_strategy == :active_record
|
28
|
+
prop.nullable = !association.options[:required]
|
29
|
+
else
|
30
|
+
raise "Unknown associations strategy: #{config.associations_strategy}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
return prop
|
34
|
+
end
|
35
|
+
|
12
36
|
column = model_class&.columns_hash&.dig(prop.column_name.to_s)
|
13
37
|
return prop unless column
|
14
38
|
|
data/lib/typelizer/property.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Typelizer
|
2
2
|
Property = Struct.new(
|
3
3
|
:name, :type, :optional, :nullable,
|
4
|
-
:multi, :column_name, :comment, :enum,
|
4
|
+
:multi, :column_name, :comment, :enum, :deprecated,
|
5
5
|
keyword_init: true
|
6
6
|
) do
|
7
7
|
def inspect
|
@@ -9,6 +9,12 @@ module Typelizer
|
|
9
9
|
"<#{self.class.name} #{props}>"
|
10
10
|
end
|
11
11
|
|
12
|
+
def eql?(other)
|
13
|
+
return false unless other.is_a?(self.class)
|
14
|
+
|
15
|
+
fingerprint == other.fingerprint
|
16
|
+
end
|
17
|
+
|
12
18
|
def to_s
|
13
19
|
type_str = type_name
|
14
20
|
type_str = "Array<#{type_str}>" if multi
|
@@ -17,6 +23,11 @@ module Typelizer
|
|
17
23
|
"#{name}#{"?" if optional}: #{type_str}"
|
18
24
|
end
|
19
25
|
|
26
|
+
def fingerprint
|
27
|
+
props = to_h.merge(type: type_name).reject { |_, v| v.nil? }.map { |k, v| "#{k}=#{v.inspect}" }.join(" ")
|
28
|
+
"<#{self.class.name} #{props}>"
|
29
|
+
end
|
30
|
+
|
20
31
|
private
|
21
32
|
|
22
33
|
def type_name
|
data/lib/typelizer/renderer.rb
CHANGED
@@ -4,6 +4,10 @@ require "erb"
|
|
4
4
|
|
5
5
|
module Typelizer
|
6
6
|
class Renderer
|
7
|
+
def self.call(template, **context)
|
8
|
+
new(template).call(**context)
|
9
|
+
end
|
10
|
+
|
7
11
|
def initialize(template)
|
8
12
|
@erb = ERB.new(File.read(File.join(File.dirname(__FILE__), "templates/#{template}")), trim_mode: "-")
|
9
13
|
end
|
@@ -24,5 +28,9 @@ module Typelizer
|
|
24
28
|
spaces = " " * multiplier
|
25
29
|
content.to_s.each_line.map { |line| line.blank? ? line : "#{spaces}#{line}" }.join
|
26
30
|
end
|
31
|
+
|
32
|
+
def render(template, **context)
|
33
|
+
Renderer.call(template, **context)
|
34
|
+
end
|
27
35
|
end
|
28
36
|
end
|
@@ -13,7 +13,7 @@ module Typelizer
|
|
13
13
|
|
14
14
|
def properties
|
15
15
|
serializer._attributes.map do |name, attr|
|
16
|
-
build_property(name, attr)
|
16
|
+
build_property(name.is_a?(Symbol) ? name.name : name, attr)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -22,19 +22,31 @@ module Typelizer
|
|
22
22
|
:association, :one, :has_one,
|
23
23
|
:many, :has_many,
|
24
24
|
:attributes, :attribute,
|
25
|
+
:method_added,
|
25
26
|
:nested_attribute, :nested,
|
26
27
|
:meta
|
27
28
|
]
|
28
29
|
end
|
29
30
|
|
30
31
|
def typelize_method_transform(method:, name:, binding:, type:, attrs:)
|
31
|
-
|
32
|
+
if method == :method_added && binding.local_variable_defined?(:method_name)
|
33
|
+
name = binding.local_variable_get(:method_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
if [:many, :has_many].include?(method)
|
37
|
+
return {name => [type, attrs.merge(multi: true)]}
|
38
|
+
end
|
32
39
|
|
33
40
|
super
|
34
41
|
end
|
35
42
|
|
36
43
|
def root_key
|
37
|
-
serializer.new({}).send(:_key)
|
44
|
+
root = serializer.new({}).send(:_key)
|
45
|
+
if !root.nil? && has_transform_key?(serializer) && should_transform_root_key?(serializer)
|
46
|
+
fetch_key(serializer, root)
|
47
|
+
else
|
48
|
+
root
|
49
|
+
end
|
38
50
|
end
|
39
51
|
|
40
52
|
def meta_fields
|
@@ -51,6 +63,12 @@ module Typelizer
|
|
51
63
|
private
|
52
64
|
|
53
65
|
def build_property(name, attr, **options)
|
66
|
+
column_name = name
|
67
|
+
|
68
|
+
if has_transform_key?(serializer)
|
69
|
+
name = fetch_key(serializer, name)
|
70
|
+
end
|
71
|
+
|
54
72
|
case attr
|
55
73
|
when Symbol
|
56
74
|
Property.new(
|
@@ -59,7 +77,7 @@ module Typelizer
|
|
59
77
|
optional: false,
|
60
78
|
nullable: false,
|
61
79
|
multi: false,
|
62
|
-
column_name:
|
80
|
+
column_name: column_name,
|
63
81
|
**options
|
64
82
|
)
|
65
83
|
when Proc
|
@@ -69,7 +87,7 @@ module Typelizer
|
|
69
87
|
optional: false,
|
70
88
|
nullable: false,
|
71
89
|
multi: false,
|
72
|
-
column_name:
|
90
|
+
column_name: column_name,
|
73
91
|
**options
|
74
92
|
)
|
75
93
|
when ::Alba::Association
|
@@ -80,7 +98,7 @@ module Typelizer
|
|
80
98
|
optional: false,
|
81
99
|
nullable: false,
|
82
100
|
multi: false, # we override this in typelize_method_transform
|
83
|
-
column_name:
|
101
|
+
column_name: column_name,
|
84
102
|
**options
|
85
103
|
)
|
86
104
|
when ::Alba::TypedAttribute
|
@@ -91,7 +109,7 @@ module Typelizer
|
|
91
109
|
# not sure if that's a good default tbh
|
92
110
|
nullable: !alba_type.instance_variable_get(:@auto_convert),
|
93
111
|
multi: false,
|
94
|
-
column_name:
|
112
|
+
column_name: column_name,
|
95
113
|
**ts_mapper[alba_type.name.to_s],
|
96
114
|
**options
|
97
115
|
)
|
@@ -102,7 +120,7 @@ module Typelizer
|
|
102
120
|
optional: false,
|
103
121
|
nullable: false,
|
104
122
|
multi: false,
|
105
|
-
column_name:
|
123
|
+
column_name: column_name,
|
106
124
|
**options
|
107
125
|
)
|
108
126
|
when ::Alba::ConditionalAttribute
|
@@ -112,6 +130,18 @@ module Typelizer
|
|
112
130
|
end
|
113
131
|
end
|
114
132
|
|
133
|
+
def has_transform_key?(serializer)
|
134
|
+
serializer._transform_type != :none
|
135
|
+
end
|
136
|
+
|
137
|
+
def should_transform_root_key?(serializer)
|
138
|
+
serializer._transforming_root_key
|
139
|
+
end
|
140
|
+
|
141
|
+
def fetch_key(serializer, key)
|
142
|
+
::Alba.transform_key(key, transform_type: serializer._transform_type)
|
143
|
+
end
|
144
|
+
|
115
145
|
private
|
116
146
|
|
117
147
|
def ts_mapper
|
@@ -19,11 +19,13 @@ module Typelizer
|
|
19
19
|
def properties
|
20
20
|
serializer._attributes_data.merge(serializer._reflections).flat_map do |key, association|
|
21
21
|
type = association.options[:serializer] ? Interface.new(serializer: association.options[:serializer]) : nil
|
22
|
+
adapter = ActiveModelSerializers::Adapter.configured_adapter
|
22
23
|
Property.new(
|
23
|
-
name: key.to_s,
|
24
|
+
name: adapter.transform_key_casing!(key.to_s, association.options),
|
24
25
|
type: type,
|
25
26
|
optional: association.options.key?(:if) || association.options.key?(:unless),
|
26
27
|
multi: association.respond_to?(:collection?) && association.collection?,
|
28
|
+
deprecated: (association.options[:deprecated] if association.options.key?(:deprecated)),
|
27
29
|
column_name: association.name.to_s
|
28
30
|
)
|
29
31
|
end
|
@@ -13,6 +13,8 @@ module Typelizer
|
|
13
13
|
Alba
|
14
14
|
elsif defined?(ActiveModel::Serializer) && serializer.ancestors.include?(ActiveModel::Serializer)
|
15
15
|
AMS
|
16
|
+
elsif defined?(::Panko::Serializer) && serializer.ancestors.include?(::Panko::Serializer)
|
17
|
+
Panko
|
16
18
|
else
|
17
19
|
raise "Can't guess serializer plugin for #{serializer}. " \
|
18
20
|
"Please specify it with `config.serializer_plugin`."
|
@@ -11,7 +11,11 @@ module Typelizer
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def properties
|
14
|
-
serializer.
|
14
|
+
transform_keys = serializer.try(:_transform_keys)
|
15
|
+
attributes = serializer._attributes
|
16
|
+
attributes = attributes.transform_keys(&transform_keys) if transform_keys
|
17
|
+
|
18
|
+
attributes
|
15
19
|
.flat_map do |key, options|
|
16
20
|
if options[:association] == :flat
|
17
21
|
Interface.new(serializer: options.fetch(:serializer)).properties
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative "base"
|
2
|
+
|
3
|
+
module Typelizer
|
4
|
+
module SerializerPlugins
|
5
|
+
class Panko < Base
|
6
|
+
def methods_to_typelize
|
7
|
+
[:has_many, :has_one, :attributes, :method_added]
|
8
|
+
end
|
9
|
+
|
10
|
+
def properties
|
11
|
+
descriptor = serializer.new.instance_variable_get(:@descriptor)
|
12
|
+
attributes = descriptor.attributes
|
13
|
+
methods_attributes = descriptor.method_fields
|
14
|
+
has_many_associations = descriptor.has_many_associations
|
15
|
+
has_one_associations = descriptor.has_one_associations
|
16
|
+
|
17
|
+
attributes.map do |att|
|
18
|
+
attribute_property(att)
|
19
|
+
end + methods_attributes.map do |att|
|
20
|
+
attribute_property(att)
|
21
|
+
end + has_many_associations.map do |assoc|
|
22
|
+
association_property(assoc, multi: true)
|
23
|
+
end + has_one_associations.map do |assoc|
|
24
|
+
association_property(assoc, multi: false)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def typelize_method_transform(method:, name:, binding:, type:, attrs:)
|
29
|
+
if method == :method_added && binding.local_variable_defined?(:method)
|
30
|
+
name = binding.local_variable_get(:method)
|
31
|
+
end
|
32
|
+
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def attribute_property(att)
|
39
|
+
Property.new(
|
40
|
+
name: att.alias_name || att.name,
|
41
|
+
optional: false,
|
42
|
+
nullable: false,
|
43
|
+
multi: false,
|
44
|
+
column_name: att.name
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def association_property(assoc, multi: false)
|
49
|
+
key = assoc.name_str
|
50
|
+
serializer = assoc.descriptor.type
|
51
|
+
type = serializer ? Interface.new(serializer: serializer) : nil
|
52
|
+
Property.new(
|
53
|
+
name: key,
|
54
|
+
type: type,
|
55
|
+
optional: false,
|
56
|
+
nullable: false,
|
57
|
+
multi: multi,
|
58
|
+
column_name: key
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%
|
2
|
+
comment = ""
|
3
|
+
comment += property.comment.to_s if interface.config.comments
|
4
|
+
if property.deprecated
|
5
|
+
comment += "\n@deprecated #{property.deprecated.is_a?(String) ? property.deprecated : ''}"
|
6
|
+
end
|
7
|
+
-%>
|
8
|
+
<%= indent("/** #{comment.strip.split("\n").map(&:strip).join("\n * ")} */\n") unless comment.empty? -%>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= interface.overwritten_properties.any? ? "Omit<" : "" %><%= interface.parent_interface.name %><%= "['#{interface.parent_interface.root_key}']" if interface.parent_interface.root_key %><%= interface.overwritten_properties.any? ? ", " + interface.overwritten_properties.map { |pr| "'#{pr.name}'" }.join(' | ') + ">" : ""%>
|
@@ -1,31 +1,28 @@
|
|
1
|
-
|
1
|
+
<% if interface.imports.any? -%>
|
2
2
|
import type {<%= interface.imports.join(", ") %>} from '<%= interface.config.types_import_path %>'
|
3
|
-
|
3
|
+
<% end -%>
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
type <%= interface.name %><%= "Data" if interface.root_key %> = <%=
|
6
|
+
render("inheritance.ts.erb", interface: interface).strip if interface.parent_interface
|
7
|
+
-%>
|
8
|
+
<% unless interface.parent_interface && interface.properties_to_print.empty? -%>
|
9
|
+
<%= " & " if interface.parent_interface %>{
|
10
|
+
<% interface.properties_to_print.each do |property| -%>
|
11
|
+
<%= render("comment.ts.erb", interface: interface, property: property) -%>
|
8
12
|
<%= indent(property) %>;
|
9
|
-
|
13
|
+
<% end -%>
|
10
14
|
}
|
11
|
-
|
12
|
-
type <%= interface.name %> = {
|
13
|
-
<%= interface.root_key %>: <%= interface.name %>Data;
|
14
|
-
<%- interface.meta_fields&.each do |property| -%>
|
15
|
-
<%= indent(property) %>;
|
16
|
-
<%- end -%>
|
17
|
-
}
|
18
|
-
<%- else -%>
|
15
|
+
<% end %><% if interface.root_key %>
|
19
16
|
type <%= interface.name %> = {
|
20
|
-
|
21
|
-
|
17
|
+
<%= indent(interface.root_key) %>: <%= interface.name %>Data;
|
18
|
+
<% interface.meta_fields&.each do |property| -%>
|
22
19
|
<%= indent(property) %>;
|
23
|
-
|
20
|
+
<% end -%>
|
24
21
|
}
|
25
|
-
|
22
|
+
<% end -%>
|
26
23
|
|
27
|
-
|
24
|
+
<% if interface.config.verbatim_module_syntax -%>
|
28
25
|
export type { <%= interface.name %> };
|
29
|
-
|
26
|
+
<% else -%>
|
30
27
|
export default <%= interface.name %>;
|
31
|
-
|
28
|
+
<% end -%>
|
data/lib/typelizer/version.rb
CHANGED
data/lib/typelizer/writer.rb
CHANGED
@@ -32,7 +32,7 @@ module Typelizer
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def write_interface(interface)
|
35
|
-
write_file("#{interface.filename}.ts", interface.
|
35
|
+
write_file("#{interface.filename}.ts", interface.fingerprint) do
|
36
36
|
render_template("interface.ts.erb", interface: interface)
|
37
37
|
end
|
38
38
|
end
|
data/lib/typelizer.rb
CHANGED
@@ -14,6 +14,7 @@ require_relative "typelizer/serializer_plugins/auto"
|
|
14
14
|
require_relative "typelizer/serializer_plugins/oj_serializers"
|
15
15
|
require_relative "typelizer/serializer_plugins/alba"
|
16
16
|
require_relative "typelizer/serializer_plugins/ams"
|
17
|
+
require_relative "typelizer/serializer_plugins/panko"
|
17
18
|
|
18
19
|
require_relative "typelizer/model_plugins/active_record"
|
19
20
|
require_relative "typelizer/model_plugins/poro"
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typelizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-05-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: railties
|
@@ -51,8 +50,11 @@ files:
|
|
51
50
|
- lib/typelizer/serializer_plugins/auto.rb
|
52
51
|
- lib/typelizer/serializer_plugins/base.rb
|
53
52
|
- lib/typelizer/serializer_plugins/oj_serializers.rb
|
53
|
+
- lib/typelizer/serializer_plugins/panko.rb
|
54
|
+
- lib/typelizer/templates/comment.ts.erb
|
54
55
|
- lib/typelizer/templates/fingerprint.ts.erb
|
55
56
|
- lib/typelizer/templates/index.ts.erb
|
57
|
+
- lib/typelizer/templates/inheritance.ts.erb
|
56
58
|
- lib/typelizer/templates/inline_type.ts.erb
|
57
59
|
- lib/typelizer/templates/interface.ts.erb
|
58
60
|
- lib/typelizer/version.rb
|
@@ -67,7 +69,6 @@ metadata:
|
|
67
69
|
homepage_uri: https://github.com/skryukov/typelizer
|
68
70
|
source_code_uri: https://github.com/skryukov/typelizer
|
69
71
|
rubygems_mfa_required: 'true'
|
70
|
-
post_install_message:
|
71
72
|
rdoc_options: []
|
72
73
|
require_paths:
|
73
74
|
- lib
|
@@ -82,8 +83,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
83
|
- !ruby/object:Gem::Version
|
83
84
|
version: '0'
|
84
85
|
requirements: []
|
85
|
-
rubygems_version: 3.
|
86
|
-
signing_key:
|
86
|
+
rubygems_version: 3.6.2
|
87
87
|
specification_version: 4
|
88
88
|
summary: A TypeScript type generator for Ruby serializers.
|
89
89
|
test_files: []
|