# Chapter 5 — Modules, `import`, and packaging (the honest version) This chapter answers the most common disappointment: **“Why can’t I use `import` in my Bare OS script?”** --- ## Short answer In-image scripts (`/boot/init.js`, `/bin/*`, `~/tool.js`) are executed as **`AsyncFunction` bodies**, not as ES modules. The JavaScript engine never runs the **ESM loader** for those strings. Therefore: - **`import x from 'y'`** is a **syntax error** in that context (top-level `import` is only valid in modules). - **`require`** is likewise unavailable unless the host injected a global (do not rely on it for portable utilities). --- ## How `/bin` utilities still share code The **bare-os-coreutils** build concatenates: 1. **`lib/runtime.js`** (shared helpers: `bareStdin`, mode formatting, …) 2. Optional **preamble** files (`sed-engine.js`, `awk-engine.js`, `man-render.js`) 3. **`src/.js`** (must contain only `async function run` and helpers in the same string—**no `import`**) So “modules” become **one compiled file** on the drive. That is the **supported** pattern for shared logic in tier-1 utilities. --- ## Patterns that work for user and kernel code ### 1. Inline helpers For small scripts, define functions above `run`: ```js function double(n) { return n * 2 } async function run(ctx, argv) { ctx.console.log(String(double(21))) } ``` ### 2. Copy-paste prelude snippets You may copy minimal helpers (e.g. stdin reader) from [`runtime.js`](../packages/bare-os-coreutils/lib/runtime.js) into your script. Keep the license header in mind if you redistribute; see [LICENSE](../LICENSE). ### 3. Load another file from the drive (advanced) You _can_ `readFile` a second script as a string and… **you should not `eval` arbitrary untrusted content**. For **your own** modules stored as `~/lib/helpers.js`, a pattern is: - Store **function bodies only** or data (JSON), not full `import` syntax. - Or concatenate at **build time on the host** before uploading to Hyperdrive. There is no built-in `import()` dynamic loader wired to Hyperdrive in the stock booter. ### 4. Host-side bundling If you generate a **single** `bundle.js` on your laptop with esbuild/rollup and upload it to `~/bundle.js`, that file can use **no external `import` at runtime** because everything is already bundled. This is the closest to “npm on device” without changing the booter. --- ## Host packages (booter / seeder): full ESM When you edit **`packages/bare-os-booter/index.js`**, you are in **module** land: ```js import { runBinCommand } from './lib/kernel-runner.js' ``` Use this for **new protocols**, **drive encryption**, **alternate kernels**, etc. This is **not** the same as writing `/bin/foo`. --- ## `ctx.bare` — Holepunch-style modules without `import` When **`BARE_OS_BARE_MODULES`** is not disabled, the booter exposes **`ctx.bare`**: a **frozen** object whose keys are defined by [`bare-module-manifest.json`](../packages/bare-os-booter/lib/bare-module-manifest.json). Each entry names an npm package and a stable **`ctxKey`** (for example **`b4a`**, **`protomux`**, **`compactEncoding`**, **`holesail`**). **Drive bundles (trusted image):** the system image may include **`/lib/bare/manifest.json`** and `**/lib/bare/bundles/*.js**`. Those scripts are **IIFE** bundles built by **`bare-os-bare-libs`**. The booter executes them with **`Function`** in the same trust class as seeded **`/bin`** utilities and fills **`ctx.bare`** for the listed keys. Set `**BARE_OS_BARE_DRIVE_BUNDLES=0`** to skip this step. **Host resolution:** after drive merge, the booter uses dynamic **`import()`** for manifest entries that are **still missing**. When the booter loads from a **`pear:`** URL (**`pear run`**), rows with **`bundle: true`** are **not** host-`**import()`**ed (bare-module cannot resolve npm package names from that referrer; **`ctx.bare.holesail`** et al. come from **`/lib/bare/bundles/*`**). On **`file:`** checkouts and Node test harnesses, **`bundle: true`** may still be host-imported as a fallback when a drive bundle is absent. Optional packages that fail to load (for example native-only modules on the wrong host) are skipped without aborting boot. **Hardening:** set **`BARE_OS_BARE_MODULES=0`** to omit **`ctx.bare`** entirely (the property is absent on **`ctx`**). Runtime caps **`bareCtxModules`** and **`bareDriveBundles`** mirror these toggles. Full ecosystem context: [Chapter 12 — Bare modules and Pear ecosystem](12-bare-modules-and-pear-ecosystem.md). --- ## Pear and Bare globals Under Pear/Bare, some globals (e.g. **`Bare`**) may exist for **host** exit and lifecycle. In-image utilities should still prefer **`ctx`** for I/O to stay consistent when the same script pattern is tested under different harnesses. Prefer **`ctx.bare.*`** over relying on **`Bare`**-specific package side effects when you need **`bare-url`** / **`bare-path`** on the Pear runtime. --- ## FAQ corner **Can I add dynamic `import` to the booter for user scripts?** Possible in theory (resolve specifiers from Hyperdrive) but **not implemented**, and it raises **security** and **package format** questions (where do dependencies live?). **Can I put `node_modules` on my personal drive?** Even if you replicated bytes, the in-image loader would not resolve them as Node does. You would need a **host** or **kernel** change to load from that tree. --- ## See also - [Chapter 12 — Bare modules and Pear ecosystem](12-bare-modules-and-pear-ecosystem.md) - [Chapter 6 — Extending `/bin`](06-extending-bin-coreutils.md) - [Chapter 9 — Security](09-security-and-trust.md) - [bare-os-coreutils README](../packages/bare-os-coreutils/README.md) --- [← User scripts](04-user-scripts-and-path.md) · [Extending /bin →](06-extending-bin-coreutils.md)