flet-box 0.0.0-stage → 1.0.2
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/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 +1824 -0
- package/bin/utils/version.js +108 -0
- package/package.json +58 -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
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/commands/createBundle.js
|
|
3
|
+
* @description Builds a production-ready, self-contained bundle of a FletBox project.
|
|
4
|
+
*
|
|
5
|
+
* The command uses esbuild to tree-shake and minify the entire app (including the
|
|
6
|
+
* flet-box dependency) into a single `src/app.js` file inside the target directory.
|
|
7
|
+
* PWA assets (service worker, manifest, icons, fonts) are copied alongside it so
|
|
8
|
+
* the output folder can be served or deployed as-is.
|
|
9
|
+
*
|
|
10
|
+
* If flet-box is not present in `node_modules` (e.g. a fresh clone without
|
|
11
|
+
* `npm install`) the package is copied from the CLI's own source tree so the build
|
|
12
|
+
* can proceed without an internet connection.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
import fs from "fs";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import { c, banner, gradient, section } from "../utils/colors.js";
|
|
20
|
+
import { buildBundleConfig, getGitRemoteUrl } from "../utils/bundleConfig.js";
|
|
21
|
+
import { INITIAL_VERSION, incrementVersion, isValidVersion } from "../utils/version.js";
|
|
22
|
+
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = path.dirname(__filename);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively copies a directory tree from `src` to `dest`, creating any
|
|
28
|
+
* missing intermediate directories. Mirrors the behaviour of `cp -R`.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} src - Absolute path to the source directory.
|
|
31
|
+
* @param {string} dest - Absolute path to the destination directory.
|
|
32
|
+
* @returns {void}
|
|
33
|
+
*/
|
|
34
|
+
const copyFolderSync = (src, dest) => {
|
|
35
|
+
if (!fs.existsSync(dest)) {
|
|
36
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
40
|
+
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const srcPath = path.join(src, entry.name);
|
|
43
|
+
const destPath = path.join(dest, entry.name);
|
|
44
|
+
|
|
45
|
+
if (entry.isDirectory()) {
|
|
46
|
+
copyFolderSync(srcPath, destPath);
|
|
47
|
+
} else {
|
|
48
|
+
fs.copyFileSync(srcPath, destPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Builds the current FletBox project into a deployable bundle directory.
|
|
55
|
+
*
|
|
56
|
+
* Output structure:
|
|
57
|
+
* ```
|
|
58
|
+
* <target>/
|
|
59
|
+
* index.html
|
|
60
|
+
* manifest.json
|
|
61
|
+
* service-worker.js
|
|
62
|
+
* run.sh
|
|
63
|
+
* assets/ ← PWA icons (duplicate of src/assets for root-relative paths)
|
|
64
|
+
* src/
|
|
65
|
+
* app.js ← single minified bundle (esbuild)
|
|
66
|
+
* assets/ ← fonts + icons
|
|
67
|
+
* styles/ ← global.css (if present)
|
|
68
|
+
* screens/ ← source files (used by "View code" feature)
|
|
69
|
+
* components/
|
|
70
|
+
* modules/
|
|
71
|
+
* database/
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @async
|
|
75
|
+
* @param {string|undefined} targetArg - Target directory name relative to the project
|
|
76
|
+
* root. Defaults to `"www"` when omitted.
|
|
77
|
+
* @returns {Promise<void>}
|
|
78
|
+
*/
|
|
79
|
+
export const createBundle = async (targetArg) => {
|
|
80
|
+
const TARGET = targetArg || "www";
|
|
81
|
+
console.log(banner("📦 FletBox Bundle", TARGET + "/"));
|
|
82
|
+
|
|
83
|
+
const projectRoot = process.cwd();
|
|
84
|
+
const targetDir = path.join(projectRoot, TARGET);
|
|
85
|
+
const srcDir = path.join(projectRoot, "src");
|
|
86
|
+
const appJs = path.join(srcDir, "app.js");
|
|
87
|
+
const nodeModulesDir = path.join(projectRoot, "node_modules");
|
|
88
|
+
const fletBoxDir = path.join(nodeModulesDir, "flet-box");
|
|
89
|
+
|
|
90
|
+
// Guard: must be run from within a FletBox project.
|
|
91
|
+
if (!fs.existsSync(appJs)) {
|
|
92
|
+
console.error(c("red", "❌ Not a FletBox project"));
|
|
93
|
+
console.log(c("gray", "Make sure you are in a project with src/app.js"));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If flet-box is not installed, copy it from the CLI package so we can bundle
|
|
98
|
+
// without requiring `npm install` first (useful in offline / CI environments).
|
|
99
|
+
if (!fs.existsSync(fletBoxDir)) {
|
|
100
|
+
console.log(
|
|
101
|
+
c("yellow", "⚠️ flet-box not found in node_modules, copying..."),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (!fs.existsSync(nodeModulesDir)) {
|
|
105
|
+
fs.mkdirSync(nodeModulesDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const sourceFletBox = path.join(__dirname, "..", "..");
|
|
109
|
+
fs.mkdirSync(fletBoxDir, { recursive: true });
|
|
110
|
+
|
|
111
|
+
// Copy src/ from the CLI package into node_modules/flet-box/src/.
|
|
112
|
+
const sourceSrc = path.join(sourceFletBox, "src");
|
|
113
|
+
const destSrc = path.join(fletBoxDir, "src");
|
|
114
|
+
if (fs.existsSync(sourceSrc)) {
|
|
115
|
+
copyFolderSync(sourceSrc, destSrc);
|
|
116
|
+
console.log(c("green", "✅ Copied flet-box/src"));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Copy package.json so esbuild can resolve the package correctly.
|
|
120
|
+
const sourcePackage = path.join(sourceFletBox, "package.json");
|
|
121
|
+
if (fs.existsSync(sourcePackage)) {
|
|
122
|
+
fs.copyFileSync(sourcePackage, path.join(fletBoxDir, "package.json"));
|
|
123
|
+
console.log(c("green", "✅ Copied flet-box/package.json"));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Ensure esbuild is available; install it as a devDependency if not.
|
|
128
|
+
try {
|
|
129
|
+
execSync("npx --no-install esbuild --version", { stdio: "pipe" });
|
|
130
|
+
} catch {
|
|
131
|
+
console.log(c("yellow", "📦 Installing esbuild (devDependency)…"));
|
|
132
|
+
execSync("npm install --save-dev esbuild", { stdio: "inherit" });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Wipe and recreate the target directory for a clean, reproducible output.
|
|
136
|
+
console.log(c("blue", "\n📁 Creating structure..."));
|
|
137
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
138
|
+
fs.mkdirSync(path.join(targetDir, "src"), { recursive: true });
|
|
139
|
+
fs.mkdirSync(path.join(targetDir, "assets"), { recursive: true });
|
|
140
|
+
|
|
141
|
+
// Copy root-level PWA and preview files to the bundle root.
|
|
142
|
+
for (const file of ["index.html", "manifest.json", "run.sh", "service-worker.js"]) {
|
|
143
|
+
const src = path.join(projectRoot, file);
|
|
144
|
+
if (fs.existsSync(src)) {
|
|
145
|
+
const dest = path.join(targetDir, file);
|
|
146
|
+
fs.copyFileSync(src, dest);
|
|
147
|
+
// run.sh must be executable so `bash run.sh` works without chmod by the user.
|
|
148
|
+
if (file === "run.sh") fs.chmodSync(dest, 0o755);
|
|
149
|
+
console.log(c("green", `✅ Copied: ${file}`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Provide a minimal fallback index.html when the project doesn't have one
|
|
154
|
+
// (e.g. blank template that hasn't been customised yet).
|
|
155
|
+
if (!fs.existsSync(path.join(targetDir, "index.html"))) {
|
|
156
|
+
const defaultHtml = `<!DOCTYPE html>
|
|
157
|
+
<html lang="en">
|
|
158
|
+
<head>
|
|
159
|
+
<meta charset="UTF-8">
|
|
160
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
161
|
+
<link rel="stylesheet" href="./src/assets/fonts/icons.css">
|
|
162
|
+
<title>FletBox App</title>
|
|
163
|
+
<style>
|
|
164
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
165
|
+
body { font-family: system-ui, sans-serif; }
|
|
166
|
+
#root { width: 100%; height: 100vh; }
|
|
167
|
+
</style>
|
|
168
|
+
</head>
|
|
169
|
+
<body>
|
|
170
|
+
<div id="root"></div>
|
|
171
|
+
<script type="module" src="./src/app.js"></script>
|
|
172
|
+
</body>
|
|
173
|
+
</html>`;
|
|
174
|
+
fs.writeFileSync(path.join(targetDir, "index.html"), defaultHtml);
|
|
175
|
+
console.log(c("green", "✅ Created: index.html"));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Copy assets (PWA icons + icon fonts) to both src/assets and the root assets/
|
|
179
|
+
// directory so that both relative paths used in index.html resolve correctly.
|
|
180
|
+
if (fs.existsSync(path.join(srcDir, "assets"))) {
|
|
181
|
+
fs.cpSync(
|
|
182
|
+
path.join(srcDir, "assets"),
|
|
183
|
+
path.join(targetDir, "src", "assets"),
|
|
184
|
+
{ recursive: true },
|
|
185
|
+
);
|
|
186
|
+
fs.cpSync(
|
|
187
|
+
path.join(srcDir, "assets"),
|
|
188
|
+
path.join(targetDir, "assets"),
|
|
189
|
+
{ recursive: true },
|
|
190
|
+
);
|
|
191
|
+
console.log(c("green", "✅ Copied: src/assets (fonts + PWA icons)"));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Copy global CSS (loaded directly by index.html, not bundled by esbuild).
|
|
195
|
+
const globalCss = path.join(srcDir, "styles", "global.css");
|
|
196
|
+
if (fs.existsSync(globalCss)) {
|
|
197
|
+
fs.mkdirSync(path.join(targetDir, "src", "styles"), { recursive: true });
|
|
198
|
+
fs.copyFileSync(globalCss, path.join(targetDir, "src", "styles", "global.css"));
|
|
199
|
+
console.log(c("green", "✅ Copied: global.css"));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Bundle the entire application (app + flet-box) into a single minified ESM file.
|
|
203
|
+
// --external:*.css and --external:*.woff2 prevent esbuild from trying to inline
|
|
204
|
+
// binary assets, which would break the output or inflate the bundle size.
|
|
205
|
+
console.log(c("blue", "\n📦 Bundling app.js (esbuild + tree-shaking)..."));
|
|
206
|
+
|
|
207
|
+
const bundleCmd = [
|
|
208
|
+
"npx esbuild src/app.js",
|
|
209
|
+
"--bundle",
|
|
210
|
+
`--outfile=${TARGET}/src/app.js`,
|
|
211
|
+
"--format=esm",
|
|
212
|
+
"--minify",
|
|
213
|
+
"--tree-shaking=true",
|
|
214
|
+
"--target=es2020",
|
|
215
|
+
"--define:FLETBOX_DEV=false",
|
|
216
|
+
"--external:*.css",
|
|
217
|
+
"--external:*.woff2",
|
|
218
|
+
"--resolve-extensions=.js,.json",
|
|
219
|
+
].join(" \n ");
|
|
220
|
+
// The newlines are for readability only; join them back before passing to the shell.
|
|
221
|
+
const cmd = bundleCmd.replace(/\n\s*/g, " ");
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
execSync(cmd, {
|
|
225
|
+
stdio: "inherit",
|
|
226
|
+
env: { ...process.env, NODE_PATH: nodeModulesDir },
|
|
227
|
+
});
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(c("red", "❌ Esbuild failed:"), error.message);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ---- OTA manifest -------------------------------------------------------
|
|
234
|
+
// Stamp a version header into the bundle (so the deployed app.js is
|
|
235
|
+
// self-describing), then hash the FINAL file and write www/config.json.
|
|
236
|
+
// The running app fetches this manifest to decide whether to self-update.
|
|
237
|
+
const outAppJs = path.join(targetDir, "src", "app.js");
|
|
238
|
+
const pkg = JSON.parse(
|
|
239
|
+
fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"),
|
|
240
|
+
);
|
|
241
|
+
const appVersion = isValidVersion(pkg.version) ? pkg.version : INITIAL_VERSION;
|
|
242
|
+
const builtAt = new Date().toISOString();
|
|
243
|
+
|
|
244
|
+
// Visual reference only: createBundle NEVER bumps the version — only
|
|
245
|
+
// `flet-box release` does. Print what each release flag would produce so the
|
|
246
|
+
// next version is visible at build time.
|
|
247
|
+
console.log(section("🔢", `Versión del bundle: ${appVersion}`, "#34d399"));
|
|
248
|
+
console.log(c("gray", " Qué haría cada release:"));
|
|
249
|
+
for (const [flag, type] of [
|
|
250
|
+
["--patch", "patch"],
|
|
251
|
+
["--minor", "minor"],
|
|
252
|
+
["--major", "major"],
|
|
253
|
+
]) {
|
|
254
|
+
const next = incrementVersion(appVersion, type);
|
|
255
|
+
console.log(
|
|
256
|
+
` ${c("cyan", flag.padEnd(8))} ` +
|
|
257
|
+
`${c("gray", appVersion)} ${c("gray", "→")} ` +
|
|
258
|
+
`${gradient(next, "#34d399", "#22d3ee")}`,
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const bundledCode = fs.readFileSync(outAppJs, "utf8");
|
|
263
|
+
const header = `/*! FletBox bundle v${appVersion} · built ${builtAt} */\n`;
|
|
264
|
+
const stampedCode = header + bundledCode;
|
|
265
|
+
fs.writeFileSync(outAppJs, stampedCode);
|
|
266
|
+
|
|
267
|
+
const bundleConfig = buildBundleConfig({
|
|
268
|
+
projectRoot,
|
|
269
|
+
bundleContent: stampedCode,
|
|
270
|
+
bundlePath: "src/app.js",
|
|
271
|
+
});
|
|
272
|
+
fs.writeFileSync(
|
|
273
|
+
path.join(targetDir, "config.json"),
|
|
274
|
+
JSON.stringify(bundleConfig, null, 2) + "\n",
|
|
275
|
+
);
|
|
276
|
+
console.log(
|
|
277
|
+
c("green", "✅ Wrote config.json ") +
|
|
278
|
+
c("gray", `(v${bundleConfig.version} · ${bundleConfig.hash.slice(0, 12)}…)`),
|
|
279
|
+
);
|
|
280
|
+
if (!bundleConfig.update.url) {
|
|
281
|
+
const remote = getGitRemoteUrl(projectRoot);
|
|
282
|
+
console.log(
|
|
283
|
+
"\n" +
|
|
284
|
+
c("yellow", "⚠️ THIS APP DOES NOT SUPPORT OTA UPDATES.") +
|
|
285
|
+
"\n" +
|
|
286
|
+
c(
|
|
287
|
+
"gray",
|
|
288
|
+
" The bundle still works: installed apps will run the version that is inside this build (the local/current bundle).",
|
|
289
|
+
) +
|
|
290
|
+
"\n" +
|
|
291
|
+
c(
|
|
292
|
+
"gray",
|
|
293
|
+
" OTA activates only once a PUBLIC GitHub repository exists. " +
|
|
294
|
+
(remote
|
|
295
|
+
? `Your origin remote is: ${c("cyan", remote)}`
|
|
296
|
+
: "No git remote `origin` was found in this project."),
|
|
297
|
+
) +
|
|
298
|
+
"\n" +
|
|
299
|
+
c("gray", " To enable OTA updates:") +
|
|
300
|
+
"\n" +
|
|
301
|
+
c("gray", " 1. Create the repository on GitHub (must be PUBLIC and readable).") +
|
|
302
|
+
"\n" +
|
|
303
|
+
c("gray", " 2. Push this project so `origin` is that GitHub repo, then rebuild here.") +
|
|
304
|
+
"\n" +
|
|
305
|
+
c("gray", " (or set `update.url` in fletbox.config.json / `repository` in package.json)") +
|
|
306
|
+
"\n",
|
|
307
|
+
);
|
|
308
|
+
} else if (/TU-USUARIO|TU-REPO/.test(bundleConfig.update.url)) {
|
|
309
|
+
console.log(
|
|
310
|
+
c(
|
|
311
|
+
"yellow",
|
|
312
|
+
"⚠️ update.url is still the placeholder — OTA points at a fake site. Replace TU-USUARIO/TU-REPO in fletbox.config.json before releasing.",
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
} else {
|
|
316
|
+
console.log(
|
|
317
|
+
c("green", "⬆️ OTA source: ") +
|
|
318
|
+
c("gray", bundleConfig.update.url + " · override with `fletbox.config.json`"),
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ---- OTA loader ---------------------------------------------------------
|
|
323
|
+
// Ship bin/ota/loader.js verbatim as www/loader.js and rewrite index.html so
|
|
324
|
+
// the module entry is the loader instead of src/app.js. The loader runs first,
|
|
325
|
+
// checks config.json for a newer bundle, and then imports the app. When OTA is
|
|
326
|
+
// disabled (no update URL) the loader still works — it just runs the shipped
|
|
327
|
+
// bundle directly, so wiring it unconditionally keeps the output consistent.
|
|
328
|
+
const loaderSrc = path.join(__dirname, "..", "ota", "loader.js");
|
|
329
|
+
const loaderDest = path.join(targetDir, "loader.js");
|
|
330
|
+
if (fs.existsSync(loaderSrc)) {
|
|
331
|
+
fs.copyFileSync(loaderSrc, loaderDest);
|
|
332
|
+
console.log(c("green", "✅ Wrote loader.js (OTA boot)"));
|
|
333
|
+
|
|
334
|
+
const indexFile = path.join(targetDir, "index.html");
|
|
335
|
+
if (fs.existsSync(indexFile)) {
|
|
336
|
+
const html = fs.readFileSync(indexFile, "utf8");
|
|
337
|
+
// Swap the bundled app entry (src="src/app.js" or "./src/app.js") for the
|
|
338
|
+
// loader, preserving any other attributes (e.g. type="module").
|
|
339
|
+
const patched = html.replace(
|
|
340
|
+
/<script\b([^>]*?)\bsrc=(["'])\.?\/?src\/app\.js\2([^>]*)>\s*<\/script>/i,
|
|
341
|
+
(_m, before, _q, after) =>
|
|
342
|
+
`<script${before}src="./loader.js"${after}></script>`,
|
|
343
|
+
);
|
|
344
|
+
if (patched !== html) {
|
|
345
|
+
fs.writeFileSync(indexFile, patched);
|
|
346
|
+
console.log(c("green", "✅ index.html → loads ./loader.js"));
|
|
347
|
+
} else {
|
|
348
|
+
console.log(
|
|
349
|
+
c(
|
|
350
|
+
"yellow",
|
|
351
|
+
"⚠️ Could not find the app.js <script> in index.html to wire the loader — OTA boot not installed.",
|
|
352
|
+
),
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Preserve the app source tree so the in-app "View code" feature can read it.
|
|
359
|
+
for (const dir of ["modules", "screens", "database", "components"]) {
|
|
360
|
+
const src = path.join(srcDir, dir);
|
|
361
|
+
if (fs.existsSync(src)) {
|
|
362
|
+
copyFolderSync(src, path.join(targetDir, "src", dir));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const stats = fs.statSync(path.join(targetDir, "src", "app.js"));
|
|
367
|
+
const sizeKB = (stats.size / 1024).toFixed(1);
|
|
368
|
+
|
|
369
|
+
console.log(
|
|
370
|
+
`\n${gradient(" ✅ Bundle complete! ", "#34d399", "#22d3ee")}${c("gray", `(${sizeKB} KB — single app.js)`)}`,
|
|
371
|
+
);
|
|
372
|
+
console.log(c("gray", ` 📁 Output: ${gradient(TARGET + "/", "#8b5cf6", "#d946ef")}`));
|
|
373
|
+
console.log(c("gray", ` 🚀 Try: ${gradient(`cd ${TARGET} && bash run.sh`, "#22d3ee", "#38bdf8")}`));
|
|
374
|
+
console.log(c("gray", ` ⚡ Or also: ${gradient(`flet-box run-bundle ${TARGET === "www" ? "" : TARGET}`.trim(), "#22d3ee", "#38bdf8")}`));
|
|
375
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/commands/createComponent.js
|
|
3
|
+
* @description Generates one or more FletBox component files inside `src/components/`.
|
|
4
|
+
*
|
|
5
|
+
* Accepts either a component name (e.g. `UserCard`) or an integer (1–10) to
|
|
6
|
+
* generate a numbered batch (`Component1`, `Component2`, …).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { c, gradient, rainbow, section } from "../utils/colors.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the source code for a new FletBox component module.
|
|
15
|
+
*
|
|
16
|
+
* The generated component is a named export that renders a `Container` with
|
|
17
|
+
* a horizontal `Row` containing an icon and a text label. Props are spread
|
|
18
|
+
* onto the outer `Container` so callers can customise layout without wrapping.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} name - PascalCase component name used for the export identifier.
|
|
21
|
+
* @returns {string} JavaScript source code for the component file.
|
|
22
|
+
*/
|
|
23
|
+
const componentTemplate = (name) => `// components/${name}.js
|
|
24
|
+
import { Container, Row, Text, Icon, colors } from 'flet-box';
|
|
25
|
+
|
|
26
|
+
export const ${name} = (props) => {
|
|
27
|
+
const {
|
|
28
|
+
title,
|
|
29
|
+
icon = 'widgets',
|
|
30
|
+
children,
|
|
31
|
+
...rest
|
|
32
|
+
} = props;
|
|
33
|
+
|
|
34
|
+
return Container({
|
|
35
|
+
padding: 16,
|
|
36
|
+
borderRadius: 16,
|
|
37
|
+
backgroundColor: colors.surface,
|
|
38
|
+
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
|
|
39
|
+
...rest,
|
|
40
|
+
child: Row({
|
|
41
|
+
gap: 12,
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
children: [
|
|
44
|
+
Icon({ name: icon, size: 24, color: colors.primary }),
|
|
45
|
+
Text({ text: title || '${name} Component', size: 16, color: colors.text, weight: 'bold' }),
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default ${name};
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scaffolds one or more component files in the current project's `src/components/` directory.
|
|
56
|
+
*
|
|
57
|
+
* When `input` is a numeric string the function generates that many components
|
|
58
|
+
* (capped at 10) named `Component1` … `Component<n>`. When it is a plain name
|
|
59
|
+
* it generates a single file named `<input>.js`.
|
|
60
|
+
*
|
|
61
|
+
* Existing files are skipped with a warning — the command never overwrites work.
|
|
62
|
+
*
|
|
63
|
+
* @async
|
|
64
|
+
* @param {string} input - Either a component name (e.g. `"UserCard"`) or a
|
|
65
|
+
* numeric string (e.g. `"3"`) to batch-create numbered components.
|
|
66
|
+
* @returns {Promise<void>}
|
|
67
|
+
*/
|
|
68
|
+
export const createComponent = async (input) => {
|
|
69
|
+
const projectRoot = process.cwd();
|
|
70
|
+
const componentsDir = path.join(projectRoot, "src", "components");
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(componentsDir)) {
|
|
73
|
+
console.error(
|
|
74
|
+
c("red", '❌ Not a FletBox project. Run "flet-box create my-app" first.'),
|
|
75
|
+
);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const componentNames = [];
|
|
80
|
+
|
|
81
|
+
// Numeric input: create a batch of sequentially numbered components.
|
|
82
|
+
if (!isNaN(input) && Number.isInteger(parseFloat(input))) {
|
|
83
|
+
const count = Math.min(parseInt(input), 10);
|
|
84
|
+
for (let i = 1; i <= count; i++) {
|
|
85
|
+
componentNames.push(`Component${i}`);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
componentNames.push(input);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let created = 0;
|
|
92
|
+
const createdFiles = [];
|
|
93
|
+
for (const name of componentNames) {
|
|
94
|
+
const fileName = `${name}.js`;
|
|
95
|
+
const filePath = path.join(componentsDir, fileName);
|
|
96
|
+
|
|
97
|
+
if (fs.existsSync(filePath)) {
|
|
98
|
+
// Skip silently with a warning — never overwrite existing components.
|
|
99
|
+
console.log(c("yellow", ` ⚠️ ${gradient(fileName, "#fbbf24", "#fb923c")} already exists, skipping ...`));
|
|
100
|
+
} else {
|
|
101
|
+
fs.writeFileSync(filePath, componentTemplate(name));
|
|
102
|
+
createdFiles.push(fileName);
|
|
103
|
+
created++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log(`\n${section("🧩", "COMPONENTS GENERATED")}`);
|
|
108
|
+
createdFiles.forEach((f) =>
|
|
109
|
+
console.log(` ${c("brightGreen", "✔")} ${rainbow(f)}`),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
console.log(
|
|
113
|
+
`\n${gradient(" ✅ " + created + " component(s) created! ", "#34d399", "#22d3ee")}\n`,
|
|
114
|
+
);
|
|
115
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file bin/commands/createScreen.js
|
|
3
|
+
* @description Generates one or more FletBox screen files inside `src/screens/`.
|
|
4
|
+
*
|
|
5
|
+
* Accepts either a screen name (e.g. `Settings`) or an integer (1–10) to
|
|
6
|
+
* generate a numbered batch (`Screen1`, `Screen2`, …). After creation the
|
|
7
|
+
* command prints the import snippets and route entries needed to wire the new
|
|
8
|
+
* screens into `src/app.js`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { c, banner, gradient, rainbow, section } from "../utils/colors.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the source code for a new FletBox screen module.
|
|
17
|
+
*
|
|
18
|
+
* The generated screen shows a centred gradient icon, a heading, a subtitle,
|
|
19
|
+
* and a pair of Back / Next action buttons — enough structure to be useful
|
|
20
|
+
* immediately without being opinionated about business logic.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} name - PascalCase screen name used for the export identifier.
|
|
23
|
+
* The file will be named `${name}Screen.js` and the export `${name}Screen`.
|
|
24
|
+
* @returns {string} JavaScript source code for the screen file.
|
|
25
|
+
*/
|
|
26
|
+
const screenTemplate = (name) => `// screens/${name}Screen.js
|
|
27
|
+
import {
|
|
28
|
+
Container, Column, Row, Text, Icon, Button, gradient, colors,
|
|
29
|
+
} from 'flet-box';
|
|
30
|
+
|
|
31
|
+
export const ${name}Screen = () => {
|
|
32
|
+
return Container({
|
|
33
|
+
flex: 1,
|
|
34
|
+
padding: 20,
|
|
35
|
+
child: Column({
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
gap: 18,
|
|
39
|
+
children: [
|
|
40
|
+
Container({
|
|
41
|
+
width: 96,
|
|
42
|
+
height: 96,
|
|
43
|
+
borderRadius: 48,
|
|
44
|
+
gradient: gradient('linear', ['#6366f1', '#a855f7'], 135),
|
|
45
|
+
boxShadow: '0 12px 32px rgba(99, 102, 241, 0.45)',
|
|
46
|
+
alignment: 'center',
|
|
47
|
+
child: Icon({ name: 'rocket_launch', size: 44, color: '#ffffff' }),
|
|
48
|
+
}),
|
|
49
|
+
Text({ text: '${name}', size: 26, weight: 'bold', color: colors.primary }),
|
|
50
|
+
Text({
|
|
51
|
+
text: 'Touch the world with FletBox ✨',
|
|
52
|
+
size: 15,
|
|
53
|
+
color: colors.textSecondary,
|
|
54
|
+
textAlign: 'center',
|
|
55
|
+
}),
|
|
56
|
+
Row({
|
|
57
|
+
gap: 12,
|
|
58
|
+
alignItems: 'center',
|
|
59
|
+
children: [
|
|
60
|
+
Button({
|
|
61
|
+
text: 'Back',
|
|
62
|
+
variant: 'outlined',
|
|
63
|
+
iconLeft: 'arrow_back',
|
|
64
|
+
onPress: () => history.back(),
|
|
65
|
+
}),
|
|
66
|
+
Button({
|
|
67
|
+
text: 'Next',
|
|
68
|
+
iconRight: 'arrow_forward',
|
|
69
|
+
gradient: gradient('linear', ['#6366f1', '#8b5cf6'], 135),
|
|
70
|
+
onPress: () => console.log('Next'),
|
|
71
|
+
}),
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
],
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default ${name}Screen;
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Scaffolds one or more screen files in the current project's `src/screens/` directory.
|
|
84
|
+
*
|
|
85
|
+
* When `input` is a numeric string the function generates that many screens
|
|
86
|
+
* (capped at 10) named `Screen1Screen.js` … `Screen<n>Screen.js`. When it
|
|
87
|
+
* is a plain name it generates a single file named `<input>Screen.js`.
|
|
88
|
+
*
|
|
89
|
+
* After writing the files the command prints ready-to-paste import statements
|
|
90
|
+
* and route object entries so developers can wire them up in `src/app.js`
|
|
91
|
+
* without searching for the correct syntax.
|
|
92
|
+
*
|
|
93
|
+
* Existing files are skipped with a warning — the command never overwrites work.
|
|
94
|
+
*
|
|
95
|
+
* @async
|
|
96
|
+
* @param {string} input - Either a screen name (e.g. `"Settings"`) or a
|
|
97
|
+
* numeric string (e.g. `"3"`) to batch-create numbered screens.
|
|
98
|
+
* @returns {Promise<void>}
|
|
99
|
+
*/
|
|
100
|
+
export const createScreen = async (input) => {
|
|
101
|
+
const projectRoot = process.cwd();
|
|
102
|
+
const screensDir = path.join(projectRoot, "src", "screens");
|
|
103
|
+
|
|
104
|
+
if (!fs.existsSync(screensDir)) {
|
|
105
|
+
console.error(
|
|
106
|
+
c("red", '❌ Not a FletBox project. Run "flet-box create my-app" first.'),
|
|
107
|
+
);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const screenNames = [];
|
|
112
|
+
|
|
113
|
+
// Numeric input: create a batch of sequentially numbered screens (max 10).
|
|
114
|
+
if (!isNaN(input) && Number.isInteger(parseFloat(input))) {
|
|
115
|
+
const count = Math.min(parseInt(input), 10);
|
|
116
|
+
for (let i = 1; i <= count; i++) {
|
|
117
|
+
screenNames.push(`Screen${i}`);
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
screenNames.push(input);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let created = 0;
|
|
124
|
+
const createdFiles = [];
|
|
125
|
+
for (const name of screenNames) {
|
|
126
|
+
const fileName = `${name}Screen.js`;
|
|
127
|
+
const filePath = path.join(screensDir, fileName);
|
|
128
|
+
|
|
129
|
+
if (fs.existsSync(filePath)) {
|
|
130
|
+
// Skip with a warning — never overwrite existing screens.
|
|
131
|
+
console.log(c("yellow", ` ⚠️ ${gradient(fileName, "#fbbf24", "#fb923c")} already exists, skipping ...`));
|
|
132
|
+
} else {
|
|
133
|
+
fs.writeFileSync(filePath, screenTemplate(name));
|
|
134
|
+
createdFiles.push(fileName);
|
|
135
|
+
created++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log(`\n${section("🎨", "SCREENS GENERATED")}`);
|
|
140
|
+
createdFiles.forEach((f) =>
|
|
141
|
+
console.log(` ${c("brightGreen", "✔")} ${rainbow(f)}`),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Print wiring instructions so the developer knows exactly what to add to app.js.
|
|
145
|
+
if (created > 0) {
|
|
146
|
+
console.log(`\n${section("🔌", "WIRE THEM UP in src/app.js")}`);
|
|
147
|
+
screenNames.forEach((name) => {
|
|
148
|
+
console.log(` ${c("brightCyan", "import")} { ${rainbow(`${name}Screen`)} } ${c("brightCyan", "from")} '${c("gray", `./screens/${name}Screen.js`)}';`);
|
|
149
|
+
});
|
|
150
|
+
console.log("");
|
|
151
|
+
screenNames.forEach((name) => {
|
|
152
|
+
console.log(
|
|
153
|
+
` ${c("brightMagenta", "·")} '/${c("brightMagenta", name.toLowerCase())}': { body: ${rainbow(`${name}Screen`)} },`,
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(
|
|
159
|
+
`\n${gradient(" ✅ " + created + " screen(s) created! ", "#34d399", "#22d3ee")}\n`,
|
|
160
|
+
);
|
|
161
|
+
};
|