onelaraveljs 1.1.3 → 1.1.5
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 +142 -47
- package/package.json +1 -1
- package/scripts/dev-context.js +66 -35
package/README.md
CHANGED
|
@@ -1,87 +1,182 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OneLaravelJS Framework
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**OneLaravelJS** là thư viện lõi JavaScript dành cho các ứng dụng OneLaravel. Nó cung cấp nền tảng runtime, quản lý view, routing và khả năng tương tác hai chiều mạnh mẽ giữa Laravel Blade và JavaScript.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Thư viện này đóng vai trò là "Engine", trong khi ứng dụng Laravel của bạn cung cấp "Map" (Views và Cấu hình).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🏗 Cài đặt
|
|
10
|
+
|
|
11
|
+
Cài đặt thư viện thông qua npm:
|
|
6
12
|
|
|
7
13
|
```bash
|
|
8
14
|
npm install onelaraveljs
|
|
9
15
|
```
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
Sau khi cài đặt, bạn sẽ có quyền truy cập vào:
|
|
18
|
+
- **Runtime Library**: Các file JS để chạy ứng dụng.
|
|
19
|
+
- **CLI Tools**: Công cụ `onejs-build` để biên dịch Blade template thành JavaScript modules.
|
|
20
|
+
|
|
21
|
+
---
|
|
12
22
|
|
|
13
|
-
|
|
23
|
+
## 📂 Cấu trúc dự án khuyến nghị
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
Để OneLaravelJS hoạt động hiệu quả, dự án của bạn nên tuân thủ cấu trúc sau:
|
|
16
26
|
|
|
17
|
-
Ensure your project has the following recommended structure for frontend assets:
|
|
18
27
|
```
|
|
19
|
-
|
|
20
|
-
├──
|
|
21
|
-
├──
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
my-laravel-project/
|
|
29
|
+
├── build.config.json # File cấu hình build (BẮT BUỘC)
|
|
30
|
+
├── package.json
|
|
31
|
+
├── resources/
|
|
32
|
+
│ ├── js/
|
|
33
|
+
│ │ ├── app.js # Entry point chính
|
|
34
|
+
│ │ ├── build/ # Output của Webpack (Bundle)
|
|
35
|
+
│ │ ├── config/ # Output của Compiler (Registry maps)
|
|
36
|
+
│ │ ├── core/ # Output của Compiler (Proxy files)
|
|
37
|
+
│ │ └── views/ # Output của Compiler (Compiled View Components)
|
|
38
|
+
│ └── views/
|
|
39
|
+
│ ├── _system/ # System views required by Framework
|
|
40
|
+
│ └── web/ # User views
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## ⚙️ Cấu hình (`build.config.json`)
|
|
46
|
+
|
|
47
|
+
Tạo file `build.config.json` tại thư mục gốc dự án để định nghĩa các ngữ cảnh (contexts) build:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"contexts": {
|
|
52
|
+
"web": {
|
|
53
|
+
"sources": [
|
|
54
|
+
"resources/views/_system",
|
|
55
|
+
"resources/views/web"
|
|
56
|
+
],
|
|
57
|
+
"output": {
|
|
58
|
+
"views": "resources/js/views/web",
|
|
59
|
+
"register": "resources/js/config/templates.web.js",
|
|
60
|
+
"bundle": "resources/js/build/web.bundle.js"
|
|
61
|
+
},
|
|
62
|
+
"dist": {
|
|
63
|
+
"bundle": "public/static/web/js/main.bundle.js",
|
|
64
|
+
"css": "public/static/web/css",
|
|
65
|
+
"assets": "public/static/web/assets"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🛠 Công cụ CLI (`onejs-build`)
|
|
75
|
+
|
|
76
|
+
OneLaravelJS đi kèm với trình biên dịch mạnh mẽ để chuyển đổi Blade Templates thành JavaScript Components.
|
|
77
|
+
|
|
78
|
+
### Các lệnh phổ biến:
|
|
79
|
+
|
|
80
|
+
Thêm vào `package.json` của bạn:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
"scripts": {
|
|
84
|
+
"build:templates": "onejs-build all",
|
|
85
|
+
"build:templates:web": "onejs-build web",
|
|
86
|
+
"dev:blade": "onejs-build"
|
|
87
|
+
}
|
|
24
88
|
```
|
|
25
89
|
|
|
26
|
-
|
|
90
|
+
- **`onejs-build all`**: Biên dịch tất cả các context được định nghĩa trong `build.config.json`.
|
|
91
|
+
- **`onejs-build web`**: Chỉ biên dịch context `web`.
|
|
92
|
+
- **`onejs-build`**: Chạy chế độ Interactive (Menu chọn).
|
|
27
93
|
|
|
28
|
-
|
|
94
|
+
### Cơ chế hoạt động:
|
|
95
|
+
1. Đọc `build.config.json`.
|
|
96
|
+
2. Quét các file `.blade.php` trong thư mục `sources`.
|
|
97
|
+
3. Phân tích cú pháp Blade, Directives (@if, @foreach), và OneJS directives (x-data, x-bind).
|
|
98
|
+
4. Sinh ra các file ES6 Module tại thư mục `output.views`.
|
|
99
|
+
5. Tạo file Registry (`templates.web.js`) để map tên view sang file JS.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 🚀 Sử dụng trong ứng dụng (`app.js`)
|
|
104
|
+
|
|
105
|
+
Tại `resources/js/app.js`, bạn cần kết nối Core Framework với Registry views đã được biên dịch:
|
|
29
106
|
|
|
30
107
|
```javascript
|
|
31
108
|
import { App, viewLoader } from 'onelaraveljs';
|
|
32
|
-
import { viewRegistry } from './config/viewRegistry.generated.js';
|
|
33
109
|
|
|
34
|
-
//
|
|
35
|
-
|
|
110
|
+
// Import Registry đã được CLI sinh ra (thông qua Proxy hoặc trực tiếp)
|
|
111
|
+
// Lưu ý: ViewTemplates thường được export từ file generated resources/js/core/ViewTemplate.js
|
|
112
|
+
import { ViewTemplates } from './core/ViewTemplate.js';
|
|
113
|
+
|
|
114
|
+
// 1. Dependency Injection: Nạp danh sách views vào Core
|
|
115
|
+
viewLoader.setRegistry(ViewTemplates);
|
|
36
116
|
|
|
37
|
-
// 2.
|
|
38
|
-
//
|
|
117
|
+
// 2. Khởi tạo App
|
|
118
|
+
// App sẽ tự động đọc window.APP_CONFIGS từ Blade để cấu hình Env, Routes...
|
|
39
119
|
if (window.APP_CONFIGS) {
|
|
40
120
|
App.init();
|
|
41
121
|
}
|
|
42
122
|
|
|
43
|
-
// 3. Export
|
|
123
|
+
// 3. Export global (Tùy chọn, dùng cho debug hoặc legacy scripts)
|
|
44
124
|
window.App = App;
|
|
45
125
|
```
|
|
46
126
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Ensure your main layout file injects the configuration:
|
|
50
|
-
|
|
51
|
-
```html
|
|
52
|
-
<script>
|
|
53
|
-
window.APP_CONFIGS = {
|
|
54
|
-
env: { ... },
|
|
55
|
-
routes: [ ... ],
|
|
56
|
-
// ... other configurations
|
|
57
|
-
};
|
|
58
|
-
</script>
|
|
59
|
-
<script src="{{ mix('js/app.js') }}"></script>
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## Core Concepts
|
|
127
|
+
---
|
|
63
128
|
|
|
64
|
-
|
|
129
|
+
## 🧠 Core Concepts
|
|
65
130
|
|
|
66
|
-
|
|
67
|
-
|
|
131
|
+
### 1. View System
|
|
132
|
+
OneJS coi mỗi file Blade là một Component.
|
|
133
|
+
- **Server**: Render HTML tĩnh (SEO).
|
|
134
|
+
- **Client**: Hydrate HTML đó thành Interactive Component.
|
|
68
135
|
|
|
69
|
-
###
|
|
136
|
+
### 2. ViewLoader & Registry
|
|
137
|
+
- **ViewLoader**: Là "bộ não" tải view lười (lazy-load). Nó không biết gì về ứng dụng của bạn cho đến khi được cung cấp Registry.
|
|
138
|
+
- **ViewRegistry**: Là "cuốn danh bạ" map tên view (`web.home`) tới file code (`WebHome.js`). File này được sinh tự động.
|
|
70
139
|
|
|
71
|
-
|
|
140
|
+
### 3. Event Service
|
|
141
|
+
Hệ thống Event Bus tích hợp sẵn:
|
|
72
142
|
|
|
73
143
|
```javascript
|
|
74
144
|
import { App } from 'onelaraveljs';
|
|
75
145
|
|
|
76
|
-
//
|
|
77
|
-
App.Event.on('
|
|
78
|
-
console.log(
|
|
146
|
+
// Lắng nghe
|
|
147
|
+
App.Event.on('cart:updated', (data) => {
|
|
148
|
+
console.log('Cart count:', data.count);
|
|
79
149
|
});
|
|
80
150
|
|
|
81
|
-
//
|
|
82
|
-
App.Event.emit('
|
|
151
|
+
// Phát sự kiện
|
|
152
|
+
App.Event.emit('cart:updated', { count: 5 });
|
|
83
153
|
```
|
|
84
154
|
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## 🤝 Đóng góp & Phát triển (Development)
|
|
158
|
+
|
|
159
|
+
Nếu bạn muốn chỉnh sửa source code của chính thư viện `onelaraveljs`:
|
|
160
|
+
|
|
161
|
+
### Cấu trúc Source:
|
|
162
|
+
- `bin/`: Chứa file thực thi CLI.
|
|
163
|
+
- `scripts/`: Chứa logic biên dịch (Python/Node).
|
|
164
|
+
- `build.py`: Script chính điều phối build.
|
|
165
|
+
- `compiler/`: Bộ biên dịch Blade sang JS (Python).
|
|
166
|
+
- `src/`: Source code JS runtime.
|
|
167
|
+
- `core/`: Logic cốt lõi (Router, ViewEngine...).
|
|
168
|
+
|
|
169
|
+
### Quy trình phát triển:
|
|
170
|
+
1. Clone repo về máy.
|
|
171
|
+
2. Chạy `npm install`.
|
|
172
|
+
3. Symlink sang dự án test (`npm link` hoặc chỉnh sửa trực tiếp trong `node_modules` để debug nhanh).
|
|
173
|
+
4. Đảm bảo biến môi trường `ONEJS_PROJECT_ROOT` được xử lý đúng trong các script build nếu chạy thủ công.
|
|
174
|
+
|
|
175
|
+
### Testing:
|
|
176
|
+
Kiểm tra các thay đổi bằng cách chạy build trên một dự án Laravel thực tế sử dụng thư viện này (ví dụ `onelaravel`).
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
85
180
|
## License
|
|
86
181
|
|
|
87
|
-
MIT
|
|
182
|
+
MIT License.
|
package/package.json
CHANGED
package/scripts/dev-context.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { spawn } from 'child_process';
|
|
12
|
-
import { watch } from 'fs';
|
|
12
|
+
import { watch, existsSync } from 'fs';
|
|
13
13
|
import { readFileSync, writeFileSync } from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
@@ -20,6 +20,7 @@ const __dirname = path.dirname(__filename);
|
|
|
20
20
|
|
|
21
21
|
// Get context from command line argument
|
|
22
22
|
const context = process.argv[2] || 'web'; // Default to 'web'
|
|
23
|
+
const projectRoot = process.env.ONEJS_PROJECT_ROOT || process.cwd();
|
|
23
24
|
|
|
24
25
|
if (!['web', 'admin'].includes(context)) {
|
|
25
26
|
console.error('❌ Invalid context. Use: web or admin');
|
|
@@ -27,7 +28,7 @@ if (!['web', 'admin'].includes(context)) {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// Load build config
|
|
30
|
-
const buildConfigPath = path.resolve(
|
|
31
|
+
const buildConfigPath = path.resolve(projectRoot, 'build.config.json');
|
|
31
32
|
const buildConfig = JSON.parse(readFileSync(buildConfigPath, 'utf-8'));
|
|
32
33
|
const contextConfig = buildConfig.contexts[context];
|
|
33
34
|
|
|
@@ -40,9 +41,8 @@ const config = {
|
|
|
40
41
|
context: context,
|
|
41
42
|
watchPaths: {
|
|
42
43
|
blade: contextConfig.sources, // Array of blade source paths from config
|
|
43
|
-
jsCore: 'resources/js
|
|
44
|
-
|
|
45
|
-
compiler: 'scripts/compiler'
|
|
44
|
+
jsCore: 'resources/js', // Watch entire js directory
|
|
45
|
+
compiler: 'node_modules/onelaraveljs/scripts/compiler'
|
|
46
46
|
},
|
|
47
47
|
buildCommand: `npm run build:${context}`,
|
|
48
48
|
phpServeCommand: 'php artisan serve',
|
|
@@ -62,7 +62,8 @@ class ContextDevServer {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
async start() {
|
|
65
|
-
console.log(`🚀 Starting Development Server for [${config.context.toUpperCase()}] context
|
|
65
|
+
console.log(`🚀 Starting Development Server for [${config.context.toUpperCase()}] context...`);
|
|
66
|
+
console.log(`📂 Project Root: ${projectRoot}\n`);
|
|
66
67
|
|
|
67
68
|
// Initial build (templates + webpack)
|
|
68
69
|
await this.runBuild();
|
|
@@ -83,7 +84,7 @@ class ContextDevServer {
|
|
|
83
84
|
console.log(`📦 Context: ${config.context.toUpperCase()}`);
|
|
84
85
|
console.log('📝 Watching:');
|
|
85
86
|
console.log(` - Blade files: ${config.watchPaths.blade.join(', ')}`);
|
|
86
|
-
console.log(` - JS Core: ${config.watchPaths.jsCore} (excluding views)`);
|
|
87
|
+
console.log(` - JS Core: ${config.watchPaths.jsCore} (excluding generated views)`);
|
|
87
88
|
console.log(` - Compiler: ${config.watchPaths.compiler}`);
|
|
88
89
|
console.log(`🌐 Laravel Server: http://localhost:${config.phpPort}`);
|
|
89
90
|
console.log(`🔄 Auto-reload: Active (add script to your layout)`);
|
|
@@ -105,7 +106,7 @@ class ContextDevServer {
|
|
|
105
106
|
return new Promise((resolve, reject) => {
|
|
106
107
|
const build = spawn('sh', ['-c', config.buildCommand], {
|
|
107
108
|
stdio: 'inherit',
|
|
108
|
-
cwd:
|
|
109
|
+
cwd: projectRoot
|
|
109
110
|
});
|
|
110
111
|
|
|
111
112
|
build.on('close', (code) => {
|
|
@@ -179,7 +180,7 @@ class ContextDevServer {
|
|
|
179
180
|
})();
|
|
180
181
|
`;
|
|
181
182
|
|
|
182
|
-
const publicPath = path.resolve(
|
|
183
|
+
const publicPath = path.resolve(projectRoot, 'public/reload-dev.js');
|
|
183
184
|
writeFileSync(publicPath, scriptContent, 'utf-8');
|
|
184
185
|
console.log(`✅ Created reload script at public/reload-dev.js\n`);
|
|
185
186
|
}
|
|
@@ -187,9 +188,9 @@ class ContextDevServer {
|
|
|
187
188
|
startPhpServe() {
|
|
188
189
|
console.log(`🐘 Starting PHP server on port ${config.phpPort}...`);
|
|
189
190
|
|
|
190
|
-
this.processes.phpServe = spawn('php', ['artisan', 'serve', `--port=${config.phpPort}`],
|
|
191
|
-
|
|
192
|
-
cwd:
|
|
191
|
+
this.processes.phpServe = spawn('php', ['artisan', 'serve', `--port=${config.phpPort}`],
|
|
192
|
+
{ stdio: 'inherit',
|
|
193
|
+
cwd: projectRoot
|
|
193
194
|
});
|
|
194
195
|
|
|
195
196
|
this.processes.phpServe.on('error', (err) => {
|
|
@@ -206,33 +207,63 @@ class ContextDevServer {
|
|
|
206
207
|
startWatcher() {
|
|
207
208
|
// Watch blade files (multiple source directories from config)
|
|
208
209
|
config.watchPaths.blade.forEach(bladePath => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
const absoluteBladePath = path.resolve(projectRoot, bladePath);
|
|
211
|
+
if (existsSync(absoluteBladePath)) {
|
|
212
|
+
console.log(`👀 Watching blade files in: ${bladePath}`);
|
|
213
|
+
watch(absoluteBladePath, { recursive: true }, (eventType, filename) => {
|
|
214
|
+
if (filename && filename.endsWith('.blade.php')) {
|
|
215
|
+
this.handleFileChange('blade', path.join(bladePath, filename));
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
console.warn(`⚠️ Blade path not found: ${bladePath}`);
|
|
220
|
+
}
|
|
215
221
|
});
|
|
216
222
|
|
|
217
|
-
// Watch JS core files (
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
// Watch JS core files (resources/js)
|
|
224
|
+
const jsCorePath = path.resolve(projectRoot, config.watchPaths.jsCore);
|
|
225
|
+
if (existsSync(jsCorePath)) {
|
|
226
|
+
console.log(`👀 Watching JS core in: ${config.watchPaths.jsCore}`);
|
|
227
|
+
watch(jsCorePath, { recursive: true }, (eventType, filename) => {
|
|
228
|
+
if (filename && filename.endsWith('.js')) {
|
|
229
|
+
const fullPath = path.join(jsCorePath, filename);
|
|
230
|
+
// Skip if file is in excluded directories
|
|
231
|
+
const isInViews = fullPath.includes(path.sep + 'views' + path.sep) || fullPath.endsWith(path.sep + 'views');
|
|
232
|
+
const isInBuild = fullPath.includes(path.sep + 'build' + path.sep) || fullPath.endsWith(path.sep + 'build');
|
|
233
|
+
const isInConfig = fullPath.includes(path.sep + 'config' + path.sep) || fullPath.endsWith(path.sep + 'config');
|
|
234
|
+
const isGeneratedCore = fullPath.includes(path.sep + 'core' + path.sep + 'ViewTemplate.js');
|
|
235
|
+
|
|
236
|
+
if (!isInViews && !isInBuild && !isInConfig && !isGeneratedCore) {
|
|
237
|
+
this.handleFileChange('js-core', filename);
|
|
238
|
+
}
|
|
225
239
|
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
228
242
|
|
|
229
|
-
// Watch compiler files
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
243
|
+
// Watch compiler files - Check if exists first to avoid crash
|
|
244
|
+
let compilerPath = config.watchPaths.compiler;
|
|
245
|
+
// fallback mainly for dev env if scripts/compiler exists
|
|
246
|
+
if (!compilerPath.includes('node_modules')) {
|
|
247
|
+
if (!require('fs').existsSync(path.resolve(projectRoot, 'scripts/compiler'))) {
|
|
248
|
+
// Try looking in node_modules
|
|
249
|
+
compilerPath = path.resolve(projectRoot, 'node_modules/onelaraveljs/scripts/compiler');
|
|
250
|
+
} else {
|
|
251
|
+
compilerPath = path.resolve(projectRoot, 'scripts/compiler');
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
compilerPath = path.resolve(projectRoot, compilerPath);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (require('fs').existsSync(compilerPath)) {
|
|
258
|
+
console.log(`👀 Watching compiler in: ${compilerPath}`);
|
|
259
|
+
watch(compilerPath, { recursive: true }, (eventType, filename) => {
|
|
260
|
+
if (filename && (filename.endsWith('.py') || filename.endsWith('.js'))) {
|
|
261
|
+
this.handleFileChange('compiler', filename);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
console.log(`⚠️ Compiler path not found for watching: ${compilerPath}`);
|
|
266
|
+
}
|
|
236
267
|
}
|
|
237
268
|
|
|
238
269
|
handleFileChange(type, filename) {
|