bimba-cli 0.3.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.md +93 -69
  2. package/bun.lock +161 -0
  3. package/index.js +41 -7
  4. package/package.json +1 -1
  5. package/serve.js +180 -0
package/README.md CHANGED
@@ -1,69 +1,93 @@
1
- This tool helps to work with [Imba](https://imba.io) projects under [Bun](https://bun.sh). That is why it is called Bun+IMBA = BIMBA 😉
2
-
3
- It includes the plugin for Bun to compile .imba files and also the CLI tool for buiding .imba files, since the plugins can't be passed to Bun via shell command `bun build`.
4
-
5
- First of all install this tool like any other npm package:
6
- ```bash
7
- bun add bimba-cli -d
8
- ```
9
-
10
- Then create a `bunfig.toml` file in the root folder of your project, and add only one line in it (I could not find any workaround to do this automatically):
11
- ```bash
12
- preload = ["bimba-cli/plugin.js"]
13
- ```
14
-
15
- You are done!
16
-
17
- ### Backend development
18
- Now to run an .imba file in Bun's environment you can use the usual Bun syntax:
19
- ```bash
20
- bun run src/index.imba
21
- ```
22
- Or with the watch argument:
23
- ```bash
24
- bun --watch run src/index.imba
25
- ```
26
-
27
- ### Frontend development
28
- For frontend you will need to compile and bundle your source code from .imba to .js. And here the bimba-cli will help:
29
- ```bash
30
- bunx bimba src/index.imba --outdir public
31
- ```
32
- Or with the watch argument:
33
- ```bash
34
- bunx bimba src/index.imba --outdir public --watch
35
- ```
36
-
37
- Here are all the available argumentsthat you can pass to the bimba:
38
-
39
- `--watch` - Monitors changes in the directory where the entry point is located, and rebuilds the projects when the change occures. Keep entrypoint file in the subfolder, otherwise Bun will trigger several times since the cache dir update also triggers rebuilding.
40
-
41
- `--clearcache` - If is set, the cache directory is deleted when bimba exits. Works only in the watch mode, since when bundling cache will be used next time to speed up the compiling time.
42
-
43
- ```bash
44
- bunx bimba src/index.imba --outdir public --watch --clearcache
45
- ```
46
-
47
- `--sourcemap` - Tells Bun how to inculde sourcemap files in the final .js. It is `none` by default.
48
-
49
- ```bash
50
- bunx bimba src/index.imba --outdir public --sourcemap inline
51
- ```
52
-
53
- `--minify` - If is set the final JS code in the bundle produced by Bun will be minified. It is `false` by default.
54
-
55
- ```bash
56
- bunx bimba src/index.imba --outdir public --minify
57
- ```
58
-
59
- `--platform` - The value of this argument will be passed to the Imba compiler. By default it is `browswer`. The value `node` does not work under Bun.
60
-
61
- ```bash
62
- bunx bimba src/index.imba --outdir public --platform browser
63
- ```
64
-
65
- #### Live reload
66
- Initially I have implemented the live reload functionality, but then decided to refuse it. There is a pretty good VS Code extension: [Lite Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer).
67
- It does everything needed out of the box.
68
-
69
- Just let bimba rebuild the sources on change and Lite Server will reload the page in the browser.
1
+ This tool helps to work with [Imba](https://imba.io) projects under [Bun](https://bun.sh). That is why it is called Bun+IMBA = BIMBA 😉
2
+
3
+ It includes the plugin for Bun to compile .imba files and also the CLI tool for building .imba files, since the plugins can't be passed to Bun via shell command `bun build`.
4
+
5
+ First of all install this tool like any other npm package:
6
+ ```bash
7
+ bun add bimba-cli -d
8
+ ```
9
+
10
+ ---
11
+
12
+ ## Backend development
13
+
14
+ To run an .imba file in Bun's environment, create a `bunfig.toml` file in the root folder of your project:
15
+ ```toml
16
+ preload = ["bimba-cli/plugin.js"]
17
+ ```
18
+
19
+ Then use the usual Bun syntax:
20
+ ```bash
21
+ bun run src/index.imba
22
+ bun --watch run src/index.imba
23
+ ```
24
+
25
+ ---
26
+
27
+ ## Frontend development
28
+
29
+ ### Dev server (HMR)
30
+
31
+ bimba includes a dev server with Hot Module Replacement for Imba custom elements:
32
+
33
+ ```bash
34
+ bunx bimba src/index.imba --serve --port 5200 --html public/index.html
35
+ ```
36
+
37
+ **How it works:**
38
+ - Serves your HTML file and compiles `.imba` files on demand (no bundling step)
39
+ - Watches `src/` for changes and pushes updates over WebSocket
40
+ - Injects an importmap built from your `package.json` dependencies
41
+ - Injects an HMR client that swaps component prototypes without a full page reload
42
+
43
+ **HTML setup:** add a `data-entrypoint` attribute to the script tag that loads your bundle. The dev server will replace it with your `.imba` entrypoint and inject the importmap above it:
44
+
45
+ ```html
46
+ <script type='module' src="./js/index.js" data-entrypoint></script>
47
+ ```
48
+
49
+ **Dev server flags:**
50
+
51
+ `--serve` — start dev server instead of bundling
52
+
53
+ `--port <number>` port to listen on (default: `5200`)
54
+
55
+ `--html <path>` — path to your HTML file (auto-detected from `./index.html`, `./public/index.html`, `./src/index.html` if omitted)
56
+
57
+ Static files are resolved relative to the HTML file's directory first, then from the project root (for `node_modules`, `src`, etc.).
58
+
59
+ ---
60
+
61
+ ### Production bundle
62
+
63
+ To compile and bundle your source code from .imba to .js:
64
+ ```bash
65
+ bunx bimba src/index.imba --outdir public/js --minify
66
+ ```
67
+
68
+ With watch:
69
+ ```bash
70
+ bunx bimba src/index.imba --outdir public/js --watch --clearcache
71
+ ```
72
+
73
+ ---
74
+
75
+ ### All CLI flags
76
+
77
+ `--outdir <path>` — output folder for compiled JS (required in bundle mode)
78
+
79
+ `--watch` — watch the entrypoint directory for changes and rebuild. Keep the entrypoint in a subfolder (e.g. `src/`), otherwise cache updates will trigger extra rebuilds.
80
+
81
+ `--clearcache` — delete the cache directory on exit (Ctrl+C). Works only in watch mode.
82
+
83
+ `--minify` — minify the output JS. Enabled by default in bundle mode.
84
+
85
+ `--sourcemap <inline|external|none>` — how to include source maps in the output (default: `none`).
86
+
87
+ `--target <browser|node>` — platform flag passed to the Imba compiler (default: `browser`). The `node` value does not work under Bun.
88
+
89
+ `--serve` — start dev server with HMR instead of bundling.
90
+
91
+ `--port <number>` — port for the dev server (default: `5200`). Used with `--serve`.
92
+
93
+ `--html <path>` — custom HTML file path. Used with `--serve`.
package/bun.lock ADDED
@@ -0,0 +1,161 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "configVersion": 1,
4
+ "workspaces": {
5
+ "": {
6
+ "name": "bimba-cli",
7
+ "devDependencies": {
8
+ "imba": "latest",
9
+ },
10
+ },
11
+ },
12
+ "packages": {
13
+ "@antfu/install-pkg": ["@antfu/install-pkg@0.1.1", "", { "dependencies": { "execa": "^5.1.1", "find-up": "^5.0.0" } }, "sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ=="],
14
+
15
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.15.18", "", { "os": "android", "cpu": "arm" }, "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw=="],
16
+
17
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.15.18", "", { "os": "linux", "cpu": "none" }, "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ=="],
18
+
19
+ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
20
+
21
+ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
22
+
23
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
24
+
25
+ "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
26
+
27
+ "colord": ["colord@2.9.3", "", {}, "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="],
28
+
29
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
30
+
31
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
32
+
33
+ "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
34
+
35
+ "envinfo": ["envinfo@7.21.0", "", { "bin": { "envinfo": "dist/cli.js" } }, "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow=="],
36
+
37
+ "esbuild": ["esbuild@0.15.18", "", { "optionalDependencies": { "@esbuild/android-arm": "0.15.18", "@esbuild/linux-loong64": "0.15.18", "esbuild-android-64": "0.15.18", "esbuild-android-arm64": "0.15.18", "esbuild-darwin-64": "0.15.18", "esbuild-darwin-arm64": "0.15.18", "esbuild-freebsd-64": "0.15.18", "esbuild-freebsd-arm64": "0.15.18", "esbuild-linux-32": "0.15.18", "esbuild-linux-64": "0.15.18", "esbuild-linux-arm": "0.15.18", "esbuild-linux-arm64": "0.15.18", "esbuild-linux-mips64le": "0.15.18", "esbuild-linux-ppc64le": "0.15.18", "esbuild-linux-riscv64": "0.15.18", "esbuild-linux-s390x": "0.15.18", "esbuild-netbsd-64": "0.15.18", "esbuild-openbsd-64": "0.15.18", "esbuild-sunos-64": "0.15.18", "esbuild-windows-32": "0.15.18", "esbuild-windows-64": "0.15.18", "esbuild-windows-arm64": "0.15.18" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q=="],
38
+
39
+ "esbuild-android-64": ["esbuild-android-64@0.15.18", "", { "os": "android", "cpu": "x64" }, "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA=="],
40
+
41
+ "esbuild-android-arm64": ["esbuild-android-arm64@0.15.18", "", { "os": "android", "cpu": "arm64" }, "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ=="],
42
+
43
+ "esbuild-darwin-64": ["esbuild-darwin-64@0.15.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg=="],
44
+
45
+ "esbuild-darwin-arm64": ["esbuild-darwin-arm64@0.15.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA=="],
46
+
47
+ "esbuild-freebsd-64": ["esbuild-freebsd-64@0.15.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA=="],
48
+
49
+ "esbuild-freebsd-arm64": ["esbuild-freebsd-arm64@0.15.18", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA=="],
50
+
51
+ "esbuild-linux-32": ["esbuild-linux-32@0.15.18", "", { "os": "linux", "cpu": "ia32" }, "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg=="],
52
+
53
+ "esbuild-linux-64": ["esbuild-linux-64@0.15.18", "", { "os": "linux", "cpu": "x64" }, "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw=="],
54
+
55
+ "esbuild-linux-arm": ["esbuild-linux-arm@0.15.18", "", { "os": "linux", "cpu": "arm" }, "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA=="],
56
+
57
+ "esbuild-linux-arm64": ["esbuild-linux-arm64@0.15.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug=="],
58
+
59
+ "esbuild-linux-mips64le": ["esbuild-linux-mips64le@0.15.18", "", { "os": "linux", "cpu": "none" }, "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ=="],
60
+
61
+ "esbuild-linux-ppc64le": ["esbuild-linux-ppc64le@0.15.18", "", { "os": "linux", "cpu": "ppc64" }, "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w=="],
62
+
63
+ "esbuild-linux-riscv64": ["esbuild-linux-riscv64@0.15.18", "", { "os": "linux", "cpu": "none" }, "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg=="],
64
+
65
+ "esbuild-linux-s390x": ["esbuild-linux-s390x@0.15.18", "", { "os": "linux", "cpu": "s390x" }, "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ=="],
66
+
67
+ "esbuild-netbsd-64": ["esbuild-netbsd-64@0.15.18", "", { "os": "none", "cpu": "x64" }, "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg=="],
68
+
69
+ "esbuild-openbsd-64": ["esbuild-openbsd-64@0.15.18", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ=="],
70
+
71
+ "esbuild-sunos-64": ["esbuild-sunos-64@0.15.18", "", { "os": "sunos", "cpu": "x64" }, "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw=="],
72
+
73
+ "esbuild-windows-32": ["esbuild-windows-32@0.15.18", "", { "os": "win32", "cpu": "ia32" }, "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ=="],
74
+
75
+ "esbuild-windows-64": ["esbuild-windows-64@0.15.18", "", { "os": "win32", "cpu": "x64" }, "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw=="],
76
+
77
+ "esbuild-windows-arm64": ["esbuild-windows-arm64@0.15.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ=="],
78
+
79
+ "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
80
+
81
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
82
+
83
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
84
+
85
+ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
86
+
87
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
88
+
89
+ "get-port": ["get-port@5.1.1", "", {}, "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="],
90
+
91
+ "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
92
+
93
+ "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
94
+
95
+ "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
96
+
97
+ "imba": ["imba@2.0.0-alpha.247", "", { "dependencies": { "@antfu/install-pkg": "^0.1.1", "chokidar": "^3.4.3", "colord": "^2.9.3", "cross-spawn": "^7.0.3", "debug": "^4.3.4", "dotenv": "^16.0.3", "envinfo": "^7.8.1", "esbuild": "^0.15.2", "fdir": "^6.1.0", "get-port": "^5.1.1", "local-pkg": "^0.4.2", "lodash.mergewith": "^4.6.2", "prompts": "^2.4.2" }, "peerDependencies": { "@testing-library/dom": "*", "@testing-library/jest-dom": "*", "vite": "*", "vite-node": "*", "vitest": "*" }, "optionalPeers": ["@testing-library/dom", "@testing-library/jest-dom", "vite", "vite-node", "vitest"], "bin": { "imba": "bin/imba", "imbac": "bin/imbac" } }, "sha512-wrM2yDk78nnXC5kS1Br0hcsLgHjoIRNEX0XIsDNNNqVOjwAe6L63I0UjiJlXvLukhAMRV07acBv9kE9lH95YnQ=="],
98
+
99
+ "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
100
+
101
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
102
+
103
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
104
+
105
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
106
+
107
+ "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
108
+
109
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
110
+
111
+ "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
112
+
113
+ "local-pkg": ["local-pkg@0.4.3", "", {}, "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g=="],
114
+
115
+ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
116
+
117
+ "lodash.mergewith": ["lodash.mergewith@4.6.2", "", {}, "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="],
118
+
119
+ "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
120
+
121
+ "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
122
+
123
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
124
+
125
+ "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
126
+
127
+ "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
128
+
129
+ "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
130
+
131
+ "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
132
+
133
+ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
134
+
135
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
136
+
137
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
138
+
139
+ "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
140
+
141
+ "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="],
142
+
143
+ "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
144
+
145
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
146
+
147
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
148
+
149
+ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
150
+
151
+ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
152
+
153
+ "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
154
+
155
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
156
+
157
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
158
+
159
+ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
160
+ }
161
+ }
package/index.js CHANGED
@@ -6,6 +6,7 @@ import {theme} from './utils.js';
6
6
  import fs from 'fs'
7
7
  import path from 'path';
8
8
  import { rmSync } from "node:fs";
9
+ import { serve } from './serve.js';
9
10
 
10
11
 
11
12
  let flags = {}
@@ -22,6 +23,9 @@ try {
22
23
  minify: { type: 'boolean' },
23
24
  target: { type: 'string' },
24
25
  sourcemap: { type: 'string' },
26
+ serve: { type: 'boolean' },
27
+ port: { type: 'string' },
28
+ html: { type: 'string' },
25
29
  },
26
30
  strict: true,
27
31
  allowPositionals: true,
@@ -37,6 +41,18 @@ catch (error) {
37
41
  process.exit(0);
38
42
  }
39
43
 
44
+ // Ensure bunfig.toml exists and contains the required preload line
45
+ const bunfigPath = path.join(process.cwd(), 'bunfig.toml');
46
+ const preloadLine = 'preload = ["bimba-cli/plugin.js"]';
47
+ if (!fs.existsSync(bunfigPath)) {
48
+ fs.writeFileSync(bunfigPath, preloadLine + '\n');
49
+ } else {
50
+ const content = fs.readFileSync(bunfigPath, 'utf8');
51
+ if (!content.includes(preloadLine)) {
52
+ fs.appendFileSync(bunfigPath, (content.endsWith('\n') ? '' : '\n') + preloadLine + '\n');
53
+ }
54
+ }
55
+
40
56
  // help: more on bun building params here: https://bun.sh/docs/bundler
41
57
  if(flags.help) {
42
58
  console.log("");
@@ -50,23 +66,40 @@ if(flags.help) {
50
66
  console.log(" "+theme.flags('--watch')+" Watch for changes in the entrypoint folder");
51
67
  console.log(" "+theme.flags('--clearcache')+" Clear cache on exit, works only when in watch mode");
52
68
  console.log("");
69
+ console.log("Dev server (HMR):");
70
+ console.log(" "+theme.flags('--serve')+" Start dev server with Hot Module Replacement");
71
+ console.log(" "+theme.flags('--port <number>')+" Port for the dev server (default: 5200)");
72
+ console.log(" "+theme.flags('--html <path>')+" Custom HTML file path (auto-detected if omitted)");
73
+ console.log("");
53
74
  process.exit(0);
54
75
  }
55
76
 
56
77
 
78
+ let bundling = false;
79
+
80
+ // serve mode
81
+ if (flags.serve) {
82
+ if (!entrypoint) {
83
+ console.log("");
84
+ console.log("You should provide entrypoint: "+theme.flags('bimba file.imba --serve'));
85
+ console.log("");
86
+ process.exit(1);
87
+ }
88
+ serve(entrypoint, { port: parseInt(flags.port) || 5200, html: flags.html });
89
+ }
57
90
  // no entrypoint or outdir
58
- if(!entrypoint || !flags.outdir) {
91
+ else if(!entrypoint || !flags.outdir) {
59
92
  console.log("");
60
93
  console.log("You should provide entrypoint and the output dir: "+theme.flags('bimba file.imba --outdir public'));
61
94
  console.log("For more information: "+theme.flags('--help'));
62
95
  console.log("");
63
96
  process.exit(1);
64
97
  }
65
-
66
98
  // build
67
- let bundling = false;
68
- bundle();
69
- watch(bundle);
99
+ else {
100
+ bundle();
101
+ watch(bundle);
102
+ }
70
103
 
71
104
  function watch(callback) {
72
105
  if (flags.watch) {
@@ -83,6 +116,7 @@ function watch(callback) {
83
116
  }
84
117
  }
85
118
 
119
+
86
120
  async function bundle() {
87
121
  if (bundling) return;
88
122
  bundling = true;
@@ -101,7 +135,7 @@ async function bundle() {
101
135
 
102
136
  console.log(theme.folder("──────────────────────────────────────────────────────────────────────"));
103
137
  console.log(theme.start(`Start building the Imba entrypoint: ${theme.filedir(entrypoint)}`));
104
-
138
+
105
139
  let result = undefined
106
140
  try {
107
141
  result = await Bun.build({
@@ -118,7 +152,7 @@ async function bundle() {
118
152
  else
119
153
  console.log(theme.start(theme.success("Success") +` It took ${theme.time(Date.now() - start)} ms to bundle ${theme.count(stats.bundled)} file${stats.bundled > 1 ? 's' : ''} to the folder: ${theme.filedir(flags.outdir)}`));
120
154
 
121
- if(!result.success && !stats.errors){
155
+ if (!result.success && !stats.errors) {
122
156
  for (const log of result.logs) {
123
157
  console.log(log);
124
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bimba-cli",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/HeapVoid/bimba.git"
package/serve.js ADDED
@@ -0,0 +1,180 @@
1
+ import { serve as bunServe } from 'bun'
2
+ import * as compiler from 'imba/compiler'
3
+ import { watch, existsSync } from 'fs'
4
+ import path from 'path'
5
+ import { theme } from './utils.js'
6
+
7
+ const hmrClient = `
8
+ <script>
9
+ const _originalDefine = customElements.define.bind(customElements);
10
+ const _registry = new Map();
11
+ const _updated = new Set();
12
+
13
+ customElements.define = function(name, cls, opts) {
14
+ const existing = _registry.get(name);
15
+ if (existing) {
16
+ Object.getOwnPropertyNames(cls.prototype).forEach(key => {
17
+ if (key === 'constructor') return;
18
+ try { Object.defineProperty(existing.prototype, key, Object.getOwnPropertyDescriptor(cls.prototype, key)); } catch(e) {}
19
+ });
20
+ Object.getOwnPropertyNames(cls).forEach(key => {
21
+ if (['length','name','prototype','arguments','caller'].includes(key)) return;
22
+ try { Object.defineProperty(existing, key, Object.getOwnPropertyDescriptor(cls, key)); } catch(e) {}
23
+ });
24
+ _updated.add(name);
25
+ } else {
26
+ _registry.set(name, cls);
27
+ _originalDefine(name, cls, opts);
28
+ }
29
+ };
30
+
31
+ function resetElement(el) {
32
+ Object.getOwnPropertySymbols(el).forEach(s => {
33
+ try { if (el[s] instanceof Node) el[s] = undefined; } catch(e) {}
34
+ });
35
+ el.innerHTML = '';
36
+ }
37
+
38
+ const ws = new WebSocket('ws://' + location.host + '/__hmr__');
39
+ ws.onmessage = (e) => {
40
+ const data = JSON.parse(e.data);
41
+ if (data.type === 'update') {
42
+ _updated.clear();
43
+ import('/' + data.file + '?t=' + Date.now()).then(() => {
44
+ const updatedClasses = [..._updated].map(n => _registry.get(n)).filter(Boolean);
45
+ const found = [];
46
+ if (updatedClasses.length) {
47
+ document.querySelectorAll('*').forEach(el => {
48
+ for (const cls of updatedClasses) {
49
+ if (el instanceof cls) { found.push(el); break; }
50
+ }
51
+ });
52
+ }
53
+ found.forEach(resetElement);
54
+ _updated.clear();
55
+ imba.commit();
56
+ });
57
+ } else if (data.type === 'reload') {
58
+ location.reload();
59
+ }
60
+ };
61
+ </script>`
62
+
63
+ async function compileFile(filepath) {
64
+ const code = await Bun.file(filepath).text()
65
+ return compiler.compile(code, { sourcePath: filepath, platform: 'browser' })
66
+ }
67
+
68
+ function findHtml(flagHtml) {
69
+ if (flagHtml) return flagHtml;
70
+ const candidates = ['./index.html', './public/index.html', './src/index.html'];
71
+ return candidates.find(p => existsSync(p)) || './index.html';
72
+ }
73
+
74
+ // Build importmap from package.json dependencies.
75
+ // Packages with an .imba entry point are served locally; others via esm.sh.
76
+ async function buildImportMap() {
77
+ const imports = {
78
+ "imba/runtime": "https://esm.sh/imba/runtime",
79
+ "imba": "https://esm.sh/imba"
80
+ };
81
+ try {
82
+ const pkg = JSON.parse(await Bun.file('./package.json').text());
83
+ for (const [name] of Object.entries(pkg.dependencies || {})) {
84
+ if (name === 'imba') continue;
85
+ try {
86
+ const depPkg = JSON.parse(await Bun.file(`./node_modules/${name}/package.json`).text());
87
+ const entry = depPkg.module || depPkg.main;
88
+ if (entry && entry.endsWith('.imba')) {
89
+ imports[name] = `/node_modules/${name}/${entry}`;
90
+ } else {
91
+ imports[name] = `https://esm.sh/${name}`;
92
+ }
93
+ } catch(e) {
94
+ imports[name] = `https://esm.sh/${name}`;
95
+ }
96
+ }
97
+ } catch(e) { /* no package.json, use defaults */ }
98
+
99
+ const json = JSON.stringify({ imports }, null, '\t\t\t\t');
100
+ return `\t\t<script type="importmap">\n\t\t\t${json}\n\t\t</script>`;
101
+ }
102
+
103
+ // Transform production HTML for dev:
104
+ // - removes existing importmap block
105
+ // - removes <script data-bimba> from its position
106
+ // - injects importmap + entrypoint script + HMR client before </head>
107
+ function transformHtml(html, entrypoint, importMapTag) {
108
+ html = html.replace(/<script\s+type=["']importmap["'][^>]*>[\s\S]*?<\/script>/gi, '');
109
+ html = html.replace(/<script([^>]*)\bdata-entrypoint\b([^>]*)><\/script>/gi, '');
110
+
111
+ const entryUrl = '/' + entrypoint.replace(/^\.\//, '').replaceAll('\\', '/');
112
+ const entryScript = `\t\t<script type='module' src='${entryUrl}'></script>`;
113
+
114
+ html = html.replace('</head>', `${importMapTag}\n${entryScript}\n${hmrClient}\n\t</head>`);
115
+ return html;
116
+ }
117
+
118
+ export function serve(entrypoint, flags) {
119
+ const port = flags.port || 5200
120
+ const htmlPath = findHtml(flags.html)
121
+ const htmlDir = path.dirname(htmlPath)
122
+ const srcDir = path.dirname(entrypoint)
123
+ const sockets = new Set()
124
+ let importMapTag = null
125
+
126
+ watch(srcDir, { recursive: true }, (_event, filename) => {
127
+ if (!filename || !filename.endsWith('.imba')) return
128
+ const rel = path.join(path.relative('.', srcDir), filename).replaceAll('\\', '/')
129
+ // console.log(theme.action('changed: ') + theme.filename(rel))
130
+ for (const socket of sockets)
131
+ socket.send(JSON.stringify({ type: 'update', file: rel }))
132
+ })
133
+
134
+ bunServe({
135
+ port,
136
+ development: true,
137
+
138
+ fetch: async (req, server) => {
139
+ const url = new URL(req.url)
140
+ const pathname = url.pathname
141
+
142
+ if (pathname === '/__hmr__') {
143
+ if (server.upgrade(req)) return undefined
144
+ }
145
+
146
+ if (pathname === '/' || pathname.endsWith('.html')) {
147
+ const htmlFile = pathname === '/' ? htmlPath : '.' + pathname
148
+ let html = await Bun.file(htmlFile).text()
149
+ if (!importMapTag) importMapTag = await buildImportMap()
150
+ html = transformHtml(html, entrypoint, importMapTag)
151
+ return new Response(html, { headers: { 'Content-Type': 'text/html' } })
152
+ }
153
+
154
+ if (pathname.endsWith('.imba')) {
155
+ try {
156
+ const out = await compileFile('.' + pathname)
157
+ return new Response(out.js, { headers: { 'Content-Type': 'application/javascript' } })
158
+ } catch (e) {
159
+ return new Response(e.message, { status: 500 })
160
+ }
161
+ }
162
+
163
+ // Static files: check htmlDir first (assets relative to HTML), then root (node_modules, src, etc.)
164
+ const htmlDirFile = Bun.file(path.join(htmlDir, pathname))
165
+ if (await htmlDirFile.exists()) return new Response(htmlDirFile)
166
+ const file = Bun.file('.' + pathname)
167
+ if (await file.exists()) return new Response(file)
168
+ return new Response('Not Found', { status: 404 })
169
+ },
170
+
171
+ websocket: {
172
+ open: (ws) => sockets.add(ws),
173
+ close: (ws) => sockets.delete(ws),
174
+ }
175
+ })
176
+
177
+ console.log(theme.folder('──────────────────────────────────────────────────────────────────────'))
178
+ console.log(theme.start(`Dev server running at `) + theme.success(`http://localhost:${port}`))
179
+ console.log(theme.folder('──────────────────────────────────────────────────────────────────────'))
180
+ }