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 CHANGED
@@ -1,87 +1,182 @@
1
- # onelaraveljs
1
+ # OneLaravelJS Framework
2
2
 
3
- The Core JavaScript Framework for OneLaravel applications. This library provides the fundamental runtime, view management, and routing capabilities for building OneLaravel SPAs.
3
+ **OneLaravelJS** thư viện lõi JavaScript dành cho các ứng dụng OneLaravel. cung cấp nền tảng runtime, quản lý view, routing khả năng tương tác hai chiều mạnh mẽ giữa Laravel Blade và JavaScript.
4
4
 
5
- ## Installation
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
- ## Usage
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
- OneJS is designed to be the "engine" while your application provides the "map" (Views and Configuration).
23
+ ## 📂 Cấu trúc dự án khuyến nghị
14
24
 
15
- ### 1. Project Structure
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
- resources/js/
20
- ├── app.js # Main entry point
21
- ├── config/
22
- │ └── viewRegistry.generated.js # (Generated by build tools)
23
- └── views/ # Your specific application views
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
- ### 2. Main Entry (`app.js`)
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
- In your project's main JavaScript entry point, you need to wire up the Core with your Project's views:
94
+ ### 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
- // 1. Dependency Injection: Tell the Core where to find your views
35
- viewLoader.setRegistry(viewRegistry);
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. Initialize the App
38
- // The App will look for window.APP_CONFIGS (injected by Laravel Blade) to configure itself
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 for UMD/Debugging (Optional)
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
- ### 3. Server-Side Setup (Blade)
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
- ### ViewLoader vs ViewRegistry
129
+ ## 🧠 Core Concepts
65
130
 
66
- - **ViewLoader** (Library): The logic responsible for dynamically importing and caching views. It is "blind" until it receives a registry.
67
- - **ViewRegistry** (Project): A map of view names (e.g., `'auth.login'`) to dynamic import functions. This is typically auto-generated by your build tools.
131
+ ### 1. View System
132
+ OneJS coi mỗi file Blade một Component.
133
+ - **Server**: Render HTML tĩnh (SEO).
134
+ - **Client**: Hydrate HTML đó thành Interactive Component.
68
135
 
69
- ### Event Service
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
- OneJS comes with a built-in Event Service for cross-component communication:
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
- // Listen
77
- App.Event.on('user:logged-in', (user) => {
78
- console.log(user);
146
+ // Lắng nghe
147
+ App.Event.on('cart:updated', (data) => {
148
+ console.log('Cart count:', data.count);
79
149
  });
80
150
 
81
- // Emit
82
- App.Event.emit('user:logged-in', { id: 1, name: 'John' });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "onelaraveljs",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "OneLaravel JS Framework Core & Compiler",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -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(__dirname, '../build.config.json');
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/onejs', // Watch entire onejs directory
44
- jsViewsExclude: 'resources/js/onejs/views', // Exclude views directory
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...\n`);
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: path.resolve(__dirname, '..')
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(__dirname, '../public/reload-dev.js');
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
- stdio: 'inherit',
192
- cwd: path.resolve(__dirname, '..')
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
- console.log(`👀 Watching blade files in: ${bladePath}`);
210
- watch(bladePath, { recursive: true }, (eventType, filename) => {
211
- if (filename && filename.endsWith('.blade.php')) {
212
- this.handleFileChange('blade', path.join(bladePath, filename));
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 (entire onejs directory, excluding views)
218
- console.log(`👀 Watching JS core in: ${config.watchPaths.jsCore} (excluding views)`);
219
- watch(config.watchPaths.jsCore, { recursive: true }, (eventType, filename) => {
220
- if (filename && filename.endsWith('.js')) {
221
- const fullPath = path.join(config.watchPaths.jsCore, filename);
222
- // Skip if file is in views directory
223
- if (!fullPath.includes(path.sep + 'views' + path.sep) && !fullPath.endsWith(path.sep + 'views')) {
224
- this.handleFileChange('js-core', filename);
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
- console.log(`👀 Watching compiler in: ${config.watchPaths.compiler}`);
231
- watch(config.watchPaths.compiler, { recursive: true }, (eventType, filename) => {
232
- if (filename && (filename.endsWith('.py') || filename.endsWith('.js'))) {
233
- this.handleFileChange('compiler', filename);
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) {