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.
Files changed (119) hide show
  1. package/README.md +1 -1
  2. package/bin/onejs-build.js +32 -0
  3. package/package.json +11 -3
  4. package/scripts/README-template-compiler.md +133 -0
  5. package/scripts/README.md +61 -0
  6. package/scripts/__pycache__/build.cpython-314.pyc +0 -0
  7. package/scripts/__pycache__/compile.cpython-313.pyc +0 -0
  8. package/scripts/__pycache__/compile.cpython-314.pyc +0 -0
  9. package/scripts/build.py +573 -0
  10. package/scripts/check-system-errors.php +214 -0
  11. package/scripts/compile.py +101 -0
  12. package/scripts/compiler/README_CONFIG.md +196 -0
  13. package/scripts/compiler/__init__.py +18 -0
  14. package/scripts/compiler/__pycache__/__init__.cpython-313.pyc +0 -0
  15. package/scripts/compiler/__pycache__/__init__.cpython-314.pyc +0 -0
  16. package/scripts/compiler/__pycache__/binding_directive_service.cpython-314.pyc +0 -0
  17. package/scripts/compiler/__pycache__/class_binding_handler.cpython-314.pyc +0 -0
  18. package/scripts/compiler/__pycache__/compiler_utils.cpython-313.pyc +0 -0
  19. package/scripts/compiler/__pycache__/compiler_utils.cpython-314.pyc +0 -0
  20. package/scripts/compiler/__pycache__/conditional_handlers.cpython-313.pyc +0 -0
  21. package/scripts/compiler/__pycache__/conditional_handlers.cpython-314.pyc +0 -0
  22. package/scripts/compiler/__pycache__/config.cpython-313.pyc +0 -0
  23. package/scripts/compiler/__pycache__/config.cpython-314.pyc +0 -0
  24. package/scripts/compiler/__pycache__/declaration_tracker.cpython-314.pyc +0 -0
  25. package/scripts/compiler/__pycache__/directive_processors.cpython-313.pyc +0 -0
  26. package/scripts/compiler/__pycache__/directive_processors.cpython-314.pyc +0 -0
  27. package/scripts/compiler/__pycache__/echo_processor.cpython-314.pyc +0 -0
  28. package/scripts/compiler/__pycache__/event_directive_processor.cpython-313.pyc +0 -0
  29. package/scripts/compiler/__pycache__/event_directive_processor.cpython-314.pyc +0 -0
  30. package/scripts/compiler/__pycache__/function_generators.cpython-313.pyc +0 -0
  31. package/scripts/compiler/__pycache__/function_generators.cpython-314.pyc +0 -0
  32. package/scripts/compiler/__pycache__/loop_handlers.cpython-313.pyc +0 -0
  33. package/scripts/compiler/__pycache__/loop_handlers.cpython-314.pyc +0 -0
  34. package/scripts/compiler/__pycache__/main_compiler.cpython-313.pyc +0 -0
  35. package/scripts/compiler/__pycache__/main_compiler.cpython-314.pyc +0 -0
  36. package/scripts/compiler/__pycache__/parsers.cpython-313.pyc +0 -0
  37. package/scripts/compiler/__pycache__/parsers.cpython-314.pyc +0 -0
  38. package/scripts/compiler/__pycache__/php_converter.cpython-313.pyc +0 -0
  39. package/scripts/compiler/__pycache__/php_converter.cpython-314.pyc +0 -0
  40. package/scripts/compiler/__pycache__/php_js_converter.cpython-313.pyc +0 -0
  41. package/scripts/compiler/__pycache__/php_js_converter.cpython-314.pyc +0 -0
  42. package/scripts/compiler/__pycache__/register_parser.cpython-313.pyc +0 -0
  43. package/scripts/compiler/__pycache__/register_parser.cpython-314.pyc +0 -0
  44. package/scripts/compiler/__pycache__/section_handlers.cpython-313.pyc +0 -0
  45. package/scripts/compiler/__pycache__/section_handlers.cpython-314.pyc +0 -0
  46. package/scripts/compiler/__pycache__/show_directive_handler.cpython-314.pyc +0 -0
  47. package/scripts/compiler/__pycache__/style_directive_handler.cpython-314.pyc +0 -0
  48. package/scripts/compiler/__pycache__/template_analyzer.cpython-313.pyc +0 -0
  49. package/scripts/compiler/__pycache__/template_analyzer.cpython-314.pyc +0 -0
  50. package/scripts/compiler/__pycache__/template_processor.cpython-313.pyc +0 -0
  51. package/scripts/compiler/__pycache__/template_processor.cpython-314.pyc +0 -0
  52. package/scripts/compiler/__pycache__/template_processors.cpython-313.pyc +0 -0
  53. package/scripts/compiler/__pycache__/template_processors.cpython-314.pyc +0 -0
  54. package/scripts/compiler/__pycache__/utils.cpython-313.pyc +0 -0
  55. package/scripts/compiler/__pycache__/utils.cpython-314.pyc +0 -0
  56. package/scripts/compiler/__pycache__/wrapper_parser.cpython-313.pyc +0 -0
  57. package/scripts/compiler/__pycache__/wrapper_parser.cpython-314.pyc +0 -0
  58. package/scripts/compiler/binding_directive_service.py +103 -0
  59. package/scripts/compiler/class_binding_handler.py +347 -0
  60. package/scripts/compiler/cli.py +34 -0
  61. package/scripts/compiler/code_generator.py +141 -0
  62. package/scripts/compiler/compiler.config.json +36 -0
  63. package/scripts/compiler/compiler_utils.py +55 -0
  64. package/scripts/compiler/conditional_handlers.py +252 -0
  65. package/scripts/compiler/config.py +107 -0
  66. package/scripts/compiler/declaration_tracker.py +420 -0
  67. package/scripts/compiler/directive_processors.py +603 -0
  68. package/scripts/compiler/echo_processor.py +667 -0
  69. package/scripts/compiler/event_directive_processor.py +1099 -0
  70. package/scripts/compiler/fetch_parser.py +49 -0
  71. package/scripts/compiler/function_generators.py +310 -0
  72. package/scripts/compiler/loop_handlers.py +224 -0
  73. package/scripts/compiler/main_compiler.py +1763 -0
  74. package/scripts/compiler/parsers.py +1418 -0
  75. package/scripts/compiler/php_converter.py +470 -0
  76. package/scripts/compiler/php_js_converter.py +603 -0
  77. package/scripts/compiler/register_parser.py +480 -0
  78. package/scripts/compiler/section_handlers.py +122 -0
  79. package/scripts/compiler/show_directive_handler.py +85 -0
  80. package/scripts/compiler/style_directive_handler.py +169 -0
  81. package/scripts/compiler/template_analyzer.py +162 -0
  82. package/scripts/compiler/template_processor.py +1167 -0
  83. package/scripts/compiler/template_processors.py +1557 -0
  84. package/scripts/compiler/test_compiler.py +69 -0
  85. package/scripts/compiler/utils.py +54 -0
  86. package/scripts/compiler/variables_analyzer.py +135 -0
  87. package/scripts/compiler/view_identifier_generator.py +278 -0
  88. package/scripts/compiler/wrapper_parser.py +78 -0
  89. package/scripts/dev-context.js +311 -0
  90. package/scripts/dev.js +109 -0
  91. package/scripts/generate-assets-order.js +200 -0
  92. package/scripts/migrate-namespace.php +146 -0
  93. package/scripts/node/MIGRATION.md +190 -0
  94. package/scripts/node/README.md +269 -0
  95. package/scripts/node/build.js +208 -0
  96. package/scripts/node/compiler/compiler-utils.js +38 -0
  97. package/scripts/node/compiler/conditional-handlers.js +45 -0
  98. package/scripts/node/compiler/config.js +178 -0
  99. package/scripts/node/compiler/directive-processors.js +51 -0
  100. package/scripts/node/compiler/event-directive-processor.js +182 -0
  101. package/scripts/node/compiler/function-generators.js +239 -0
  102. package/scripts/node/compiler/loop-handlers.js +45 -0
  103. package/scripts/node/compiler/main-compiler.js +236 -0
  104. package/scripts/node/compiler/parsers.js +358 -0
  105. package/scripts/node/compiler/php-converter.js +227 -0
  106. package/scripts/node/compiler/register-parser.js +32 -0
  107. package/scripts/node/compiler/section-handlers.js +46 -0
  108. package/scripts/node/compiler/template-analyzer.js +50 -0
  109. package/scripts/node/compiler/template-processor.js +371 -0
  110. package/scripts/node/compiler/template-processors.js +219 -0
  111. package/scripts/node/compiler/utils.js +203 -0
  112. package/scripts/node/compiler/wrapper-parser.js +25 -0
  113. package/scripts/node/package.json +24 -0
  114. package/scripts/node/test-compiler.js +52 -0
  115. package/scripts/node-run.cjs +28 -0
  116. package/scripts/standardize-directories.php +92 -0
  117. package/templates/view.module.js +2 -0
  118. package/templates/view.tpl-raw.js +13 -0
  119. package/templates/wraper.js +71 -0
