kalo-cli 0.2.27 → 0.3.0

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 (60) hide show
  1. package/README.md +80 -46
  2. package/bin/kalo.ts +3 -2
  3. package/package.json +2 -1
  4. package/plopfile.ts +182 -25
  5. package/dist/docs/app.js +0 -42644
  6. package/dist/docs/index.html +0 -32
  7. package/generators/constants.ts +0 -52
  8. package/generators/generator/django-app/index.ts +0 -72
  9. package/generators/generator/django-app/templates/admin.py.hbs +0 -6
  10. package/generators/generator/django-app/templates/apps.py.hbs +0 -9
  11. package/generators/generator/django-app/templates/init.py.hbs +0 -0
  12. package/generators/generator/django-app/templates/models_init.py.hbs +0 -2
  13. package/generators/generator/django-app/templates/urls.py.hbs +0 -8
  14. package/generators/generator/django-app/templates/views.py.hbs +0 -5
  15. package/generators/generator/django-channel/index.ts +0 -80
  16. package/generators/generator/django-channel/templates/consumer.py.hbs +0 -47
  17. package/generators/generator/django-channel/templates/routing.py.hbs +0 -8
  18. package/generators/generator/django-form/index.ts +0 -64
  19. package/generators/generator/django-form/templates/form.py.hbs +0 -15
  20. package/generators/generator/django-form/templates/forms_file.py.hbs +0 -6
  21. package/generators/generator/django-form/templates/model_form.py.hbs +0 -19
  22. package/generators/generator/django-view/index.ts +0 -96
  23. package/generators/generator/django-view/templates/view_cbv.py.hbs +0 -14
  24. package/generators/generator/django-view/templates/view_fbv.py.hbs +0 -10
  25. package/generators/generator/django-view/templates/view_template.html.hbs +0 -8
  26. package/generators/generator/main/index.ts +0 -70
  27. package/generators/generator/wagtail-admin/index.ts +0 -124
  28. package/generators/generator/wagtail-admin/templates/admin_view.html.hbs +0 -21
  29. package/generators/generator/wagtail-admin/templates/admin_view.py.hbs +0 -15
  30. package/generators/generator/wagtail-admin/templates/component.html.hbs +0 -6
  31. package/generators/generator/wagtail-admin/templates/component.py.hbs +0 -11
  32. package/generators/generator/wagtail-admin/templates/wagtail_hooks.py.hbs +0 -18
  33. package/generators/generator/wagtail-block/index.ts +0 -53
  34. package/generators/generator/wagtail-block/templates/block_class.py.hbs +0 -16
  35. package/generators/generator/wagtail-block/templates/block_template.html.hbs +0 -5
  36. package/generators/generator/wagtail-page/actions/model.ts +0 -20
  37. package/generators/generator/wagtail-page/actions/orderable.ts +0 -23
  38. package/generators/generator/wagtail-page/actions/page.ts +0 -42
  39. package/generators/generator/wagtail-page/actions/snippet.ts +0 -20
  40. package/generators/generator/wagtail-page/index.ts +0 -63
  41. package/generators/generator/wagtail-page/templates/django_model.py.hbs +0 -21
  42. package/generators/generator/wagtail-page/templates/orderable_model.py.hbs +0 -27
  43. package/generators/generator/wagtail-page/templates/page_pair_model.py.hbs +0 -69
  44. package/generators/generator/wagtail-page/templates/page_template.html.hbs +0 -14
  45. package/generators/generator/wagtail-page/templates/snippet_model.py.hbs +0 -29
  46. package/generators/ia/ai-enhancer/index.ts +0 -319
  47. package/generators/ia/docs/index.ts +0 -36
  48. package/generators/ia/docs/keywords.json +0 -1158
  49. package/generators/ia/help/index.ts +0 -85
  50. package/generators/main/index.ts +0 -422
  51. package/generators/utils/ai/common.ts +0 -141
  52. package/generators/utils/ai/index.ts +0 -2
  53. package/generators/utils/analysis.ts +0 -88
  54. package/generators/utils/code-manipulation.ts +0 -229
  55. package/generators/utils/config.ts +0 -43
  56. package/generators/utils/filesystem.ts +0 -131
  57. package/generators/utils/index.ts +0 -35
  58. package/generators/utils/plop-actions.ts +0 -104
  59. package/generators/utils/search.ts +0 -24
  60. package/tsconfig.json +0 -29
