dars-framework 1.2.3__tar.gz → 1.2.4__tar.gz

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 (126) hide show
  1. dars_framework-1.2.4/PKG-INFO +245 -0
  2. {dars_framework-1.2.3 → dars_framework-1.2.4}/README.md +6 -1
  3. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/doctor.py +15 -31
  4. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/ui.py +13 -0
  5. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/main.py +22 -8
  6. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/config.py +1 -0
  7. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/js_bridge.py +60 -0
  8. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/security.py +16 -4
  9. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/version.py +2 -2
  10. dars_framework-1.2.4/dars_framework.egg-info/PKG-INFO +245 -0
  11. {dars_framework-1.2.3 → dars_framework-1.2.4}/pyproject.toml +3 -5
  12. dars_framework-1.2.3/PKG-INFO +0 -15
  13. dars_framework-1.2.3/dars_framework.egg-info/PKG-INFO +0 -15
  14. {dars_framework-1.2.3 → dars_framework-1.2.4}/LICENSE +0 -0
  15. {dars_framework-1.2.3 → dars_framework-1.2.4}/MANIFEST.in +0 -0
  16. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/__init__.py +0 -0
  17. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/all.py +0 -0
  18. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/__init__.py +0 -0
  19. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/__init__.py +0 -0
  20. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/detect.py +0 -0
  21. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/installers.py +0 -0
  22. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/persist.py +0 -0
  23. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/doctor/preflight.py +0 -0
  24. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/hot_reload.py +0 -0
  25. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/preview.py +0 -0
  26. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/cli/translations.py +0 -0
  27. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/__init__.py +0 -0
  28. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/__init__.py +0 -0
  29. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/accordion.py +0 -0
  30. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/card.py +0 -0
  31. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/modal.py +0 -0
  32. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/navbar.py +0 -0
  33. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/table.py +0 -0
  34. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/advanced/tabs.py +0 -0
  35. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/__init__.py +0 -0
  36. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/button.py +0 -0
  37. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/checkbox.py +0 -0
  38. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/container.py +0 -0
  39. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/datepicker.py +0 -0
  40. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/image.py +0 -0
  41. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/input.py +0 -0
  42. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/link.py +0 -0
  43. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/markdown.py +0 -0
  44. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/page.py +0 -0
  45. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/progressbar.py +0 -0
  46. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/radiobutton.py +0 -0
  47. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/select.py +0 -0
  48. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/slider.py +0 -0
  49. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/spinner.py +0 -0
  50. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/text.py +0 -0
  51. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/textarea.py +0 -0
  52. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/basic/tooltip.py +0 -0
  53. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/layout/__init__.py +0 -0
  54. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/layout/anchor.py +0 -0
  55. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/layout/flex.py +0 -0
  56. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/components/layout/grid.py +0 -0
  57. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/__init__.py +0 -0
  58. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/app.py +0 -0
  59. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/component.py +0 -0
  60. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/events.py +0 -0
  61. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/properties.py +0 -0
  62. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/core/state.py +0 -0
  63. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/apps_test/health_check.py +0 -0
  64. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/run_tests.py +0 -0
  65. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/tests/test_advanced_components.py +0 -0
  66. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/tests/test_basic_components.py +0 -0
  67. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/tests/test_core_and_cli.py +0 -0
  68. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/tests/test_layout_components.py +0 -0
  69. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/dars_tests/tests/test_version_check.py +0 -0
  70. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/__init__.py +0 -0
  71. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/app.md +0 -0
  72. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/cli.md +0 -0
  73. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/components.md +0 -0
  74. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/custom_components.md +0 -0
  75. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/events.md +0 -0
  76. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/exporters.md +0 -0
  77. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/getting_started.md +0 -0
  78. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/index.md +0 -0
  79. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/scripts.md +0 -0
  80. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/docs/state_management.md +0 -0
  81. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/__init__.py +0 -0
  82. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/base.py +0 -0
  83. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/OLD/html_css_js_OLD4.py +0 -0
  84. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/OLD/html_css_js_old.py +0 -0
  85. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/OLD/html_css_js_old2.py +0 -0
  86. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/__init__.py +0 -0
  87. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/html_css_js.py +0 -0
  88. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/exporters/web/vdom.py +0 -0
  89. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/js_lib.py +0 -0
  90. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/scripts/__init__.py +0 -0
  91. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/scripts/dscript.py +0 -0
  92. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/scripts/script.py +0 -0
  93. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/__init__.py +0 -0
  94. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  95. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/README.md +0 -0
  96. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/__pycache__/dynamic_event_demo.cpython-311.pyc +0 -0
  97. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/Modal_Demo/advanced_modal_demo.py +0 -0
  98. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/SimpleDashboard/dashboard.py +0 -0
  99. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/SimpleModermWeb/modern_web_app.py +0 -0
  100. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/VariousComponents/all_components_demo.py +0 -0
  101. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/__init__.py +0 -0
  102. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/advanced/dState/state_mods_demo.py +0 -0
  103. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Forms/form_components.py +0 -0
  104. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Forms/simple_form.py +0 -0
  105. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/HelloWorld/hello_world.py +0 -0
  106. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Layouts/flex_layout_responsive.py +0 -0
  107. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Layouts/grid_layout_responsive.py +0 -0
  108. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Layouts/layout_multipage_demo.py +0 -0
  109. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/Multipage/multipage_example.py +0 -0
  110. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/PWA/icon-192x192.png +0 -0
  111. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/PWA/icon-512x512.png +0 -0
  112. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/PWA/pwa_custom_icons.py +0 -0
  113. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/basic/__init__.py +0 -0
  114. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/demo/__pycache__/complete_app.cpython-311.pyc +0 -0
  115. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/demo/complete_app.py +0 -0
  116. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/markdown/MarkdownTemplate/README.md +0 -0
  117. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/markdown/MarkdownTemplate/markdown_template.py +0 -0
  118. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/markdown/MarkdownTemplate/other_docs.md +0 -0
  119. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/examples/markdown/__init__.py +0 -0
  120. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars/templates/html/__init__.py +0 -0
  121. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars_framework.egg-info/SOURCES.txt +0 -0
  122. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars_framework.egg-info/dependency_links.txt +0 -0
  123. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars_framework.egg-info/entry_points.txt +0 -0
  124. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars_framework.egg-info/requires.txt +0 -0
  125. {dars_framework-1.2.3 → dars_framework-1.2.4}/dars_framework.egg-info/top_level.txt +0 -0
  126. {dars_framework-1.2.3 → dars_framework-1.2.4}/setup.cfg +0 -0
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: dars_framework
3
+ Version: 1.2.4
4
+ Summary: Dars is a Python UI framework for building modern, interactive web apps with only Python code. Write your interface in Python, export it to static HTML/CSS/JS, and deploy anywhere.
5
+ Author-email: ztamdev <ztadevs@gmail.com>
6
+ License-Expression: MIT
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: rich==14.2.0
10
+ Requires-Dist: bs4==0.0.2
11
+ Requires-Dist: uvicorn==0.35.0
12
+ Requires-Dist: fastapi==0.116.1
13
+ Requires-Dist: markdown2==2.5.4
14
+ Requires-Dist: requests==2.32.5
15
+ Requires-Dist: rjsmin==1.2.5
16
+ Dynamic: license-file
17
+
18
+ <h1 align="center">Dars Framework</h1>
19
+
20
+ <p align="center">
21
+ <img src="./Dars-logo.png" alt="Dars Framework Logo" width="200" />
22
+ </p>
23
+
24
+ <p align="center">
25
+ <em>Dars is a Python UI framework for building modern, interactive web apps with Python code. Write your interface in Python, export it to static HTML/CSS/JS, and deploy anywhere.</em>
26
+ </p>
27
+
28
+ ```bash
29
+ pip install dars-framework
30
+ ```
31
+
32
+ > Some Javascript or frontend stack required.
33
+
34
+ Try dars without installing nothing just visit the [Dars Playground](https://dars-playground.vercel.app/)
35
+
36
+ ## How It Works
37
+ - Build your UI using Python classes and components (like Text, Button, Container, Page, etc).
38
+ - Preview instantly with hot-reload using `app.rTimeCompile()`.
39
+ - Export your app to static web files with a single CLI command.
40
+ - Use multipage, layouts, scripts, and more—see docs for advanced features.
41
+ - For more information visit the [Documentation](https://ztamdev.github.io/Dars-Framework/documentation.html)
42
+
43
+ ## Quick Example: Your First App
44
+ ```python
45
+ from dars.all import *
46
+
47
+ app = App(title="Hello World", theme="dark")
48
+ # Crear componentes
49
+ index = Page(
50
+ Text(
51
+ text="Hello World",
52
+ style={
53
+ 'font-size': '48px',
54
+ 'color': '#2c3e50',
55
+ 'margin-bottom': '20px',
56
+ 'font-weight': 'bold',
57
+ 'text-align': 'center'
58
+ }
59
+ ),
60
+ Text(
61
+ text="Hello World",
62
+ style={
63
+ 'font-size': '20px',
64
+ 'color': '#7f8c8d',
65
+ 'margin-bottom': '40px',
66
+ 'text-align': 'center'
67
+ }
68
+ ),
69
+
70
+ Button(
71
+ text="Click Me!",
72
+ on_click= dScript("alert('Hello World')"),
73
+ on_mouse_enter=dScript("this.style.backgroundColor = '#2980b9';"),
74
+ on_mouse_leave=dScript("this.style.backgroundColor = '#3498db';"),
75
+ style={
76
+ 'background-color': '#3498db',
77
+ 'color': 'white',
78
+ 'padding': '15px 30px',
79
+ 'border': 'none',
80
+ 'border-radius': '8px',
81
+ 'font-size': '18px',
82
+ 'cursor': 'pointer',
83
+ 'transition': 'background-color 0.3s'
84
+ }
85
+ ),
86
+ style={
87
+ 'display': 'flex',
88
+ 'flex-direction': 'column',
89
+ 'align-items': 'center',
90
+ 'justify-content': 'center',
91
+ 'min-height': '100vh',
92
+ 'background-color': '#f0f2f5',
93
+ 'font-family': 'Arial, sans-serif'
94
+ }
95
+ )
96
+ index.attr()
97
+ app.add_page("index", index, title="Hello World", index=True)
98
+
99
+ if __name__ == "__main__":
100
+ app.rTimeCompile()
101
+
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Reactivity and State System
107
+
108
+ **Dars Framework** includes a built-in **reactive state system** (`dState` / `cState`) that allows dynamic and modular DOM updates directly from Python.
109
+ It enables fully event-driven interfaces without requiring manual JavaScript.
110
+
111
+ ### Key Concepts
112
+
113
+ * **`dState(name, component, states)`**
114
+ Creates a reactive state controller bound to a specific component and a list of possible states.
115
+
116
+ * **`cState(idx, mods=[...])`**
117
+ Defines rules (modifications) that are automatically applied when entering a specific state.
118
+
119
+ * **`Mod` Helpers**
120
+ A compact way to modify DOM elements on state changes: `inc`, `dec`, `set`, `toggle_class`, `append_text`, `prepend_text`, `goto`, and more.
121
+
122
+ * **Deferred Mutations**
123
+ Using `component.attr(..., defer=True)` or `component.mod(...)` inside a `cComp=True` state defers HTML updates until an event occurs, preventing authoring-time mutations.
124
+
125
+ ### Example Template
126
+
127
+ A complete example demonstrating `dState`, `cState`, `Mod`, and deferred updates is available [here](https://github.com/ZtaMDev/Dars-Framework/blob/CrystalMain/dars/templates/examples/advanced/dState/state_mods_demo.py)
128
+
129
+ <img width="384" height="187" alt="imagen" src="https://github.com/user-attachments/assets/7750ee7f-768f-48da-94df-2fa00339a99c" /> <img width="361" height="215" alt="imagen" src="https://github.com/user-attachments/assets/9b8a3e67-2424-49b4-aee0-9f1c0f747d66" />
130
+
131
+
132
+
133
+ ### Features
134
+
135
+ * Reactive Mod system with compact `Mod` helpers
136
+ * Unified event model — any component can use `on_*` props (`on_click`, `on_input`, `on_change`, etc.)
137
+ * Deferred rendering for safer, predictable state transitions (`cComp=True`)
138
+ * Navigation between states using `goto`, including relative moves (`'+1'`, `'-1'`)
139
+ * Consistent, event-time mutation flow for reliable behavior
140
+ * Secure minification for production bundles (strong JS/CSS minifier integrated into the build pipeline)
141
+
142
+ ---
143
+
144
+ ## CLI Usage
145
+ | Command | What it does |
146
+ |-----------------------------------------|--------------------------------------------|
147
+ | `dars export my_app.py --format html` | Export app to HTML/CSS/JS in `./my_app_web` |
148
+ | `dars preview ./my_app_web` | Preview exported app locally |
149
+ | `dars init my_project` | Create a new Dars project (also creates dars.config.json) |
150
+ | `dars init --update` | Create/Update dars.config.json in current dir |
151
+ | `dars build` | Build using dars.config.json (entry/outdir/format) |
152
+ | `dars config validate` | Validate dars.config.json and print report |
153
+ | `dars info my_app.py` | Show info about your app |
154
+ | `dars formats` | List supported export formats |
155
+ | `dars --help` | Show help and all CLI options |
156
+
157
+ Tip: use `dars doctor` to review optional tooling that can enhance bundling/minification.
158
+
159
+ ## More
160
+
161
+ - Visit dars [official website](https://ztamdev.github.io/Dars-Framework/)
162
+ - Visit the dars official [Documentation](https://ztamdev.github.io/Dars-Framework/documentation.html) now on separate website.
163
+ - Try dars without installing nothing just visit the [Dars Playground](https://dars-playground.vercel.app/)
164
+
165
+ ## Local Execution and Live Preview
166
+
167
+ To test your app locally before exporting, use the hot-reload preview from any Python file that defines your app:
168
+
169
+ ```python
170
+ if __name__ == "__main__":
171
+ app.rTimeCompile()
172
+ ```
173
+
174
+ Then run your file directly:
175
+
176
+ ```bash
177
+ python my_app.py
178
+ ```
179
+
180
+ This will start a local server at http://localhost:8000 so you can view your app in the browser—no manual export needed. You can change the port with:
181
+
182
+ ```bash
183
+ python my_app.py --port 8088
184
+ ```
185
+
186
+ ---
187
+
188
+ You can also use the CLI preview command on an exported app:
189
+
190
+ ```bash
191
+ dars preview ./my_exported_app
192
+ ```
193
+
194
+ This will start a local server at http://localhost:8000 to view your exported app in the browser.
195
+
196
+ ---
197
+
198
+ ## Project Configuration (dars.config.json)
199
+
200
+ Dars can read build/export settings from a `dars.config.json` at your project root. It is created automatically by `dars init`, and you can add it to existing projects with `dars init --update`.
201
+
202
+ Example default:
203
+
204
+ ```json
205
+ {
206
+ "entry": "main.py",
207
+ "format": "html",
208
+ "outdir": "dist",
209
+ "publicDir": null,
210
+ "include": [],
211
+ "exclude": ["**/__pycache__", ".git", ".venv", "node_modules"],
212
+ "bundle": true,
213
+ "viteMinify": true
214
+ }
215
+ ```
216
+
217
+ - `entry`: Python entry file. Used by `dars build` and `dars export config`.
218
+ - `format`: Export format. Currently only `html` is supported.
219
+ - `outdir`: Output directory. Used by `dars build` and default for `dars export` when not overridden.
220
+ - `publicDir`: Folder (e.g., `public/` or `assets/`) copied into the output. If null, it is autodetected.
221
+ - `include`/`exclude`: Basic filters for copying from `publicDir`.
222
+ - `bundle`: Reserved for future use. CLI exports and build already bundle appropriately.
223
+ - `viteMinify`: Toggle the Vite-based minifier for JS. When `false`, the build uses esbuild-based minification. If neither is available, a conservative Python fallback is used.
224
+
225
+ Validate your config:
226
+
227
+ ```bash
228
+ dars config validate
229
+ ```
230
+
231
+ Build using config:
232
+
233
+ ```bash
234
+ dars build
235
+ ```
236
+
237
+ Export using the config entry and outdir:
238
+
239
+ ```bash
240
+ dars export config --format html
241
+ ```
242
+
243
+ ---
244
+
245
+ See LandingPage docs for details: state_management.md, events.md, scripts.md.
@@ -120,6 +120,7 @@ A complete example demonstrating `dState`, `cState`, `Mod`, and deferred updates
120
120
  * Deferred rendering for safer, predictable state transitions (`cComp=True`)
121
121
  * Navigation between states using `goto`, including relative moves (`'+1'`, `'-1'`)
122
122
  * Consistent, event-time mutation flow for reliable behavior
123
+ * Secure minification for production bundles (strong JS/CSS minifier integrated into the build pipeline)
123
124
 
124
125
  ---
125
126
 
@@ -136,6 +137,8 @@ A complete example demonstrating `dState`, `cState`, `Mod`, and deferred updates
136
137
  | `dars formats` | List supported export formats |
137
138
  | `dars --help` | Show help and all CLI options |
138
139
 
140
+ Tip: use `dars doctor` to review optional tooling that can enhance bundling/minification.
141
+
139
142
  ## More
140
143
 
141
144
  - Visit dars [official website](https://ztamdev.github.io/Dars-Framework/)
@@ -189,7 +192,8 @@ Example default:
189
192
  "publicDir": null,
190
193
  "include": [],
191
194
  "exclude": ["**/__pycache__", ".git", ".venv", "node_modules"],
192
- "bundle": false
195
+ "bundle": true,
196
+ "viteMinify": true
193
197
  }
194
198
  ```
195
199
 
@@ -199,6 +203,7 @@ Example default:
199
203
  - `publicDir`: Folder (e.g., `public/` or `assets/`) copied into the output. If null, it is autodetected.
200
204
  - `include`/`exclude`: Basic filters for copying from `publicDir`.
201
205
  - `bundle`: Reserved for future use. CLI exports and build already bundle appropriately.
206
+ - `viteMinify`: Toggle the Vite-based minifier for JS. When `false`, the build uses esbuild-based minification. If neither is available, a conservative Python fallback is used.
202
207
 
203
208
  Validate your config:
204
209
 
@@ -1,7 +1,7 @@
1
1
  import os, sys
2
2
  from typing import Dict, List
3
3
  from .detect import detect_node, detect_bun, detect_esbuild, detect_vite, read_pyproject_deps, check_python_deps
4
- from .installers import install_node, install_bun, install_esbuild, install_vite
4
+ from .installers import install_bun
5
5
  from .persist import load_config, save_config
6
6
  from .ui import render_report, prompt_action, confirm_install
7
7
  from rich.console import Console
@@ -23,11 +23,13 @@ def run_doctor(check_only: bool = False, auto_yes: bool = False, install_all: bo
23
23
 
24
24
  render_report(node, bun, py, esb, vit)
25
25
 
26
+ # Mandatory only for doctor purposes: Python deps
26
27
  missing_items: List[str] = []
27
- if not node.get('ok'): missing_items.append('Node.js LTS')
28
- if not bun.get('ok'): missing_items.append('Bun stable')
29
28
  if py.get('missing'): missing_items.append('Python deps')
29
+ # Node, Bun, esbuild, vite treated as optional. Bun can be auto-installed if user wants.
30
30
  optional_missing: List[str] = []
31
+ if not node.get('ok'): optional_missing.append('Node.js (optional)')
32
+ if not bun.get('ok'): optional_missing.append('Bun (optional)')
31
33
  if not esb.get('ok'): optional_missing.append('esbuild (optional)')
32
34
  if not vit.get('ok'): optional_missing.append('vite (optional)')
33
35
 
@@ -73,37 +75,21 @@ def run_doctor(check_only: bool = False, auto_yes: bool = False, install_all: bo
73
75
 
74
76
  # choice == '1' => Install ALL missing
75
77
  summary: List[str] = []
76
- if not node.get('ok'): summary.append('Node.js LTS (winget)')
77
- if not bun.get('ok'): summary.append('Bun (winget)')
78
+ # Only Bun and Python deps are installable from doctor; Node/esbuild/vite show links in the report
79
+ if not bun.get('ok'): summary.append('Bun (winget/installer)')
78
80
  if py.get('missing'): summary.append(f"Python deps: {', '.join(py['missing'])}")
79
- if optional_missing:
80
- summary.extend(optional_missing)
81
+ # Do not include other optional tools in install summary
81
82
 
82
83
  if not auto_yes:
83
84
  if not confirm_install(summary):
84
85
  return 1
85
86
 
86
- # Installers: always run Node then Bun sequentially (idempotent if already installed)
87
+ # Installers: only Bun, optionally Python deps
87
88
  with console.status("[cyan]Installing selected items...[/cyan]"):
88
- try:
89
- install_node()
90
- except Exception:
91
- pass
92
89
  try:
93
90
  install_bun()
94
91
  except Exception:
95
92
  pass
96
- # Optional developer tools
97
- try:
98
- if not esb.get('ok'):
99
- install_esbuild()
100
- except Exception:
101
- pass
102
- try:
103
- if not vit.get('ok'):
104
- install_vite()
105
- except Exception:
106
- pass
107
93
 
108
94
  # Python deps via pip
109
95
  if py.get('missing'):
@@ -124,11 +110,12 @@ def run_doctor(check_only: bool = False, auto_yes: bool = False, install_all: bo
124
110
 
125
111
  render_report(node2, bun2, py2, esb2, vit2)
126
112
 
127
- all_ok = node2.get('ok') and bun2.get('ok') and not py2.get('missing')
113
+ all_ok = not py2.get('missing')
128
114
 
129
115
  cfg['requirements']['node'].update({'ok': bool(node2.get('ok')), 'version': node2.get('version')})
130
116
  cfg['requirements']['bun'].update({'ok': bool(bun2.get('ok')), 'version': bun2.get('version')})
131
117
  cfg['python_deps'] = {'ok': not bool(py2.get('missing')), 'missing': py2.get('missing') or []}
118
+ # doctor satisfaction now tied only to Python deps
132
119
  cfg['satisfied'] = bool(all_ok)
133
120
  save_config(cfg)
134
121
 
@@ -137,16 +124,12 @@ def run_doctor(check_only: bool = False, auto_yes: bool = False, install_all: bo
137
124
 
138
125
  def run_forcedev() -> int:
139
126
  """Force-install everything without initial verification or prompts.
140
- - Attempts Node LTS and Bun installers unconditionally (best-effort)
127
+ - Attempts Bun installer unconditionally (best-effort)
141
128
  - Installs/updates all Python deps from pyproject.toml
142
129
  - Re-checks and persists satisfied state
143
130
  Returns 0 if environment ends OK, else 1.
144
131
  """
145
132
  # Best-effort installs (no UI)
146
- try:
147
- install_node()
148
- except Exception:
149
- pass
150
133
  try:
151
134
  install_bun()
152
135
  except Exception:
@@ -163,11 +146,12 @@ def run_forcedev() -> int:
163
146
 
164
147
  # Re-check and persist
165
148
  cfg = load_config()
166
- node2 = detect_node()
167
149
  bun2 = detect_bun()
168
150
  py2 = check_python_deps(read_pyproject_deps())
169
- all_ok = node2.get('ok') and bun2.get('ok') and not py2.get('missing')
151
+ all_ok = bun2.get('ok') and not py2.get('missing')
170
152
 
153
+ # keep node state updated for UI even if not installed by forcedev
154
+ node2 = detect_node()
171
155
  cfg['requirements']['node'].update({'ok': bool(node2.get('ok')), 'version': node2.get('version')})
172
156
  cfg['requirements']['bun'].update({'ok': bool(bun2.get('ok')), 'version': bun2.get('version')})
173
157
  cfg['python_deps'] = {'ok': not bool(py2.get('missing')), 'missing': py2.get('missing') or []}
@@ -31,6 +31,19 @@ def render_report(node: Dict, bun: Dict, py: Dict, esb: Dict = None, vit: Dict =
31
31
  bullets = "\n".join([f" • {req}" for req in p_missing])
32
32
  console.print(Panel(bullets or "", title="Missing Python packages", border_style="yellow"))
33
33
 
34
+ # Helpful links for optional tools
35
+ tips: List[str] = []
36
+ if node is not None and not node.get("ok"):
37
+ tips.append("Node.js LTS (manual): https://nodejs.org/en/download")
38
+ if bun is not None and not bun.get("ok"):
39
+ tips.append("Bun (auto-install available) — Windows: winget / PS: irm bun.sh/install.ps1 | iex — macOS/Linux: curl -fsSL https://bun.sh/install | bash")
40
+ if esb is not None and not esb.get("ok"):
41
+ tips.append("esbuild (optional): https://esbuild.github.io/getting-started/")
42
+ if vit is not None and not vit.get("ok"):
43
+ tips.append("Vite (optional): https://vite.dev/guide/")
44
+ if tips:
45
+ console.print(Panel("\n".join([f" • {t}" for t in tips]), title="How to install (optional)", border_style="cyan"))
46
+
34
47
 
35
48
  def prompt_action(has_missing: bool) -> str:
36
49
  console.print(Panel("Select an action", border_style="cyan"))
@@ -795,13 +795,6 @@ def main():
795
795
 
796
796
  exporter = DarsExporter()
797
797
 
798
- # Run preflight gating for all commands except 'doctor'
799
- if getattr(args, 'command', None) and args.command != 'doctor':
800
- try:
801
- check_and_gate(args.command)
802
- except SystemExit as e:
803
- # If doctor failed or user cancelled, abort the command
804
- sys.exit(e.code if isinstance(e.code, int) else 1)
805
798
 
806
799
  if args.command == 'export':
807
800
  # If file points to config, resolve from dars.config.json
@@ -827,6 +820,12 @@ def main():
827
820
  # If config exists and user didn't override output explicitly, use cfg.outdir
828
821
  project_root = os.path.dirname(os.path.abspath(file_arg))
829
822
  cfg, cfg_found = load_config(project_root)
823
+ # Apply viteMinify setting to env for downstream minifier
824
+ try:
825
+ vite_flag = cfg.get('viteMinify', True)
826
+ os.environ['DARS_VITE_MINIFY'] = '1' if vite_flag else '0'
827
+ except Exception:
828
+ pass
830
829
  outdir = args.output
831
830
  if cfg_found and (args.output == './dist' or args.output == 'dist'):
832
831
  resolved = resolve_paths(cfg, project_root)
@@ -868,7 +867,8 @@ def main():
868
867
  target_dir = args.name or '.'
869
868
  project_root = os.path.abspath(target_dir)
870
869
  os.makedirs(project_root, exist_ok=True)
871
- write_default_config(project_root, overwrite=False)
870
+ # Merge with DEFAULT_CONFIG and write back to ensure new keys (e.g., viteMinify)
871
+ update_config(project_root, {})
872
872
  ensure_dars_lib(project_root)
873
873
  console.print("[green]✔ dars.config.json created/updated[/green]")
874
874
  elif not args.name:
@@ -884,10 +884,24 @@ def main():
884
884
  if not found:
885
885
  console.print("[yellow][Dars] Warning: dars.config.json not found. Run 'dars init --update' to create it.[/yellow]")
886
886
  resolved = resolve_paths(cfg, project_root)
887
+ # Apply viteMinify setting to env for downstream minifier
888
+ try:
889
+ vite_flag = cfg.get('viteMinify', True)
890
+ os.environ['DARS_VITE_MINIFY'] = '1' if vite_flag else '0'
891
+ except Exception:
892
+ pass
887
893
  entry = resolved.get('entry_abs') or os.path.join(project_root, cfg.get('entry', 'main.py'))
888
894
  format_name = cfg.get('format', 'html')
889
895
  outdir = resolved.get('outdir_abs') or os.path.join(project_root, 'dist')
890
896
 
897
+ # Build-only heads-up: esbuild optional, but recommended for better bundling
898
+ try:
899
+ from dars.core.js_bridge import esbuild_available as _esb_ok
900
+ if not _esb_ok():
901
+ console.print("[yellow][Dars] Notice: esbuild no está disponible. El bundle se hará con minificación básica. Ejecuta 'dars doctor' para ver requerimientos opcionales.[/yellow]")
902
+ except Exception:
903
+ pass
904
+
891
905
  # Validate entry file exists
892
906
  if not os.path.exists(entry):
893
907
  console.print(f"[red]{translator.get('error_entry_not_found_in_config')}: {entry}[/red]")
@@ -10,6 +10,7 @@ DEFAULT_CONFIG = {
10
10
  "include": [],
11
11
  "exclude": ["**/__pycache__", ".git", ".venv", "node_modules"],
12
12
  "bundle": True,
13
+ "viteMinify": True,
13
14
  }
14
15
 
15
16
  CONFIG_FILENAME = "dars.config.json"
@@ -31,6 +31,17 @@ def has_bun() -> bool:
31
31
  return which("bun") is not None
32
32
 
33
33
 
34
+ def vite_available() -> bool:
35
+ # Prefer bun x vite
36
+ if has_bun():
37
+ code, out, _ = _run(["bun", "x", "vite", "--version"])
38
+ if code == 0:
39
+ return True
40
+ # Fallback to npx vite
41
+ code, out, _ = _run(["npx", "--yes", "vite", "--version"])
42
+ return code == 0
43
+
44
+
34
45
  def bun_add(packages: List[str], dev: bool = True, cwd: Optional[str] = None) -> bool:
35
46
  if not has_bun():
36
47
  return False
@@ -97,3 +108,52 @@ def esbuild_minify_js(src_path: str, out_path: Optional[str] = None) -> bool:
97
108
  def esbuild_minify_css(src_path: str, out_path: Optional[str] = None) -> bool:
98
109
  # esbuild can minify CSS if input is CSS
99
110
  return esbuild_minify_js(src_path, out_path)
111
+
112
+
113
+ def vite_minify_js(src_path: str, out_path: Optional[str] = None) -> bool:
114
+ """Use Vite build (Rollup) to minify a single JS entry file.
115
+ Creates a temp vite.config.mjs pointing to the absolute src_path, builds to a temp outDir, and copies the result to out_path.
116
+ """
117
+ if not vite_available():
118
+ return False
119
+ try:
120
+ import json
121
+ import shutil
122
+ workdir = tempfile.mkdtemp(prefix="dars_vite_")
123
+ outdir = os.path.join(workdir, "out")
124
+ os.makedirs(outdir, exist_ok=True)
125
+ abs_src = os.path.abspath(src_path)
126
+ vite_config = os.path.join(workdir, "vite.config.mjs")
127
+ with open(vite_config, "w", encoding="utf-8") as f:
128
+ f.write(
129
+ "export default {\n"
130
+ " build: {\n"
131
+ " minify: 'esbuild',\n"
132
+ " sourcemap: false,\n"
133
+ " rollupOptions: { input: ['" + abs_src.replace('\\', '\\\\') + "'] },\n"
134
+ " outDir: 'out',\n"
135
+ " emptyOutDir: true\n"
136
+ " }\n"
137
+ "};\n"
138
+ )
139
+ # Run vite build
140
+ cmd = ["bun", "x", "vite", "build", "--config", vite_config] if has_bun() else ["npx", "--yes", "vite", "build", "--config", vite_config]
141
+ code, _out, _err = _run(cmd, cwd=workdir)
142
+ if code != 0:
143
+ shutil.rmtree(workdir, ignore_errors=True)
144
+ return False
145
+ # Find a single .js in outdir
146
+ chosen = None
147
+ for name in os.listdir(outdir):
148
+ if name.endswith(".js"):
149
+ chosen = os.path.join(outdir, name)
150
+ break
151
+ if not chosen:
152
+ shutil.rmtree(workdir, ignore_errors=True)
153
+ return False
154
+ target = out_path or src_path
155
+ shutil.copyfile(chosen, target)
156
+ shutil.rmtree(workdir, ignore_errors=True)
157
+ return True
158
+ except Exception:
159
+ return False
@@ -1,7 +1,13 @@
1
1
  import os
2
2
  import re
3
3
  from typing import Iterable, Set
4
- from dars.core.js_bridge import esbuild_minify_js as _esbuild_minify_js, esbuild_minify_css as _esbuild_minify_css, esbuild_available as _esbuild_available
4
+ from dars.core.js_bridge import (
5
+ esbuild_minify_js as _esbuild_minify_js,
6
+ esbuild_minify_css as _esbuild_minify_css,
7
+ esbuild_available as _esbuild_available,
8
+ vite_minify_js as _vite_minify_js,
9
+ vite_available as _vite_available,
10
+ )
5
11
 
6
12
  SAFE_JS_EXT = {'.js', '.mjs', '.cjs'}
7
13
  SAFE_CSS_EXT = {'.css'}
@@ -40,8 +46,9 @@ _js_string_splitter = re.compile(r'(".*?"|\'.*?\'|`.*?`)', re.DOTALL)
40
46
 
41
47
  def minify_js(src: str) -> str:
42
48
  """Minify a JS source string. Uses esbuild if available; otherwise Python fallback."""
43
- # Fast path: dump to temp file and use esbuild when available
44
- if _esbuild_available():
49
+ # Fast path: dump to temp file and use Vite/esbuild when available
50
+ _vite_enabled = os.getenv('DARS_VITE_MINIFY', '1') == '1'
51
+ if (_vite_enabled and _vite_available()) or _esbuild_available():
45
52
  try:
46
53
  import tempfile
47
54
  with tempfile.NamedTemporaryFile('w', delete=False, suffix='.js', encoding='utf-8') as tf_in:
@@ -49,7 +56,12 @@ def minify_js(src: str) -> str:
49
56
  in_path = tf_in.name
50
57
  with tempfile.NamedTemporaryFile('r', delete=False, suffix='.js', encoding='utf-8') as tf_out:
51
58
  out_path = tf_out.name
52
- if _esbuild_minify_js(in_path, out_path):
59
+ ok = False
60
+ if _vite_enabled and _vite_available():
61
+ ok = _vite_minify_js(in_path, out_path)
62
+ if not ok and _esbuild_available():
63
+ ok = _esbuild_minify_js(in_path, out_path)
64
+ if ok:
53
65
  try:
54
66
  with open(out_path, 'r', encoding='utf-8') as fr:
55
67
  return fr.read()
@@ -1,2 +1,2 @@
1
- __version__ = "1.2.3"
2
- __release_url__ = "https://github.com/ZtaMDev/Dars-Framework/releases/tag/1.2.3"
1
+ __version__ = "1.2.4"
2
+ __release_url__ = "https://github.com/ZtaMDev/Dars-Framework/releases/tag/1.2.4"