ts-morph 0.1.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +8 -0
- data/CHANGELOG.md +33 -0
- data/LICENSE.txt +21 -0
- data/PUBLISHING.md +184 -0
- data/README.md +287 -0
- data/Rakefile +10 -0
- data/SUMMARY.md +114 -0
- data/examples/basic_transformation.rb +63 -0
- data/examples/rails_integration.rb +174 -0
- data/ext/ts_morph/extconf.rb +649 -0
- data/lib/ts/morph/error.rb +17 -0
- data/lib/ts/morph/node_service.rb +351 -0
- data/lib/ts/morph/transformations.rb +98 -0
- data/lib/ts/morph/transformer.rb +82 -0
- data/lib/ts/morph/version.rb +7 -0
- data/lib/ts/morph.rb +50 -0
- data/sig/ts/morph.rbs +6 -0
- metadata +150 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'bundler/setup'
|
|
5
|
+
require 'ts/morph'
|
|
6
|
+
|
|
7
|
+
# Ensure Node dependencies are installed
|
|
8
|
+
unless Ts::Morph.service_available?
|
|
9
|
+
puts "Installing Node.js dependencies..."
|
|
10
|
+
Ts::Morph.install_dependencies
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Sample React component code
|
|
14
|
+
code = <<~TSX
|
|
15
|
+
import React from 'react';
|
|
16
|
+
|
|
17
|
+
const LoginForm = () => {
|
|
18
|
+
return (
|
|
19
|
+
<form>
|
|
20
|
+
<input type="email" placeholder="Email" />
|
|
21
|
+
<input type="password" placeholder="Password" />
|
|
22
|
+
<Button>Login</Button>
|
|
23
|
+
<Button>Cancel</Button>
|
|
24
|
+
</form>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default LoginForm;
|
|
29
|
+
TSX
|
|
30
|
+
|
|
31
|
+
puts "Original code:"
|
|
32
|
+
puts code
|
|
33
|
+
puts "\n" + "="*50 + "\n"
|
|
34
|
+
|
|
35
|
+
# Define transformations
|
|
36
|
+
transformations = [
|
|
37
|
+
# Add variant props to buttons
|
|
38
|
+
{
|
|
39
|
+
type: 'addDefaultProps',
|
|
40
|
+
component: 'Button',
|
|
41
|
+
props: { variant: 'primary', size: 'medium' }
|
|
42
|
+
},
|
|
43
|
+
# Wrap form in FormProvider
|
|
44
|
+
{
|
|
45
|
+
type: 'wrapComponents',
|
|
46
|
+
target: 'form',
|
|
47
|
+
wrapper: 'FormProvider'
|
|
48
|
+
},
|
|
49
|
+
# Add necessary imports
|
|
50
|
+
{
|
|
51
|
+
type: 'addImports',
|
|
52
|
+
imports: [
|
|
53
|
+
{ from: '@/components/ui', named: ['Button', 'FormProvider'] },
|
|
54
|
+
{ from: '@/hooks', named: ['useForm'] }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# Transform the code
|
|
60
|
+
transformed = Ts::Morph.transform(code, transformations)
|
|
61
|
+
|
|
62
|
+
puts "Transformed code:"
|
|
63
|
+
puts transformed
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of integrating ts-morph with a Rails application
|
|
5
|
+
# This would typically go in app/services/react_transformer_service.rb
|
|
6
|
+
|
|
7
|
+
require 'bundler/setup'
|
|
8
|
+
require 'ts/morph'
|
|
9
|
+
require 'ts/morph/transformations'
|
|
10
|
+
|
|
11
|
+
class ReactTransformerService
|
|
12
|
+
include Ts::Morph::Transformations
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# Transform React code for a specific site configuration
|
|
16
|
+
def transform_for_site(react_code, site)
|
|
17
|
+
transformations = build_transformations_for_site(site)
|
|
18
|
+
Ts::Morph.transform(react_code, transformations)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Standardize all components in the code
|
|
22
|
+
def standardize_components(react_code)
|
|
23
|
+
transformations = [
|
|
24
|
+
# Ensure all buttons have consistent styling
|
|
25
|
+
add_default_props('Button', {
|
|
26
|
+
variant: 'primary',
|
|
27
|
+
size: 'medium',
|
|
28
|
+
className: 'transition-all duration-200'
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
# Wrap forms with validation provider
|
|
32
|
+
wrap_components('form', 'FormProvider'),
|
|
33
|
+
|
|
34
|
+
# Add standard imports
|
|
35
|
+
add_imports([
|
|
36
|
+
import_spec(from: '@/components/ui', named: ['Button', 'Card', 'Input']),
|
|
37
|
+
import_spec(from: '@/lib/utils', named: ['cn']),
|
|
38
|
+
import_spec(from: 'react', named: ['useState', 'useEffect'])
|
|
39
|
+
])
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
Ts::Morph.transform(react_code, transformations)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Apply a theme to components
|
|
46
|
+
def apply_theme(react_code, theme_name)
|
|
47
|
+
theme = load_theme(theme_name)
|
|
48
|
+
|
|
49
|
+
transformations = [
|
|
50
|
+
inject_styles(theme[:component_styles]),
|
|
51
|
+
add_imports([import_spec(from: '@/themes', default: 'theme')])
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
Ts::Morph.transform(react_code, transformations)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def build_transformations_for_site(site)
|
|
60
|
+
transformations = []
|
|
61
|
+
|
|
62
|
+
# Add site-specific button styling
|
|
63
|
+
if site.button_style
|
|
64
|
+
transformations << add_default_props('Button',
|
|
65
|
+
site.button_style.symbolize_keys
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Apply site theme
|
|
70
|
+
if site.theme_name
|
|
71
|
+
theme = load_theme(site.theme_name)
|
|
72
|
+
transformations << inject_styles(theme[:component_styles])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Add site-specific imports
|
|
76
|
+
if site.uses_analytics?
|
|
77
|
+
transformations << add_imports([
|
|
78
|
+
import_spec(from: '@/analytics', named: ['trackEvent'])
|
|
79
|
+
])
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
transformations
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def load_theme(theme_name)
|
|
86
|
+
themes = {
|
|
87
|
+
'modern' => {
|
|
88
|
+
component_styles: {
|
|
89
|
+
'Container' => {
|
|
90
|
+
padding: '2rem',
|
|
91
|
+
maxWidth: '1200px',
|
|
92
|
+
margin: '0 auto'
|
|
93
|
+
},
|
|
94
|
+
'Card' => {
|
|
95
|
+
borderRadius: '12px',
|
|
96
|
+
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
|
97
|
+
padding: '1.5rem'
|
|
98
|
+
},
|
|
99
|
+
'Button' => {
|
|
100
|
+
borderRadius: '8px',
|
|
101
|
+
fontWeight: '600'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
'minimal' => {
|
|
106
|
+
component_styles: {
|
|
107
|
+
'Container' => {
|
|
108
|
+
padding: '1rem',
|
|
109
|
+
maxWidth: '960px',
|
|
110
|
+
margin: '0 auto'
|
|
111
|
+
},
|
|
112
|
+
'Card' => {
|
|
113
|
+
border: '1px solid #e5e5e5',
|
|
114
|
+
padding: '1rem'
|
|
115
|
+
},
|
|
116
|
+
'Button' => {
|
|
117
|
+
border: '1px solid currentColor',
|
|
118
|
+
background: 'transparent'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
themes[theme_name] || themes['modern']
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Example usage
|
|
130
|
+
if __FILE__ == $0
|
|
131
|
+
# Sample component
|
|
132
|
+
component_code = <<~TSX
|
|
133
|
+
import React from 'react';
|
|
134
|
+
|
|
135
|
+
const Dashboard = () => {
|
|
136
|
+
return (
|
|
137
|
+
<Container>
|
|
138
|
+
<h1>Welcome to Dashboard</h1>
|
|
139
|
+
<Card>
|
|
140
|
+
<h2>Your Stats</h2>
|
|
141
|
+
<Button onClick={() => console.log('Refresh')}>
|
|
142
|
+
Refresh Data
|
|
143
|
+
</Button>
|
|
144
|
+
</Card>
|
|
145
|
+
</Container>
|
|
146
|
+
);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export default Dashboard;
|
|
150
|
+
TSX
|
|
151
|
+
|
|
152
|
+
puts "Original component:"
|
|
153
|
+
puts component_code
|
|
154
|
+
puts "\n" + "="*50 + "\n"
|
|
155
|
+
|
|
156
|
+
# Mock site object
|
|
157
|
+
Site = Struct.new(:button_style, :theme_name, :uses_analytics?) do
|
|
158
|
+
def uses_analytics?
|
|
159
|
+
self[:uses_analytics?] || false
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
site = Site.new(
|
|
164
|
+
{ variant: 'primary', size: 'large' },
|
|
165
|
+
'modern',
|
|
166
|
+
true
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Transform for site
|
|
170
|
+
transformed = ReactTransformerService.transform_for_site(component_code, site)
|
|
171
|
+
|
|
172
|
+
puts "Transformed for site:"
|
|
173
|
+
puts transformed
|
|
174
|
+
end
|