@saolabs/compiler
v1.0.0
Published
Saola Compiler - Transforms .sao files to Blade and JavaScript
Downloads
71
Maintainers
Readme
Saola Compiler
Saola Compiler là một công cụ biên dịch chuyên biệt chuyển đổi các file template .sao thành hai định dạng output:
- Blade files (.blade.php) cho Laravel Server-Side Rendering (SSR)
- JavaScript View files (.js) cho client-side rendering
Cấu Trúc Compiler
compiler/
├── index.js # Main compiler - Node.js wrapper
├── cli.js # CLI entry point
├── config-manager.js # Manages sao.config.json
├── python/ # Python compiler (13k+ lines from onejs)
│ ├── main_compiler.py
│ ├── cli.py
│ ├── function_generators.py
│ ├── section_handlers.py
│ ├── php_js_converter.py
│ └── ... (26 modules)
├── package.json # Package configuration
└── README.md # This fileQuy Trình Biên Dịch
Node.js Wrapper (index.js)
- Đọc
.saofiles - Tách các phần: declarations, blade template, script, style
- GHI NGAY Blade file (declarations + template)
- Song song: Gọi Python compiler để generate JavaScript
- Copy app files vào temp directory
Python Compiler
- Xử lý Blade syntax → JavaScript
- Generate prerender & render functions
- Xử lý directives (@await, @fetch, @section, etc)
- Convert PHP expressions sang JavaScript
- Tạo state management code
Key Features
✅ Declaration Order Preservation: Giữ nguyên thứ tự khai báo ✅ Auto-Create Folders: Tự động tạo temp directories ✅ Namespace Support: Nhiều namespaces mỗi context ✅ App Files Copy: Tự động copy app files vào temp ✅ PHP to JS Conversion: Chuyển đổi PHP expressions ✅ Prerender/Render Separation: Riêng biệt SSR và dynamic rendering ✅ Parallel Processing: Blade và JS compile song song
File Cấu Hình: sao.config.json
Đặt tại project root của Laravel:
{
"packages": {
"saola": "1.0.0"
},
"paths": {
"resources": "resources",
"saoView": "resources/sao",
"bladeView": "resources/views",
"temp": "resources/js/temp",
"public": "public/static/one"
},
"output": {
"default": "app/js",
"contexts": {
"admin": "admin/js",
"web": "web/js",
"mobile": "mobile/js"
}
},
"contexts": {
"web": {
"name": "Web",
"app": ["web/app"],
"views": {
"web": "web/views"
},
"blade": {
"web": "web"
},
"temp": {
"views": "web/views",
"app": "web/app",
"registry": "web/registry.js"
}
},
"admin": {
"name": "Admin Panel",
"app": ["admin/app"],
"views": {
"admin": "admin/views"
},
"blade": {
"admin": "admin"
},
"temp": {
"views": "admin/views",
"app": "admin/app",
"registry": "admin/registry.js"
}
},
"default": {
"name": "Default - All Contexts",
"app": ["admin/app", "web/app"],
"views": {
"web": "web/views",
"admin": "admin/views"
},
"blade": {
"web": "web",
"admin": "admin"
},
"temp": {
"views": "app/views",
"app": "app/app",
"registry": "app/registry.js"
}
}
}
}Config Structure
paths (Base Paths)
Các base paths cho các mục khác nhau:
saoView: Base cho views và app sourcesbladeView: Base cho blade outputstemp: Base cho JS temp outputspublic: Base cho production outputs
contexts
Mỗi context có các relative paths (sẽ được prefix với base paths):
app[]: Array các thư mục app sources (sẽ copy vào compiled.app)views{}: Object namespace → view path mappingblade{}: Object namespace → blade output path mappingcompiled.views: Temp views output directorycompiled.app: Temp app output directorycompiled.registry: Registry file path
Lưu ý: default context không phải là context thật, dùng khi không chỉ định context.
Cách Sử Dụng
1. Installation
npm install @saolab/compiler
# hoặc local install để test
cd /path/to/saola-compiler && npm pack
cd /path/to/your-project && npm install ../saola-compiler/saolab-compiler-1.0.0.tgz2. CLI Commands
# Compile specific context
npx sao-compile web
npx sao-compile admin
# Compile all contexts (skips 'default')
npx sao-compile all
# Watch mode
npx sao-compile web --watch
# Show help
npx sao-compile --help3. Quy Trình Compile
Input Structure:
resources/sao/
├── web/
│ ├── app/ ← App sources
│ │ ├── helpers/
│ │ └── services/
│ └── views/ ← .sao files
│ └── pages/
│ ├── home.sao
│ └── about.sao
└── admin/
├── app/
└── views/Output Structure:
resources/
├── views/ ← Blade outputs
│ ├── web/
│ │ └── pages/
│ │ ├── home.blade.php
│ │ └── about.blade.php
│ └── admin/
└── js/temp/ ← JS temp outputs
├── web/
│ ├── app/ ← Copied from sources
│ │ ├── helpers/
│ │ └── services/
│ └── views/ ← Compiled JS
│ ├── WebPagesHome.js
│ └── WebPagesAbout.js
└── admin/4. Path Resolution
Compiler tự động resolve paths:
views: paths.saoView + context.views[namespace]
→ resources/sao + web/views
→ resources/sao/web/views
blade: paths.bladeView + context.blade[namespace]
→ resources/views + web
→ resources/views/web
temp: paths.compiled + context.compiled.views
→ resources/js/temp + web/views
→ resources/js/temp/web/viewsWatch mode (development)
npm run dev:web npm run dev:admin
Or run CLI directly
onejs-build web onejs-build admin --watch
## Input: .sao File Format
File `.sao` có 4 phần:
```one
@useState($isOpen, false)
@const($API_URL = '/api/users')
<blade>
<div class="component" @click($setIsOpen(!$isOpen))>
Status: {{ $isOpen ? 'Open' : 'Closed' }}
</div>
</blade>
<script setup>
export default {
toggle() {
setIsOpen(!isOpen);
}
}
</script>
<style scoped>
.component {
padding: 10px;
}
</style>Output Structures
Blade Output (resources/views/web/admin/users/List.blade.php)
@useState($isOpen, false)
<div class="component" @click(...)>
Status: {{ $isOpen ? 'Open' : 'Closed' }}
</div>JavaScript Output (resources/sao/js/temp/web/views/WebAdminUsersList.js)
class WebAdminUsersListView extends View {
constructor(App, systemData) {
super(__VIEW_PATH__, __VIEW_TYPE__);
this.__ctrl__.setApp(App);
}
__setup__(__data__, systemData) {
// 8-step initialization process
// ... state management
// ... lifecycle callbacks
// ... render function
}
}
export function WebAdminUsersList(data, systemData) {
const App = app.make("App");
const view = new WebAdminUsersListView(App, systemData);
view.__setup__(data, systemData);
return view;
}Registry Output (resources/sao/js/temp/web/registry.js)
export const ViewRegistry = {
'web.admin.users.List': () => import('./views/WebAdminUsersList.js'),
'web.pages.Home': () => import('./views/WebPagesHome.js'),
// ... more views
};Directory Structure Sync
CRITICAL: Cấu trúc folder PHẢI đồng bộ giữa input và Blade output
Input: resources/sao/app/web/views/admin/users/List.sao
└─context─┘ └─folder path──┘
Output: resources/views/web/admin/users/List.blade.php
└─context─┘ └─folder path──┘Rules:
- ✅ Filename PHẢI giống nhau (.sao → .blade.php)
- ✅ Folder path PHẢI đồng bộ hoàn toàn
- ✅ Context prefix từ config
- ❌ JavaScript không cần match folder structure (tên file JS đã include path)
Quy Trình Build 4 Bước
- KHỞI TẠO: Đọc & parse sao.config.json
- DUYỆT & PHÂN TÍCH: Parse tất cả .sao files
- SINH RA OUTPUT: Generate Blade, JavaScript, Registry
- BUNDLING: Webpack bundle + minify cho production
State Management Patterns
@useState
@useState($count, 0)
@useState($isOpen, false)State Setters
// Auto-generated setters
setCount(count + 1);
setIsOpen(!isOpen);Lifecycle Callbacks
commitConstructorData() // Initialize states
updateVariableData(data) // Update all variables
updateVariableItemData() // Update individual items
prerender() // Pre-render hook
render() // Main render functionDirectives Supported
Event Binding
@click(handler)@input(handler)@change(handler)@submit(handler)@keyup(handler),@keydown(handler)@focus(handler),@blur(handler)@mouseenter(handler),@mouseleave(handler)
Data Binding
@bind($var)- Two-way binding@val($var)- Value binding@checked($var)- Checkbox binding@selected($var)- Select option binding
Conditional & Looping
@if,@elseif,@else,@endif@foreach,@endforeach@for,@endfor@while,@endwhile
Attributes & Styling
@attr([...])- Dynamic attributes@class([...])- Dynamic classes@style([...])- Dynamic styles@show($condition)- Toggle visibility@hide($condition)- Hide element
Error Handling
Compiler provides:
- ✅ Syntax error detection
- ✅ Line and column references
- ✅ Helpful error messages
- ✅ Validation at compile time
- ✅ Warnings for common issues
Performance Optimizations
- ✅ Incremental builds (watch mode)
- ✅ File hash comparison
- ✅ Dependency tracking
- ✅ Cascading rebuilds when imports change
- ✅ Caching of parsed ASTs
Troubleshooting
sao.config.json not found
# Create the file at project root
cat > sao.config.json << 'EOF'
{
"root": "resources/sao/app",
...
}
EOFContext not found
Check that context name in sao.config.json matches CLI argument:
onejs-build web # 'web' must be in config.contextsFiles not being compiled
- Check file has .sao extension
- Verify file is in paths specified in sao.config.json
- Run compiler with explicit context:
onejs-build web
Development
Run tests
npm testDebug mode
DEBUG=saolab:* sao-build webLicense
MIT
