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,103 @@
1
+ """
2
+ Binding Directive Service - Xử lý @val và @bind directives
3
+ @val và @bind là alias của nhau, cùng tạo ra data-binding attribute
4
+ """
5
+
6
+ import re
7
+
8
+ class BindingDirectiveService:
9
+ def __init__(self):
10
+ pass
11
+
12
+ def process_binding_directive(self, content, directive_pattern='val|bind'):
13
+ """
14
+ Process binding directives (@val and @bind are aliases)
15
+ @val($userState->name) -> data-binding="userState.name"
16
+ @bind($username) -> data-binding="username"
17
+ Both directives produce the same output
18
+ """
19
+ def replace_binding_with_nested_parens(content):
20
+ result = content
21
+ while True:
22
+ # Find @val or @bind directive
23
+ match = re.search(rf'@({directive_pattern})\s*\(', result)
24
+ if not match:
25
+ break
26
+
27
+ start_pos = match.end() - 1 # Position of opening (
28
+ paren_count = 0
29
+ i = start_pos
30
+
31
+ # Find matching closing parenthesis
32
+ while i < len(result):
33
+ if result[i] == '(':
34
+ paren_count += 1
35
+ elif result[i] == ')':
36
+ paren_count -= 1
37
+ if paren_count == 0:
38
+ # Found matching closing parenthesis
39
+ expression = result[start_pos + 1:i].strip()
40
+ binding_value = self._convert_php_to_binding(expression)
41
+ replacement = f'data-binding="{binding_value}" data-view-id="${{__VIEW_ID__}}"'
42
+ result = result[:match.start()] + replacement + result[i + 1:]
43
+ break
44
+ i += 1
45
+ else:
46
+ # No matching parenthesis found, break to avoid infinite loop
47
+ break
48
+
49
+ return result
50
+
51
+ return replace_binding_with_nested_parens(content)
52
+
53
+ def process_val_directive(self, content):
54
+ """
55
+ Process @val directive (alias of process_binding_directive)
56
+ @val($userState->name) -> data-binding="userState.name"
57
+ @val($user['name']) -> data-binding="user.name"
58
+ """
59
+ return self.process_binding_directive(content, 'val')
60
+
61
+ def process_bind_directive(self, content):
62
+ """
63
+ Process @bind directive (alias of process_binding_directive)
64
+ @bind($username) -> data-binding="username"
65
+ """
66
+ return self.process_binding_directive(content, 'bind')
67
+
68
+ def _convert_php_to_binding(self, php_expression):
69
+ """
70
+ Convert PHP expression to JavaScript binding notation
71
+ $userState->name -> userState.name
72
+ $user['name'] -> user.name
73
+ $username -> username
74
+ User::find($id)->profile->displayName -> User.find(id).profile.displayName
75
+ """
76
+ # Remove leading/trailing whitespace
77
+ php_expression = php_expression.strip()
78
+
79
+ # Convert $variable to variable (remove $ prefix)
80
+ result = re.sub(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', r'\1', php_expression)
81
+
82
+ # Convert PHP static method accessor (::) to (.)
83
+ result = re.sub(r'::', '.', result)
84
+
85
+ # Convert object accessor (->) to (.)
86
+ result = re.sub(r'->', '.', result)
87
+
88
+ # Convert array access ['key'] to .key
89
+ result = re.sub(r"\['([^']+)'\]", r'.\1', result)
90
+ result = re.sub(r'\["([^"]+)"\]', r'.\1', result)
91
+
92
+ # Convert numeric array access [0] to .0 (though this is less common)
93
+ result = re.sub(r'\[(\d+)\]', r'.\1', result)
94
+
95
+ return result
96
+
97
+ def process_all_binding_directives(self, content):
98
+ """
99
+ Process both @val and @bind directives in content (they are aliases)
100
+ This method processes both directives in a single pass
101
+ """
102
+ # Process both @val and @bind directives together (they are aliases)
103
+ return self.process_binding_directive(content, 'val|bind')
@@ -0,0 +1,347 @@
1
+ """
2
+ Class Binding Handler - Xử lý @class directive
3
+ Supports: static classes, conditional classes, và mixed binding
4
+ Uses separate __classBinding() method for reactive class management
5
+ """
6
+
7
+ import re
8
+ from php_js_converter import php_to_js_advanced
9
+
10
+ class ClassBindingHandler:
11
+ def __init__(self, state_variables=None):
12
+ self.state_variables = state_variables or set()
13
+
14
+ def process_class_directive(self, content):
15
+ """
16
+ Process @class directive with multiple formats:
17
+ 1. @class('static-class') - static class
18
+ 2. @class('class', $condition) - conditional class
19
+ 3. @class(['class1' => $cond1, 'class2' => $cond2]) - array binding
20
+ 4. @class(['static', 'dynamic' => $cond]) - mixed
21
+
22
+ Multiple @class on same line will be merged into one
23
+
24
+ Output format:
25
+ - Static only: class="static-class"
26
+ - With dynamic: ${this.__classBinding([...])}
27
+
28
+ Note: Supports multi-line directives within HTML tag attributes
29
+ """
30
+ result = content
31
+
32
+ # Process all @class directives globally (supports multi-line)
33
+ while True:
34
+ match = re.search(r'@class\s*\(', result)
35
+ if not match:
36
+ break
37
+
38
+ # Find matching closing parenthesis (can span multiple lines)
39
+ start_pos = match.end() - 1
40
+ paren_count = 0
41
+ i = start_pos
42
+
43
+ while i < len(result):
44
+ if result[i] == '(':
45
+ paren_count += 1
46
+ elif result[i] == ')':
47
+ paren_count -= 1
48
+ if paren_count == 0:
49
+ # Found complete directive
50
+ expression = result[start_pos + 1:i].strip()
51
+ replacement = self._generate_class_output(expression)
52
+ result = result[:match.start()] + replacement + result[i + 1:]
53
+ break
54
+ i += 1
55
+ else:
56
+ # No matching closing paren found - skip this one
57
+ break
58
+
59
+ return result
60
+
61
+ def _merge_multiple_class_directives(self, line):
62
+ """
63
+ Merge multiple @class directives on the same line into one
64
+ """
65
+ all_bindings = []
66
+
67
+ # Extract all @class expressions
68
+ while True:
69
+ match = re.search(r'@class\s*\(', line)
70
+ if not match:
71
+ break
72
+
73
+ start_pos = match.end() - 1
74
+ paren_count = 0
75
+ i = start_pos
76
+
77
+ # Find matching closing parenthesis
78
+ while i < len(line):
79
+ if line[i] == '(':
80
+ paren_count += 1
81
+ elif line[i] == ')':
82
+ paren_count -= 1
83
+ if paren_count == 0:
84
+ expression = line[start_pos + 1:i].strip()
85
+ bindings = self._parse_class_expression(expression)
86
+ all_bindings.extend(bindings)
87
+ # Remove this @class directive
88
+ line = line[:match.start()] + line[i + 1:]
89
+ break
90
+ i += 1
91
+ else:
92
+ break
93
+
94
+ # Generate single output for all merged bindings
95
+ if all_bindings:
96
+ replacement = self._generate_output_from_bindings(all_bindings)
97
+ # Insert at the first @class position (which is now gone)
98
+ # Find a good insertion point - before the first > or at the start
99
+ insertion_point = line.find('>')
100
+ if insertion_point > 0:
101
+ line = line[:insertion_point] + ' ' + replacement + line[insertion_point:]
102
+ else:
103
+ line = line + ' ' + replacement
104
+
105
+ return line
106
+
107
+ def _process_single_class_directive(self, line):
108
+ """
109
+ Process a single @class directive on a line
110
+ """
111
+ match = re.search(r'@class\s*\(', line)
112
+ if not match:
113
+ return line
114
+
115
+ start_pos = match.end() - 1
116
+ paren_count = 0
117
+ i = start_pos
118
+
119
+ while i < len(line):
120
+ if line[i] == '(':
121
+ paren_count += 1
122
+ elif line[i] == ')':
123
+ paren_count -= 1
124
+ if paren_count == 0:
125
+ expression = line[start_pos + 1:i].strip()
126
+ replacement = self._generate_class_output(expression)
127
+ line = line[:match.start()] + replacement + line[i + 1:]
128
+ break
129
+ i += 1
130
+
131
+ return line
132
+
133
+ def _generate_class_output(self, expression):
134
+ """
135
+ Generate output for class directive
136
+ Returns either static class="..." or ${this.__attr({"class": {...}})}
137
+ """
138
+ bindings = self._parse_class_expression(expression)
139
+ return self._generate_output_from_bindings(bindings)
140
+
141
+ def _generate_output_from_bindings(self, bindings):
142
+ """
143
+ Generate output from a list of bindings
144
+ Returns either static class="..." or ${this.__classBinding([...])}
145
+ """
146
+ # Check if all bindings are static
147
+ has_dynamic = any(b['type'] == 'binding' for b in bindings)
148
+
149
+ if not has_dynamic:
150
+ # All static - output direct class attribute
151
+ static_classes = [b['value'] for b in bindings]
152
+ return f'class="{" ".join(static_classes)}"'
153
+ else:
154
+ # Has dynamic - use __classBinding
155
+ return self._generate_class_binding_config(bindings)
156
+
157
+ def _generate_class_binding_config(self, bindings):
158
+ """
159
+ Generate __classBinding() config
160
+ Format: ${this.__classBinding([{type: "static", value: "...", states: [...], checker: () => ...}])}
161
+ """
162
+ binding_configs = []
163
+
164
+ for binding in bindings:
165
+ if binding['type'] == 'static':
166
+ binding_configs.append(f'{{type: "static", value: "{binding["value"]}"}}')
167
+ elif binding['type'] == 'binding':
168
+ states = binding.get('states', [])
169
+ states_str = ', '.join([f'"{s}"' for s in states])
170
+ checker = binding['checker']
171
+ class_name = binding['value']
172
+ binding_configs.append(f'{{type: "binding", value: "{class_name}", states: [{states_str}], checker: () => {checker}}}')
173
+
174
+ configs_joined = ', '.join(binding_configs)
175
+ return f'${{this.__classBinding([{configs_joined}])}}'
176
+
177
+ def _parse_class_expression(self, expression):
178
+ """
179
+ Parse class expression and return list of binding dicts
180
+ Returns: [{"type": "static"|"binding", "value": "class-name", ...}, ...]
181
+ """
182
+ expression = expression.strip()
183
+
184
+ # Case 1: Simple string - @class('class')
185
+ if self._is_simple_string(expression):
186
+ class_name = self._extract_string_value(expression)
187
+ return [{"type": "static", "value": class_name}]
188
+
189
+ # Case 2: Two arguments - @class('class', $condition)
190
+ if ',' in expression and not expression.startswith('['):
191
+ parts = self._split_arguments(expression)
192
+ if len(parts) == 2:
193
+ class_name = self._extract_string_value(parts[0].strip())
194
+ condition = parts[1].strip()
195
+ states = self._extract_state_variables(condition)
196
+ js_condition = self._convert_php_to_js(condition)
197
+ return [{
198
+ "type": "binding",
199
+ "value": class_name,
200
+ "states": states,
201
+ "checker": js_condition
202
+ }]
203
+
204
+ # Case 3: Array format - @class(['class1' => $cond, 'class2'])
205
+ if expression.startswith('[') and expression.endswith(']'):
206
+ return self._parse_array_expression(expression)
207
+
208
+ # Fallback: treat as static class
209
+ return [{"type": "static", "value": expression}]
210
+
211
+ def _parse_array_expression(self, expression):
212
+ """
213
+ Parse array expression: ['class1' => $cond, 'static', 'class2' => $cond2]
214
+ Returns list of binding dicts
215
+ """
216
+ # Remove outer brackets
217
+ inner = expression[1:-1].strip()
218
+
219
+ # Split by comma, but respect nested structures
220
+ items = self._split_array_items(inner)
221
+
222
+ bindings = []
223
+ for item in items:
224
+ item = item.strip()
225
+ if not item:
226
+ continue
227
+
228
+ # Check if it's a key => value pair
229
+ if '=>' in item:
230
+ parts = item.split('=>', 1)
231
+ class_name = self._extract_string_value(parts[0].strip())
232
+ condition = parts[1].strip()
233
+
234
+ # Check if condition is a static string
235
+ if self._is_simple_string(condition):
236
+ # Static value like 'demo' => 'dump'
237
+ static_value = self._extract_string_value(condition)
238
+ bindings.append({"type": "static", "value": static_value})
239
+ else:
240
+ # Dynamic condition
241
+ states = self._extract_state_variables(condition)
242
+ js_condition = self._convert_php_to_js(condition)
243
+ bindings.append({
244
+ "type": "binding",
245
+ "value": class_name,
246
+ "states": states,
247
+ "checker": js_condition
248
+ })
249
+ else:
250
+ # Static class without condition
251
+ class_name = self._extract_string_value(item)
252
+ bindings.append({"type": "static", "value": class_name})
253
+
254
+ return bindings
255
+
256
+ def _split_array_items(self, content):
257
+ """
258
+ Split array items by comma, respecting nested structures
259
+ """
260
+ items = []
261
+ current = []
262
+ depth = 0
263
+ in_string = False
264
+ string_char = None
265
+ i = 0
266
+
267
+ while i < len(content):
268
+ char = content[i]
269
+
270
+ # Handle string boundaries
271
+ if char in ('"', "'") and (i == 0 or content[i-1] != '\\'):
272
+ if not in_string:
273
+ in_string = True
274
+ string_char = char
275
+ elif char == string_char:
276
+ in_string = False
277
+ string_char = None
278
+
279
+ # Handle nested structures
280
+ if not in_string:
281
+ if char in '([{':
282
+ depth += 1
283
+ elif char in ')]}':
284
+ depth -= 1
285
+ elif char == ',' and depth == 0:
286
+ items.append(''.join(current))
287
+ current = []
288
+ i += 1
289
+ continue
290
+
291
+ current.append(char)
292
+ i += 1
293
+
294
+ if current:
295
+ items.append(''.join(current))
296
+
297
+ return items
298
+
299
+ def _split_arguments(self, expression):
300
+ """
301
+ Split function arguments by comma, respecting nested structures
302
+ """
303
+ return self._split_array_items(expression)
304
+
305
+ def _is_simple_string(self, expression):
306
+ """
307
+ Check if expression is a simple quoted string
308
+ """
309
+ expression = expression.strip()
310
+ return (expression.startswith("'") and expression.endswith("'")) or \
311
+ (expression.startswith('"') and expression.endswith('"'))
312
+
313
+ def _extract_string_value(self, expression):
314
+ """
315
+ Extract string value from quoted string
316
+ 'class' -> class
317
+ "class" -> class
318
+ """
319
+ expression = expression.strip()
320
+ if (expression.startswith("'") and expression.endswith("'")) or \
321
+ (expression.startswith('"') and expression.endswith('"')):
322
+ return expression[1:-1]
323
+ return expression
324
+
325
+ def _extract_state_variables(self, php_expression):
326
+ """
327
+ Extract state variable names from PHP expression
328
+ $abc -> abc
329
+ $abc->test($cde) -> abc, cde
330
+ $demo && !$on || !$r->e -> demo, on, r
331
+ """
332
+ # Find all $variable patterns
333
+ variables = set()
334
+ matches = re.findall(r'\$([a-zA-Z_][a-zA-Z0-9_]*)', php_expression)
335
+ for var in matches:
336
+ # Only include if it's a state variable
337
+ if var in self.state_variables or True: # Include all for now
338
+ variables.add(var)
339
+
340
+ return sorted(list(variables))
341
+
342
+ def _convert_php_to_js(self, php_expression):
343
+ """
344
+ Convert PHP expression to JavaScript
345
+ Uses the existing php_to_js_advanced function
346
+ """
347
+ return php_to_js_advanced(php_expression)
@@ -0,0 +1,34 @@
1
+ """
2
+ Command line interface cho Blade Compiler
3
+ """
4
+
5
+ import sys
6
+ import os
7
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
8
+ from main_compiler import BladeCompiler
9
+
10
+ def main():
11
+ if len(sys.argv) < 3:
12
+ print("Sử dụng: python cli.py <input.blade> <output.js>")
13
+ sys.exit(1)
14
+
15
+ input_file = sys.argv[1]
16
+ output_file = sys.argv[2]
17
+
18
+ try:
19
+ with open(input_file, 'r', encoding='utf-8') as f:
20
+ blade_code = f.read()
21
+
22
+ compiler = BladeCompiler()
23
+ js_code = compiler.compile_blade_to_js(blade_code, 'test')
24
+
25
+ with open(output_file, 'w', encoding='utf-8') as f:
26
+ f.write(js_code)
27
+
28
+ print(f"Đã compile thành công từ {input_file} sang {output_file}")
29
+ except Exception as e:
30
+ print(f"Lỗi: {e}")
31
+ sys.exit(1)
32
+
33
+ if __name__ == "__main__":
34
+ main()
@@ -0,0 +1,141 @@
1
+ """
2
+ Generator cho JavaScript code output
3
+ """
4
+
5
+ import json
6
+ import re
7
+ from config import JS_FUNCTION_PREFIX, HTML_ATTR_PREFIX
8
+ from variables_analyzer import analyze_render_uses_vars, analyze_sections_info
9
+
10
+ def generate_prerender(has_await, has_fetch, extended_view, sections_code, template_content, vars_line, view_id_line, output_content_line="", uses_vars=True, sections_info=None):
11
+ """Generate prerender function based on @await or @fetch directive presence"""
12
+ if sections_info is None:
13
+ sections_info = {}
14
+
15
+ if not has_await and not has_fetch and not sections_code:
16
+ return "function(__$spaViewData$__ = {}) {\n return null;\n}"
17
+
18
+ if not uses_vars and not sections_info:
19
+ return "function(__$spaViewData$__ = {}) {\n return null;\n}"
20
+
21
+ if not has_await and not has_fetch and not vars_line:
22
+ return "function(__$spaViewData$__ = {}) {\n return null;\n}"
23
+
24
+ # Initialize prerender_sections
25
+ prerender_sections = []
26
+
27
+ if sections_info:
28
+ # Process sections based on their configuration
29
+ for section_name, section_info in sections_info.items():
30
+ section_type = section_info.get("type", "short")
31
+ preloader = section_info.get("preloader", False)
32
+ use_vars = section_info.get("useVars", False)
33
+
34
+ if section_type == "long" and not use_vars:
35
+ # Long section không dùng biến - render trực tiếp trong prerender
36
+ # Không cần đợi fetch/await vì không dùng dynamic data
37
+ section_pattern = f'{JS_FUNCTION_PREFIX}\.section\(\'{section_name}\',[^)]+\)'
38
+ section_match = re.search(section_pattern, sections_code, re.DOTALL)
39
+ if section_match:
40
+ prerender_sections.append("${" + section_match.group(0) + "}")
41
+ elif section_type == "short" and not use_vars:
42
+ # Short section không dùng biến - render trực tiếp trong prerender
43
+ section_pattern = f'{JS_FUNCTION_PREFIX}\.section\(\'{section_name}\',[^)]+\)'
44
+ section_match = re.search(section_pattern, sections_code, re.DOTALL)
45
+ if section_match:
46
+ prerender_sections.append("${" + section_match.group(0) + "}")
47
+ elif preloader:
48
+ # Section có preloader (dùng biến + có fetch/await) - tạo section preloader
49
+ prerender_sections.append("${" + JS_FUNCTION_PREFIX + ".section('" + section_name + "', `<div class=\"" + HTML_ATTR_PREFIX + "preloader\" ref=\"${__VIEW_ID__}\" data-section-name=\"" + section_name + "\">${" + JS_FUNCTION_PREFIX + ".text('loading')}</div>`)}")
50
+
51
+ # Generate function based on context
52
+ if extended_view:
53
+ if has_await or has_fetch:
54
+ return f"""function(__$spaViewData$__ = {{}}) {{
55
+ {vars_line}{view_id_line} let __outputRenderedContent__ = '';
56
+ try {{
57
+ __outputRenderedContent__ = `{chr(10).join(prerender_sections) if prerender_sections else sections_code}`;
58
+ }} catch(e) {{
59
+ __outputRenderedContent__ = this.showError(e.message);
60
+ console.warn(e);
61
+ }}
62
+ return this.__extends('{extended_view}');
63
+ }}"""
64
+ else:
65
+ return f"""function(__$spaViewData$__ = {{}}) {{
66
+ {view_id_line} let __outputRenderedContent__ = '';
67
+ try {{
68
+ __outputRenderedContent__ = `{chr(10).join(prerender_sections) if prerender_sections else sections_code}`;
69
+ }} catch(e) {{
70
+ __outputRenderedContent__ = this.showError(e.message);
71
+ console.warn(e);
72
+ }}
73
+ return this.__extends('{extended_view}');
74
+ }}"""
75
+ else:
76
+ if has_await or has_fetch:
77
+ return f"""function(__$spaViewData$__ = {{}}) {{
78
+ {vars_line}{view_id_line} let __outputRenderedContent__ = '';
79
+ try {{
80
+ __outputRenderedContent__ = `<div class="{HTML_ATTR_PREFIX}preloader" ref="${{__VIEW_ID__}}" data-view-name="${{__VIEW_PATH__}}">${{{JS_FUNCTION_PREFIX}.text('loading')}}</div>`;
81
+ }} catch(e) {{
82
+ __outputRenderedContent__ = this.showError(e.message);
83
+ console.warn(e);
84
+ }}
85
+ return __outputRenderedContent__;
86
+ }}"""
87
+ else:
88
+ static_content = sections_code if sections_code else template_content
89
+ return f"""function(__$spaViewData$__ = {{}}) {{
90
+ {vars_line}{view_id_line} let __outputRenderedContent__ = '';
91
+ try {{
92
+ __outputRenderedContent__ = `{static_content}`;
93
+ }} catch(e) {{
94
+ __outputRenderedContent__ = this.showError(e.message);
95
+ console.warn(e);
96
+ }}
97
+ return __outputRenderedContent__;
98
+ }}"""
99
+
100
+ def generate_view_engine(view_name, extended_view, sections_info, has_await, has_fetch, fetch_config, uses_vars, has_sections, has_section_preload, has_prerender, prerender_func, render_func, init_func, css_functions, wrapper_config=None):
101
+ """Generate complete view engine code"""
102
+
103
+ sections_json = json.dumps(sections_info, ensure_ascii=False, separators=(',', ':'))
104
+
105
+ if extended_view:
106
+ super_view_config = f"'{extended_view}'"
107
+ has_super_view = "true"
108
+ else:
109
+ super_view_config = "null"
110
+ has_super_view = "false"
111
+
112
+ # Add wrapper config to view engine object
113
+ wrapper_config_line = ""
114
+ if wrapper_config:
115
+ wrapper_config_line = f",\n wrapperConfig: {wrapper_config}"
116
+
117
+ return f"""function(data = {{}}) {{
118
+ onst __VIEW_PATH__ = '{view_name}';
119
+ const __VIEW_ID__ = {JS_FUNCTION_PREFIX}.generateViewId();
120
+ const self = new {JS_FUNCTION_PREFIX}.Engine('{view_name}', {{
121
+ superView: {super_view_config},
122
+ hasSuperView: {has_super_view},
123
+ sections: {sections_json}{wrapper_config_line},
124
+ hasAwaitData: {str(has_await).lower()},
125
+ hasFetchData: {str(has_fetch).lower()},
126
+ fetch: {json.dumps(fetch_config, ensure_ascii=False).replace('"`', '`').replace('`"', '`') if fetch_config else 'null'},
127
+ data: data,
128
+ viewId: __VIEW_ID__,
129
+ path: __VIEW_PATH__,
130
+ usesVars: {str(uses_vars).lower()},
131
+ hasSections: {str(has_sections).lower()},
132
+ hasSectionPreload: {str(has_section_preload).lower()},
133
+ hasPrerender: {str(has_prerender).lower()},
134
+ prerender: {prerender_func},
135
+ render: {render_func}{css_functions}
136
+ }});
137
+ return self;
138
+ function onStateChange(state) {{
139
+ self.data = state;
140
+ }}
141
+ }}"""
@@ -0,0 +1,36 @@
1
+ {
2
+ "paths": {
3
+ "views_input": "resources/views",
4
+ "js_input": "resources/js/onejs",
5
+ "build_output": "resources/js/build",
6
+ "build_scopes": "resources/js/build/scopes",
7
+ "public_static": "public/static",
8
+ "app_output": "public/static/app",
9
+ "scopes_output": "public/static/app/scopes"
10
+ },
11
+ "files": {
12
+ "view_templates": "view.templates.js",
13
+ "wrapper": "wraper.js",
14
+ "main": "main.js"
15
+ },
16
+ "patterns": {
17
+ "blade": "**/*.blade.php",
18
+ "js": "**/*.js"
19
+ },
20
+ "settings": {
21
+ "default_scope": "web",
22
+ "auto_create_dirs": true,
23
+ "verbose": false
24
+ },
25
+ "build_directories": [
26
+ "components",
27
+ "web",
28
+ "admin",
29
+ "layouts",
30
+ "partials",
31
+ "custom",
32
+ "base",
33
+ "test",
34
+ "templates"
35
+ ]
36
+ }