ether-code 0.1.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.
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/cli/compiler.js +298 -0
- package/cli/ether.js +532 -0
- package/cli/watcher.js +106 -0
- package/generators/css-generator.js +583 -0
- package/generators/graphql-generator.js +868 -0
- package/generators/html-generator.js +745 -0
- package/generators/js-generator.js +909 -0
- package/generators/node-generator.js +467 -0
- package/generators/php-generator.js +706 -0
- package/generators/python-generator.js +913 -0
- package/generators/react-generator.js +599 -0
- package/generators/ruby-generator.js +904 -0
- package/generators/sql-generator.js +988 -0
- package/generators/ts-generator.js +569 -0
- package/i18n/i18n-css.json +743 -0
- package/i18n/i18n-graphql.json +1531 -0
- package/i18n/i18n-html.json +572 -0
- package/i18n/i18n-js.json +2790 -0
- package/i18n/i18n-node.json +2442 -0
- package/i18n/i18n-php.json +4306 -0
- package/i18n/i18n-python.json +3080 -0
- package/i18n/i18n-react.json +1784 -0
- package/i18n/i18n-ruby.json +1858 -0
- package/i18n/i18n-sql.json +3466 -0
- package/i18n/i18n-ts.json +442 -0
- package/lexer/ether-lexer.js +728 -0
- package/lexer/tokens.js +292 -0
- package/package.json +45 -0
- package/parsers/ast-css.js +545 -0
- package/parsers/ast-graphql.js +424 -0
- package/parsers/ast-html.js +886 -0
- package/parsers/ast-js.js +750 -0
- package/parsers/ast-node.js +2440 -0
- package/parsers/ast-php.js +957 -0
- package/parsers/ast-react.js +580 -0
- package/parsers/ast-ruby.js +895 -0
- package/parsers/ast-ts.js +1352 -0
- package/parsers/css-parser.js +1981 -0
- package/parsers/graphql-parser.js +2011 -0
- package/parsers/html-parser.js +1181 -0
- package/parsers/js-parser.js +2564 -0
- package/parsers/node-parser.js +2644 -0
- package/parsers/php-parser.js +3037 -0
- package/parsers/react-parser.js +1035 -0
- package/parsers/ruby-parser.js +2680 -0
- package/parsers/ts-parser.js +3881 -0
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const { JSGenerator } = require('./js-generator')
|
|
3
|
+
|
|
4
|
+
class ReactGenerator extends JSGenerator {
|
|
5
|
+
constructor(i18nPath = null) {
|
|
6
|
+
super(i18nPath)
|
|
7
|
+
this.reactMap = {}
|
|
8
|
+
this.hookMap = {}
|
|
9
|
+
|
|
10
|
+
if (i18nPath) {
|
|
11
|
+
this.loadReactI18n(i18nPath)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.buildReactMaps()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
loadReactI18n(filePath) {
|
|
18
|
+
const content = fs.readFileSync(filePath, 'utf-8')
|
|
19
|
+
const i18n = JSON.parse(content)
|
|
20
|
+
|
|
21
|
+
const addToMap = (map, section) => {
|
|
22
|
+
if (!section) return
|
|
23
|
+
for (const [key, translations] of Object.entries(section)) {
|
|
24
|
+
if (translations && translations.fr && translations.react) {
|
|
25
|
+
map[translations.fr.toLowerCase()] = translations.react
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const sectionName of Object.keys(i18n)) {
|
|
31
|
+
addToMap(this.reactMap, i18n[sectionName])
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
buildReactMaps() {
|
|
36
|
+
this.hookMap = {
|
|
37
|
+
'état': 'useState',
|
|
38
|
+
'utiliser état': 'useState',
|
|
39
|
+
'effet': 'useEffect',
|
|
40
|
+
'utiliser effet': 'useEffect',
|
|
41
|
+
'contexte': 'useContext',
|
|
42
|
+
'utiliser contexte': 'useContext',
|
|
43
|
+
'réducteur': 'useReducer',
|
|
44
|
+
'utiliser réducteur': 'useReducer',
|
|
45
|
+
'rappel': 'useCallback',
|
|
46
|
+
'utiliser rappel': 'useCallback',
|
|
47
|
+
'mémo': 'useMemo',
|
|
48
|
+
'utiliser mémo': 'useMemo',
|
|
49
|
+
'référence': 'useRef',
|
|
50
|
+
'utiliser référence': 'useRef',
|
|
51
|
+
'effet mise en page': 'useLayoutEffect',
|
|
52
|
+
'utiliser effet mise en page': 'useLayoutEffect',
|
|
53
|
+
'valeur imperative': 'useImperativeHandle',
|
|
54
|
+
'debug': 'useDebugValue',
|
|
55
|
+
'utiliser debug': 'useDebugValue',
|
|
56
|
+
'utiliser id': 'useId',
|
|
57
|
+
'transition': 'useTransition',
|
|
58
|
+
'utiliser transition': 'useTransition',
|
|
59
|
+
'valeur différée': 'useDeferredValue',
|
|
60
|
+
'utiliser valeur différée': 'useDeferredValue',
|
|
61
|
+
'effet insertion': 'useInsertionEffect',
|
|
62
|
+
'valeur sync externe': 'useSyncExternalStore'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.componentMap = {
|
|
66
|
+
'fragment': 'Fragment',
|
|
67
|
+
'suspense': 'Suspense',
|
|
68
|
+
'limite erreur': 'ErrorBoundary',
|
|
69
|
+
'portail': 'Portal',
|
|
70
|
+
'strict': 'StrictMode',
|
|
71
|
+
'mode strict': 'StrictMode',
|
|
72
|
+
'profileur': 'Profiler'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.apiMap = {
|
|
76
|
+
'créer élément': 'createElement',
|
|
77
|
+
'cloner élément': 'cloneElement',
|
|
78
|
+
'est élément valide': 'isValidElement',
|
|
79
|
+
'enfants': 'Children',
|
|
80
|
+
'créer ref': 'createRef',
|
|
81
|
+
'transferer ref': 'forwardRef',
|
|
82
|
+
'memoriser': 'mémo',
|
|
83
|
+
'paresseux': 'lazy',
|
|
84
|
+
'créer contexte': 'createContext',
|
|
85
|
+
'créer portail': 'createPortal'
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
translate(word) {
|
|
90
|
+
const lower = word.toLowerCase()
|
|
91
|
+
return this.hookMap[lower] ||
|
|
92
|
+
this.componentMap[lower] ||
|
|
93
|
+
this.apiMap[lower] ||
|
|
94
|
+
this.reactMap[lower] ||
|
|
95
|
+
super.translate(word)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
generate(ast) {
|
|
99
|
+
this.output = ''
|
|
100
|
+
this.indent = 0
|
|
101
|
+
|
|
102
|
+
if (Array.isArray(ast)) {
|
|
103
|
+
for (const node of ast) {
|
|
104
|
+
const result = this.generateNode(node)
|
|
105
|
+
if (result !== undefined && result !== '' && this.output === '') {
|
|
106
|
+
return result
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} else if (ast && ast.type) {
|
|
110
|
+
const result = this.generateNode(ast)
|
|
111
|
+
if (result !== undefined && result !== '' && this.output === '') {
|
|
112
|
+
return result
|
|
113
|
+
}
|
|
114
|
+
} else if (ast && ast.body) {
|
|
115
|
+
for (const node of ast.body) {
|
|
116
|
+
const result = this.generateNode(node)
|
|
117
|
+
if (result !== undefined && result !== '' && this.output === '') {
|
|
118
|
+
return result
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return this.output.trim()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
generateNode(node) {
|
|
127
|
+
if (!node) return ''
|
|
128
|
+
|
|
129
|
+
switch (node.type) {
|
|
130
|
+
case 'JSXElement':
|
|
131
|
+
return this.generateJSXElement(node)
|
|
132
|
+
case 'JSXFragment':
|
|
133
|
+
return this.generateJSXFragment(node)
|
|
134
|
+
case 'JSXText':
|
|
135
|
+
return this.generateJSXText(node)
|
|
136
|
+
case 'JSXExpression':
|
|
137
|
+
case 'JSXExpressionContainer':
|
|
138
|
+
return this.generateJSXExpression(node)
|
|
139
|
+
case 'JSXSpreadAttribute':
|
|
140
|
+
return this.generateJSXSpreadAttribute(node)
|
|
141
|
+
case 'JSXAttribute':
|
|
142
|
+
return this.generateJSXAttribute(node)
|
|
143
|
+
case 'ComponentDeclaration':
|
|
144
|
+
return this.generateComponent(node)
|
|
145
|
+
case 'HookCall':
|
|
146
|
+
return this.generateHookCall(node)
|
|
147
|
+
default:
|
|
148
|
+
return super.generateNode(node)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
generateJSXElement(node) {
|
|
153
|
+
const tagName = this.translateJSXTag(node.openingElement?.name || node.name || node.tag)
|
|
154
|
+
const attributes = this.generateJSXAttributes(node.openingElement?.attributes || node.attributes || [])
|
|
155
|
+
const children = node.children || []
|
|
156
|
+
const selfClosing = node.selfClosing || node.openingElement?.selfClosing || children.length === 0
|
|
157
|
+
|
|
158
|
+
if (selfClosing && children.length === 0) {
|
|
159
|
+
return `<${tagName}${attributes} />`
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let result = `<${tagName}${attributes}>`
|
|
163
|
+
|
|
164
|
+
const hasComplexChildren = children.some(c =>
|
|
165
|
+
c.type === 'JSXElement' || c.type === 'JSXFragment' ||
|
|
166
|
+
(c.type === 'JSXText' && c.value?.includes('\n'))
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if (hasComplexChildren) {
|
|
170
|
+
result += '\n'
|
|
171
|
+
this.indent++
|
|
172
|
+
for (const child of children) {
|
|
173
|
+
const childContent = this.generateJSXChild(child)
|
|
174
|
+
if (childContent.trim()) {
|
|
175
|
+
result += this.getIndent() + childContent + '\n'
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
this.indent--
|
|
179
|
+
result += this.getIndent() + `</${tagName}>`
|
|
180
|
+
} else {
|
|
181
|
+
for (const child of children) {
|
|
182
|
+
result += this.generateJSXChild(child)
|
|
183
|
+
}
|
|
184
|
+
result += `</${tagName}>`
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return result
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
generateJSXFragment(node) {
|
|
191
|
+
const children = node.children || []
|
|
192
|
+
|
|
193
|
+
if (children.length === 0) {
|
|
194
|
+
return '<></>'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let result = '<>\n'
|
|
198
|
+
this.indent++
|
|
199
|
+
|
|
200
|
+
for (const child of children) {
|
|
201
|
+
const childContent = this.generateJSXChild(child)
|
|
202
|
+
if (childContent.trim()) {
|
|
203
|
+
result += this.getIndent() + childContent + '\n'
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
this.indent--
|
|
208
|
+
result += this.getIndent() + '</>'
|
|
209
|
+
|
|
210
|
+
return result
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
generateJSXChild(child) {
|
|
214
|
+
if (!child) return ''
|
|
215
|
+
|
|
216
|
+
switch (child.type) {
|
|
217
|
+
case 'JSXElement':
|
|
218
|
+
return this.generateJSXElement(child)
|
|
219
|
+
case 'JSXFragment':
|
|
220
|
+
return this.generateJSXFragment(child)
|
|
221
|
+
case 'JSXText':
|
|
222
|
+
return this.generateJSXText(child)
|
|
223
|
+
case 'JSXExpressionContainer':
|
|
224
|
+
case 'JSXExpression':
|
|
225
|
+
return this.generateJSXExpression(child)
|
|
226
|
+
default:
|
|
227
|
+
if (typeof child === 'string') {
|
|
228
|
+
return child.trim()
|
|
229
|
+
}
|
|
230
|
+
return this.generateNode(child)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
generateJSXText(node) {
|
|
235
|
+
const text = node.value || node.raw || ''
|
|
236
|
+
return text.trim()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
generateJSXExpression(node) {
|
|
240
|
+
const expression = node.expression || node.value
|
|
241
|
+
if (!expression) return '{}'
|
|
242
|
+
|
|
243
|
+
if (expression.type === 'JSXEmptyExpression') {
|
|
244
|
+
return '{/* */}'
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return '{' + this.generateNode(expression) + '}'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
generateJSXAttributes(attributes) {
|
|
251
|
+
if (!attributes || attributes.length === 0) {
|
|
252
|
+
return ''
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const parts = attributes.map(attr => {
|
|
256
|
+
if (attr.type === 'JSXSpreadAttribute') {
|
|
257
|
+
return this.generateJSXSpreadAttribute(attr)
|
|
258
|
+
}
|
|
259
|
+
return this.generateJSXAttribute(attr)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
return ' ' + parts.join(' ')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
generateJSXAttribute(attr) {
|
|
266
|
+
const name = this.translateJSXAttribute(attr.name?.name || attr.name)
|
|
267
|
+
const value = attr.value
|
|
268
|
+
|
|
269
|
+
if (value === null || value === undefined || value === true) {
|
|
270
|
+
return name
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (typeof value === 'string') {
|
|
274
|
+
return `${name}="${value}"`
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (value.type === 'JSXExpressionContainer' || value.type === 'JSXExpression') {
|
|
278
|
+
return `${name}=${this.generateJSXExpression(value)}`
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (value.type === 'Literal') {
|
|
282
|
+
if (typeof value.value === 'string') {
|
|
283
|
+
return `${name}="${value.value}"`
|
|
284
|
+
}
|
|
285
|
+
return `${name}={${this.generateLiteral(value)}}`
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return `${name}={${this.generateNode(value)}}`
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
generateJSXSpreadAttribute(attr) {
|
|
292
|
+
const expression = attr.argument || attr.expression
|
|
293
|
+
return `{...${this.generateNode(expression)}}`
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
translateJSXTag(tag) {
|
|
297
|
+
if (typeof tag !== 'string') {
|
|
298
|
+
if (tag.type === 'JSXMemberExpression') {
|
|
299
|
+
return this.generateJSXMemberExpression(tag)
|
|
300
|
+
}
|
|
301
|
+
if (tag.type === 'Identifier') {
|
|
302
|
+
return this.translateJSXTag(tag.name)
|
|
303
|
+
}
|
|
304
|
+
return tag
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const lower = tag.toLowerCase()
|
|
308
|
+
|
|
309
|
+
if (this.componentMap[lower]) {
|
|
310
|
+
return this.componentMap[lower]
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const translations = {
|
|
314
|
+
'division': 'div',
|
|
315
|
+
'paragraphe': 'p',
|
|
316
|
+
'titre1': 'h1',
|
|
317
|
+
'titre2': 'h2',
|
|
318
|
+
'titre3': 'h3',
|
|
319
|
+
'titre4': 'h4',
|
|
320
|
+
'titre5': 'h5',
|
|
321
|
+
'titre6': 'h6',
|
|
322
|
+
'portee': 'span',
|
|
323
|
+
'bouton': 'button',
|
|
324
|
+
'lien': 'a',
|
|
325
|
+
'image': 'img',
|
|
326
|
+
'formulaire': 'form',
|
|
327
|
+
'champ': 'input',
|
|
328
|
+
'zone texte': 'textarea',
|
|
329
|
+
'étiquette': 'label',
|
|
330
|
+
'etiquette': 'label',
|
|
331
|
+
'sélection': 'select',
|
|
332
|
+
'option': 'option',
|
|
333
|
+
'liste': 'ul',
|
|
334
|
+
'liste ordonnée': 'ol',
|
|
335
|
+
'élément liste': 'li',
|
|
336
|
+
'tableau': 'table',
|
|
337
|
+
'ligne': 'tr',
|
|
338
|
+
'cellule': 'td',
|
|
339
|
+
'entête cellule': 'th',
|
|
340
|
+
'corps tableau': 'tbody',
|
|
341
|
+
'entête tableau': 'thead',
|
|
342
|
+
'pied tableau': 'tfoot',
|
|
343
|
+
'entête': 'header',
|
|
344
|
+
'piedpage': 'footer',
|
|
345
|
+
'navigation': 'nav',
|
|
346
|
+
'section': 'section',
|
|
347
|
+
'article': 'article',
|
|
348
|
+
'côté': 'aside',
|
|
349
|
+
'principal': 'main',
|
|
350
|
+
'video': 'video',
|
|
351
|
+
'audio': 'audio',
|
|
352
|
+
'toile': 'canvas',
|
|
353
|
+
'cadre': 'iframe'
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (translations[lower]) {
|
|
357
|
+
return translations[lower]
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (/^[A-Z]/.test(tag)) {
|
|
361
|
+
return tag
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return tag
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
translateJSXAttribute(attr) {
|
|
368
|
+
const lower = attr.toLowerCase()
|
|
369
|
+
|
|
370
|
+
const translations = {
|
|
371
|
+
'classe': 'className',
|
|
372
|
+
'pour': 'htmlFor',
|
|
373
|
+
'style': 'style',
|
|
374
|
+
'au clic': 'onClick',
|
|
375
|
+
'au double clic': 'onDoubleClick',
|
|
376
|
+
'au changement': 'onChange',
|
|
377
|
+
'a la soumission': 'onSubmit',
|
|
378
|
+
'au survol': 'onMouseOver',
|
|
379
|
+
'au survol fin': 'onMouseOut',
|
|
380
|
+
'souris entrée': 'onMouseEnter',
|
|
381
|
+
'souris sortie': 'onMouseLeave',
|
|
382
|
+
'souris bas': 'onMouseDown',
|
|
383
|
+
'souris haut': 'onMouseUp',
|
|
384
|
+
'au focus': 'onFocus',
|
|
385
|
+
'au focus perdu': 'onBlur',
|
|
386
|
+
'touche bas': 'onKeyDown',
|
|
387
|
+
'touche haut': 'onKeyUp',
|
|
388
|
+
'touche presse': 'onKeyPress',
|
|
389
|
+
'au chargement': 'onLoad',
|
|
390
|
+
'erreur': 'onError',
|
|
391
|
+
'au défilement': 'onScroll',
|
|
392
|
+
'toucher début': 'onTouchStart',
|
|
393
|
+
'toucher mouvement': 'onTouchMove',
|
|
394
|
+
'toucher fin': 'onTouchEnd',
|
|
395
|
+
'glisser début': 'onDragStart',
|
|
396
|
+
'glisser': 'onDrag',
|
|
397
|
+
'glisser fin': 'onDragEnd',
|
|
398
|
+
'deposer': 'onDrop',
|
|
399
|
+
'copier': 'onCopy',
|
|
400
|
+
'coller': 'onPaste',
|
|
401
|
+
'valeur': 'value',
|
|
402
|
+
'valeur défaut': 'defaultValue',
|
|
403
|
+
'coche': 'checked',
|
|
404
|
+
'coché défaut': 'defaultChecked',
|
|
405
|
+
'desactive': 'disabled',
|
|
406
|
+
'lecture seule': 'readOnly',
|
|
407
|
+
'requis': 'required',
|
|
408
|
+
'placeholder': 'placeholder',
|
|
409
|
+
'indicateur': 'placeholder',
|
|
410
|
+
'type': 'type',
|
|
411
|
+
'nom': 'name',
|
|
412
|
+
'identifiant': 'id',
|
|
413
|
+
'référence': 'ref',
|
|
414
|
+
'clé': 'key',
|
|
415
|
+
'enfants': 'children',
|
|
416
|
+
'html interne': 'dangerouslySetInnerHTML',
|
|
417
|
+
'tabindex': 'tabIndex',
|
|
418
|
+
'largeur': 'width',
|
|
419
|
+
'hauteur': 'height',
|
|
420
|
+
'source': 'src',
|
|
421
|
+
'alternatif': 'alt',
|
|
422
|
+
'adresse': 'href',
|
|
423
|
+
'cible': 'target',
|
|
424
|
+
'role': 'role',
|
|
425
|
+
'aria étiquette': 'aria-label',
|
|
426
|
+
'aria etiquette': 'aria-label',
|
|
427
|
+
'aria decrit par': 'aria-describedby',
|
|
428
|
+
'aria cache': 'aria-hidden',
|
|
429
|
+
'aria étendu': 'aria-expanded',
|
|
430
|
+
'aria selectionne': 'aria-selected',
|
|
431
|
+
'aria desactive': 'aria-disabled'
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (translations[lower]) {
|
|
435
|
+
return translations[lower]
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (attr.startsWith('on') || attr.startsWith('aria-') || attr.startsWith('data-')) {
|
|
439
|
+
return attr
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return attr
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
generateJSXMemberExpression(node) {
|
|
446
|
+
const object = node.object?.name || this.generateNode(node.object)
|
|
447
|
+
const property = node.property?.name || this.generateNode(node.property)
|
|
448
|
+
return `${object}.${property}`
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
generateComponent(node) {
|
|
452
|
+
const name = node.name || node.id?.name || 'Component'
|
|
453
|
+
const isArrow = node.arrow !== false
|
|
454
|
+
const props = node.props || node.params || []
|
|
455
|
+
const propsStr = this.generateComponentProps(props)
|
|
456
|
+
|
|
457
|
+
if (isArrow) {
|
|
458
|
+
this.writeLine(`const ${name} = (${propsStr}) => {`)
|
|
459
|
+
} else {
|
|
460
|
+
this.writeLine(`function ${name}(${propsStr}) {`)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
this.indent++
|
|
464
|
+
|
|
465
|
+
if (node.hooks) {
|
|
466
|
+
for (const hook of node.hooks) {
|
|
467
|
+
this.generateHookCall(hook)
|
|
468
|
+
}
|
|
469
|
+
this.writeLine('')
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (node.handlers) {
|
|
473
|
+
for (const handler of node.handlers) {
|
|
474
|
+
this.generateNode(handler)
|
|
475
|
+
}
|
|
476
|
+
this.writeLine('')
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (node.return || node.render) {
|
|
480
|
+
const jsx = this.generateNode(node.return || node.render)
|
|
481
|
+
if (jsx.includes('\n')) {
|
|
482
|
+
this.writeLine('return (')
|
|
483
|
+
this.indent++
|
|
484
|
+
this.writeLine(jsx)
|
|
485
|
+
this.indent--
|
|
486
|
+
this.writeLine(');')
|
|
487
|
+
} else {
|
|
488
|
+
this.writeLine(`return ${jsx};`)
|
|
489
|
+
}
|
|
490
|
+
} else if (node.body) {
|
|
491
|
+
const body = node.body?.body || node.body || []
|
|
492
|
+
for (const stmt of body) {
|
|
493
|
+
this.generateNode(stmt)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
this.indent--
|
|
498
|
+
this.writeLine('};')
|
|
499
|
+
this.writeLine('')
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
generateComponentProps(props) {
|
|
503
|
+
if (!props || props.length === 0) {
|
|
504
|
+
return ''
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (props.length === 1 && props[0].destructure) {
|
|
508
|
+
const destructured = props[0].properties || props[0].names || []
|
|
509
|
+
const propsList = destructured.map(p => {
|
|
510
|
+
if (typeof p === 'string') return p
|
|
511
|
+
const name = p.name || p.key
|
|
512
|
+
if (p.default !== undefined) {
|
|
513
|
+
return `${name} = ${this.generateNode(p.default)}`
|
|
514
|
+
}
|
|
515
|
+
return name
|
|
516
|
+
})
|
|
517
|
+
return `{ ${propsList.join(', ')} }`
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return props.map(p => p.name || p).join(', ')
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
generateHookCall(node) {
|
|
524
|
+
const hookName = this.translate(node.hook || node.name)
|
|
525
|
+
const args = (node.arguments || node.args || []).map(a => this.generateNode(a))
|
|
526
|
+
|
|
527
|
+
if (node.destructure) {
|
|
528
|
+
const [first, second] = node.destructure
|
|
529
|
+
if (second) {
|
|
530
|
+
this.writeLine(`const [${first}, ${second}] = ${hookName}(${args.join(', ')});`)
|
|
531
|
+
} else {
|
|
532
|
+
this.writeLine(`const ${first} = ${hookName}(${args.join(', ')});`)
|
|
533
|
+
}
|
|
534
|
+
} else if (node.result) {
|
|
535
|
+
this.writeLine(`const ${node.result} = ${hookName}(${args.join(', ')});`)
|
|
536
|
+
} else {
|
|
537
|
+
this.writeLine(`${hookName}(${args.join(', ')});`)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
generateArrowFunction(node) {
|
|
542
|
+
if (node.jsx) {
|
|
543
|
+
const params = (node.params || []).map(p => this.generateNode(p)).join(', ')
|
|
544
|
+
const paramsStr = node.params?.length === 1 ? params : `(${params})`
|
|
545
|
+
const jsx = this.generateNode(node.body)
|
|
546
|
+
|
|
547
|
+
if (jsx.includes('\n')) {
|
|
548
|
+
return `${paramsStr} => (\n${this.getIndent()} ${jsx.split('\n').join('\n' + this.getIndent() + ' ')}\n${this.getIndent()})`
|
|
549
|
+
}
|
|
550
|
+
return `${paramsStr} => ${jsx}`
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return super.generateArrowFunction(node)
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
generateImportDeclaration(node) {
|
|
557
|
+
const source = this.generateLiteral(node.source)
|
|
558
|
+
const specifiers = node.specifiers || []
|
|
559
|
+
|
|
560
|
+
if (specifiers.length === 0) {
|
|
561
|
+
this.writeLine(`import ${source};`)
|
|
562
|
+
return
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const defaultSpec = specifiers.find(s => s.type === 'ImportDefaultSpecifier')
|
|
566
|
+
const namespaceSpec = specifiers.find(s => s.type === 'ImportNamespaceSpecifier')
|
|
567
|
+
const namedSpecs = specifiers.filter(s => s.type === 'ImportSpecifier')
|
|
568
|
+
|
|
569
|
+
let importParts = []
|
|
570
|
+
|
|
571
|
+
if (defaultSpec) {
|
|
572
|
+
const name = defaultSpec.local?.name || defaultSpec.local
|
|
573
|
+
importParts.push(this.translate(name))
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (namespaceSpec) {
|
|
577
|
+
importParts.push(`* as ${this.generateNode(namespaceSpec.local)}`)
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (namedSpecs.length > 0) {
|
|
581
|
+
const named = namedSpecs.map(s => {
|
|
582
|
+
const imported = s.imported?.name || s.imported
|
|
583
|
+
const local = s.local?.name || s.local
|
|
584
|
+
const translatedImported = this.translate(imported)
|
|
585
|
+
const translatedLocal = this.translate(local)
|
|
586
|
+
return translatedImported === translatedLocal
|
|
587
|
+
? translatedImported
|
|
588
|
+
: `${translatedImported} as ${translatedLocal}`
|
|
589
|
+
}).join(', ')
|
|
590
|
+
importParts.push(`{ ${named} }`)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
this.writeLine(`import ${importParts.join(', ')} from ${source};`)
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
module.exports = {
|
|
598
|
+
ReactGenerator
|
|
599
|
+
}
|