onelaraveljs 1.0.0 → 1.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.
- package/README.md +1 -1
- package/bin/onejs-build.js +32 -0
- package/package.json +11 -3
- package/scripts/README-template-compiler.md +133 -0
- package/scripts/README.md +61 -0
- package/scripts/__pycache__/build.cpython-314.pyc +0 -0
- package/scripts/__pycache__/compile.cpython-313.pyc +0 -0
- package/scripts/__pycache__/compile.cpython-314.pyc +0 -0
- package/scripts/build.py +573 -0
- package/scripts/check-system-errors.php +214 -0
- package/scripts/compile.py +101 -0
- package/scripts/compiler/README_CONFIG.md +196 -0
- package/scripts/compiler/__init__.py +18 -0
- package/scripts/compiler/__pycache__/__init__.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/__init__.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/binding_directive_service.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/class_binding_handler.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/compiler_utils.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/compiler_utils.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/conditional_handlers.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/conditional_handlers.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/config.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/config.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/declaration_tracker.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/directive_processors.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/directive_processors.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/echo_processor.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/event_directive_processor.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/event_directive_processor.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/function_generators.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/function_generators.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/loop_handlers.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/loop_handlers.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/main_compiler.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/main_compiler.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/parsers.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/parsers.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/php_converter.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/php_converter.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/php_js_converter.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/php_js_converter.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/register_parser.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/register_parser.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/section_handlers.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/section_handlers.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/show_directive_handler.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/style_directive_handler.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/template_analyzer.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/template_analyzer.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/template_processor.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/template_processor.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/template_processors.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/template_processors.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/utils.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/utils.cpython-314.pyc +0 -0
- package/scripts/compiler/__pycache__/wrapper_parser.cpython-313.pyc +0 -0
- package/scripts/compiler/__pycache__/wrapper_parser.cpython-314.pyc +0 -0
- package/scripts/compiler/binding_directive_service.py +103 -0
- package/scripts/compiler/class_binding_handler.py +347 -0
- package/scripts/compiler/cli.py +34 -0
- package/scripts/compiler/code_generator.py +141 -0
- package/scripts/compiler/compiler.config.json +36 -0
- package/scripts/compiler/compiler_utils.py +55 -0
- package/scripts/compiler/conditional_handlers.py +252 -0
- package/scripts/compiler/config.py +107 -0
- package/scripts/compiler/declaration_tracker.py +420 -0
- package/scripts/compiler/directive_processors.py +603 -0
- package/scripts/compiler/echo_processor.py +667 -0
- package/scripts/compiler/event_directive_processor.py +1099 -0
- package/scripts/compiler/fetch_parser.py +49 -0
- package/scripts/compiler/function_generators.py +310 -0
- package/scripts/compiler/loop_handlers.py +224 -0
- package/scripts/compiler/main_compiler.py +1763 -0
- package/scripts/compiler/parsers.py +1418 -0
- package/scripts/compiler/php_converter.py +470 -0
- package/scripts/compiler/php_js_converter.py +603 -0
- package/scripts/compiler/register_parser.py +480 -0
- package/scripts/compiler/section_handlers.py +122 -0
- package/scripts/compiler/show_directive_handler.py +85 -0
- package/scripts/compiler/style_directive_handler.py +169 -0
- package/scripts/compiler/template_analyzer.py +162 -0
- package/scripts/compiler/template_processor.py +1167 -0
- package/scripts/compiler/template_processors.py +1557 -0
- package/scripts/compiler/test_compiler.py +69 -0
- package/scripts/compiler/utils.py +54 -0
- package/scripts/compiler/variables_analyzer.py +135 -0
- package/scripts/compiler/view_identifier_generator.py +278 -0
- package/scripts/compiler/wrapper_parser.py +78 -0
- package/scripts/dev-context.js +311 -0
- package/scripts/dev.js +109 -0
- package/scripts/generate-assets-order.js +200 -0
- package/scripts/migrate-namespace.php +146 -0
- package/scripts/node/MIGRATION.md +190 -0
- package/scripts/node/README.md +269 -0
- package/scripts/node/build.js +208 -0
- package/scripts/node/compiler/compiler-utils.js +38 -0
- package/scripts/node/compiler/conditional-handlers.js +45 -0
- package/scripts/node/compiler/config.js +178 -0
- package/scripts/node/compiler/directive-processors.js +51 -0
- package/scripts/node/compiler/event-directive-processor.js +182 -0
- package/scripts/node/compiler/function-generators.js +239 -0
- package/scripts/node/compiler/loop-handlers.js +45 -0
- package/scripts/node/compiler/main-compiler.js +236 -0
- package/scripts/node/compiler/parsers.js +358 -0
- package/scripts/node/compiler/php-converter.js +227 -0
- package/scripts/node/compiler/register-parser.js +32 -0
- package/scripts/node/compiler/section-handlers.js +46 -0
- package/scripts/node/compiler/template-analyzer.js +50 -0
- package/scripts/node/compiler/template-processor.js +371 -0
- package/scripts/node/compiler/template-processors.js +219 -0
- package/scripts/node/compiler/utils.js +203 -0
- package/scripts/node/compiler/wrapper-parser.js +25 -0
- package/scripts/node/package.json +24 -0
- package/scripts/node/test-compiler.js +52 -0
- package/scripts/node-run.cjs +28 -0
- package/scripts/standardize-directories.php +92 -0
- package/templates/view.module.js +2 -0
- package/templates/view.tpl-raw.js +13 -0
- 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
|