@@ -0,0 +1,69 @@
1
+ """
2
+ Test cases cho Blade Compiler
3
+ """
4
+
5
+ from main_compiler import BladeCompiler
6
+
7
+ def test_basic_compilation():
8
+ """Test basic blade compilation"""
9
+ compiler = BladeCompiler()
10
+
11
+ blade_code = """@vars($users = [], $title = 'Test')
12
+ <h1>{{ $title }}</h1>
13
+ <ul>
14
+ @foreach($users as $user)
15
+ <li>{{ $user->name }}</li>
16
+ @endforeach
17
+ </ul>"""
18
+
19
+ result = compiler.compile_blade_to_js(blade_code, 'test')
20
+ print("=== BASIC COMPILATION TEST ===")
21
+ print(result)
22
+ print()
23
+
24
+ def test_extends_compilation():
25
+ """Test extends compilation"""
26
+ compiler = BladeCompiler()
27
+
28
+ blade_code = """@extends('layouts.app')
29
+
30
+ @section('title', 'Home Page')
31
+
32
+ @section('content')
33
+ <h1>Welcome!</h1>
34
+ <p>{{ $message }}</p>
35
+ @endsection"""
36
+
37
+ result = compiler.compile_blade_to_js(blade_code, 'home')
38
+ print("=== EXTENDS COMPILATION TEST ===")
39
+ print(result)
40
+ print()
41
+
42
+ def test_async_compilation():
43
+ """Test async directives compilation"""
44
+ compiler = BladeCompiler()
45
+
46
+ blade_code = """@vars($users)
47
+ @fetch('/api/users')
48
+ @await($users)
49
+
50
+ <div>
51
+ <h1>Users</h1>
52
+ @foreach($users as $user)
53
+ <div>{{ $user->name }}</div>
54
+ @endforeach
55
+ </div>"""
56
+
57
+ result = compiler.compile_blade_to_js(blade_code, 'users')
58
+ print("=== ASYNC COMPILATION TEST ===")
59
+ print(result)
60
+ print()
61
+
62
+ def run_all_tests():
63
+ """Run all test cases"""
64
+ test_basic_compilation()
65
+ test_extends_compilation()
66
+ test_async_compilation()
67
+
68
+ if __name__ == "__main__":
69
+ run_all_tests()
@@ -0,0 +1,54 @@
1
+ """
2
+ Các hàm tiện ích chung cho compiler
3
+ """
4
+
5
+ import re
6
+ import json
7
+
8
+ def extract_balanced_parentheses(text, start_pos):
9
+ """Extract content inside balanced parentheses starting from start_pos"""
10
+ if start_pos >= len(text) or text[start_pos] != '(':
11
+ return None, start_pos
12
+
13
+ paren_count = 0
14
+ content_start = start_pos + 1
15
+ i = start_pos
16
+
17
+ while i < len(text):
18
+ if text[i] == '(':
19
+ paren_count += 1
20
+ elif text[i] == ')':
21
+ paren_count -= 1
22
+ if paren_count == 0:
23
+ content = text[content_start:i]
24
+ return content, i + 1
25
+ i += 1
26
+
27
+ # Unbalanced parentheses
28
+ return text[content_start:], len(text)
29
+
30
+ def format_attrs(attrs_dict):
31
+ """Format attributes dictionary to JavaScript object string"""
32
+ return json.dumps(attrs_dict, separators=(',', ':'))
33
+
34
+ def normalize_quotes(expr):
35
+ """Normalize single quotes to double quotes in JSON"""
36
+ if not expr:
37
+ return expr
38
+
39
+ # Replace single quotes with double quotes, but be careful with nested quotes
40
+ expr = re.sub(r"'([^']*)'", r'"\1"', expr)
41
+ return expr
42
+
43
+ def format_js_output(content, indent_level=0):
44
+ """Format JavaScript output with proper indentation"""
45
+ lines = content.split('\n')
46
+ formatted_lines = []
47
+
48
+ for line in lines:
49
+ if line.strip():
50
+ formatted_lines.append(' ' * indent_level + line.strip())
51
+ else:
52
+ formatted_lines.append('')
53
+
54
+ return '\n'.join(formatted_lines)
@@ -0,0 +1,135 @@
1
+ """
2
+ Phân tích việc sử dụng biến trong template
3
+ """
4
+
5
+ import re
6
+ from config import JS_FUNCTION_PREFIX
7
+
8
+ def analyze_section_variables(section_content, vars_declaration):
9
+ """Analyze if a section uses variables from @vars directive"""
10
+ if not vars_declaration:
11
+ return False
12
+
13
+ # Extract variable names from vars_declaration
14
+ vars_match = re.search(r'let\s*\{\s*([^}]+)\s*\}', vars_declaration)
15
+ if not vars_match:
16
+ return False
17
+
18
+ vars_content = vars_match.group(1)
19
+ var_names = []
20
+ for var_part in vars_content.split(','):
21
+ var_name = var_part.split('=')[0].strip()
22
+ var_names.append(var_name)
23
+
24
+ # Check if section content uses any of these variables
25
+ for var_name in var_names:
26
+ patterns = [
27
+ f'${{{var_name}}}',
28
+ f'${{{JS_FUNCTION_PREFIX}.escString({var_name})}}',
29
+ f'${{{JS_FUNCTION_PREFIX}.foreach({var_name}',
30
+ f', {var_name})',
31
+ f'({var_name})',
32
+ f', {var_name}',
33
+ f' {var_name}',
34
+ ]
35
+
36
+ for pattern in patterns:
37
+ if pattern in section_content:
38
+ return True
39
+
40
+ return False
41
+
42
+ def analyze_render_uses_vars(sections_code, template_content, vars_declaration):
43
+ """Analyze if render function uses variables from @vars directive"""
44
+ if not vars_declaration:
45
+ return False
46
+
47
+ # Extract variable names from vars_declaration
48
+ vars_match = re.search(r'let\s*\{\s*([^}]+)\s*\}', vars_declaration)
49
+ if not vars_match:
50
+ return False
51
+
52
+ vars_content = vars_match.group(1)
53
+ var_names = []
54
+ for var_part in vars_content.split(','):
55
+ var_name = var_part.split('=')[0].strip()
56
+ var_names.append(var_name)
57
+
58
+ # Combine all render content
59
+ render_content = sections_code + template_content
60
+
61
+ # Check if any variable is used in render content
62
+ for var_name in var_names:
63
+ patterns = [
64
+ f'${{{var_name}}}',
65
+ f'${{{JS_FUNCTION_PREFIX}.escString({var_name})}}',
66
+ f'${{{JS_FUNCTION_PREFIX}.foreach({var_name}',
67
+ f', {var_name})',
68
+ f'({var_name})',
69
+ f', {var_name}',
70
+ f' {var_name}',
71
+ ]
72
+
73
+ for pattern in patterns:
74
+ if pattern in render_content:
75
+ return True
76
+
77
+ return False
78
+
79
+ def analyze_sections_info(sections_code, vars_declaration, has_await=False, has_fetch=False):
80
+ """Analyze sections and return detailed information about each section"""
81
+ sections_info = {}
82
+
83
+ if not sections_code:
84
+ return sections_info
85
+
86
+ # Extract variable names from vars_declaration
87
+ var_names = []
88
+ if vars_declaration:
89
+ vars_match = re.search(r'let\s*\{\s*([^}]+)\s*\}', vars_declaration)
90
+ if vars_match:
91
+ vars_content = vars_match.group(1)
92
+ for var_part in vars_content.split(','):
93
+ var_name = var_part.split('=')[0].strip()
94
+ var_names.append(var_name)
95
+
96
+ # Find all APP.View.section() calls in the sections_code
97
+ section_matches = re.findall(fr'{JS_FUNCTION_PREFIX}\.section\([^)]+\)', sections_code, re.DOTALL)
98
+
99
+ for section_call in section_matches:
100
+ # Extract section name and content
101
+ if ',' in section_call:
102
+ name_match = re.search(fr"{JS_FUNCTION_PREFIX}\.section\(['\"]([^'\"]+)['\"]", section_call)
103
+ if name_match:
104
+ section_name = name_match.group(1)
105
+
106
+ # Find the content part after the first comma
107
+ comma_pos = section_call.find(',')
108
+ section_content = section_call[comma_pos + 1:].strip()
109
+ section_content = section_content.rstrip(');').strip()
110
+
111
+ # Check if this is a long section
112
+ is_long_section = False
113
+ if section_content.startswith('`') and section_content.endswith('`'):
114
+ inner_content = section_content[1:-1]
115
+ is_long_section = ('\n' in inner_content or
116
+ '<' in inner_content or
117
+ len(inner_content.strip()) > 50)
118
+ elif section_content.startswith('`') and not section_content.endswith('`'):
119
+ is_long_section = True
120
+
121
+ # Check if section uses variables
122
+ has_variables = analyze_section_variables(section_content, vars_declaration)
123
+
124
+ # Determine section type and preload status
125
+ section_type = "long" if is_long_section else "short"
126
+ use_vars = has_variables
127
+ preloader = has_variables and (has_await or has_fetch)
128
+
129
+ sections_info[section_name] = {
130
+ "type": section_type,
131
+ "preloader": preloader,
132
+ "useVars": use_vars
133
+ }
134
+
135
+ return sections_info
@@ -0,0 +1,278 @@
1
+ """
2
+ View Identifier Generator for Blade Compiler
3
+ Generates view identification attributes for server-rendered content
4
+ """
5
+
6
+ import re
7
+ from utils import extract_balanced_parentheses
8
+
9
+ class ViewIdentifierGenerator:
10
+ def __init__(self):
11
+ self.view_id_counter = 0
12
+ self.view_hierarchy = {}
13
+
14
+ def generate_view_attributes(self, view_name, view_type='view', parent_view=None):
15
+ """Generate view identification attributes"""
16
+ view_id = self.generate_view_id(view_name)
17
+
18
+ attributes = {
19
+ 'data-spa-view': self.extract_view_scope(view_name),
20
+ 'data-spa-view-name': view_name,
21
+ 'data-spa-view-path': view_name,
22
+ 'data-spa-view-id': view_id,
23
+ 'data-spa-view-type': view_type
24
+ }
25
+
26
+ if parent_view:
27
+ attributes['data-spa-view-parent'] = parent_view
28
+
29
+ return attributes
30
+
31
+ def generate_view_id(self, view_name):
32
+ """Generate unique view ID"""
33
+ self.view_id_counter += 1
34
+ # Convert view name to ID format
35
+ clean_name = re.sub(r'[^a-zA-Z0-9]', '-', view_name)
36
+ return f"{clean_name}-{self.view_id_counter}"
37
+
38
+ def extract_view_scope(self, view_name):
39
+ """Extract view scope from view name"""
40
+ parts = view_name.split('.')
41
+ if len(parts) > 1:
42
+ return parts[-1] # Last part as scope
43
+ return 'main'
44
+
45
+ def process_template_with_identifiers(self, blade_code, view_name, view_type='view'):
46
+ """Process template and add view identification attributes"""
47
+
48
+ # Generate attributes for main container
49
+ main_attributes = self.generate_view_attributes(view_name, view_type)
50
+
51
+ # Add attributes to main container elements
52
+ blade_code = self.add_attributes_to_containers(blade_code, main_attributes)
53
+
54
+ # Process sections with identifiers
55
+ blade_code = self.process_sections_with_identifiers(blade_code, view_name)
56
+
57
+ # Process conditional blocks with identifiers
58
+ blade_code = self.process_conditionals_with_identifiers(blade_code, view_name)
59
+
60
+ return blade_code
61
+
62
+ def add_attributes_to_containers(self, blade_code, attributes):
63
+ """Add view identification attributes to container elements"""
64
+
65
+ # Find main container elements (div, section, main, article, etc.)
66
+ container_patterns = [
67
+ r'<div([^>]*)>',
68
+ r'<section([^>]*)>',
69
+ r'<main([^>]*)>',
70
+ r'<article([^>]*)>',
71
+ r'<aside([^>]*)>',
72
+ r'<header([^>]*)>',
73
+ r'<footer([^>]*)>'
74
+ ]
75
+
76
+ for pattern in container_patterns:
77
+ def add_attributes(match):
78
+ existing_attrs = match.group(1)
79
+ new_attrs = ' '.join([f'{k}="{v}"' for k, v in attributes.items()])
80
+
81
+ if existing_attrs.strip():
82
+ return f'<{match.group(0)[1:-1]} {new_attrs}>'
83
+ else:
84
+ return f'<{match.group(0)[1:-1]} {new_attrs}>'
85
+
86
+ blade_code = re.sub(pattern, add_attributes, blade_code)
87
+
88
+ return blade_code
89
+
90
+ def process_sections_with_identifiers(self, blade_code, view_name):
91
+ """Process @section directives with view identifiers"""
92
+
93
+ # Find @section directives
94
+ section_pattern = r'@section\s*\(\s*[\'"]([^\'"]+)[\'"]\s*\)'
95
+
96
+ def process_section(match):
97
+ section_name = match.group(1)
98
+ section_id = f"{view_name}.{section_name}"
99
+ section_attributes = self.generate_view_attributes(section_id, 'section', view_name)
100
+
101
+ # Add attributes to the section content
102
+ attrs_str = ' '.join([f'{k}="{v}"' for k, v in section_attributes.items()])
103
+
104
+ return f'@section(\'{section_name}\')\n<div {attrs_str}>'
105
+
106
+ blade_code = re.sub(section_pattern, process_section, blade_code)
107
+
108
+ # Find @endsection directives
109
+ endsection_pattern = r'@endsection'
110
+ blade_code = re.sub(endsection_pattern, '</div>\n@endsection', blade_code)
111
+
112
+ return blade_code
113
+
114
+ def process_conditionals_with_identifiers(self, blade_code, view_name):
115
+ """Process conditional blocks with view identifiers"""
116
+
117
+ # Find @if directives
118
+ if_pattern = r'@if\s*\([^)]+\)'
119
+
120
+ def process_if(match):
121
+ if_content = match.group(0)
122
+ if_id = f"{view_name}.if-{self.view_id_counter}"
123
+ if_attributes = self.generate_view_attributes(if_id, 'conditional', view_name)
124
+
125
+ attrs_str = ' '.join([f'{k}="{v}"' for k, v in if_attributes.items()])
126
+
127
+ return f'{if_content}\n<div {attrs_str}>'
128
+
129
+ blade_code = re.sub(if_pattern, process_if, blade_code)
130
+
131
+ # Find @endif directives
132
+ endif_pattern = r'@endif'
133
+ blade_code = re.sub(endif_pattern, '</div>\n@endif', blade_code)
134
+
135
+ return blade_code
136
+
137
+ def generate_server_side_attributes(self, view_name, view_type='view'):
138
+ """Generate attributes for server-side rendering"""
139
+
140
+ attributes = self.generate_view_attributes(view_name, view_type)
141
+
142
+ # Add server-side specific attributes
143
+ attributes.update({
144
+ 'data-server-rendered': 'true',
145
+ 'data-hydration-ready': 'false'
146
+ })
147
+
148
+ return attributes
149
+
150
+ def generate_meta_tags(self, view_name, view_type='view'):
151
+ """Generate meta tags for view identification"""
152
+
153
+ view_id = self.generate_view_id(view_name)
154
+
155
+ meta_tags = f"""
156
+ <!-- View Identification Meta Tags -->
157
+ <meta name="spa-view-name" content="{view_name}">
158
+ <meta name="spa-view-path" content="{view_name}">
159
+ <meta name="spa-view-id" content="{view_id}">
160
+ <meta name="spa-view-type" content="{view_type}">
161
+ """
162
+
163
+ return meta_tags
164
+
165
+ def process_yield_directives(self, blade_code, view_name):
166
+ """Process @yield directives with view identifiers"""
167
+
168
+ # Find @yield directives
169
+ yield_pattern = r'@yield\s*\(\s*[\'"]([^\'"]+)[\'"]\s*\)'
170
+
171
+ def process_yield(match):
172
+ yield_name = match.group(1)
173
+ yield_id = f"{view_name}.yield-{yield_name}"
174
+ yield_attributes = self.generate_view_attributes(yield_id, 'yield', view_name)
175
+
176
+ attrs_str = ' '.join([f'{k}="{v}"' for k, v in yield_attributes.items()])
177
+
178
+ return f'<div {attrs_str}>@yield(\'{yield_name}\')</div>'
179
+
180
+ blade_code = re.sub(yield_pattern, process_yield, blade_code)
181
+
182
+ return blade_code
183
+
184
+ def generate_view_boundary_comments(self, view_name, view_type='view'):
185
+ """Generate HTML comments for view boundaries"""
186
+
187
+ view_id = self.generate_view_id(view_name)
188
+
189
+ start_comment = f"<!-- SPA VIEW START: {view_name} (ID: {view_id}, Type: {view_type}) -->"
190
+ end_comment = f"<!-- SPA VIEW END: {view_name} -->"
191
+
192
+ return start_comment, end_comment
193
+
194
+ def process_loop_directives(self, blade_code, view_name):
195
+ """Process loop directives with view identifiers"""
196
+
197
+ # Find @foreach directives
198
+ foreach_pattern = r'@foreach\s*\([^)]+\)'
199
+
200
+ def process_foreach(match):
201
+ foreach_content = match.group(0)
202
+ loop_id = f"{view_name}.loop-{self.view_id_counter}"
203
+ loop_attributes = self.generate_view_attributes(loop_id, 'loop', view_name)
204
+
205
+ attrs_str = ' '.join([f'{k}="{v}"' for k, v in loop_attributes.items()])
206
+
207
+ return f'{foreach_content}\n<div {attrs_str}>'
208
+
209
+ blade_code = re.sub(foreach_pattern, process_foreach, blade_code)
210
+
211
+ # Find @endforeach directives
212
+ endforeach_pattern = r'@endforeach'
213
+ blade_code = re.sub(endforeach_pattern, '</div>\n@endforeach', blade_code)
214
+
215
+ return blade_code
216
+
217
+ def generate_debug_attributes(self, view_name, debug_mode=False):
218
+ """Generate debug attributes for development"""
219
+
220
+ if not debug_mode:
221
+ return {}
222
+
223
+ return {
224
+ 'data-debug-view': view_name,
225
+ 'data-debug-timestamp': str(int(__import__('time').time())),
226
+ 'data-debug-version': '1.0.0'
227
+ }
228
+
229
+ def process_complete_template(self, blade_code, view_name, view_type='view', debug_mode=False):
230
+ """Process complete template with all view identifiers"""
231
+
232
+ # Add meta tags
233
+ meta_tags = self.generate_meta_tags(view_name, view_type)
234
+ blade_code = self.insert_meta_tags(blade_code, meta_tags)
235
+
236
+ # Process template with identifiers
237
+ blade_code = self.process_template_with_identifiers(blade_code, view_name, view_type)
238
+
239
+ # Process yield directives
240
+ blade_code = self.process_yield_directives(blade_code, view_name)
241
+
242
+ # Process loop directives
243
+ blade_code = self.process_loop_directives(blade_code, view_name)
244
+
245
+ # Add debug attributes if enabled
246
+ if debug_mode:
247
+ debug_attrs = self.generate_debug_attributes(view_name, debug_mode)
248
+ blade_code = self.add_debug_attributes(blade_code, debug_attrs)
249
+
250
+ return blade_code
251
+
252
+ def insert_meta_tags(self, blade_code, meta_tags):
253
+ """Insert meta tags into head section"""
254
+
255
+ # Find head tag
256
+ head_pattern = r'(<head[^>]*>)'
257
+
258
+ def insert_meta(match):
259
+ return f"{match.group(1)}\n{meta_tags}"
260
+
261
+ blade_code = re.sub(head_pattern, insert_meta, blade_code, flags=re.IGNORECASE)
262
+
263
+ return blade_code
264
+
265
+ def add_debug_attributes(self, blade_code, debug_attrs):
266
+ """Add debug attributes to elements"""
267
+
268
+ # Add debug attributes to main containers
269
+ container_pattern = r'<div([^>]*data-spa-view[^>]*)>'
270
+
271
+ def add_debug(match):
272
+ existing_attrs = match.group(1)
273
+ debug_str = ' '.join([f'{k}="{v}"' for k, v in debug_attrs.items()])
274
+ return f'<div {existing_attrs} {debug_str}>'
275
+
276
+ blade_code = re.sub(container_pattern, add_debug, blade_code)
277
+
278
+ return blade_code
@@ -0,0 +1,78 @@
1
+ """
2
+ Parser để đọc file wraper.js và extract nội dung
3
+ """
4
+
5
+ import re
6
+ import os
7
+
8
+ class WrapperParser:
9
+ def __init__(self):
10
+ self.wrapper_function_content = ""
11
+ self.wrapper_config_content = ""
12
+
13
+ def parse_wrapper_file(self, file_path="resources/js/templates/wraper.js"):
14
+ """Parse file wraper.js và extract nội dung theo comment đánh dấu"""
15
+ # Reset state trước khi parse
16
+ self.wrapper_function_content = ""
17
+ self.wrapper_config_content = ""
18
+
19
+ # Convert to absolute path if relative
20
+ if not os.path.isabs(file_path):
21
+ # Try path relative to current working directory
22
+ if not os.path.exists(file_path):
23
+ # Try path relative to script directory
24
+ script_dir = os.path.dirname(os.path.abspath(__file__))
25
+ alt_path = os.path.join(script_dir, "..", "..", file_path)
26
+ if os.path.exists(alt_path):
27
+ file_path = alt_path
28
+
29
+ if not os.path.exists(file_path):
30
+ print(f"Warning: Wrapper file not found: {file_path}")
31
+ return "", ""
32
+
33
+ try:
34
+ with open(file_path, 'r', encoding='utf-8') as f:
35
+ content = f.read()
36
+ except Exception as e:
37
+ print(f"Error reading wrapper file: {e}")
38
+ return "", ""
39
+
40
+ # Extract function wraper content - từ "// start wrapper" đến "// end wrapper"
41
+ start_marker = "// start wrapper"
42
+ end_marker = "// end wrapper"
43
+
44
+ start_pos = content.find(start_marker)
45
+ if start_pos != -1:
46
+ start_pos = content.find('\n', start_pos) + 1 # Bỏ qua dòng comment
47
+ end_pos = content.find(end_marker)
48
+ if end_pos != -1:
49
+ self.wrapper_function_content = content[start_pos:end_pos].strip()
50
+ else:
51
+ print("Warning: End wrapper marker not found")
52
+ else:
53
+ print("Warning: Start wrapper marker not found")
54
+
55
+ # Extract WRAPPER_CONFIG content - từ "// start wrapper config" đến "// end wrapper config"
56
+ start_config_marker = "// start wrapper config"
57
+ end_config_marker = "// end wrapper config"
58
+
59
+ start_config_pos = content.find(start_config_marker)
60
+ if start_config_pos != -1:
61
+ start_config_pos = content.find('\n', start_config_pos) + 1 # Bỏ qua dòng comment
62
+ end_config_pos = content.find(end_config_marker)
63
+ if end_config_pos != -1:
64
+ self.wrapper_config_content = content[start_config_pos:end_config_pos].strip()
65
+ else:
66
+ print("Warning: End wrapper config marker not found")
67
+ else:
68
+ print("Warning: Start wrapper config marker not found")
69
+
70
+ return self.wrapper_function_content, self.wrapper_config_content
71
+
72
+ def get_wrapper_function_content(self):
73
+ """Lấy nội dung function wraper"""
74
+ return self.wrapper_function_content
75
+
76
+ def get_wrapper_config_content(self):
77
+ """Lấy nội dung WRAPPER_CONFIG"""
78
+ return self.wrapper_config_content