onelaraveljs 1.0.0 → 1.1.3

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.
Files changed (121) hide show
  1. package/README.md +1 -1
  2. package/bin/onejs-build.js +32 -0
  3. package/index.js +3 -1
  4. package/package.json +11 -3
  5. package/scripts/README-template-compiler.md +133 -0
  6. package/scripts/README.md +61 -0
  7. package/scripts/__pycache__/build.cpython-314.pyc +0 -0
  8. package/scripts/__pycache__/compile.cpython-313.pyc +0 -0
  9. package/scripts/__pycache__/compile.cpython-314.pyc +0 -0
  10. package/scripts/build.py +574 -0
  11. package/scripts/check-system-errors.php +214 -0
  12. package/scripts/compile.py +101 -0
  13. package/scripts/compiler/README_CONFIG.md +196 -0
  14. package/scripts/compiler/__init__.py +18 -0
  15. package/scripts/compiler/__pycache__/__init__.cpython-313.pyc +0 -0
  16. package/scripts/compiler/__pycache__/__init__.cpython-314.pyc +0 -0
  17. package/scripts/compiler/__pycache__/binding_directive_service.cpython-314.pyc +0 -0
  18. package/scripts/compiler/__pycache__/class_binding_handler.cpython-314.pyc +0 -0
  19. package/scripts/compiler/__pycache__/compiler_utils.cpython-313.pyc +0 -0
  20. package/scripts/compiler/__pycache__/compiler_utils.cpython-314.pyc +0 -0
  21. package/scripts/compiler/__pycache__/conditional_handlers.cpython-313.pyc +0 -0
  22. package/scripts/compiler/__pycache__/conditional_handlers.cpython-314.pyc +0 -0
  23. package/scripts/compiler/__pycache__/config.cpython-313.pyc +0 -0
  24. package/scripts/compiler/__pycache__/config.cpython-314.pyc +0 -0
  25. package/scripts/compiler/__pycache__/declaration_tracker.cpython-314.pyc +0 -0
  26. package/scripts/compiler/__pycache__/directive_processors.cpython-313.pyc +0 -0
  27. package/scripts/compiler/__pycache__/directive_processors.cpython-314.pyc +0 -0
  28. package/scripts/compiler/__pycache__/echo_processor.cpython-314.pyc +0 -0
  29. package/scripts/compiler/__pycache__/event_directive_processor.cpython-313.pyc +0 -0
  30. package/scripts/compiler/__pycache__/event_directive_processor.cpython-314.pyc +0 -0
  31. package/scripts/compiler/__pycache__/function_generators.cpython-313.pyc +0 -0
  32. package/scripts/compiler/__pycache__/function_generators.cpython-314.pyc +0 -0
  33. package/scripts/compiler/__pycache__/loop_handlers.cpython-313.pyc +0 -0
  34. package/scripts/compiler/__pycache__/loop_handlers.cpython-314.pyc +0 -0
  35. package/scripts/compiler/__pycache__/main_compiler.cpython-313.pyc +0 -0
  36. package/scripts/compiler/__pycache__/main_compiler.cpython-314.pyc +0 -0
  37. package/scripts/compiler/__pycache__/parsers.cpython-313.pyc +0 -0
  38. package/scripts/compiler/__pycache__/parsers.cpython-314.pyc +0 -0
  39. package/scripts/compiler/__pycache__/php_converter.cpython-313.pyc +0 -0
  40. package/scripts/compiler/__pycache__/php_converter.cpython-314.pyc +0 -0
  41. package/scripts/compiler/__pycache__/php_js_converter.cpython-313.pyc +0 -0
  42. package/scripts/compiler/__pycache__/php_js_converter.cpython-314.pyc +0 -0
  43. package/scripts/compiler/__pycache__/register_parser.cpython-313.pyc +0 -0
  44. package/scripts/compiler/__pycache__/register_parser.cpython-314.pyc +0 -0
  45. package/scripts/compiler/__pycache__/section_handlers.cpython-313.pyc +0 -0
  46. package/scripts/compiler/__pycache__/section_handlers.cpython-314.pyc +0 -0
  47. package/scripts/compiler/__pycache__/show_directive_handler.cpython-314.pyc +0 -0
  48. package/scripts/compiler/__pycache__/style_directive_handler.cpython-314.pyc +0 -0
  49. package/scripts/compiler/__pycache__/template_analyzer.cpython-313.pyc +0 -0
  50. package/scripts/compiler/__pycache__/template_analyzer.cpython-314.pyc +0 -0
  51. package/scripts/compiler/__pycache__/template_processor.cpython-313.pyc +0 -0
  52. package/scripts/compiler/__pycache__/template_processor.cpython-314.pyc +0 -0
  53. package/scripts/compiler/__pycache__/template_processors.cpython-313.pyc +0 -0
  54. package/scripts/compiler/__pycache__/template_processors.cpython-314.pyc +0 -0
  55. package/scripts/compiler/__pycache__/utils.cpython-313.pyc +0 -0
  56. package/scripts/compiler/__pycache__/utils.cpython-314.pyc +0 -0
  57. package/scripts/compiler/__pycache__/wrapper_parser.cpython-313.pyc +0 -0
  58. package/scripts/compiler/__pycache__/wrapper_parser.cpython-314.pyc +0 -0
  59. package/scripts/compiler/binding_directive_service.py +103 -0
  60. package/scripts/compiler/class_binding_handler.py +347 -0
  61. package/scripts/compiler/cli.py +34 -0
  62. package/scripts/compiler/code_generator.py +141 -0
  63. package/scripts/compiler/compiler.config.json +36 -0
  64. package/scripts/compiler/compiler_utils.py +55 -0
  65. package/scripts/compiler/conditional_handlers.py +252 -0
  66. package/scripts/compiler/config.py +107 -0
  67. package/scripts/compiler/declaration_tracker.py +420 -0
  68. package/scripts/compiler/directive_processors.py +603 -0
  69. package/scripts/compiler/echo_processor.py +667 -0
  70. package/scripts/compiler/event_directive_processor.py +1099 -0
  71. package/scripts/compiler/fetch_parser.py +49 -0
  72. package/scripts/compiler/function_generators.py +310 -0
  73. package/scripts/compiler/loop_handlers.py +224 -0
  74. package/scripts/compiler/main_compiler.py +1763 -0
  75. package/scripts/compiler/parsers.py +1418 -0
  76. package/scripts/compiler/php_converter.py +470 -0
  77. package/scripts/compiler/php_js_converter.py +603 -0
  78. package/scripts/compiler/register_parser.py +480 -0
  79. package/scripts/compiler/section_handlers.py +122 -0
  80. package/scripts/compiler/show_directive_handler.py +85 -0
  81. package/scripts/compiler/style_directive_handler.py +169 -0
  82. package/scripts/compiler/template_analyzer.py +162 -0
  83. package/scripts/compiler/template_processor.py +1167 -0
  84. package/scripts/compiler/template_processors.py +1557 -0
  85. package/scripts/compiler/test_compiler.py +69 -0
  86. package/scripts/compiler/utils.py +54 -0
  87. package/scripts/compiler/variables_analyzer.py +135 -0
  88. package/scripts/compiler/view_identifier_generator.py +278 -0
  89. package/scripts/compiler/wrapper_parser.py +78 -0
  90. package/scripts/dev-context.js +311 -0
  91. package/scripts/dev.js +109 -0
  92. package/scripts/generate-assets-order.js +208 -0
  93. package/scripts/migrate-namespace.php +146 -0
  94. package/scripts/node/MIGRATION.md +190 -0
  95. package/scripts/node/README.md +269 -0
  96. package/scripts/node/build.js +208 -0
  97. package/scripts/node/compiler/compiler-utils.js +38 -0
  98. package/scripts/node/compiler/conditional-handlers.js +45 -0
  99. package/scripts/node/compiler/config.js +178 -0
  100. package/scripts/node/compiler/directive-processors.js +51 -0
  101. package/scripts/node/compiler/event-directive-processor.js +182 -0
  102. package/scripts/node/compiler/function-generators.js +239 -0
  103. package/scripts/node/compiler/loop-handlers.js +45 -0
  104. package/scripts/node/compiler/main-compiler.js +236 -0
  105. package/scripts/node/compiler/parsers.js +358 -0
  106. package/scripts/node/compiler/php-converter.js +227 -0
  107. package/scripts/node/compiler/register-parser.js +32 -0
  108. package/scripts/node/compiler/section-handlers.js +46 -0
  109. package/scripts/node/compiler/template-analyzer.js +50 -0
  110. package/scripts/node/compiler/template-processor.js +371 -0
  111. package/scripts/node/compiler/template-processors.js +219 -0
  112. package/scripts/node/compiler/utils.js +203 -0
  113. package/scripts/node/compiler/wrapper-parser.js +25 -0
  114. package/scripts/node/package.json +24 -0
  115. package/scripts/node/test-compiler.js +52 -0
  116. package/scripts/node-run.cjs +28 -0
  117. package/scripts/standardize-directories.php +92 -0
  118. package/src/core/ViewManager.js +4 -4
  119. package/templates/view.module.js +2 -0
  120. package/templates/view.tpl-raw.js +13 -0
  121. package/templates/wraper.js +71 -0