@@ -1,32 +0,0 @@
1
- <!doctype html>
2
- <html lang="fr">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Documentation Keywords</title>
7
- <style>
8
- body { font-family: system-ui, -apple-system, sans-serif; max-width: 900px; margin: 0 auto; padding: 2rem; background: #f9fafb; color: #1f2937; }
9
- h1 { text-align: center; margin-bottom: 2rem; color: #111827; }
10
- .search-container { position: sticky; top: 1rem; z-index: 10; background: #f9fafb; padding-bottom: 1rem; }
11
- .search-box { width: 100%; padding: 1rem; font-size: 1.1rem; border: 2px solid #e5e7eb; border-radius: 12px; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); transition: border-color 0.2s; box-sizing: border-box; }
12
- .search-box:focus { outline: none; border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); }
13
- .card { background: white; padding: 1.5rem; margin-bottom: 1.5rem; border-radius: 12px; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); border: 1px solid #f3f4f6; transition: transform 0.2s; }
14
- .card:hover { transform: translateY(-2px); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); }
15
- .tags { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; }
16
- .tag { background: #eff6ff; color: #1d4ed8; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.875rem; font-weight: 500; }
17
- .instruction { font-size: 1.05rem; margin-bottom: 1rem; line-height: 1.6; }
18
- .doc-section { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #f3f4f6; }
19
- .doc-content { color: #4b5563; line-height: 1.6; margin-bottom: 0.5rem; }
20
- .official-doc { background-color: #f0fdf4; border: 1px solid #bbf7d0; padding: 1rem; border-radius: 8px; margin-top: 1rem; color: #166534; }
21
- .official-label { display: block; font-weight: 600; color: #15803d; margin-bottom: 0.5rem; font-size: 0.9rem; text-transform: uppercase; letter-spacing: 0.05em; }
22
- .label { font-weight: 600; color: #111827; margin-right: 0.5rem; }
23
- .search-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1rem; }
24
- @media (max-width: 640px) { .search-grid { grid-template-columns: 1fr; } }
25
- .empty-state { text-align: center; color: #6b7280; margin-top: 3rem; font-size: 1.1rem; }
26
- </style>
27
- </head>
28
- <body>
29
- <div id="root"></div>
30
- <script type="module" src="./app.js"></script>
31
- </body>
32
- </html>
@@ -1,52 +0,0 @@
1
- export const WAGTAIL_PAGE_TYPES = [
2
- { name: 'Paire de Pages Standard (Index + Detail) : Pour une structure hiérarchique (ex: Liste de blog -> Article de blog).', value: 'page_pair' },
3
- { name: 'Modèle Orderable : Pour gérer des listes ordonnées d\'éléments dans une page (ex: Carrousel, Galerie).', value: 'orderable' },
4
- { name: 'Snippet Wagtail : Pour du contenu réutilisable hors arborescence (ex: Catégories, Auteurs, Publicités).', value: 'snippet' },
5
- { name: 'Modèle Django Standard : Une table de base de données classique sans fonctionnalités CMS Wagtail.', value: 'model' },
6
- ];
7
-
8
- export const MAIN_COMPONENT_TYPES = [
9
- { name: 'Modifier une classe existante (AI Enhancer)', value: 'ai_enhancer' },
10
- ...WAGTAIL_PAGE_TYPES,
11
- { name: 'Block StreamField', value: 'block' },
12
- { name: 'Vue Django (FBV/CBV)', value: 'view' },
13
- { name: 'Formulaire Django', value: 'form' },
14
- { name: 'Consumer Django Channel', value: 'channel' },
15
- { name: 'Extension Admin Wagtail', value: 'admin_ext' },
16
- ];
17
-
18
- export const VIEW_TYPES = [
19
- { name: 'Vue basée sur une fonction (FBV) : Idéale pour une logique sur-mesure simple sans l\'abstraction des classes.', value: 'fbv' },
20
- { name: 'TemplateView (CBV) : Affiche une page HTML statique ou avec des données contextuelles simples.', value: 'TemplateView' },
21
- { name: 'ListView (CBV) : Génère une page listant plusieurs objets d\'un modèle (ex: catalogue, blog).', value: 'ListView' },
22
- { name: 'DetailView (CBV) : Affiche les informations détaillées d\'un objet unique (ex: fiche produit, article).', value: 'DetailView' },
23
- { name: 'CreateView (CBV) : Génère un formulaire pour ajouter une nouvelle entrée dans la base de données.', value: 'CreateView' },
24
- { name: 'UpdateView (CBV) : Génère un formulaire pré-rempli pour modifier une entrée existante.', value: 'UpdateView' },
25
- { name: 'DeleteView (CBV) : Affiche une page de confirmation avant la suppression définitive d\'un objet.', value: 'DeleteView' },
26
- ];
27
-
28
- export const FORM_TYPES = [
29
- { name: 'Formulaire Standard (forms.Form) : Pour traiter des données génériques (ex: recherche, contact).', value: 'form' },
30
- { name: 'Formulaire de Modèle (forms.ModelForm) : Crée automatiquement des champs basés sur un modèle existant.', value: 'model_form' },
31
- ];
32
-
33
- export const ADMIN_EXT_TYPES = [
34
- { name: 'Vue d\'Administration (Page Personnalisée) : Ajoute une page complète dans le menu admin Wagtail.', value: 'view' },
35
- { name: 'Composant Template (Panneau UI) : Crée un élément d\'interface réutilisable (Panel) pour les formulaires d\'édition.', value: 'component' },
36
- ];
37
-
38
- export const CHANNEL_TYPES = [
39
- { name: 'Consumer Websocket Asynchrone : Pour haute performance (chat, notifications) avec asyncio.', value: 'async' },
40
- { name: 'Consumer Websocket Synchrone : Pour logique bloquante standard ou code hérité.', value: 'sync' },
41
- ];
42
-
43
- export const AI_PROVIDER_TYPES = ['Qwen', 'Gemini'];
44
-
45
- export const AI_TEMP_TYPES = [
46
- { name: 'Strict & Précis (0.1)', value: 0.1 },
47
- { name: 'Équilibré (0.5)', value: 0.5 },
48
- { name: 'Créatif (0.9)', value: 0.9 }
49
- ];
50
-
51
- export const EXIT_VALUE = '__EXIT__';
52
- export const BACK_VALUE = '__BACK__';
@@ -1,72 +0,0 @@
1
- import { PlopGeneratorConfig } from 'plop';
2
- import * as path from 'path';
3
- import { resolveAppPaths } from '../../utils/filesystem.ts';
4
-
5
- /**
6
- * Generator configuration for creating a new Django Application.
7
- * Sets up the standard directory structure, models, views, urls, and admin files.
8
- *
9
- * @type {PlopGeneratorConfig}
10
- */
11
- export const djangoAppGenerator: PlopGeneratorConfig = {
12
- description: 'Créer une nouvelle application Django avec la structure standard',
13
- prompts: [
14
- {
15
- type: 'input',
16
- name: 'name',
17
- message: 'Quel est le nom de l\'application Django ? (Cela créera un dossier avec models, views, urls pour une fonctionnalité)',
18
- validate: (value) => {
19
- if (/.+/.test(value)) {
20
- return true;
21
- }
22
- return 'Le nom est requis';
23
- },
24
- },
25
- ],
26
- actions: (data: any) => {
27
- const { appDir, appPath } = resolveAppPaths(data.config, data.name);
28
- return [
29
- {
30
- type: 'add',
31
- path: `${appDir}/__init__.py`,
32
- templateFile: 'generators/generator/django-app/templates/init.py.hbs',
33
- skipIfExists: true,
34
- },
35
- {
36
- type: 'add',
37
- path: `${appPath}/__init__.py`,
38
- templateFile: 'generators/generator/django-app/templates/init.py.hbs',
39
- },
40
- {
41
- type: 'add',
42
- path: `${appPath}/apps.py`,
43
- templateFile: 'generators/generator/django-app/templates/apps.py.hbs',
44
- },
45
- {
46
- type: 'add',
47
- path: `${appPath}/models/__init__.py`,
48
- templateFile: 'generators/generator/django-app/templates/models_init.py.hbs',
49
- },
50
- {
51
- type: 'add',
52
- path: `${appPath}/tests/__init__.py`,
53
- templateFile: 'generators/generator/django-app/templates/init.py.hbs',
54
- },
55
- {
56
- type: 'add',
57
- path: `${appPath}/views.py`,
58
- templateFile: 'generators/generator/django-app/templates/views.py.hbs',
59
- },
60
- {
61
- type: 'add',
62
- path: `${appPath}/urls.py`,
63
- templateFile: 'generators/generator/django-app/templates/urls.py.hbs',
64
- },
65
- {
66
- type: 'add',
67
- path: `${appPath}/admin.py`,
68
- templateFile: 'generators/generator/django-app/templates/admin.py.hbs',
69
- },
70
- ];
71
- },
72
- };
@@ -1,6 +0,0 @@
1
- from django.contrib import admin
2
- from .models import *
3
-
4
- # Register your models here.
5
- # <!-- CLASS = {{pascalCase name}}Admin START AI_GENERATED -->
6
- # <!-- CLASS = {{pascalCase name}}Admin END AI_GENERATED -->
@@ -1,9 +0,0 @@
1
- from django.apps import AppConfig
2
-
3
-
4
- class {{pascalCase name}}Config(AppConfig):
5
- default_auto_field = 'django.db.models.BigAutoField'
6
- name = 'app.{{name}}'
7
-
8
- # <!-- CLASS = {{pascalCase name}}Config START AI_GENERATED -->
9
- # <!-- CLASS = {{pascalCase name}}Config END AI_GENERATED -->
@@ -1,2 +0,0 @@
1
- from .pages import *
2
- from .snippets import *
@@ -1,8 +0,0 @@
1
- from django.urls import path
2
- from . import views
3
-
4
- urlpatterns = [
5
- # <!-- CLASS = {{pascalCase name}}Urls START AI_GENERATED -->
6
- # <!-- CLASS = {{pascalCase name}}Urls END AI_GENERATED -->
7
- # path('', views.index, name='index'),
8
- ]
@@ -1,5 +0,0 @@
1
- from django.shortcuts import render
2
-
3
- # Create your views here.
4
- # <!-- CLASS = {{pascalCase name}}Views START AI_GENERATED -->
5
- # <!-- CLASS = {{pascalCase name}}Views END AI_GENERATED -->
@@ -1,80 +0,0 @@
1
- import { PlopGeneratorConfig } from 'plop';
2
- import * as path from 'path';
3
- import { ensureSuffix, createAppendActions } from '../../utils/plop-actions.ts';
4
- import { resolveAppPaths } from '../../utils/filesystem.ts';
5
- import { CHANNEL_TYPES } from '../../constants.ts';
6
- import { searchChoices } from '../../utils/search.ts';
7
-
8
- /**
9
- * Generator configuration for creating a Django Channel Consumer and Routing.
10
- * Supports Async and Sync Websocket Consumers.
11
- * Automatically configures routing and adds tests.
12
- *
13
- * @type {PlopGeneratorConfig}
14
- */
15
- export const djangoChannelGenerator: PlopGeneratorConfig = {
16
- description: 'Créer un Consumer Django Channel et son routing',
17
- prompts: [
18
- {
19
- type: 'autocomplete',
20
- name: 'type',
21
- message: 'Quel type de consumer voulez-vous créer ?',
22
- source: (answers, input) => searchChoices(input, CHANNEL_TYPES),
23
- },
24
- {
25
- type: 'input',
26
- name: 'name',
27
- message: 'Nom du Consumer (ex: ChatConsumer) :',
28
- validate: (value) => {
29
- return true;
30
- },
31
- },
32
- {
33
- type: 'input',
34
- name: 'route',
35
- message: 'Préfixe de la route URL (ex: chat -> ws/chat/room_name/) :',
36
- default: 'chat',
37
- },
38
- {
39
- type: 'input',
40
- name: 'app',
41
- message: 'Nom de l\'application cible',
42
- },
43
- ],
44
- actions: (data: any) => {
45
- const actions = [];
46
- const { appPath } = resolveAppPaths(data.config, data.app);
47
- const isAsync = data.type === 'async';
48
- data.isAsync = isAsync;
49
-
50
- // Ensure suffix
51
- data.name = ensureSuffix(data.name, 'Consumer');
52
-
53
- // Ensure consumers.py exists and Append Consumer
54
- actions.push(...createAppendActions({
55
- path: `${appPath}/consumers.py`,
56
- templateFile: 'generators/generator/django-channel/templates/consumer.py.hbs',
57
- dumbTemplate: 'import json\nfrom channels.generic.websocket import AsyncWebsocketConsumer, WebsocketConsumer\n\n',
58
- dumbData: {},
59
- }));
60
-
61
- // Ensure routing.py exists
62
- actions.push({
63
- type: 'add',
64
- path: `${appPath}/routing.py`,
65
- templateFile: 'generators/generator/django-channel/templates/routing.py.hbs',
66
- skipIfExists: true,
67
- });
68
-
69
- // Append to websocket_urlpatterns in routing.py
70
- // We look for the closing bracket of websocket_urlpatterns list
71
- actions.push({
72
- type: 'modify',
73
- path: `${appPath}/routing.py`,
74
- pattern: /(websocket_urlpatterns\s*=\s*\[)/,
75
- template: '$1\n re_path(r\'ws/{{ route }}/(?P<room_name>\\w+)/$\', consumers.{{ name }}.as_asgi()),',
76
- });
77
-
78
- return actions;
79
- },
80
- };
@@ -1,47 +0,0 @@
1
- import json
2
- from channels.generic.websocket import {{#if isAsync}}AsyncWebsocketConsumer{{else}}WebsocketConsumer{{/if}}
3
-
4
- class {{ name }}({{#if isAsync}}AsyncWebsocketConsumer{{else}}WebsocketConsumer{{/if}}):
5
- # <!-- CLASS = {{ name }} START AI_GENERATED -->
6
- # <!-- CLASS = {{ name }} END AI_GENERATED -->
7
- {{#if isAsync}}async {{/if}}def connect(self):
8
- self.room_name = self.scope['url_route']['kwargs']['room_name']
9
- self.room_group_name = 'chat_%s' % self.room_name
10
-
11
- # Join room group
12
- {{#if isAsync}}await {{/if}}self.channel_layer.group_add(
13
- self.room_group_name,
14
- self.channel_name
15
- )
16
-
17
- {{#if isAsync}}await {{/if}}self.accept()
18
-
19
- {{#if isAsync}}async {{/if}}def disconnect(self, close_code):
20
- # Leave room group
21
- {{#if isAsync}}await {{/if}}self.channel_layer.group_discard(
22
- self.room_group_name,
23
- self.channel_name
24
- )
25
-
26
- # Receive message from WebSocket
27
- {{#if isAsync}}async {{/if}}def receive(self, text_data):
28
- text_data_json = json.loads(text_data)
29
- message = text_data_json['message']
30
-
31
- # Send message to room group
32
- {{#if isAsync}}await {{/if}}self.channel_layer.group_send(
33
- self.room_group_name,
34
- {
35
- 'type': 'chat_message',
36
- 'message': message
37
- }
38
- )
39
-
40
- # Receive message from room group
41
- {{#if isAsync}}async {{/if}}def chat_message(self, event):
42
- message = event['message']
43
-
44
- # Send message to WebSocket
45
- {{#if isAsync}}await {{/if}}self.send(text_data=json.dumps({
46
- 'message': message
47
- }))
@@ -1,8 +0,0 @@
1
- from django.urls import re_path
2
- from . import consumers
3
-
4
- websocket_urlpatterns = [
5
- # <!-- CLASS = {{pascalCase name}}Routing START AI_GENERATED -->
6
- # <!-- CLASS = {{pascalCase name}}Routing END AI_GENERATED -->
7
- # re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
8
- ]
@@ -1,64 +0,0 @@
1
- import { PlopGeneratorConfig } from 'plop';
2
- import * as path from 'path';
3
- import { ensureSuffix, createAppendActions } from '../../utils/plop-actions.ts';
4
- import { resolveAppPaths } from '../../utils/filesystem.ts';
5
- import { FORM_TYPES } from '../../constants.ts';
6
- import { searchChoices } from '../../utils/search.ts';
7
-
8
- /**
9
- * Generator configuration for creating a Django Form or ModelForm.
10
- * Handles standard forms and model-bound forms.
11
- *
12
- * @type {PlopGeneratorConfig}
13
- */
14
- export const djangoFormGenerator: PlopGeneratorConfig = {
15
- description: 'Créer un Formulaire Django ou un ModelForm',
16
- prompts: [
17
- {
18
- type: 'autocomplete',
19
- name: 'type',
20
- message: 'Quel type de formulaire voulez-vous créer ?',
21
- source: (answers, input) => searchChoices(input, FORM_TYPES),
22
- },
23
- {
24
- type: 'input',
25
- name: 'name',
26
- message: 'Nom du Formulaire (ex: ContactForm, UserProfileForm) :',
27
- validate: (value) => {
28
- return true;
29
- },
30
- },
31
- {
32
- type: 'input',
33
- name: 'model',
34
- message: 'Nom du Modèle associé (ex: UserProfile) :',
35
- when: (answers) => answers.type === 'model_form',
36
- },
37
- {
38
- type: 'input',
39
- name: 'app',
40
- message: 'Nom de l\'application cible',
41
- },
42
- ],
43
- actions: (data: any) => {
44
- const actions = [];
45
- const { appPath } = resolveAppPaths(data.config, data.app);
46
- const isModelForm = data.type === 'model_form';
47
-
48
- // Ensure suffix
49
- data.name = ensureSuffix(data.name, 'Form');
50
-
51
- const templateFile = isModelForm
52
- ? 'generators/django-form/templates/model_form.py.hbs'
53
- : 'generators/django-form/templates/form.py.hbs';
54
-
55
- actions.push(...createAppendActions({
56
- path: `${appPath}/forms.py`,
57
- templateFile,
58
- dumbTemplateFile: 'generators/django-form/templates/forms_file.py.hbs',
59
- dumbData: data,
60
- }));
61
-
62
- return actions;
63
- },
64
- };
@@ -1,15 +0,0 @@
1
-
2
- from django import forms
3
-
4
-
5
- class {{ name }}(forms.Form):
6
- # TODO: Define form fields here
7
- # name = forms.CharField(max_length=100)
8
- # message = forms.CharField(widget=forms.Textarea)
9
-
10
- # <!-- CLASS = {{ name }} START AI_GENERATED -->
11
- # <!-- CLASS = {{ name }} END AI_GENERATED -->
12
- def clean(self):
13
- cleaned_data = super().clean()
14
- # TODO: Add custom validation logic
15
- return cleaned_data
@@ -1,6 +0,0 @@
1
- from django import forms
2
-
3
- # Create your forms here.
4
- # <!-- CLASS = {{pascalCase name}}Forms START AI_GENERATED -->
5
-
6
- # <!-- CLASS = {{pascalCase name}}Forms END AI_GENERATED -->
@@ -1,19 +0,0 @@
1
-
2
- from django import forms
3
- {{#if model}}from .models import {{ model }}{{/if}}
4
-
5
- class {{ name }}(forms.ModelForm):
6
- class Meta:
7
-
8
- {{#if model}} model = {{ model }}{{/if}}
9
- {{#unless model}} # model = YourModel{{/unless}}
10
- fields = '__all__' # TODO: Specify explicit fields ['field1', 'field2']
11
- # widgets = {
12
- # 'field_name': forms.TextInput(attrs={'class': 'form-control'}),
13
- # }
14
- # <!-- CLASS = {{ name }} START AI_GENERATED -->
15
- # <!-- CLASS = {{ name }} END AI_GENERATED -->
16
- def clean(self):
17
- cleaned_data = super().clean()
18
- # TODO: Add custom validation logic
19
- return cleaned_data
@@ -1,96 +0,0 @@
1
- import { PlopGeneratorConfig } from 'plop';
2
- import { createAppendActions, ensureSuffix } from '../../utils/plop-actions.ts';
3
- import { VIEW_TYPES } from '../../constants.ts';
4
- import { searchChoices } from '../../utils/search.ts';
5
- import { resolveAppPaths } from '../../utils/filesystem.ts';
6
-
7
- /**
8
- * Generator configuration for creating Django Views.
9
- * Supports Function-Based Views (FBV) and Class-Based Views (CBV) including:
10
- * - TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView.
11
- * Handles automatic suffixing logic for naming conventions.
12
- *
13
- * @type {PlopGeneratorConfig}
14
- */
15
- export const djangoViewGenerator: PlopGeneratorConfig = {
16
- description: 'Créer une Vue Django (FBV ou CBV)',
17
- prompts: [
18
- {
19
- type: 'autocomplete',
20
- name: 'type',
21
- message: 'Quel type de vue voulez-vous créer ?',
22
- source: (answers, input) => searchChoices(input, VIEW_TYPES),
23
- },
24
- {
25
- type: 'input',
26
- name: 'name',
27
- message: 'Nom de la Vue (ex: UserProfile, ProductList) :',
28
- validate: (value, answers) => {
29
- return true;
30
- }
31
- },
32
- {
33
- type: 'input',
34
- name: 'app',
35
- message: 'Nom de l\'application cible',
36
- },
37
- ],
38
- actions: (data: any) => {
39
- const actions = [];
40
- const { appPath } = resolveAppPaths(data.config, data.app);
41
- const isCBV = data.type !== 'fbv';
42
-
43
- // Helper to ensure correct suffix for CBV
44
- if (isCBV) {
45
- let suffix = data.type; // e.g. ListView, DetailView
46
- if (data.type === 'TemplateView') {
47
- suffix = 'View';
48
- }
49
-
50
- if (!data.name.endsWith(suffix)) {
51
- // Check if it ends with the "Type" part (e.g. "List" for "ListView")
52
- const typePrefix = suffix.replace(/View$/, ''); // "List"
53
-
54
- if (typePrefix && data.name.endsWith(typePrefix)) {
55
- // e.g. ProductList + ListView -> ProductListView
56
- data.name = `${data.name}View`;
57
- } else if (data.name.endsWith('View')) {
58
- // e.g. ProductView + ListView -> ProductListView
59
- data.name = data.name.replace(/View$/, suffix);
60
- } else {
61
- // e.g. Product + ListView -> ProductListView
62
- data.name = `${data.name}${suffix}`;
63
- }
64
- }
65
- } else {
66
- // FBV
67
- data.name = ensureSuffix(data.name, '_view');
68
- }
69
-
70
- // Helper for template name
71
- data.templateName = data.name.replace(/View$/, '');
72
-
73
- // Append to views.py
74
- const templateFile = isCBV
75
- ? 'generators/generator/django-view/templates/view_cbv.py.hbs'
76
- : 'generators/generator/django-view/templates/view_fbv.py.hbs';
77
-
78
- actions.push(...createAppendActions({
79
- path: `${appPath}/views.py`,
80
- templateFile,
81
- dumbTemplate: 'from django.shortcuts import render\n\n',
82
- dumbData: {},
83
- appendData: isCBV ? { viewType: data.type } : undefined
84
- }));
85
-
86
- // Create Template
87
- actions.push({
88
- type: 'add',
89
- path: `${appPath}/templates/${data.app}/{{snakeCase templateName}}.html`, // Clean filename
90
- templateFile: 'generators/generator/django-view/templates/view_template.html.hbs',
91
- skipIfExists: true,
92
- });
93
-
94
- return actions;
95
- },
96
- };
@@ -1,14 +0,0 @@
1
-
2
- from django.views.generic import {{viewType}}
3
-
4
-
5
- class {{pascalCase name}}({{viewType}}):
6
- """
7
- Class-based view for {{name}}.
8
- """
9
- template_name = '{{app}}/{{snakeCase name}}.html'
10
-
11
- # <!-- CLASS = {{pascalCase name}} START AI_GENERATED -->
12
- # <!-- CLASS = {{pascalCase name}} END AI_GENERATED -->
13
- # context_object_name = '{{snakeCase name}}'
14
- # model = MyModel
@@ -1,10 +0,0 @@
1
-
2
- from django.shortcuts import render
3
-
4
-
5
- def {{snakeCase name}}(request):
6
- """
7
- Function-based view for {{name}}.
8
- """
9
- context = {}
10
- return render(request, '{{app}}/{{snakeCase name}}.html', context)
@@ -1,8 +0,0 @@
1
- {% extends "base.html" %}
2
-
3
- <!-- CLASS = {{pascalCase name}} START AI_GENERATED -->
4
- {% block content %}
5
- <h1>{{titleCase name}}</h1>
6
- <p>Welcome to the {{name}} view.</p>
7
- {% endblock %}
8
- <!-- CLASS = {{pascalCase name}} END AI_GENERATED -->
@@ -1,70 +0,0 @@
1
- import { NodePlopAPI } from 'plop';
2
- import { djangoAppGenerator } from '../django-app/index.ts';
3
- import { wagtailPageGenerator } from '../wagtail-page/index.ts';
4
- import { wagtailBlockGenerator } from '../wagtail-block/index.ts';
5
- import { djangoViewGenerator } from '../django-view/index.ts';
6
- import { djangoFormGenerator } from '../django-form/index.ts';
7
- import { wagtailAdminGenerator } from '../wagtail-admin/index.ts';
8
- import { djangoChannelGenerator } from '../django-channel/index.ts';
9
- import { aiEnhancerGenerator } from '../../ia/ai-enhancer/index.ts';
10
- import { helpGenerator } from '../../ia/help/index.ts';
11
- import { searchChoices } from '../../utils/search.ts';
12
- import {
13
- MAIN_COMPONENT_TYPES,
14
- EXIT_VALUE,
15
- BACK_VALUE
16
- } from '../../constants.ts';
17
-
18
- const handleInput = (value: any) => {
19
- if (value === EXIT_VALUE || (typeof value === 'string' && value.toLowerCase() === 'exit')) {
20
- return EXIT_VALUE;
21
- }
22
- return value;
23
- };
24
-
25
- export default function (plop: NodePlopAPI) {
26
- // Register Sub-Generators
27
- plop.setGenerator('django-app', djangoAppGenerator);
28
- plop.setGenerator('wagtail-page', wagtailPageGenerator);
29
- plop.setGenerator('wagtail-block', wagtailBlockGenerator);
30
- plop.setGenerator('django-view', djangoViewGenerator);
31
- plop.setGenerator('django-form', djangoFormGenerator);
32
- plop.setGenerator('wagtail-admin', wagtailAdminGenerator);
33
- plop.setGenerator('django-channel', djangoChannelGenerator);
34
- plop.setGenerator('ai-enhancer', aiEnhancerGenerator);
35
- plop.setGenerator('help', helpGenerator);
36
-
37
- // Register Main Generator (Menu)
38
- plop.setGenerator('main', {
39
- description: 'Main Entry Point',
40
- prompts: [
41
- {
42
- type: 'autocomplete',
43
- name: 'generatorType',
44
- message: 'What do you want to do?',
45
- source: async (answers, input) => {
46
- const choices = [
47
- { name: 'Django App (Create new app)', value: 'django-app' },
48
- { name: 'Wagtail Page (Models, Templates)', value: 'wagtail-page' },
49
- { name: 'Wagtail Block (StreamField Blocks)', value: 'wagtail-block' },
50
- { name: 'Django View (CBV, FBV, Templates)', value: 'django-view' },
51
- { name: 'Django Form (ModelForm, Form)', value: 'django-form' },
52
- { name: 'Wagtail Admin (Snippets, ModelAdmin)', value: 'wagtail-admin' },
53
- { name: 'Django Channels (Consumers, Routing)', value: 'django-channel' },
54
- { name: 'AI Enhancer (Modify code with AI)', value: 'ai-enhancer' },
55
- { name: 'Help / Search Instructions', value: 'help' },
56
- { name: 'Exit', value: EXIT_VALUE }
57
- ];
58
- return searchChoices(input, choices);
59
- },
60
- filter: handleInput
61
- }
62
- ],
63
- actions: (data) => {
64
- if (data.generatorType === EXIT_VALUE) return [];
65
-
66
- console.log(`\nTo use this generator, please run:\n kalo ${data.generatorType}\n`);
67
- return [];
68
- }
69
- });
70
- }