flet-box 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +190 -2
- package/bin/cli.js +394 -0
- package/bin/commands/buildAndroid.js +200 -0
- package/bin/commands/buildIOS.js +237 -0
- package/bin/commands/create.js +406 -0
- package/bin/commands/createBundle.js +375 -0
- package/bin/commands/createComponent.js +115 -0
- package/bin/commands/createScreen.js +161 -0
- package/bin/commands/killServer.js +107 -0
- package/bin/commands/package.js +131 -0
- package/bin/commands/release.js +189 -0
- package/bin/commands/runBundle.js +62 -0
- package/bin/commands/runCloudflare.js +89 -0
- package/bin/commands/runNative.js +109 -0
- package/bin/commands/runServer.js +390 -0
- package/bin/ota/loader.js +361 -0
- package/bin/package.json +45 -0
- package/bin/utils/bundleConfig.js +178 -0
- package/bin/utils/capacitor.js +137 -0
- package/bin/utils/colors.js +215 -0
- package/bin/utils/helpers.js +80 -0
- package/bin/utils/spinner.js +96 -0
- package/bin/utils/templates.js +1823 -0
- package/bin/utils/version.js +108 -0
- package/package.json +54 -4
- package/src/animations/AnimatedBox.js +607 -0
- package/src/animations/AnimatedText.js +105 -0
- package/src/animations/MatrixRain.js +220 -0
- package/src/animations/ParallaxBox.js +369 -0
- package/src/animations/animate.js +332 -0
- package/src/animations/index.js +25 -0
- package/src/app.js +41 -0
- package/src/assets/icon-1024-maskable.png +0 -0
- package/src/assets/icon-192-maskable.png +0 -0
- package/src/assets/icon-192.png +0 -0
- package/src/assets/icon-512-maskable.png +0 -0
- package/src/assets/icon-512.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/App.js +44 -0
- package/src/core/hmr-client.js +142 -0
- package/src/core/index.js +34 -0
- package/src/core/pwa.js +100 -0
- package/src/core/runApp.js +430 -0
- package/src/create-index.sh +237 -0
- package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
- package/src/fonts/icons.css +23 -0
- package/src/index.d.ts +1930 -0
- package/src/index.js +288 -0
- package/src/navigations/AdaptiveScaffold.js +149 -0
- package/src/navigations/AppBar.js +331 -0
- package/src/navigations/BottomNavigation.js +298 -0
- package/src/navigations/CollapsibleSideBar.js +139 -0
- package/src/navigations/Drawer.js +333 -0
- package/src/navigations/DrawerItem.js +260 -0
- package/src/navigations/Router.js +475 -0
- package/src/navigations/Scaffold.js +537 -0
- package/src/navigations/Tabs.js +384 -0
- package/src/navigations/index.js +57 -0
- package/src/services/RamStore.js +213 -0
- package/src/services/Session.js +310 -0
- package/src/services/Storage.js +309 -0
- package/src/services/http.js +350 -0
- package/src/services/index.js +57 -0
- package/src/tools/animation.js +42 -0
- package/src/tools/array.js +95 -0
- package/src/tools/border.js +28 -0
- package/src/tools/clipboard.js +104 -0
- package/src/tools/color.js +329 -0
- package/src/tools/createList.js +215 -0
- package/src/tools/delay.js +55 -0
- package/src/tools/device.js +138 -0
- package/src/tools/dict.js +297 -0
- package/src/tools/dimensions.js +118 -0
- package/src/tools/filter.js +41 -0
- package/src/tools/flex.js +26 -0
- package/src/tools/gradient.js +34 -0
- package/src/tools/grid.js +57 -0
- package/src/tools/index.js +38 -0
- package/src/tools/mapList.js +107 -0
- package/src/tools/margin.js +93 -0
- package/src/tools/memo.js +60 -0
- package/src/tools/os.js +104 -0
- package/src/tools/padding.js +93 -0
- package/src/tools/print.js +69 -0
- package/src/tools/random.js +299 -0
- package/src/tools/ref.js +70 -0
- package/src/tools/rgba.js +4 -0
- package/src/tools/shadow.js +23 -0
- package/src/tools/string.js +85 -0
- package/src/tools/time.js +674 -0
- package/src/tools/transform.js +68 -0
- package/src/tools/transition.js +48 -0
- package/src/tools/useState.js +268 -0
- package/src/tools/uuid.js +42 -0
- package/src/utils/TextInputValidator.js +256 -0
- package/src/utils/composeUpdate.js +65 -0
- package/src/utils/getWidgetProps.js +35 -0
- package/src/utils/index.js +76 -0
- package/src/utils/markdownParser.js +368 -0
- package/src/utils/mediaTime.js +79 -0
- package/src/utils/navigation.js +263 -0
- package/src/utils/scriptInjector.js +73 -0
- package/src/utils/stackPosition.js +31 -0
- package/src/utils/stopWebRefresh.js +77 -0
- package/src/utils/styleInjector.js +38 -0
- package/src/utils/syntaxHighlight.js +339 -0
- package/src/utils/themes.js +347 -0
- package/src/utils/units.js +87 -0
- package/src/utils/visualEffects.js +169 -0
- package/src/widget-factory/addChildren.js +114 -0
- package/src/widget-factory/assignProps.js +256 -0
- package/src/widget-factory/createWidget.js +28 -0
- package/src/widget-factory/effects.js +132 -0
- package/src/widget-factory/index.js +7 -0
- package/src/widget-factory/lifecycle.js +210 -0
- package/src/widget-factory/makeParentable.js +89 -0
- package/src/widget-factory/pixTool.js +40 -0
- package/src/widget-factory/processProps.js +122 -0
- package/src/widget-factory/reactivity.js +98 -0
- package/src/widget-factory/remTool.js +34 -0
- package/src/widget-factory/stackPosition.js +87 -0
- package/src/widget-factory/stringTool.js +26 -0
- package/src/widget-factory/tools.js +249 -0
- package/src/widget-factory/translateProps.js +1049 -0
- package/src/widget-factory/warnings.js +284 -0
- package/src/widget-factory/widgetFactory.js +200 -0
- package/src/widgets/Accordion.js +562 -0
- package/src/widgets/AdMob.js +315 -0
- package/src/widgets/AdSense.js +268 -0
- package/src/widgets/AlertDialog.js +162 -0
- package/src/widgets/Audio.js +311 -0
- package/src/widgets/Avatar.js +210 -0
- package/src/widgets/Badge.js +189 -0
- package/src/widgets/BottomSheet.js +287 -0
- package/src/widgets/Button.js +247 -0
- package/src/widgets/Card.js +33 -0
- package/src/widgets/Carousel.js +301 -0
- package/src/widgets/Chart.js +632 -0
- package/src/widgets/Checkbox.js +106 -0
- package/src/widgets/Chip.js +153 -0
- package/src/widgets/CircularBar.js +352 -0
- package/src/widgets/CircularChart.js +549 -0
- package/src/widgets/CodeViewer.js +236 -0
- package/src/widgets/Column.js +29 -0
- package/src/widgets/Container.js +31 -0
- package/src/widgets/DataTable.js +257 -0
- package/src/widgets/Divider.js +93 -0
- package/src/widgets/DraggBox.js +115 -0
- package/src/widgets/Dropdown.js +505 -0
- package/src/widgets/DroppBox.js +180 -0
- package/src/widgets/FloatingActionButton.js +91 -0
- package/src/widgets/GridView.js +20 -0
- package/src/widgets/Icon.js +33 -0
- package/src/widgets/Image.js +15 -0
- package/src/widgets/Input.js +565 -0
- package/src/widgets/Inspector.js +254 -0
- package/src/widgets/InstallButton.js +110 -0
- package/src/widgets/ListTile.js +224 -0
- package/src/widgets/ListView.js +503 -0
- package/src/widgets/Markdown.js +1236 -0
- package/src/widgets/Modal.js +496 -0
- package/src/widgets/Pagination.js +195 -0
- package/src/widgets/ProgressBar.js +178 -0
- package/src/widgets/QRCode.js +467 -0
- package/src/widgets/Radio.js +132 -0
- package/src/widgets/Rating.js +261 -0
- package/src/widgets/Row.js +29 -0
- package/src/widgets/Skeleton.js +243 -0
- package/src/widgets/Slider.js +527 -0
- package/src/widgets/SnackBar.js +227 -0
- package/src/widgets/Stack.js +29 -0
- package/src/widgets/Stepper.js +296 -0
- package/src/widgets/Switch.js +102 -0
- package/src/widgets/Text.js +98 -0
- package/src/widgets/Tooltip.js +267 -0
- package/src/widgets/TreeView.js +370 -0
- package/src/widgets/Video.js +92 -0
- package/src/widgets/index.js +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maenys Javier Quesada Reyes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,191 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="src/assets/logo.png" alt="FletBox" width="110" />
|
|
4
|
+
|
|
5
|
+
<h1>FletBox</h1>
|
|
6
|
+
|
|
7
|
+
<b>Ship web, PWA, Android & iOS from one codebase — with zero runtime dependencies.</b><br>
|
|
8
|
+
A declarative, Flutter/Flet-like UI framework in plain vanilla JavaScript.<br>
|
|
9
|
+
Real DOM · No Virtual DOM · No build config to learn.
|
|
10
|
+
|
|
11
|
+
<br>
|
|
12
|
+
|
|
13
|
+
<a href="https://github.com/sponsors/kuko53348"><img src="https://img.shields.io/badge/%E2%9D%A4%20Sponsor-FletBox-ff69b4?style=for-the-badge&logo=githubsponsors" alt="Sponsor FletBox"></a>
|
|
14
|
+
<a href="https://www.youtube.com/@flet-box"><img src="https://img.shields.io/badge/YouTube-@flet--box-red?style=for-the-badge&logo=youtube" alt="YouTube @flet-box"></a>
|
|
15
|
+
|
|
16
|
+
<br><br>
|
|
17
|
+
|
|
18
|
+
<a href="https://github.com/kuko53348/flet-box/blob/HEAD/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License: MIT"></a>
|
|
19
|
+
<img src="https://img.shields.io/badge/dependencies-0-brightgreen?style=flat-square" alt="Zero dependencies">
|
|
20
|
+
<img src="https://img.shields.io/badge/platforms-web%20%7C%20PWA%20%7C%20android%20%7C%20ios-007acc?style=flat-square" alt="Platforms">
|
|
21
|
+
<a href="https://github.com/kuko53348/flet-box"><img src="https://img.shields.io/github/stars/kuko53348/flet-box?style=social" alt="GitHub stars"></a>
|
|
22
|
+
|
|
23
|
+
<br><br>
|
|
24
|
+
|
|
25
|
+
<a href="https://kuko53348.github.io/flet-box-docs/">📖 Docs</a> •
|
|
26
|
+
<a href="#quick-start">Quick start</a> •
|
|
27
|
+
<a href="https://www.youtube.com/@flet-box">▶ YouTube</a> •
|
|
28
|
+
<a href="#support--sponsorship">❤️ Sponsor</a>
|
|
29
|
+
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
**Stop paying the framework tax.** FletBox has no runtime dependencies, no Virtual DOM, and nothing to install beyond Node. Write widgets in the syntax you already know — plain JavaScript — and deploy to browser, PWA, Android, and iOS from the same source.
|
|
35
|
+
|
|
36
|
+
> ▶ **Learn it on YouTube:** [@flet-box](https://www.youtube.com/@flet-box) — tutorials, walkthroughs, and new widgets.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
FletBox isn't on the npm registry, so clone it and link the CLI locally:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
brew install node # macOS/Linux: ensure Node >= 18
|
|
44
|
+
|
|
45
|
+
git clone https://github.com/kuko53348/flet-box.git
|
|
46
|
+
cd flet-box
|
|
47
|
+
npm link # link the flet-box CLI globally
|
|
48
|
+
|
|
49
|
+
flet-box create appName # scaffold your first app
|
|
50
|
+
cd appName
|
|
51
|
+
npm link flet-box # link FletBox into the new project
|
|
52
|
+
flet-box run-spa # run as SPA with hot reload
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
import { runApp, Container, Text, Button, useState } from "flet-box";
|
|
59
|
+
|
|
60
|
+
const App = () => {
|
|
61
|
+
const [count, setCount] = useState("count", 0);
|
|
62
|
+
|
|
63
|
+
return Container({
|
|
64
|
+
width: "100%",
|
|
65
|
+
minHeight: "100vh",
|
|
66
|
+
display: "flex",
|
|
67
|
+
justifyContent: "center",
|
|
68
|
+
alignItems: "center",
|
|
69
|
+
child: [
|
|
70
|
+
Text({ text: `Counter: ${count}`, fontSize: 24, fontWeight: 700 }),
|
|
71
|
+
Button({ text: "Increment", onClick: () => setCount((v) => v + 1) }),
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
runApp(App);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Features
|
|
80
|
+
|
|
81
|
+
- Declarative, widget-based UI · reactive state with `useState` · router for SPAs
|
|
82
|
+
- Zero runtime dependencies; ESM only, Node >= 18 · real DOM, no Virtual DOM
|
|
83
|
+
- Local storage, HTTP services, theme, PWA, and production builds
|
|
84
|
+
- Android & iOS from the CLI (APK, simulator `.app`, `.xcarchive`)
|
|
85
|
+
- Utility API for layout, text, color, animation, and more
|
|
86
|
+
|
|
87
|
+
## Commands
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm run dev # dev server on port 8000
|
|
91
|
+
npm run build # production bundle with esbuild
|
|
92
|
+
|
|
93
|
+
# inside a generated project
|
|
94
|
+
flet-box createBundle www # bundle the app into www/
|
|
95
|
+
flet-box build android # Android debug APK
|
|
96
|
+
flet-box build ios # iOS Simulator app (macOS + Xcode)
|
|
97
|
+
flet-box build ios --archive # unsigned iOS archive, sign in Xcode
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Android and iOS
|
|
101
|
+
|
|
102
|
+
`flet-box create` ships the Capacitor config, so the same SPA builds for both platforms. FletBox isn't on the npm registry — link it before installing:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
flet-box create my-app && cd my-app
|
|
106
|
+
npm link flet-box # or: npm install ../path/to/flet-box
|
|
107
|
+
npm install # Capacitor CLI, sharp, platform package
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- **Android** — Node >= 20.9, a JDK, and the Android SDK. `flet-box build android` → debug APK; `flet-box build android --release [--aab]` → unsigned release artifact to sign yourself (`apksigner` / Play App Signing).
|
|
111
|
+
- **iOS** — macOS, the **full** Xcode app (not Command Line Tools), a simulator runtime, and CocoaPods. `flet-box build ios` → simulator; `flet-box build ios --archive` → sign in Xcode.
|
|
112
|
+
- Drop a 1024×1024 icon at `assets/logo.png` and rebuild — launcher icons, launch images, and the launch screen regenerate automatically.
|
|
113
|
+
- Capacitor serves from `https://localhost`, so a remote API must allow that origin in CORS.
|
|
114
|
+
|
|
115
|
+
Full guide, including manual `npx cap` workflows: [Mobile and platforms](docs/guides/mobile-and-platforms.md).
|
|
116
|
+
|
|
117
|
+
## Documentation
|
|
118
|
+
|
|
119
|
+
Read the docs online: **[The FletBox Book — kuko53348.github.io/flet-box-docs](https://kuko53348.github.io/flet-box-docs/)**.
|
|
120
|
+
|
|
121
|
+
They are **one continuous book** — the cover maps every page: [The FletBox Book source](docs/README.md).
|
|
122
|
+
|
|
123
|
+
- [Your first app](docs/guides/first-app.md) — build a working todo app in 20 minutes
|
|
124
|
+
- [The widget book](docs/widget/README.md) — every widget with property tables and examples
|
|
125
|
+
- [State, Router & Services](docs/guides/state.md) — shared state, routing, storage, and HTTP
|
|
126
|
+
- [Tools & Utilities](docs/tools/README.md) — every helper function with signatures
|
|
127
|
+
- [The CLI](docs/cli/README.md) — create, run, and build projects
|
|
128
|
+
- [FletBox Server](docs/server/README.md) — backend toolkit: API, auth, security, data services
|
|
129
|
+
- [SQLite and the API server](docs/server/data-services.md) — where your data lives + a full SQLite API example
|
|
130
|
+
- [Mobile and platforms](docs/guides/mobile-and-platforms.md) — Android, iOS, PWA, and desktop
|
|
131
|
+
- [Architecture](docs/arquitectura.md) — the widget contract and the single rendering engine
|
|
132
|
+
- [Contributing](docs/CONTRIBUTING.md) — how to add code and docs
|
|
133
|
+
|
|
134
|
+
## Support & sponsorship
|
|
135
|
+
|
|
136
|
+
<div align="center">
|
|
137
|
+
|
|
138
|
+
<a href="https://github.com/sponsors/kuko53348">
|
|
139
|
+
<img src="https://img.shields.io/badge/%E2%9D%A4%20Become_a_Sponsor-Support_FletBox-ff69b4?style=for-the-badge&logo=githubsponsors" alt="Become a Sponsor">
|
|
140
|
+
</a>
|
|
141
|
+
|
|
142
|
+
<br><br>
|
|
143
|
+
|
|
144
|
+
**FletBox is MIT-licensed and free forever — for individuals and companies alike.**
|
|
145
|
+
|
|
146
|
+
Zero dependencies. One developer. No corporate budget. Sponsoring is what keeps
|
|
147
|
+
it independent — and what turns "nights and weekends" into a framework you can
|
|
148
|
+
build a business on.
|
|
149
|
+
|
|
150
|
+
### ► [Sponsor FletBox on GitHub](https://github.com/sponsors/kuko53348) ◄
|
|
151
|
+
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
| 💎 Sponsor tier | What you get |
|
|
155
|
+
|---|---|
|
|
156
|
+
| ☕ **$3 / mo** — Coffee | My thanks + the good karma of keeping open source free |
|
|
157
|
+
| 🚀 **$10 / mo** — Supporter | Shout-out in the Sponsors wall + priority on issue replies |
|
|
158
|
+
| 🏢 **$50 / mo** — Backer | Your name/logo in this README & docs + feature-request voting |
|
|
159
|
+
| 🤝 **Custom** — Partner | Logo + link, priority support, and a say in the roadmap — [email me](mailto:kuko53348@gmail.com) |
|
|
160
|
+
|
|
161
|
+
Your sponsorship funds **new widgets, the full docs book, test coverage, the mobile toolchain, and the time to ship it all.** One-time contributions welcome on the [sponsorship page](https://github.com/sponsors/kuko53348).
|
|
162
|
+
|
|
163
|
+
### Other ways to support
|
|
164
|
+
|
|
165
|
+
- 💳 **Crypto** (Polygon / MATIC-POL): `0x6d437bB66af8d2c44670eA18F059BE1417Dcd7bA`
|
|
166
|
+
- 💼 **Commercial support, consulting, or priority help:** [kuko53348@gmail.com](mailto:kuko53348@gmail.com)
|
|
167
|
+
- ▶ **Subscribe on YouTube:** [@flet-box](https://www.youtube.com/@flet-box)
|
|
168
|
+
- ⭐ **Star the repo** and tell a colleague — visibility is free and it helps enormously
|
|
169
|
+
|
|
170
|
+
<sub>Living in Cuba makes platforms like Mastercard hard to access, so crypto support is especially valuable. Every contribution — a sponsor tier, a donation, a star, or spreading the word — keeps FletBox growing. Thank you. 🙏</sub>
|
|
171
|
+
|
|
172
|
+
### ❤️ Our sponsors
|
|
173
|
+
|
|
174
|
+
<!-- Add sponsor logos/links here as they come in. Example:
|
|
175
|
+
<a href="https://github.com/their-username"><img src="https://github.com/their-username.png" width="60" height="60" alt="@their-username"></a>
|
|
176
|
+
-->
|
|
177
|
+
|
|
178
|
+
<div align="center">
|
|
179
|
+
|
|
180
|
+
_This space is empty — **be the first!**_
|
|
181
|
+
|
|
182
|
+
<a href="https://github.com/sponsors/kuko53348">
|
|
183
|
+
<img src="https://img.shields.io/badge/Your_name_here-Sponsor_FletBox-ff69b4?style=for-the-badge" alt="Your name here">
|
|
184
|
+
</a>
|
|
185
|
+
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT — free to use, modify, redistribute, and build commercial products with.
|
|
191
|
+
See [LICENSE](LICENSE). Copyright (c) 2026 Maenys Javier Quesada Reyes.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file bin/cli.js
|
|
5
|
+
* @description FletBox CLI entry point with hot reload support.
|
|
6
|
+
*
|
|
7
|
+
* Parses process.argv, sets up the global Ctrl+C handler, and dispatches
|
|
8
|
+
* to the appropriate command handler. All commands are lazy-imported so
|
|
9
|
+
* startup stays fast even on slow disks.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createRequire } from "module";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import path from "path";
|
|
15
|
+
import { createProject } from "./commands/create.js";
|
|
16
|
+
import { packageManager } from "./commands/package.js";
|
|
17
|
+
import { createBundle } from "./commands/createBundle.js";
|
|
18
|
+
import { buildAndroid } from "./commands/buildAndroid.js";
|
|
19
|
+
import { buildIOS } from "./commands/buildIOS.js";
|
|
20
|
+
import { runNative } from "./commands/runNative.js";
|
|
21
|
+
import { runDevServer } from "./commands/runServer.js";
|
|
22
|
+
import { createScreen } from "./commands/createScreen.js";
|
|
23
|
+
import { createComponent } from "./commands/createComponent.js";
|
|
24
|
+
import { killServer } from "./commands/killServer.js";
|
|
25
|
+
import { runBundle } from "./commands/runBundle.js";
|
|
26
|
+
import { runCloudflareDemo } from "./commands/runCloudflare.js";
|
|
27
|
+
import { release } from "./commands/release.js";
|
|
28
|
+
import { c, gradient, section, banner, divider, width, setupCtrlC } from "./utils/colors.js";
|
|
29
|
+
|
|
30
|
+
const require = createRequire(import.meta.url);
|
|
31
|
+
const { version } = require("../package.json");
|
|
32
|
+
|
|
33
|
+
// Register the global Ctrl+C handler early so every command benefits from it.
|
|
34
|
+
setupCtrlC();
|
|
35
|
+
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
const command = args[0];
|
|
38
|
+
const projectName = args[1];
|
|
39
|
+
|
|
40
|
+
// Clear the terminal only when attached to an interactive session to avoid
|
|
41
|
+
// polluting piped output (e.g. CI logs).
|
|
42
|
+
if (process.stdout.isTTY) {
|
|
43
|
+
console.clear();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Prints the full help message, including all available commands and options.
|
|
48
|
+
* Rendered with gradient colors when the terminal supports truecolor.
|
|
49
|
+
*
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
52
|
+
const showHelp = () => {
|
|
53
|
+
const cmd = (name, desc, alias = "") =>
|
|
54
|
+
` ${c("bold", gradient(name, "#22d3ee", "#6366f1"))}${" ".repeat(Math.max(1, 22 - width(name)))}${c("gray", desc)}${alias ? c("dim", " (" + alias + ")") : ""}`;
|
|
55
|
+
|
|
56
|
+
const opt = (name, desc) =>
|
|
57
|
+
` ${c("brightCyan", name)}${" ".repeat(Math.max(1, 24 - name.length))}${c("gray", desc)}`;
|
|
58
|
+
|
|
59
|
+
console.log(`
|
|
60
|
+
${banner("🚀 FletBox CLI", `v${version}`)}
|
|
61
|
+
|
|
62
|
+
${section("🧭", "COMMANDS")}
|
|
63
|
+
${cmd("create <name>", "Create a new project")}
|
|
64
|
+
${cmd("create --adaptive", "responsive (mobile + desktop)")}
|
|
65
|
+
${cmd("create --blank", "empty project")}
|
|
66
|
+
${cmd("create --full", "AppBar+Drawer+BottomNav")}
|
|
67
|
+
${cmd("create --sidebar", "desktop (Sidebar+AppBar)")}
|
|
68
|
+
${cmd("create --repo <user/repo>", "set GitHub repo for OTA updates")}
|
|
69
|
+
${cmd("screen <name|num>", "Create screen(s) (1-10)")}
|
|
70
|
+
${cmd("component <name|num>", "Create component(s) (1-10)")}
|
|
71
|
+
${cmd("createBundle [dir]", "Unified production bundle")}
|
|
72
|
+
${cmd(" └─ [dir]", "target folder (default www)")}
|
|
73
|
+
${cmd("release [--patch]", "Bump version, rebuild, tag (OTA release)")}
|
|
74
|
+
${cmd(" └─ --minor|--major", "bump that position instead of patch")}
|
|
75
|
+
${cmd(" └─ --push", "push the commit + tag to origin")}
|
|
76
|
+
${cmd("build android", "Build Android debug APK")}
|
|
77
|
+
${cmd("build android --release", "Build unsigned release APK/AAB to sign yourself")}
|
|
78
|
+
${cmd("build ios", "Build iOS Simulator app (macOS + Xcode)")}
|
|
79
|
+
${cmd("build ios --archive", "Build unsigned device archive (.xcarchive)")}
|
|
80
|
+
${cmd("run-native <android|ios>", "Compile & launch on emulator/device")}
|
|
81
|
+
${cmd(" └─ --device", "use a physical device instead (alias: rn)")}
|
|
82
|
+
${cmd(" └─ --livereload", "hot reload on the device")}
|
|
83
|
+
${cmd("run", "Static file server")}
|
|
84
|
+
${cmd("run --hot", "Dev server with hot reload")}
|
|
85
|
+
${cmd("run-spa", "SPA + hot reload")}
|
|
86
|
+
${cmd("cloudflare", "Serve demo on a public Cloudflare HTTPS tunnel")}
|
|
87
|
+
${cmd(" └─ alias", "cf | demo | tunnel")}
|
|
88
|
+
${cmd("run-bundle [dir]", "Build & serve the bundle (single app.js)")}
|
|
89
|
+
${cmd("pkg", "Open the package manager")}
|
|
90
|
+
${cmd("kill-server [port]", "Kill servers on a port")}
|
|
91
|
+
${cmd("--version | --help", "Version and this help")}
|
|
92
|
+
|
|
93
|
+
${section("⚙️", "OPTIONS")}
|
|
94
|
+
${opt("--port <n>, -p <n>", "Port (default 8000) — asked if not provided")}
|
|
95
|
+
${opt("--hot", "Hot reload with run")}
|
|
96
|
+
${opt("--quiet", "No request logging")}
|
|
97
|
+
${opt("--dry-run | --list", "kill-server: just list, do not kill")}
|
|
98
|
+
|
|
99
|
+
${section("✨", "EXAMPLES")}
|
|
100
|
+
${c("gray", "flet-box create my-app --adaptive")}
|
|
101
|
+
${c("gray", "flet-box build android # create a debug APK")}
|
|
102
|
+
${c("gray", "flet-box build android --release --aab # unsigned .aab for Play")}
|
|
103
|
+
${c("gray", "flet-box build ios # create an iOS Simulator app")}
|
|
104
|
+
${c("gray", "flet-box build ios --archive # create an .xcarchive to sign in Xcode")}
|
|
105
|
+
${c("gray", "flet-box run-native android # build & launch on an emulator")}
|
|
106
|
+
${c("gray", "flet-box run-native ios --device --livereload # hot reload on iPhone")}
|
|
107
|
+
${c("gray", "cd my-app && flet-box run-spa")}
|
|
108
|
+
${c("gray", "flet-box run-bundle # build & preview production")}
|
|
109
|
+
${c("gray", "flet-box cloudflare # public HTTPS demo link in console")}
|
|
110
|
+
${c("gray", "flet-box kill-server 8000 # kill the parasite server")}
|
|
111
|
+
|
|
112
|
+
${divider("", "#3730a3")}
|
|
113
|
+
${c("dim", " FletBox — ultra-light vanilla-JS UI framework")}
|
|
114
|
+
`);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Parses the CLI flags from `process.argv`.
|
|
119
|
+
*
|
|
120
|
+
* Recognised flags:
|
|
121
|
+
* - `--port <n>` / `-p <n>` — override the default port (8000).
|
|
122
|
+
* - `--quiet` — suppress per-request HTTP logging.
|
|
123
|
+
* - `--hot` / `--hot-reload`— enable hot-module-replacement.
|
|
124
|
+
* - `--dry-run` / `--list` / `--check` — list-only mode for `kill-server`.
|
|
125
|
+
*
|
|
126
|
+
* Invalid port values produce a warning and fall back to the default.
|
|
127
|
+
*
|
|
128
|
+
* @returns {{ port: number, portExplicit: boolean, logRequests: boolean, hot: boolean, dryRun: boolean }}
|
|
129
|
+
*/
|
|
130
|
+
const parseFlags = () => {
|
|
131
|
+
const flags = {
|
|
132
|
+
port: 8000,
|
|
133
|
+
portExplicit: false,
|
|
134
|
+
logRequests: true,
|
|
135
|
+
hot: false,
|
|
136
|
+
dryRun: false,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < args.length; i++) {
|
|
140
|
+
const arg = args[i];
|
|
141
|
+
|
|
142
|
+
if ((arg === "--port" || arg === "-p") && args[i + 1]) {
|
|
143
|
+
const p = Number(args[i + 1]);
|
|
144
|
+
if (Number.isInteger(p) && p > 0 && p < 65536) {
|
|
145
|
+
flags.port = p;
|
|
146
|
+
flags.portExplicit = true;
|
|
147
|
+
} else {
|
|
148
|
+
console.warn(
|
|
149
|
+
c("yellow", `⚠️ Invalid port "${args[i + 1]}", using ${flags.port}`),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
i++;
|
|
153
|
+
} else if (arg === "--quiet") {
|
|
154
|
+
flags.logRequests = false;
|
|
155
|
+
} else if (arg === "--hot" || arg === "--hot-reload") {
|
|
156
|
+
flags.hot = true;
|
|
157
|
+
} else if (arg === "--dry-run" || arg === "--list" || arg === "--check") {
|
|
158
|
+
flags.dryRun = true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return flags;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Guards commands that must run inside a FletBox project directory.
|
|
167
|
+
* Exits with a descriptive error when `src/app.js` is not found in the
|
|
168
|
+
* current working directory — this prevents confusing failures later.
|
|
169
|
+
*
|
|
170
|
+
* @returns {void}
|
|
171
|
+
*/
|
|
172
|
+
const ensureProject = () => {
|
|
173
|
+
const inProject = fs.existsSync(path.join(process.cwd(), "src", "app.js"));
|
|
174
|
+
if (!inProject) {
|
|
175
|
+
console.error(c("red", "❌ Not inside a FletBox project"));
|
|
176
|
+
console.log(
|
|
177
|
+
c(
|
|
178
|
+
"gray",
|
|
179
|
+
"Run this inside a project folder with src/app.js (create one with: flet-box create <name>)",
|
|
180
|
+
),
|
|
181
|
+
);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Main async entry point. Parses flags, then delegates to the matching
|
|
188
|
+
* command handler via a `switch` on the first positional argument.
|
|
189
|
+
*
|
|
190
|
+
* Unrecognised commands print the help message and exit with code 1.
|
|
191
|
+
*
|
|
192
|
+
* @async
|
|
193
|
+
* @returns {Promise<void>}
|
|
194
|
+
*/
|
|
195
|
+
async function main() {
|
|
196
|
+
try {
|
|
197
|
+
const flags = parseFlags();
|
|
198
|
+
|
|
199
|
+
switch (command) {
|
|
200
|
+
case "create":
|
|
201
|
+
case "new":
|
|
202
|
+
if (!projectName) {
|
|
203
|
+
console.error(c("red", "❌ Error: Project name required"));
|
|
204
|
+
console.log(
|
|
205
|
+
c(
|
|
206
|
+
"gray",
|
|
207
|
+
"Usage: flet-box create <project-name> [--adaptive|--blank|--full|--sidebar|--repo user/repo]",
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
if (!/^[a-z0-9-]+$/i.test(projectName)) {
|
|
213
|
+
console.error(
|
|
214
|
+
c(
|
|
215
|
+
"red",
|
|
216
|
+
"❌ Error: Invalid project name. Use only letters, numbers, and hyphens.",
|
|
217
|
+
),
|
|
218
|
+
);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
// Forward all remaining args (template flags like --adaptive) to createProject.
|
|
222
|
+
await createProject(projectName, args.slice(2));
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case "screen":
|
|
226
|
+
case "screens": {
|
|
227
|
+
ensureProject();
|
|
228
|
+
const screenInput = args[1];
|
|
229
|
+
if (!screenInput) {
|
|
230
|
+
console.error(c("red", "❌ Error: Screen name or number required"));
|
|
231
|
+
console.log(c("gray", "Usage: flet-box screen <name>"));
|
|
232
|
+
console.log(c("gray", " flet-box screen <number> (1-10)"));
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
235
|
+
await createScreen(screenInput);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
case "component":
|
|
240
|
+
case "comp": {
|
|
241
|
+
ensureProject();
|
|
242
|
+
const componentInput = args[1];
|
|
243
|
+
if (!componentInput) {
|
|
244
|
+
console.error(
|
|
245
|
+
c("red", "❌ Error: Component name or number required"),
|
|
246
|
+
);
|
|
247
|
+
console.log(c("gray", "Usage: flet-box component <name>"));
|
|
248
|
+
console.log(c("gray", " flet-box component <number> (1-10)"));
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
await createComponent(componentInput);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
case "createBundle":
|
|
256
|
+
case "bundle":
|
|
257
|
+
await createBundle(args[1]);
|
|
258
|
+
break;
|
|
259
|
+
|
|
260
|
+
case "release":
|
|
261
|
+
ensureProject();
|
|
262
|
+
await release(args.slice(1));
|
|
263
|
+
break;
|
|
264
|
+
|
|
265
|
+
case "build":
|
|
266
|
+
if (args[1] === "android") {
|
|
267
|
+
buildAndroid(args.slice(2));
|
|
268
|
+
} else if (args[1] === "ios") {
|
|
269
|
+
buildIOS(args.slice(2));
|
|
270
|
+
} else {
|
|
271
|
+
await createBundle(args[1]);
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
case "run-native":
|
|
276
|
+
case "runNative":
|
|
277
|
+
case "rn":
|
|
278
|
+
runNative(args.slice(1));
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
case "pkg":
|
|
282
|
+
case "package":
|
|
283
|
+
case "manager":
|
|
284
|
+
await packageManager();
|
|
285
|
+
break;
|
|
286
|
+
|
|
287
|
+
case "kill-server":
|
|
288
|
+
case "kill":
|
|
289
|
+
case "killServer":
|
|
290
|
+
case "killsrv":
|
|
291
|
+
await killServer(
|
|
292
|
+
args[1] && /^\d+$/.test(args[1]) ? args[1] : flags.port,
|
|
293
|
+
{ dryRun: flags.dryRun },
|
|
294
|
+
);
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case "run":
|
|
298
|
+
case "dev":
|
|
299
|
+
case "serve":
|
|
300
|
+
ensureProject();
|
|
301
|
+
if (args[1] === "android" || args[1] === "ios") {
|
|
302
|
+
console.log(
|
|
303
|
+
c(
|
|
304
|
+
"dim",
|
|
305
|
+
`ℹ️ \`flet-box run ${args[1]}\` launches the native app — see \`flet-box run-native ${args[1]}\`.`,
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
runNative(args.slice(1));
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
await runDevServer({
|
|
312
|
+
spaMode: false,
|
|
313
|
+
hotReload: flags.hot,
|
|
314
|
+
port: flags.port,
|
|
315
|
+
askPort: !flags.portExplicit,
|
|
316
|
+
logRequests: flags.logRequests,
|
|
317
|
+
});
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case "run-bundle":
|
|
321
|
+
case "runBundle":
|
|
322
|
+
case "preview": {
|
|
323
|
+
ensureProject();
|
|
324
|
+
// Accept an explicit target directory as the second argument, but reject
|
|
325
|
+
// anything that looks like a flag (starts with "-") to avoid misparses.
|
|
326
|
+
const targetDir =
|
|
327
|
+
args[1] &&
|
|
328
|
+
/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/.test(args[1]) &&
|
|
329
|
+
!args[1].startsWith("-")
|
|
330
|
+
? args[1]
|
|
331
|
+
: "www";
|
|
332
|
+
await runBundle({
|
|
333
|
+
target: targetDir,
|
|
334
|
+
port: flags.port,
|
|
335
|
+
askPort: !flags.portExplicit,
|
|
336
|
+
logRequests: flags.logRequests,
|
|
337
|
+
});
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
case "run-spa":
|
|
342
|
+
case "runSpa":
|
|
343
|
+
case "spa":
|
|
344
|
+
ensureProject();
|
|
345
|
+
await runDevServer({
|
|
346
|
+
spaMode: true,
|
|
347
|
+
hotReload: true,
|
|
348
|
+
port: flags.port,
|
|
349
|
+
askPort: !flags.portExplicit,
|
|
350
|
+
logRequests: flags.logRequests,
|
|
351
|
+
});
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
case "cloudflare":
|
|
355
|
+
case "cf":
|
|
356
|
+
case "demo":
|
|
357
|
+
case "tunnel":
|
|
358
|
+
ensureProject();
|
|
359
|
+
await runCloudflareDemo({
|
|
360
|
+
port: flags.port,
|
|
361
|
+
logRequests: flags.logRequests,
|
|
362
|
+
});
|
|
363
|
+
break;
|
|
364
|
+
|
|
365
|
+
case "--version":
|
|
366
|
+
case "-v":
|
|
367
|
+
console.log(`flet-box v${version}`);
|
|
368
|
+
break;
|
|
369
|
+
|
|
370
|
+
case "--help":
|
|
371
|
+
case "-h":
|
|
372
|
+
case "help":
|
|
373
|
+
showHelp();
|
|
374
|
+
break;
|
|
375
|
+
|
|
376
|
+
default:
|
|
377
|
+
if (command) {
|
|
378
|
+
console.error(c("red", `❌ Unknown command: ${command}`));
|
|
379
|
+
console.log("");
|
|
380
|
+
showHelp();
|
|
381
|
+
} else {
|
|
382
|
+
showHelp();
|
|
383
|
+
}
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.error(c("red", "❌ CLI Error:"), error.message);
|
|
388
|
+
// Show the full stack trace only when DEBUG is set, to keep normal output clean.
|
|
389
|
+
if (process.env.DEBUG) console.error(error);
|
|
390
|
+
process.exit(1);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
main();
|