typelizer 0.1.5 → 0.2.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 +22 -2
- data/README.md +47 -4
- data/lib/tasks/generate.rake +15 -2
- data/lib/typelizer/config.rb +5 -2
- data/lib/typelizer/dsl.rb +4 -2
- data/lib/typelizer/generator.rb +9 -5
- data/lib/typelizer/interface.rb +11 -2
- data/lib/typelizer/model_plugins/active_record.rb +19 -3
- data/lib/typelizer/model_plugins/poro.rb +8 -0
- data/lib/typelizer/property.rb +3 -1
- data/lib/typelizer/railtie.rb +6 -4
- data/lib/typelizer/templates/index.ts.erb +4 -0
- data/lib/typelizer/templates/interface.ts.erb +5 -0
- data/lib/typelizer/version.rb +1 -1
- data/lib/typelizer.rb +3 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d00f7bda8328c5148fe2bb6053d27066dd128fd834432c92d39417c3bbdc9405
|
4
|
+
data.tar.gz: 8b7fc9baa4b646cd91965b1316d9c9dcb22867a5bc8b65407c3fa36bbff9d85c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9174cd537b757ea89f7a886c1443e12a78f16b269bc16ccb2e49866128c47471df83e55c32cc60ef7148ea4e8cd15d5cc1552f907dad903931c043af97ab6bc2
|
7
|
+
data.tar.gz: f4d59939214975e140d1a9db3cdc8c5bd9a3278baf6f59d686af7434499917ae30581fcb2a2681c1e106372fcda819250a47f5ba61a4844819dac660f8dea93a
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning].
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
## [0.2.0] - 2024-11-26
|
11
|
+
|
12
|
+
## Added
|
13
|
+
|
14
|
+
- Add support for enum attributes declared using `ActiveRecord::Enum` or explicitly in serializers ([@envek])
|
15
|
+
- Add support for comments in generated TypeScript interfaces ([@envek])
|
16
|
+
- Add TypeScript verbatim module syntax support through `verbatim_module_syntax` config option ([@patvice])
|
17
|
+
- Add `typelizer:generate:refresh` command to clean output directory and regenerate all interfaces ([@patvice])
|
18
|
+
- Allow disabling Typelizer in Rails development with `DISABLE_TYPELIZER` environment variable to `true` ([@okuramasafumi])
|
19
|
+
|
20
|
+
## Fixes
|
21
|
+
|
22
|
+
- Do not override `Typelizer.dirs` in the railtie initializer ([@patvice])
|
23
|
+
- Do not raise on empty nested serializers ([@skryukov])
|
24
|
+
- Attribute options merging in inherited serializers ([@envek])
|
25
|
+
- Allow recursive type definition ([@okuramasafumi])
|
26
|
+
|
10
27
|
## [0.1.5] - 2024-10-07
|
11
28
|
|
12
29
|
## Fixed
|
@@ -58,10 +75,13 @@ and this project adheres to [Semantic Versioning].
|
|
58
75
|
- Initial release ([@skryukov])
|
59
76
|
|
60
77
|
[@davidrunger]: https://github.com/davidrunger
|
61
|
-
[@
|
78
|
+
[@envek]: https://github.com/envek
|
79
|
+
[@okuramasafumi]: https://github.com/okuramasafumi
|
80
|
+
[@patvice]: https://github.com/patvice
|
62
81
|
[@skryukov]: https://github.com/skryukov
|
63
82
|
|
64
|
-
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.
|
83
|
+
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.2.0...HEAD
|
84
|
+
[0.2.0]: https://github.com/skryukov/typelizer/compare/v0.1.5...v0.2.0
|
65
85
|
[0.1.5]: https://github.com/skryukov/typelizer/compare/v0.1.4...v0.1.5
|
66
86
|
[0.1.4]: https://github.com/skryukov/typelizer/compare/v0.1.3...v0.1.4
|
67
87
|
[0.1.3]: https://github.com/skryukov/typelizer/compare/v0.1.2...v0.1.3
|
data/README.md
CHANGED
@@ -14,6 +14,7 @@ Typelizer is a Ruby gem that automatically generates TypeScript interfaces from
|
|
14
14
|
- [TypeScript Integration](#typescript-integration)
|
15
15
|
- [Manual Generation](#manual-generation)
|
16
16
|
- [Automatic Generation in Development](#automatic-generation-in-development)
|
17
|
+
- [Disabling Typelizer](#disabling-typelizer)
|
17
18
|
- [Configuration](#configuration)
|
18
19
|
- [Global Configuration](#global-configuration)
|
19
20
|
- [Config Options](#config-options)
|
@@ -79,9 +80,34 @@ class PostResource < ApplicationResource
|
|
79
80
|
attribute :author_name do |post|
|
80
81
|
post.author.name
|
81
82
|
end
|
83
|
+
|
84
|
+
typelize :string, nullable: true, comment: "Author's avatar URL"
|
85
|
+
attribute :avatar do
|
86
|
+
"https://example.com/avatar.png" if active?
|
87
|
+
end
|
82
88
|
end
|
83
89
|
```
|
84
90
|
|
91
|
+
`typelize` can be used with a Hash to specify multiple types at once.
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
class PostResource < ApplicationResource
|
95
|
+
attributes :id, :title, :body, :published_at
|
96
|
+
|
97
|
+
attribute :author_name do |post|
|
98
|
+
post.author.name
|
99
|
+
end
|
100
|
+
|
101
|
+
typelize author_name: :string, published_at: :string
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
You can also specify more complex type definitions using a lower-level API:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
typelize attribute_name: ["string", "Date", optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description"]
|
109
|
+
```
|
110
|
+
|
85
111
|
### TypeScript Integration
|
86
112
|
|
87
113
|
Typelizer generates TypeScript interfaces in the specified output directory:
|
@@ -91,6 +117,7 @@ Typelizer generates TypeScript interfaces in the specified output directory:
|
|
91
117
|
export interface Post {
|
92
118
|
id: number;
|
93
119
|
title: string;
|
120
|
+
category?: "news" | "article" | "blog" | null;
|
94
121
|
body: string;
|
95
122
|
published_at: string | null;
|
96
123
|
author_name: string;
|
@@ -156,11 +183,15 @@ See the [Configuration](#configuration) section for more options.
|
|
156
183
|
|
157
184
|
### Manual Generation
|
158
185
|
|
159
|
-
To manually generate TypeScript interfaces:
|
186
|
+
To manually generate TypeScript interfaces use one of the following commands:
|
160
187
|
|
161
|
-
```
|
162
|
-
|
163
|
-
|
188
|
+
```bash
|
189
|
+
# Generate new interfaces
|
190
|
+
rails typelizer:generate
|
191
|
+
|
192
|
+
# Clean output directory and regenerate all interfaces
|
193
|
+
rails typelizer:generate:refresh
|
194
|
+
````
|
164
195
|
|
165
196
|
### Automatic Generation in Development
|
166
197
|
|
@@ -170,6 +201,10 @@ When [Listen](https://github.com/guard/listen) is installed, Typelizer automatic
|
|
170
201
|
Typelizer.listen = false
|
171
202
|
```
|
172
203
|
|
204
|
+
### Disabling Typelizer
|
205
|
+
|
206
|
+
Sometimes we want to use Typelizer only with manual generation. To disable Typelizer during development, we can set `DISABLE_TYPELIZER` environment variable to `true`. This doesn't affect manual generation.
|
207
|
+
|
173
208
|
## Configuration
|
174
209
|
|
175
210
|
### Global Configuration
|
@@ -227,6 +262,14 @@ Typelizer.configure do |config|
|
|
227
262
|
# List of type names that should be considered global in TypeScript
|
228
263
|
# (i.e. not prefixed with the import path)
|
229
264
|
config.types_global << %w[Array Date Record File FileList]
|
265
|
+
|
266
|
+
# Support TypeScript's Verbatim module syntax option (default: false)
|
267
|
+
# Will change imports and exports of types from default to support this syntax option
|
268
|
+
config.verbatim_module_syntax = false
|
269
|
+
|
270
|
+
# Support comments in generated TypeScript interfaces (default: false)
|
271
|
+
# Will add comments to the generated interfaces
|
272
|
+
config.comments = false
|
230
273
|
end
|
231
274
|
```
|
232
275
|
|
data/lib/tasks/generate.rake
CHANGED
@@ -1,14 +1,27 @@
|
|
1
1
|
namespace :typelizer do
|
2
2
|
desc "Generate TypeScript interfaces from serializers"
|
3
3
|
task generate: :environment do
|
4
|
+
benchmark do
|
5
|
+
Typelizer::Generator.call
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Removes all files in output folder and refreshs all generate TypeScript interfaces from serializers"
|
10
|
+
task "generate:refresh": :environment do
|
11
|
+
benchmark do
|
12
|
+
Typelizer::Generator.call(force: true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def benchmark(&block)
|
4
17
|
require "benchmark"
|
5
18
|
|
6
|
-
ENV["
|
19
|
+
ENV["DISABLE_TYPELIZER"] = "false"
|
7
20
|
|
8
21
|
puts "Generating TypeScript interfaces..."
|
9
22
|
serializers = []
|
10
23
|
time = Benchmark.realtime do
|
11
|
-
serializers =
|
24
|
+
serializers = block.call
|
12
25
|
end
|
13
26
|
|
14
27
|
puts "Finished in #{time} seconds"
|
data/lib/typelizer/config.rb
CHANGED
@@ -24,6 +24,8 @@ module Typelizer
|
|
24
24
|
:output_dir,
|
25
25
|
:types_import_path,
|
26
26
|
:types_global,
|
27
|
+
:verbatim_module_syntax,
|
28
|
+
:comments,
|
27
29
|
keyword_init: true
|
28
30
|
) do
|
29
31
|
class << self
|
@@ -45,13 +47,15 @@ module Typelizer
|
|
45
47
|
|
46
48
|
type_mapping: TYPE_MAPPING,
|
47
49
|
null_strategy: :nullable,
|
50
|
+
comments: false,
|
48
51
|
|
49
52
|
output_dir: js_root.join("types/serializers"),
|
50
53
|
|
51
54
|
types_import_path: "@/types",
|
52
55
|
types_global: %w[Array Date Record File FileList],
|
53
56
|
|
54
|
-
properties_transformer: nil
|
57
|
+
properties_transformer: nil,
|
58
|
+
verbatim_module_syntax: false
|
55
59
|
)
|
56
60
|
end
|
57
61
|
|
@@ -70,7 +74,6 @@ module Typelizer
|
|
70
74
|
|
71
75
|
def method_missing(method, *args, &block)
|
72
76
|
return Typelizer.send(method, *args, &block) if Typelizer.respond_to?(method)
|
73
|
-
|
74
77
|
instance.send(method, *args, &block)
|
75
78
|
end
|
76
79
|
end
|
data/lib/typelizer/dsl.rb
CHANGED
@@ -64,9 +64,11 @@ module Typelizer
|
|
64
64
|
|
65
65
|
unless respond_to?(attribute_name)
|
66
66
|
define_singleton_method(attribute_name) do
|
67
|
-
result = instance_variable_get(instance_variable)
|
67
|
+
result = instance_variable_get(instance_variable) || {}
|
68
68
|
if superclass.respond_to?(attribute_name)
|
69
|
-
result.merge(superclass.send(attribute_name))
|
69
|
+
result.merge(superclass.send(attribute_name)) do |key, currentdef, supervaldef|
|
70
|
+
supervaldef.merge(currentdef)
|
71
|
+
end
|
70
72
|
else
|
71
73
|
result
|
72
74
|
end
|
data/lib/typelizer/generator.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module Typelizer
|
4
4
|
class Generator
|
5
|
-
def self.call
|
6
|
-
new.call
|
5
|
+
def self.call(**args)
|
6
|
+
new.call(**args)
|
7
7
|
end
|
8
8
|
|
9
9
|
def initialize(config = Typelizer::Config)
|
@@ -16,14 +16,18 @@ module Typelizer
|
|
16
16
|
def call(force: false)
|
17
17
|
return unless Typelizer.enabled?
|
18
18
|
|
19
|
-
read_serializers
|
20
|
-
|
21
|
-
interfaces = target_serializers.map(&:typelizer_interface).reject(&:empty?)
|
22
19
|
writer.call(interfaces, force: force)
|
23
20
|
|
24
21
|
interfaces
|
25
22
|
end
|
26
23
|
|
24
|
+
def interfaces
|
25
|
+
@interfaces ||= begin
|
26
|
+
read_serializers
|
27
|
+
target_serializers.map(&:typelizer_interface).reject(&:empty?)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
27
31
|
private
|
28
32
|
|
29
33
|
def target_serializers
|
data/lib/typelizer/interface.rb
CHANGED
@@ -66,7 +66,7 @@ module Typelizer
|
|
66
66
|
.uniq
|
67
67
|
.reject { |type| global_type?(type) }
|
68
68
|
|
69
|
-
(custom_type_imports + serializer_types).uniq
|
69
|
+
(custom_type_imports + serializer_types).uniq - Array(self_type_name)
|
70
70
|
end
|
71
71
|
|
72
72
|
def inspect
|
@@ -75,6 +75,10 @@ module Typelizer
|
|
75
75
|
|
76
76
|
private
|
77
77
|
|
78
|
+
def self_type_name
|
79
|
+
serializer.name.match(/(\w+::)?(\w+)(Serializer|Resource)/)[2]
|
80
|
+
end
|
81
|
+
|
78
82
|
def extract_typescript_types(type)
|
79
83
|
type.split(/[<>\[\],\s|]+/)
|
80
84
|
end
|
@@ -87,7 +91,12 @@ module Typelizer
|
|
87
91
|
props.map do |prop|
|
88
92
|
if serializer.respond_to?(hash_name)
|
89
93
|
dsl_type = serializer.public_send(hash_name)[prop.name.to_sym]
|
90
|
-
|
94
|
+
if dsl_type&.any?
|
95
|
+
next Property.new(prop.to_h.merge(dsl_type)).tap do |property|
|
96
|
+
property.comment ||= model_plugin.comment_for(property) if config.comments && property.comment != false
|
97
|
+
property.enum ||= model_plugin.enum_for(property) if property.enum != false
|
98
|
+
end
|
99
|
+
end
|
91
100
|
end
|
92
101
|
|
93
102
|
model_plugin.infer_types(prop)
|
@@ -2,14 +2,14 @@ module Typelizer
|
|
2
2
|
module ModelPlugins
|
3
3
|
class ActiveRecord
|
4
4
|
def initialize(model_class:, config:)
|
5
|
-
@
|
5
|
+
@model_class = model_class
|
6
6
|
@config = config
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :model_class, :config
|
10
10
|
|
11
11
|
def infer_types(prop)
|
12
|
-
column = columns_hash
|
12
|
+
column = model_class&.columns_hash&.dig(prop.column_name.to_s)
|
13
13
|
return prop unless column
|
14
14
|
|
15
15
|
prop.multi = !!column.try(:array)
|
@@ -26,9 +26,25 @@ module Typelizer
|
|
26
26
|
end
|
27
27
|
|
28
28
|
prop.type = @config.type_mapping[column.type]
|
29
|
+
prop.comment = comment_for(prop)
|
30
|
+
prop.enum = enum_for(prop)
|
31
|
+
prop.type = :string if prop.enum # Ignore underlying column type for enums
|
29
32
|
|
30
33
|
prop
|
31
34
|
end
|
35
|
+
|
36
|
+
def comment_for(prop)
|
37
|
+
column = model_class&.columns_hash&.dig(prop.column_name.to_s)
|
38
|
+
return nil unless column
|
39
|
+
|
40
|
+
prop.comment = column.comment
|
41
|
+
end
|
42
|
+
|
43
|
+
def enum_for(prop)
|
44
|
+
return unless model_class&.defined_enums&.key?(prop.column_name.to_s)
|
45
|
+
|
46
|
+
prop.enum = model_class.defined_enums[prop.column_name.to_s].keys
|
47
|
+
end
|
32
48
|
end
|
33
49
|
end
|
34
50
|
end
|
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,
|
4
|
+
:multi, :column_name, :comment, :enum,
|
5
5
|
keyword_init: true
|
6
6
|
) do
|
7
7
|
def inspect
|
@@ -20,6 +20,8 @@ module Typelizer
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def type_name
|
23
|
+
return enum.map { |v| v.to_s.inspect }.join(" | ") if enum
|
24
|
+
|
23
25
|
type.respond_to?(:name) ? type.name : type || "unknown"
|
24
26
|
end
|
25
27
|
end
|
data/lib/typelizer/railtie.rb
CHANGED
@@ -6,10 +6,12 @@ module Typelizer
|
|
6
6
|
|
7
7
|
initializer "typelizer.configure" do
|
8
8
|
Typelizer.configure do |c|
|
9
|
-
c.dirs
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
if c.dirs.empty?
|
10
|
+
c.dirs = [
|
11
|
+
Rails.root.join("app", "resources"),
|
12
|
+
Rails.root.join("app", "serializers")
|
13
|
+
]
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -1,3 +1,7 @@
|
|
1
1
|
<%- interfaces.each do |interface| -%>
|
2
|
+
<%- if interface.config.verbatim_module_syntax -%>
|
3
|
+
export type { <%= interface.name %> } from './<%= interface.filename %>'
|
4
|
+
<%- else -%>
|
2
5
|
export type { default as <%= interface.name %> } from './<%= interface.filename %>'
|
3
6
|
<%- end -%>
|
7
|
+
<%- end -%>
|
@@ -18,9 +18,14 @@ type <%= interface.name %> = {
|
|
18
18
|
<%- else -%>
|
19
19
|
type <%= interface.name %> = {
|
20
20
|
<%- interface.properties.each do |property| -%>
|
21
|
+
<%= indent("/** #{property.comment.split("\n").map(&:strip).join("\n * ")} */\n") if interface.config.comments && property.comment -%>
|
21
22
|
<%= indent(property) %>;
|
22
23
|
<%- end -%>
|
23
24
|
}
|
24
25
|
<%- end -%>
|
25
26
|
|
27
|
+
<%-if interface.config.verbatim_module_syntax -%>
|
28
|
+
export type { <%= interface.name %> };
|
29
|
+
<%- else -%>
|
26
30
|
export default <%= interface.name %>;
|
31
|
+
<%- end -%>
|
data/lib/typelizer/version.rb
CHANGED
data/lib/typelizer.rb
CHANGED
@@ -26,7 +26,9 @@ require "logger"
|
|
26
26
|
module Typelizer
|
27
27
|
class << self
|
28
28
|
def enabled?
|
29
|
-
|
29
|
+
return false if ENV["DISABLE_TYPELIZER"] == "true" || ENV["DISABLE_TYPELIZER"] == "1"
|
30
|
+
|
31
|
+
ENV["RAILS_ENV"] == "development" || ENV["RACK_ENV"] == "development" || ENV["DISABLE_TYPELIZER"] == "false"
|
30
32
|
end
|
31
33
|
|
32
34
|
attr_accessor :dirs
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typelizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Svyatoslav Kryukov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '0'
|
84
84
|
requirements: []
|
85
|
-
rubygems_version: 3.5.
|
85
|
+
rubygems_version: 3.5.23
|
86
86
|
signing_key:
|
87
87
|
specification_version: 4
|
88
88
|
summary: A TypeScript type generator for Ruby serializers.
|