@@ -0,0 +1,603 @@
1
+ """
2
+ Processors cho các Blade directives khác nhau
3
+ """
4
+
5
+ from config import JS_FUNCTION_PREFIX, HTML_ATTR_PREFIX
6
+ from php_converter import php_to_js, convert_php_array_to_json
7
+ from utils import extract_balanced_parentheses
8
+ import re
9
+
10
+ class DirectiveProcessor:
11
+ def __init__(self):
12
+ self.loop_counter = 0
13
+ self.init_functions = []
14
+ self.stack = []
15
+
16
+
17
+
18
+
19
+ def process_auth_directive(self, line):
20
+ """Process @auth directive"""
21
+ if line.startswith('@auth'):
22
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\n if({JS_FUNCTION_PREFIX}.isAuth()){{\n return `'
23
+ elif line.startswith('@guest'):
24
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\n if(!{JS_FUNCTION_PREFIX}.isAuth()){{\n return `'
25
+ return None
26
+
27
+ def process_endauth_directive(self, line):
28
+ """Process @endauth/@endguest directive"""
29
+ if line.startswith('@endauth') or line.startswith('@endguest'):
30
+ return '`\n }\n})}'
31
+ return None
32
+
33
+ def process_can_directive(self, line):
34
+ """Process @can/@cannot directive"""
35
+ if line.startswith('@cannot'):
36
+ # Extract permission from @cannot('permission')
37
+ cannot_match = re.match(r'@cannot\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
38
+ if cannot_match:
39
+ permission = cannot_match.group(1)
40
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.cannot(\'{permission}\')){{\nreturn `'
41
+ elif line.startswith('@can'):
42
+ # Extract permission from @can('permission')
43
+ can_match = re.match(r'@can\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
44
+ if can_match:
45
+ permission = can_match.group(1)
46
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.can(\'{permission}\')){{\nreturn `'
47
+ return None
48
+
49
+ def process_endcan_directive(self, line):
50
+ """Process @endcan/@endcannot directive"""
51
+ if line.startswith('@endcan') or line.startswith('@endcannot'):
52
+ return '`\n }\n})}'
53
+ return None
54
+
55
+ def process_csrf_directive(self, line):
56
+ """Process @csrf directive"""
57
+ if line.startswith('@csrf'):
58
+ return f'<input type="hidden" name="_token" value="${{{JS_FUNCTION_PREFIX}.getCsrfToken()}}">'
59
+ return None
60
+
61
+ def process_method_directive(self, line):
62
+ """Process @method directive"""
63
+ if line.startswith('@method'):
64
+ # Extract method from @method('PUT')
65
+ method_match = re.match(r'@method\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
66
+ if method_match:
67
+ method = method_match.group(1).upper()
68
+ return f'<input type="hidden" name="_method" value="{method}">'
69
+ return None
70
+
71
+ def process_error_directive(self, line):
72
+ """Process @error directive"""
73
+ if line.startswith('@error'):
74
+ # Extract field from @error('field')
75
+ error_match = re.match(r'@error\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
76
+ if error_match:
77
+ field = error_match.group(1)
78
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.hasError(\'{field}\')){{\nreturn `'
79
+ return None
80
+
81
+ def process_enderror_directive(self, line):
82
+ """Process @enderror directive"""
83
+ if line.startswith('@enderror'):
84
+ return '`\n }\n})}'
85
+ return None
86
+
87
+ def process_hassection_directive(self, line):
88
+ """Process @hasSection directive"""
89
+ if line.startswith('@hasSection'):
90
+ # Extract section from @hasSection('section')
91
+ section_match = re.match(r'@hasSection\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
92
+ if section_match:
93
+ section = section_match.group(1)
94
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.hasSection(\'{section}\')){{\nreturn `'
95
+ return None
96
+
97
+ def process_endhassection_directive(self, line):
98
+ """Process @endhassection directive"""
99
+ if line.startswith('@endhassection'):
100
+ return '`\n }\n})}'
101
+ return None
102
+
103
+ def process_empty_directive(self, line, stack, output):
104
+ """Process @empty directive"""
105
+ if line.startswith('@empty'):
106
+ # Extract variable from @empty($variable)
107
+ empty_match = re.match(r'@empty\s*\(\s*\$?(\w+)\s*\)', line)
108
+ if empty_match:
109
+ variable = empty_match.group(1)
110
+ result = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.isEmpty({variable})){{\nreturn `'
111
+ output.append(result)
112
+ stack.append(('empty', len(output)))
113
+ return True
114
+ return False
115
+
116
+ def process_isset_directive(self, line, stack, output):
117
+ """Process @isset directive"""
118
+ if line.startswith('@isset'):
119
+ # Extract variable from @isset($variable)
120
+ isset_match = re.match(r'@isset\s*\(\s*\$?(\w+)\s*\)', line)
121
+ if isset_match:
122
+ variable = isset_match.group(1)
123
+ result = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif({JS_FUNCTION_PREFIX}.isSet({variable})){{\nreturn `'
124
+ output.append(result)
125
+ stack.append(('isset', len(output)))
126
+ return True
127
+ return False
128
+
129
+ def process_endempty_directive(self, stack, output):
130
+ """Process @endempty directive"""
131
+ if stack and stack[-1][0] == 'empty':
132
+ stack.pop()
133
+ result = "`;\n }\n})}"
134
+ output.append(result)
135
+ return True
136
+ return False
137
+
138
+ def process_endisset_directive(self, stack, output):
139
+ """Process @endisset directive"""
140
+ if stack and stack[-1][0] == 'isset':
141
+ stack.pop()
142
+ result = "`;\n }\n})}"
143
+ output.append(result)
144
+ return True
145
+ return False
146
+
147
+ def process_unless_directive(self, line):
148
+ """Process @unless directive"""
149
+ if line.startswith('@unless'):
150
+ # Extract condition from @unless($condition)
151
+ unless_match = re.match(r'@unless\s*\(\s*(.*?)\s*\)', line)
152
+ if unless_match:
153
+ condition = php_to_js(unless_match.group(1))
154
+ return f'${{{JS_FUNCTION_PREFIX}.execute(() => {{\nif(!({condition})){{\nreturn `'
155
+ return None
156
+
157
+ def process_endunless_directive(self, line):
158
+ """Process @endunless directive"""
159
+ if line.startswith('@endunless'):
160
+ return '`\n }\n})}'
161
+ return None
162
+
163
+ def process_php_directive(self, line, stack, output):
164
+ """Process @php directive"""
165
+ if line.startswith('@php'):
166
+ result = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{'
167
+ output.append(result)
168
+ stack.append(('php', len(output)))
169
+ return True
170
+ return False
171
+
172
+ def process_endphp_directive(self, stack, output):
173
+ """Process @endphp directive"""
174
+ if stack and stack[-1][0] == 'php':
175
+ stack.pop()
176
+ result = '})}'
177
+ output.append(result)
178
+ return True
179
+ return False
180
+
181
+ def process_json_directive(self, line):
182
+ """Process @json directive"""
183
+ if line.startswith('@json'):
184
+ # Extract variable from @json($variable)
185
+ json_match = re.match(r'@json\s*\(\s*\$?(\w+)\s*\)', line)
186
+ if json_match:
187
+ variable = json_match.group(1)
188
+ return f'${{{JS_FUNCTION_PREFIX}.json({variable})}}'
189
+ return None
190
+
191
+ def process_lang_directive(self, line):
192
+ """Process @lang directive"""
193
+ if line.startswith('@lang'):
194
+ # Extract key from @lang('key')
195
+ lang_match = re.match(r'@lang\s*\(\s*[\'"]([^\'"]*)[\'"]\s*\)', line)
196
+ if lang_match:
197
+ key = lang_match.group(1)
198
+ return f'${{{JS_FUNCTION_PREFIX}.lang(\'{key}\')}}'
199
+ return None
200
+
201
+ def process_choice_directive(self, line):
202
+ """Process @choice directive"""
203
+ if line.startswith('@choice'):
204
+ # Extract key and count from @choice('key', count)
205
+ choice_match = re.match(r'@choice\s*\(\s*[\'"]([^\'"]*)[\'"]\s*,\s*(\d+)\s*\)', line)
206
+ if choice_match:
207
+ key = choice_match.group(1)
208
+ count = choice_match.group(2)
209
+ return f'${{{JS_FUNCTION_PREFIX}.choice(\'{key}\', {count})}}'
210
+ return None
211
+
212
+ def process_exec_directive(self, line):
213
+ """Process @exec directive"""
214
+ if not line.startswith('@exec'):
215
+ return None
216
+
217
+ # Extract balanced parentheses content
218
+ m = re.match(r'@exec\s*\(', line)
219
+ if not m:
220
+ return None
221
+ start_pos = m.end() - 1
222
+ from utils import extract_balanced_parentheses
223
+ content, end_pos = extract_balanced_parentheses(line, start_pos)
224
+
225
+ if content is None:
226
+ return None
227
+
228
+ # Convert PHP expression to JavaScript
229
+ from php_converter import php_to_js
230
+ try:
231
+ js_expr = php_to_js(content)
232
+ except Exception:
233
+ js_expr = content
234
+
235
+ # Produce string like: ${this.__execute(() => {code;})}
236
+ return '${' + JS_FUNCTION_PREFIX + '.execute(() => {' + js_expr + ';})}'
237
+
238
+ def process_out_directive(self, line):
239
+ """Process @out directive
240
+ Convert @out(<php expression>) to ${this.__output([vars], () => <js expression>)}
241
+ """
242
+ if not line.startswith('@out'):
243
+ return None
244
+
245
+ # Extract balanced parentheses content
246
+ m = re.match(r'@out\s*\(', line)
247
+ if not m:
248
+ return None
249
+ start_pos = m.end() - 1
250
+ from utils import extract_balanced_parentheses
251
+ content, end_pos = extract_balanced_parentheses(line, start_pos)
252
+ if content is None:
253
+ return None
254
+
255
+ expr = content.strip()
256
+
257
+ # Parse variable names similar to PHP implementation: ignore $ inside single-quoted strings
258
+ vars_set = []
259
+ in_single = False
260
+ in_double = False
261
+ escape = False
262
+ i = 0
263
+ while i < len(expr):
264
+ ch = expr[i]
265
+ if escape:
266
+ escape = False
267
+ i += 1
268
+ continue
269
+ if ch == '\\':
270
+ escape = True
271
+ i += 1
272
+ continue
273
+ if in_single:
274
+ if ch == "'":
275
+ in_single = False
276
+ i += 1
277
+ continue
278
+ if in_double:
279
+ if ch == '"':
280
+ in_double = False
281
+ i += 1
282
+ continue
283
+ # inside double, $var is valid
284
+ if ch == '$':
285
+ j = i + 1
286
+ if j < len(expr) and re.match(r'[a-zA-Z_]', expr[j]):
287
+ start = j
288
+ j += 1
289
+ while j < len(expr) and re.match(r'[a-zA-Z0-9_]', expr[j]):
290
+ j += 1
291
+ name = expr[start:j]
292
+ if name not in vars_set:
293
+ vars_set.append(name)
294
+ i = j
295
+ continue
296
+ i += 1
297
+ continue
298
+
299
+ # not in any quote
300
+ if ch == "'":
301
+ in_single = True
302
+ i += 1
303
+ continue
304
+ if ch == '"':
305
+ in_double = True
306
+ i += 1
307
+ continue
308
+ if ch == '$':
309
+ j = i + 1
310
+ if j < len(expr) and re.match(r'[a-zA-Z_]', expr[j]):
311
+ start = j
312
+ j += 1
313
+ while j < len(expr) and re.match(r'[a-zA-Z0-9_]', expr[j]):
314
+ j += 1
315
+ name = expr[start:j]
316
+ if name not in vars_set:
317
+ vars_set.append(name)
318
+ i = j
319
+ continue
320
+ i += 1
321
+
322
+ # Convert PHP expression to JavaScript
323
+ from php_converter import php_to_js
324
+ try:
325
+ js_expr = php_to_js(expr)
326
+ except Exception:
327
+ js_expr = expr
328
+
329
+ # Build subscribe array
330
+ if vars_set:
331
+ subscribe = ','.join([f"'{v}'" for v in vars_set])
332
+ subscribe_js = f'[{subscribe}]'
333
+ else:
334
+ subscribe_js = '[]'
335
+
336
+ # Return the output wrapper using this.__output
337
+ # Produce string like: ${this.__output(['a','b'], () => (expr))}
338
+ return '${' + "this.__output(" + subscribe_js + ", () => (" + js_expr + "))}"
339
+
340
+ def process_register_directive(self, line, stack, output):
341
+ """Process @register directive - chỉ để đánh dấu, không tạo output"""
342
+ if line.startswith('@register'):
343
+ # Chỉ đánh dấu để parser biết bắt đầu @register block
344
+ stack.append(('register', len(output)))
345
+ return True
346
+ return False
347
+
348
+ def process_endregister_directive(self, stack, output):
349
+ """Process @endregister directive - chỉ để đánh dấu, không tạo output"""
350
+ if stack and stack[-1][0] == 'register':
351
+ stack.pop()
352
+ # Không tạo output, chỉ đóng register block
353
+ return True
354
+ return False
355
+
356
+ def process_wrapper_directive(self, line, stack, output):
357
+ """Process @wrapper/@view directive"""
358
+ line_lower = line.lower()
359
+ if line_lower.startswith('@wrapper') or line_lower.startswith('@view'):
360
+ # Parse parameters từ @wrapper(...) or @view(...)
361
+ params_match = re.search(r'@(?:wrapper|view)\s*\((.*)\)', line, re.IGNORECASE)
362
+ if params_match:
363
+ params_str = params_match.group(1).strip()
364
+ if not params_str:
365
+ # Không có tham số
366
+ arg1 = "'div'"
367
+ arg2 = '{}'
368
+ else:
369
+ # Parse tham số
370
+ params = self._parse_wrapper_params(params_str)
371
+ if len(params) == 1:
372
+ arg1 = params[0] # Giữ nguyên để convert sau
373
+ arg2 = '{}'
374
+ else:
375
+ arg1 = params[0] # Giữ nguyên để convert sau
376
+ # Parse arg2 để lấy phần sau dấu =
377
+ arg2_str = params[1]
378
+ if '=' in arg2_str and not arg2_str.strip().startswith('['):
379
+ # Chỉ split nếu không phải array syntax
380
+ arg2 = arg2_str.split('=', 1)[1].strip()
381
+ else:
382
+ arg2 = arg2_str
383
+ else:
384
+ # Không có dấu ngoặc
385
+ arg1 = "'div'"
386
+ arg2 = '{}'
387
+
388
+ # Convert PHP to JavaScript
389
+ from php_converter import php_to_js
390
+ arg1_js = php_to_js(arg1)
391
+ arg2_js = php_to_js(arg2)
392
+
393
+ # Tạo output với function call
394
+ wrapper_output = '${' + JS_FUNCTION_PREFIX + '.startWrapper(' + arg1_js + ', ' + arg2_js + ', __VIEW_ID__)}'
395
+
396
+ stack.append(('wrapper', len(output)))
397
+ return wrapper_output
398
+ return False
399
+
400
+ def process_endwrapper_directive(self, stack, output):
401
+ """Process @endwrapper directive"""
402
+ if stack and stack[-1][0] == 'wrapper':
403
+ stack.pop()
404
+ return '${' + JS_FUNCTION_PREFIX + '.endWrapper(__VIEW_ID__)}'
405
+ return False
406
+
407
+ def _parse_wrapper_params(self, params_str):
408
+ """Parse wrapper parameters với support cho function calls, nested functions, và strings"""
409
+ params = []
410
+ current = ''
411
+ bracket_count = 0
412
+ paren_count = 0
413
+ in_string = False
414
+ string_char = ''
415
+ i = 0
416
+
417
+ while i < len(params_str):
418
+ char = params_str[i]
419
+
420
+ if not in_string:
421
+ if char in ['"', "'"]:
422
+ in_string = True
423
+ string_char = char
424
+ current += char
425
+ elif char == '[':
426
+ bracket_count += 1
427
+ current += char
428
+ elif char == ']':
429
+ bracket_count -= 1
430
+ current += char
431
+ elif char == '(':
432
+ paren_count += 1
433
+ current += char
434
+ elif char == ')':
435
+ paren_count -= 1
436
+ current += char
437
+ elif char == ',' and bracket_count == 0 and paren_count == 0:
438
+ params.append(current.strip())
439
+ current = ''
440
+ else:
441
+ current += char
442
+ else:
443
+ current += char
444
+ # Handle escaped quotes
445
+ if char == string_char:
446
+ # Check if it's escaped
447
+ escape_count = 0
448
+ j = i - 1
449
+ while j >= 0 and params_str[j] == '\\':
450
+ escape_count += 1
451
+ j -= 1
452
+
453
+ # If even number of backslashes, quote is not escaped
454
+ if escape_count % 2 == 0:
455
+ in_string = False
456
+
457
+ i += 1
458
+
459
+ if current.strip():
460
+ params.append(current.strip())
461
+
462
+ return params
463
+
464
+ def process_let_directive(self, line, stack, output):
465
+ """Process @let directive - khai báo biến không cần kiểm tra isset"""
466
+ if line.startswith('@let'):
467
+ # Extract expression from @let(...)
468
+ let_match = re.search(r'@let\s*\((.*)\)', line)
469
+ if let_match:
470
+ expression = let_match.group(1).strip()
471
+ # Convert PHP to JavaScript
472
+ from php_converter import php_to_js
473
+ js_expression = php_to_js(expression)
474
+ # Tạo output với function call
475
+ let_output = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{'
476
+ output.append(let_output)
477
+ # Thêm các dòng code
478
+ code_lines = js_expression.split(';')
479
+ for code_line in code_lines:
480
+ if code_line.strip():
481
+ output.append(f' {code_line.strip()};')
482
+ output.append('})}')
483
+ return True
484
+ return False
485
+
486
+ def process_const_directive(self, line, stack, output):
487
+ """Process @const directive - khai báo biến không cần kiểm tra isset"""
488
+ if line.startswith('@const'):
489
+ # Extract expression from @const(...)
490
+ const_match = re.search(r'@const\s*\((.*)\)', line)
491
+ if const_match:
492
+ expression = const_match.group(1).strip()
493
+ # Convert PHP to JavaScript
494
+ from php_converter import php_to_js
495
+ js_expression = php_to_js(expression)
496
+ # Tạo output với function call
497
+ const_output = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{'
498
+ output.append(const_output)
499
+ # Thêm các dòng code
500
+ code_lines = js_expression.split(';')
501
+ for code_line in code_lines:
502
+ if code_line.strip():
503
+ output.append(f' {code_line.strip()};')
504
+ output.append('})}')
505
+ return True
506
+ return False
507
+
508
+ def process_usestate_directive(self, line, stack, output):
509
+ """Process @useState directive - tạo state với tên biến tùy chỉnh"""
510
+ if line.startswith('@useState'):
511
+ # Extract expression from @useState(...)
512
+ usestate_match = re.search(r'@useState\s*\((.*)\)', line)
513
+ if usestate_match:
514
+ expression = usestate_match.group(1).strip()
515
+ # Parse 2 parameters: value, stateName
516
+ params = self._parse_usestate_params(expression)
517
+ if len(params) == 3:
518
+ value, state_name, set_state_name = params
519
+ # Convert PHP to JavaScript
520
+ from php_converter import php_to_js
521
+ value_js = php_to_js(value)
522
+ state_name_js = php_to_js(state_name)
523
+ set_state_name_js = php_to_js(set_state_name)
524
+ # Tạo output với function call
525
+ usestate_output = f'${{{JS_FUNCTION_PREFIX}.execute(() => {{'
526
+ output.append(usestate_output)
527
+ output.append(f' const [{state_name_js}, {set_state_name_js}] = useState({value_js});')
528
+ output.append('})}')
529
+ return True
530
+ return False
531
+
532
+ def _parse_usestate_params(self, expression):
533
+ """Parse @useState parameters"""
534
+ params = []
535
+ current = ''
536
+ paren_count = 0
537
+ in_quotes = False
538
+ quote_char = ''
539
+ i = 0
540
+
541
+ while i < len(expression):
542
+ char = expression[i]
543
+
544
+ if not in_quotes:
545
+ if char in ['"', "'"]:
546
+ in_quotes = True
547
+ quote_char = char
548
+ current += char
549
+ elif char == '(':
550
+ paren_count += 1
551
+ current += char
552
+ elif char == ')':
553
+ paren_count -= 1
554
+ current += char
555
+ elif char == ',' and paren_count == 0:
556
+ params.append(current.strip())
557
+ current = ''
558
+ else:
559
+ current += char
560
+ else:
561
+ current += char
562
+ if char == quote_char:
563
+ # Check if it's escaped
564
+ escape_count = 0
565
+ j = i - 1
566
+ while j >= 0 and expression[j] == '\\':
567
+ escape_count += 1
568
+ j -= 1
569
+
570
+ # If even number of backslashes, quote is not escaped
571
+ if escape_count % 2 == 0:
572
+ in_quotes = False
573
+
574
+ i += 1
575
+
576
+ if current.strip():
577
+ params.append(current.strip())
578
+
579
+ return params
580
+
581
+ def _format_attrs(self, attrs_str):
582
+ """Format attributes string"""
583
+ # Parse PHP array syntax và convert thành HTML attributes
584
+ if attrs_str.startswith('[') and attrs_str.endswith(']'):
585
+ # Remove brackets
586
+ attrs_str = attrs_str[1:-1].strip()
587
+ if not attrs_str:
588
+ return ''
589
+
590
+ # Parse key => value pairs
591
+ attrs = []
592
+ parts = attrs_str.split(',')
593
+ for part in parts:
594
+ part = part.strip()
595
+ if '=>' in part:
596
+ key, value = part.split('=>', 1)
597
+ key = key.strip().strip('\'"')
598
+ value = value.strip().strip('\'"')
599
+ attrs.append(f'{key}="{value}"')
600
+
601
+ return ' '.join(attrs)
602
+
603
+ return attrs_str