@currentjs/gen 0.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.
- package/CHANGELOG.md +7 -0
- package/LICENSE +56 -0
- package/README.md +686 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +143 -0
- package/dist/commands/commit.d.ts +1 -0
- package/dist/commands/commit.js +153 -0
- package/dist/commands/createApp.d.ts +1 -0
- package/dist/commands/createApp.js +64 -0
- package/dist/commands/createModule.d.ts +1 -0
- package/dist/commands/createModule.js +121 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.js +164 -0
- package/dist/commands/generateAll.d.ts +4 -0
- package/dist/commands/generateAll.js +305 -0
- package/dist/commands/infer.d.ts +1 -0
- package/dist/commands/infer.js +179 -0
- package/dist/generators/controllerGenerator.d.ts +20 -0
- package/dist/generators/controllerGenerator.js +280 -0
- package/dist/generators/domainModelGenerator.d.ts +33 -0
- package/dist/generators/domainModelGenerator.js +175 -0
- package/dist/generators/serviceGenerator.d.ts +39 -0
- package/dist/generators/serviceGenerator.js +379 -0
- package/dist/generators/storeGenerator.d.ts +31 -0
- package/dist/generators/storeGenerator.js +191 -0
- package/dist/generators/templateGenerator.d.ts +11 -0
- package/dist/generators/templateGenerator.js +143 -0
- package/dist/generators/templates/appTemplates.d.ts +27 -0
- package/dist/generators/templates/appTemplates.js +1621 -0
- package/dist/generators/templates/controllerTemplates.d.ts +43 -0
- package/dist/generators/templates/controllerTemplates.js +82 -0
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.js +21 -0
- package/dist/generators/templates/serviceTemplates.d.ts +15 -0
- package/dist/generators/templates/serviceTemplates.js +54 -0
- package/dist/generators/templates/storeTemplates.d.ts +9 -0
- package/dist/generators/templates/storeTemplates.js +260 -0
- package/dist/generators/templates/validationTemplates.d.ts +25 -0
- package/dist/generators/templates/validationTemplates.js +66 -0
- package/dist/generators/templates/viewTemplates.d.ts +16 -0
- package/dist/generators/templates/viewTemplates.js +359 -0
- package/dist/generators/validationGenerator.d.ts +24 -0
- package/dist/generators/validationGenerator.js +199 -0
- package/dist/utils/cliUtils.d.ts +6 -0
- package/dist/utils/cliUtils.js +71 -0
- package/dist/utils/colors.d.ts +26 -0
- package/dist/utils/colors.js +80 -0
- package/dist/utils/commitUtils.d.ts +46 -0
- package/dist/utils/commitUtils.js +377 -0
- package/dist/utils/constants.d.ts +52 -0
- package/dist/utils/constants.js +64 -0
- package/dist/utils/generationRegistry.d.ts +25 -0
- package/dist/utils/generationRegistry.js +192 -0
- package/howto.md +556 -0
- package/package.json +44 -0
package/howto.md
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# CurrentJS Framework Rules
|
|
2
|
+
|
|
3
|
+
## Architecture Overview
|
|
4
|
+
This is a CurrentJS framework application using clean architecture principles with the following layers:
|
|
5
|
+
- **Controllers**: Handle HTTP requests/responses and route handling
|
|
6
|
+
- **Services**: Contain business logic and orchestrate operations
|
|
7
|
+
- **Stores**: Provide data access layer and database operations
|
|
8
|
+
- **Domain Entities**: Core business models
|
|
9
|
+
- **Views**: HTML templates for server-side rendering
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
current create module Modulename # Creates "Modulename" with default structure and yaml file
|
|
15
|
+
```
|
|
16
|
+
```bash
|
|
17
|
+
current generate Modulename # Generates all TypeScript files based on the module's yaml
|
|
18
|
+
current generate # Does the same as above, but for all modules
|
|
19
|
+
```
|
|
20
|
+
```bash
|
|
21
|
+
current commit [files...] # Commits all changes in the code, so they won't be overwritten after regeneration
|
|
22
|
+
current diff [module] # Show differences between generated and current code
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## The flow
|
|
26
|
+
1. Create an empty app (`current create app`)
|
|
27
|
+
2. Create a new module: `current create module Name`
|
|
28
|
+
3. In the module's yaml file, define module's:
|
|
29
|
+
- model(s)
|
|
30
|
+
- routes & actions
|
|
31
|
+
- permissions
|
|
32
|
+
4. Generate TypeScript files: `current generate Name`
|
|
33
|
+
5. If required, make changes in the:
|
|
34
|
+
- model (i.e. some specific business rules or validations)
|
|
35
|
+
- views
|
|
36
|
+
6. Commit those changes: `current commit`
|
|
37
|
+
|
|
38
|
+
--- If needed more than CRUD: ---
|
|
39
|
+
|
|
40
|
+
7. Define action in the service by creating a method
|
|
41
|
+
8. Describe this action in the module's yaml. Additionaly, you may define routes and permissions.
|
|
42
|
+
9. `current generate Modulename`
|
|
43
|
+
10. commit changes: `current commit`
|
|
44
|
+
|
|
45
|
+
## Configuration Files
|
|
46
|
+
|
|
47
|
+
### Application Configuration (app.yaml)
|
|
48
|
+
|
|
49
|
+
**Do not modify this file**
|
|
50
|
+
|
|
51
|
+
### Module Configuration (modulename.yaml)
|
|
52
|
+
|
|
53
|
+
**The most work must be done in these files**
|
|
54
|
+
|
|
55
|
+
**Complete Module Example:**
|
|
56
|
+
```yaml
|
|
57
|
+
models:
|
|
58
|
+
- name: Post # Entity name (capitalized)
|
|
59
|
+
fields:
|
|
60
|
+
- name: title # Field name
|
|
61
|
+
type: string # Field type: string, number, boolean, datetime
|
|
62
|
+
required: true # Validation requirement
|
|
63
|
+
- name: content
|
|
64
|
+
type: string
|
|
65
|
+
required: true
|
|
66
|
+
- name: authorId
|
|
67
|
+
type: number
|
|
68
|
+
required: true
|
|
69
|
+
- name: publishedAt
|
|
70
|
+
type: datetime
|
|
71
|
+
required: false
|
|
72
|
+
- name: status
|
|
73
|
+
type: string
|
|
74
|
+
required: true
|
|
75
|
+
|
|
76
|
+
api: # REST API configuration
|
|
77
|
+
prefix: /api/posts # Base URL for API endpoints
|
|
78
|
+
endpoints:
|
|
79
|
+
- method: GET # HTTP method
|
|
80
|
+
path: / # Relative path (becomes /api/posts/)
|
|
81
|
+
action: list # Action name (references actions section)
|
|
82
|
+
- method: GET
|
|
83
|
+
path: /:id # Path parameter
|
|
84
|
+
action: get
|
|
85
|
+
- method: POST
|
|
86
|
+
path: /
|
|
87
|
+
action: create
|
|
88
|
+
- method: PUT
|
|
89
|
+
path: /:id
|
|
90
|
+
action: update
|
|
91
|
+
- method: DELETE
|
|
92
|
+
path: /:id
|
|
93
|
+
action: delete
|
|
94
|
+
- method: POST # Custom endpoint
|
|
95
|
+
path: /:id/publish
|
|
96
|
+
action: publish
|
|
97
|
+
|
|
98
|
+
routes: # Web interface configuration
|
|
99
|
+
prefix: /posts # Base URL for web pages
|
|
100
|
+
strategy: [toast, back] # Default success strategies for forms
|
|
101
|
+
endpoints:
|
|
102
|
+
- path: / # List page
|
|
103
|
+
action: list
|
|
104
|
+
view: postList # Template name
|
|
105
|
+
- path: /:id # Detail page
|
|
106
|
+
action: get
|
|
107
|
+
view: postDetail
|
|
108
|
+
- path: /create # Create form page
|
|
109
|
+
action: empty # No data loading action
|
|
110
|
+
view: postCreate
|
|
111
|
+
- path: /:id/edit # Edit form page
|
|
112
|
+
action: get # Load existing data
|
|
113
|
+
view: postUpdate
|
|
114
|
+
|
|
115
|
+
actions: # Business logic mapping
|
|
116
|
+
list:
|
|
117
|
+
handlers: [default:list] # Use built-in list handler
|
|
118
|
+
get:
|
|
119
|
+
handlers: [default:getById] # Use built-in get handler
|
|
120
|
+
create:
|
|
121
|
+
handlers: [default:create] # Built-in create
|
|
122
|
+
update:
|
|
123
|
+
handlers: [default:update]
|
|
124
|
+
delete:
|
|
125
|
+
handlers: [ # Chain multiple handlers
|
|
126
|
+
service:checkCanDelete, # Custom business logic
|
|
127
|
+
default:delete
|
|
128
|
+
]
|
|
129
|
+
publish: # Custom action
|
|
130
|
+
handlers: [
|
|
131
|
+
default:getById, # Fetch entity
|
|
132
|
+
service:validateForPublish, # Custom validation
|
|
133
|
+
service:updatePublishStatus # Custom update logic
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
permissions: # Role-based access control
|
|
137
|
+
- action: list
|
|
138
|
+
roles: [all] # Anyone (including anonymous)
|
|
139
|
+
- action: get
|
|
140
|
+
roles: [all] # Anyone can view
|
|
141
|
+
- action: create
|
|
142
|
+
roles: [authenticated] # Must be logged in
|
|
143
|
+
- action: update
|
|
144
|
+
roles: [owner, admin] # Entity owner or admin role
|
|
145
|
+
- action: delete
|
|
146
|
+
roles: [admin] # Only admin role
|
|
147
|
+
- action: publish
|
|
148
|
+
roles: [owner, editor, admin] # Multiple roles allowed
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Make sure no `ID`/`owner id`/`is deleted` fields are present in the model definition, since it's added automatically**
|
|
152
|
+
|
|
153
|
+
**Field Types:**
|
|
154
|
+
- `string` - Text data
|
|
155
|
+
- `number` - Numeric data (integer or float)
|
|
156
|
+
- `boolean` - True/false values
|
|
157
|
+
- `datetime` - Date and time values
|
|
158
|
+
|
|
159
|
+
**Built-in Action Handlers:**
|
|
160
|
+
- `default:list` - Returns paginated list of entities
|
|
161
|
+
- `default:getById` - Fetches single entity by ID
|
|
162
|
+
- `default:create` - Creates new entity with validation
|
|
163
|
+
- `default:update` - Updates existing entity by ID
|
|
164
|
+
- `default:delete` - Soft deletes entity
|
|
165
|
+
|
|
166
|
+
**Custom Action Handlers:**
|
|
167
|
+
- `service:methodName` - Calls custom method on service class
|
|
168
|
+
- Automatically generated with proper type signatures
|
|
169
|
+
- Can be chained with built-in handlers
|
|
170
|
+
|
|
171
|
+
**Strategy Options (for forms):**
|
|
172
|
+
- `toast` - Success toast notification
|
|
173
|
+
- `back` - Navigate back in browser history
|
|
174
|
+
- `message` - Inline success message
|
|
175
|
+
- `modal` - Modal success dialog
|
|
176
|
+
- `redirect` - Redirect to specific URL
|
|
177
|
+
- `refresh` - Reload current page
|
|
178
|
+
|
|
179
|
+
**Permission Roles:**
|
|
180
|
+
- `all` - Anyone (including anonymous users)
|
|
181
|
+
- `authenticated` - Any logged-in user
|
|
182
|
+
- `owner` - User who created the entity
|
|
183
|
+
- `admin`, `editor`, `user` - Custom roles from JWT token
|
|
184
|
+
- Multiple roles can be specified for each action
|
|
185
|
+
|
|
186
|
+
**Generated Files from Configuration:**
|
|
187
|
+
- Domain entity class
|
|
188
|
+
- Service class
|
|
189
|
+
- API controller with REST endpoints
|
|
190
|
+
- Web controller with page rendering
|
|
191
|
+
- Store class with database operations
|
|
192
|
+
- HTML templates for all views
|
|
193
|
+
- TypeScript interfaces and DTOs
|
|
194
|
+
|
|
195
|
+
## Module Structure
|
|
196
|
+
```
|
|
197
|
+
src/modules/ModuleName/
|
|
198
|
+
├── application/
|
|
199
|
+
│ ├── services/ModuleService.ts # Business logic
|
|
200
|
+
│ └── validation/ModuleValidation.ts # DTOs and validation
|
|
201
|
+
├── domain/
|
|
202
|
+
│ └── entities/Module.ts # Domain model
|
|
203
|
+
├── infrastructure/
|
|
204
|
+
│ ├── controllers/
|
|
205
|
+
│ │ ├── ModuleApiController.ts # REST API endpoints
|
|
206
|
+
│ │ └── ModuleWebController.ts # Web page controllers
|
|
207
|
+
│ ├── interfaces/StoreInterface.ts # Data access interface
|
|
208
|
+
│ └── stores/ModuleStore.ts # Data access implementation
|
|
209
|
+
├── views/
|
|
210
|
+
│ ├── modulelist.html # List view template
|
|
211
|
+
│ ├── moduledetail.html # Detail view template
|
|
212
|
+
│ ├── modulecreate.html # Create form template
|
|
213
|
+
│ └── moduleupdate.html # Update form template
|
|
214
|
+
└── module.yaml # Module configuration
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Best Practices
|
|
218
|
+
|
|
219
|
+
- Use Domain Driven Design and Clean Architecture (kind of).
|
|
220
|
+
- Prefer declarative configuration over imperative programming: when possible, change yamls instead of writing the code. Write the code only when it's really neccessary.
|
|
221
|
+
- CRUD operation are autogenerated (first in module's yaml, then in the generated code).
|
|
222
|
+
- If some custom action is needed, then it has to be defined in the **Service** then just put this action to the module's yaml. You may also define new methods in the *Store* and its interface (if needed).
|
|
223
|
+
- Business rules must be defined only in models. That also applies to business rules validations (in contrast to *just* validations: e.g. if field exists and is of needed type – then validators are in use)
|
|
224
|
+
|
|
225
|
+
## Core Package APIs (For Generated Code)
|
|
226
|
+
|
|
227
|
+
### @currentjs/router - Controller Decorators & Context
|
|
228
|
+
|
|
229
|
+
**Decorators (in controllers):**
|
|
230
|
+
```typescript
|
|
231
|
+
@Controller('/api/posts') // Base path for controller
|
|
232
|
+
@Get('/') // GET endpoint
|
|
233
|
+
@Get('/:id') // GET with path parameter
|
|
234
|
+
@Post('/') // POST endpoint
|
|
235
|
+
@Put('/:id') // PUT endpoint
|
|
236
|
+
@Delete('/:id') // DELETE endpoint
|
|
237
|
+
@Render('template', 'layout') // For web controllers
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Context Object (in route handlers):**
|
|
241
|
+
```typescript
|
|
242
|
+
interface IContext {
|
|
243
|
+
request: {
|
|
244
|
+
url: string;
|
|
245
|
+
path: string;
|
|
246
|
+
method: string;
|
|
247
|
+
parameters: Record<string, string | number>; // Path params + query params
|
|
248
|
+
body: any; // Parsed JSON or raw string
|
|
249
|
+
headers: Record<string, string | string[]>;
|
|
250
|
+
user?: AuthenticatedUser; // Parsed JWT user if authenticated
|
|
251
|
+
};
|
|
252
|
+
response: Record<string, any>;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Usage examples
|
|
256
|
+
const id = parseInt(ctx.request.parameters.id as string);
|
|
257
|
+
const page = parseInt(ctx.request.parameters.page as string) || 1;
|
|
258
|
+
const payload = ctx.request.body;
|
|
259
|
+
const user = ctx.request.user; // If authenticated
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Authentication Support:**
|
|
263
|
+
- JWT tokens parsed automatically from `Authorization: Bearer <token>` header
|
|
264
|
+
- User object available at `ctx.request.user` with `id`, `email`, `role` fields
|
|
265
|
+
- No manual setup required in generated code
|
|
266
|
+
|
|
267
|
+
### @currentjs/templating - Template Syntax
|
|
268
|
+
|
|
269
|
+
**Variables & Data Access:**
|
|
270
|
+
```html
|
|
271
|
+
{{ variableName }}
|
|
272
|
+
{{ object.property }}
|
|
273
|
+
{{ $root.arrayData }} <!-- Access root data -->
|
|
274
|
+
{{ $index }} <!-- Loop index -->
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Control Structures:**
|
|
278
|
+
```html
|
|
279
|
+
<!-- Loops -->
|
|
280
|
+
<tbody x-for="$root" x-row="item">
|
|
281
|
+
<tr>
|
|
282
|
+
<td>{{ item.name }}</td>
|
|
283
|
+
<td>{{ $index }}</td>
|
|
284
|
+
</tr>
|
|
285
|
+
</tbody>
|
|
286
|
+
|
|
287
|
+
<!-- Conditionals -->
|
|
288
|
+
<div x-if="user.isAdmin">Admin content</div>
|
|
289
|
+
<span x-if="errors.name">{{ errors.name }}</span>
|
|
290
|
+
|
|
291
|
+
<!-- Template includes -->
|
|
292
|
+
<userCard name="{{ user.name }}" role="admin" />
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Layout Integration:**
|
|
296
|
+
- Templates use `<!-- @template name="templateName" -->` header
|
|
297
|
+
- Main layout gets `{{ content }}` variable
|
|
298
|
+
- Forms use `{{ formData.field || '' }}` for default values
|
|
299
|
+
|
|
300
|
+
### @currentjs/provider-mysql - Database Operations
|
|
301
|
+
|
|
302
|
+
**Query Execution (in stores):**
|
|
303
|
+
```typescript
|
|
304
|
+
// Named parameters (preferred)
|
|
305
|
+
const result = await this.db.query(
|
|
306
|
+
'SELECT * FROM users WHERE status = :status AND age > :minAge',
|
|
307
|
+
{ status: 'active', minAge: 18 }
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// Result handling
|
|
311
|
+
if (result.success && result.data.length > 0) {
|
|
312
|
+
return result.data.map(row => this.rowToModel(row));
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Common Query Patterns:**
|
|
317
|
+
```typescript
|
|
318
|
+
// Insert with auto-generated fields
|
|
319
|
+
const row = { ...data, created_at: new Date(), updated_at: new Date() };
|
|
320
|
+
const result = await this.db.query(
|
|
321
|
+
`INSERT INTO table_name (${fields.join(', ')}) VALUES (${placeholders})`,
|
|
322
|
+
row
|
|
323
|
+
);
|
|
324
|
+
const newId = result.insertId;
|
|
325
|
+
|
|
326
|
+
// Update with validation
|
|
327
|
+
const query = `UPDATE table_name SET ${updateFields.join(', ')}, updated_at = :updated_at WHERE id = :id`;
|
|
328
|
+
await this.db.query(query, { ...updateData, updated_at: new Date(), id });
|
|
329
|
+
|
|
330
|
+
// Soft delete
|
|
331
|
+
await this.db.query(
|
|
332
|
+
'UPDATE table_name SET deleted_at = :deleted_at WHERE id = :id',
|
|
333
|
+
{ deleted_at: new Date(), id }
|
|
334
|
+
);
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Error Handling:**
|
|
338
|
+
```typescript
|
|
339
|
+
try {
|
|
340
|
+
const result = await this.db.query(query, params);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (error instanceof MySQLConnectionError) {
|
|
343
|
+
throw new Error(`Database connection error: ${error.message}`);
|
|
344
|
+
} else if (error instanceof MySQLQueryError) {
|
|
345
|
+
throw new Error(`Query error: ${error.message}`);
|
|
346
|
+
}
|
|
347
|
+
throw error;
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Frontend System (web/app.js)
|
|
352
|
+
|
|
353
|
+
### Translation System
|
|
354
|
+
|
|
355
|
+
**Basic Usage:**
|
|
356
|
+
```javascript
|
|
357
|
+
// Translate strings
|
|
358
|
+
t('Hello World') // Returns translated version or original
|
|
359
|
+
t('Save changes')
|
|
360
|
+
|
|
361
|
+
// Set language
|
|
362
|
+
setLang('pl') // Switch to Polish
|
|
363
|
+
setLang('en') // Switch to English
|
|
364
|
+
getCurrentLanguage() // Get current language code
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Translation File (web/translations.json):**
|
|
368
|
+
```json
|
|
369
|
+
{
|
|
370
|
+
"pl": {
|
|
371
|
+
"Hello World": "Witaj Świecie",
|
|
372
|
+
"Save changes": "Zapisz zmiany",
|
|
373
|
+
"Delete": "Usuń"
|
|
374
|
+
},
|
|
375
|
+
"ru": {
|
|
376
|
+
"Hello World": "Привет мир",
|
|
377
|
+
"Save changes": "Сохранить изменения"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### UI Feedback & Notifications
|
|
383
|
+
|
|
384
|
+
**Toast Notifications:**
|
|
385
|
+
```javascript
|
|
386
|
+
showToast('Success message', 'success') // Green toast
|
|
387
|
+
showToast('Error occurred', 'error') // Red toast
|
|
388
|
+
showToast('Information', 'info') // Blue toast
|
|
389
|
+
showToast('Warning', 'warning') // Yellow toast
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Inline Messages:**
|
|
393
|
+
```javascript
|
|
394
|
+
showMessage('messageId', 'Success!', 'success')
|
|
395
|
+
showMessage('errorContainer', 'Validation failed', 'error')
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Modal Dialogs:**
|
|
399
|
+
```javascript
|
|
400
|
+
showModal('confirmModal', 'Item saved successfully', 'success')
|
|
401
|
+
showModal('errorModal', 'Operation failed', 'error')
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Navigation & Page Actions
|
|
405
|
+
|
|
406
|
+
**Navigation Functions:**
|
|
407
|
+
```javascript
|
|
408
|
+
navigateBack() // Go back in history or to home
|
|
409
|
+
redirectTo('/posts') // Safe redirect with validation
|
|
410
|
+
reloadPage() // Reload with loading indicator
|
|
411
|
+
refreshSection('#content') // Refresh specific section
|
|
412
|
+
|
|
413
|
+
// SPA-style navigation
|
|
414
|
+
navigateToPage('/posts/123') // Loads via AJAX, updates #main
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**Content Management:**
|
|
418
|
+
```javascript
|
|
419
|
+
updateContent('#results', newHtml, 'replace') // Replace content
|
|
420
|
+
updateContent('#list', itemHtml, 'append') // Add to end
|
|
421
|
+
updateContent('#list', itemHtml, 'prepend') // Add to beginning
|
|
422
|
+
removeElement('#item-123') // Animate and remove
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Form Handling & Strategy System
|
|
426
|
+
|
|
427
|
+
**Form Strategy Configuration:**
|
|
428
|
+
```html
|
|
429
|
+
<!-- Form with strategy attributes -->
|
|
430
|
+
<form data-strategy='["toast", "back"]'
|
|
431
|
+
data-entity-name="Post"
|
|
432
|
+
data-field-types='{"age": "number", "active": "boolean"}'>
|
|
433
|
+
<input name="title" type="text" required>
|
|
434
|
+
<input name="age" type="number">
|
|
435
|
+
<input name="active" type="checkbox">
|
|
436
|
+
<button type="submit">Save</button>
|
|
437
|
+
</form>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Available Strategies:**
|
|
441
|
+
- `toast` - Show success toast notification
|
|
442
|
+
- `back` - Navigate back using browser history
|
|
443
|
+
- `message` - Show inline message in specific element
|
|
444
|
+
- `modal` - Show modal dialog
|
|
445
|
+
- `redirect` - Redirect to specific URL
|
|
446
|
+
- `refresh` - Reload the page
|
|
447
|
+
- `remove` - Remove form element
|
|
448
|
+
|
|
449
|
+
**Manual Form Submission:**
|
|
450
|
+
```javascript
|
|
451
|
+
const form = document.querySelector('#myForm');
|
|
452
|
+
submitForm(form, ['toast', 'back'], {
|
|
453
|
+
entityName: 'Post',
|
|
454
|
+
basePath: '/posts',
|
|
455
|
+
messageId: 'form-message'
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Success Handling:**
|
|
460
|
+
```javascript
|
|
461
|
+
handleFormSuccess(response, ['toast', 'back'], {
|
|
462
|
+
entityName: 'Post',
|
|
463
|
+
basePath: '/posts',
|
|
464
|
+
messageId: 'success-msg',
|
|
465
|
+
modalId: 'success-modal'
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Form Validation & Type Conversion
|
|
470
|
+
|
|
471
|
+
**Client-side Validation Setup:**
|
|
472
|
+
```javascript
|
|
473
|
+
setupFormValidation('#createForm'); // Adds required field validation
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
**Field Type Conversion:**
|
|
477
|
+
```javascript
|
|
478
|
+
// Automatic conversion based on data-field-types
|
|
479
|
+
convertFieldValue('123', 'number') // Returns 123 (number)
|
|
480
|
+
convertFieldValue('true', 'boolean') // Returns true (boolean)
|
|
481
|
+
convertFieldValue('text', 'string') // Returns 'text' (string)
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Loading States & Utilities
|
|
485
|
+
|
|
486
|
+
**Loading Indicators:**
|
|
487
|
+
```javascript
|
|
488
|
+
showLoading('#form') // Show spinner on form
|
|
489
|
+
hideLoading('#form') // Hide spinner
|
|
490
|
+
showLoading('#main') // Show spinner on main content
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Utility Functions:**
|
|
494
|
+
```javascript
|
|
495
|
+
debounce(searchFunction, 300) // Debounce for search inputs
|
|
496
|
+
getElementSafely('#selector') // Safe element selection
|
|
497
|
+
clearForm('#myForm') // Reset form and clear validation
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Event Handling & SPA Integration
|
|
501
|
+
|
|
502
|
+
**Automatic Link Handling:**
|
|
503
|
+
- Internal links automatically use AJAX navigation
|
|
504
|
+
- External links work normally
|
|
505
|
+
- Links with `data-external` skip AJAX handling
|
|
506
|
+
|
|
507
|
+
**Automatic Form Handling:**
|
|
508
|
+
- Forms with `data-strategy` use AJAX submission
|
|
509
|
+
- Regular forms work normally
|
|
510
|
+
- Automatic JSON conversion from FormData
|
|
511
|
+
|
|
512
|
+
**Custom Event Listeners:**
|
|
513
|
+
```javascript
|
|
514
|
+
// Re-initialize after dynamic content loading
|
|
515
|
+
initializeEventListeners();
|
|
516
|
+
|
|
517
|
+
// Handle specific link navigation
|
|
518
|
+
document.querySelector('#myLink').addEventListener('click', (e) => {
|
|
519
|
+
e.preventDefault();
|
|
520
|
+
navigateToPage('/custom/path');
|
|
521
|
+
});
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### Global App Object
|
|
525
|
+
|
|
526
|
+
**Accessing Functions:**
|
|
527
|
+
```javascript
|
|
528
|
+
// All functions available under window.App
|
|
529
|
+
App.showToast('Message', 'success');
|
|
530
|
+
App.navigateBack();
|
|
531
|
+
App.t('Translate this');
|
|
532
|
+
App.setLang('pl');
|
|
533
|
+
App.showLoading('#content');
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Template Data Binding
|
|
537
|
+
```html
|
|
538
|
+
<!-- List with pagination -->
|
|
539
|
+
<tbody x-for="modules" x-row="module">
|
|
540
|
+
<tr>
|
|
541
|
+
<td>{{ module.name }}</td>
|
|
542
|
+
<td><a href="/module/{{ module.id }}">View</a></td>
|
|
543
|
+
</tr>
|
|
544
|
+
</tbody>
|
|
545
|
+
|
|
546
|
+
<!-- Form with validation errors -->
|
|
547
|
+
<div x-if="errors.name" class="text-danger">{{ errors.name }}</div>
|
|
548
|
+
<input type="text" name="name" value="{{ formData.name || '' }}" class="form-control">
|
|
549
|
+
|
|
550
|
+
<!-- Form with strategy attributes -->
|
|
551
|
+
<form data-strategy='["toast", "back"]'
|
|
552
|
+
data-entity-name="Module"
|
|
553
|
+
data-field-types='{"count": "number", "active": "boolean"}'>
|
|
554
|
+
<!-- form fields -->
|
|
555
|
+
</form>
|
|
556
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@currentjs/gen",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "CLI code generator",
|
|
5
|
+
"license": "LGPL-3.0",
|
|
6
|
+
"author": "Konstantin Zavalny",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/currentjs/gen.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/currentjs/gen",
|
|
12
|
+
"bugs": "https://github.com/currentjs/gen/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"currentjs",
|
|
15
|
+
"generator",
|
|
16
|
+
"cli",
|
|
17
|
+
"code",
|
|
18
|
+
"generator"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"current": "dist/cli.js",
|
|
22
|
+
"currentjs": "dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"howto.md",
|
|
27
|
+
"CHANGELOG.md"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"clean": "rm -rf dist",
|
|
32
|
+
"prepack": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"yaml": "^2.8.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^22.18.0",
|
|
42
|
+
"typescript": "^5.6.3"
|
|
43
|
+
}
|
|
44
|
+
}
|