File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -263,15 +263,15 @@ Goal: make multi-file script projects easier to compose and maintain.
263263
264264### Developer UX
265265
266- - [ ] Add docs for module project layout best practices.
267- - [ ] Add examples for reusable helper modules and namespaced imports.
268- - [ ] Add migration guide for existing ` require ` users.
266+ - [x ] Add docs for module project layout best practices.
267+ - [x ] Add examples for reusable helper modules and namespaced imports.
268+ - [x ] Add migration guide for existing ` require ` users.
269269
270270### v0.17.0 Definition of Done
271271
272- - [ ] Module APIs are explicit and predictable.
273- - [ ] Error output for cycle/import failures is actionable.
274- - [ ] Security invariants around module paths are fully tested.
272+ - [x ] Module APIs are explicit and predictable.
273+ - [x ] Error output for cycle/import failures is actionable.
274+ - [x ] Security invariants around module paths are fully tested.
275275
276276---
277277
Original file line number Diff line number Diff line change @@ -42,6 +42,16 @@ def total_with_fee(amount)
4242end
4343```
4444
45+ Namespaced imports scale better as helper sets grow:
46+
47+ ``` vibe
48+ def quote_total(amount)
49+ require("billing/fees", as: "fees")
50+ require("billing/taxes", as: "taxes")
51+ taxes.apply(fees.apply(amount))
52+ end
53+ ```
54+
4555When ` total_with_fee ` runs, ` require("fees") ` resolves the module relative to
4656` Config.ModulePaths ` , compiles it once, and returns an object containing the
4757module’s exports. Use ` export def ` for explicit control; if no explicit exports
@@ -60,6 +70,21 @@ def compute(amount)
6070end
6171```
6272
73+ Reusable helper modules can be shared from a central namespace:
74+
75+ ``` vibe
76+ # modules/shared/currency.vibe
77+ export def cents(value)
78+ value * 100
79+ end
80+
81+ # modules/billing/taxes.vibe
82+ export def apply(amount)
83+ require("../shared/currency", as: "currency")
84+ amount + currency.cents(1)
85+ end
86+ ```
87+
6388Relative requires (` ./ ` and ` ../ ` ) resolve from the requiring module’s
6489directory and cannot escape the configured module root.
6590
Original file line number Diff line number Diff line change @@ -24,5 +24,9 @@ dives on specific topics.
2424- ` blocks.md ` – using block literals for map/select/reduce style patterns.
2525- ` integration.md ` – host integration patterns showing how Go services can
2626 expose capabilities to scripts.
27+ - ` module_project_layout.md ` – recommended structure for multi-module script
28+ repositories.
29+ - ` module_require_migration.md ` – migration checklist for modern ` require `
30+ behavior (exports, aliasing, policy hooks).
2731- ` examples/module_require.md ` – practical example showing how to share
2832 helpers with ` require ` and module search paths.
Original file line number Diff line number Diff line change 1+ # Module Project Layout Best Practices
2+
3+ Use a stable directory layout so module paths stay predictable:
4+
5+ ``` text
6+ workflows/
7+ modules/
8+ shared/
9+ math.vibe
10+ money.vibe
11+ billing/
12+ fees.vibe
13+ taxes.vibe
14+ scripts/
15+ checkout.vibe
16+ payouts.vibe
17+ ```
18+
19+ Guidelines:
20+
21+ - Keep reusable helpers under ` modules/shared/ ` .
22+ - Group domain logic by folder (` billing/ ` , ` risk/ ` , ` reporting/ ` ).
23+ - Prefer ` export def ... ` for public API surface, keep internals unexported.
24+ - Use ` require("module/path", as: "alias") ` to avoid global name collisions.
25+ - Use relative requires only within a module subtree (` ./ ` , ` ../ ` ).
26+ - Configure ` ModuleAllowList ` and ` ModuleDenyList ` in hosts that need strict import policy boundaries.
27+
28+ Example:
29+
30+ ``` vibe
31+ # modules/billing/fees.vibe
32+ export def apply(amount)
33+ amount + shared_rate()
34+ end
35+
36+ def shared_rate()
37+ rates = require("../shared/math", as: "math")
38+ math.double(1)
39+ end
40+ ```
41+
42+ ``` vibe
43+ # scripts/checkout.vibe
44+ def total(amount)
45+ require("billing/fees", as: "fees")
46+ fees.apply(amount)
47+ end
48+ ```
Original file line number Diff line number Diff line change 1+ # Migration Guide for ` require `
2+
3+ This guide helps older scripts move to the current module model.
4+
5+ ## 1. Prefer explicit exports
6+
7+ Before:
8+
9+ ``` vibe
10+ def apply_fee(amount)
11+ amount + 1
12+ end
13+ ```
14+
15+ After:
16+
17+ ``` vibe
18+ export def apply_fee(amount)
19+ amount + 1
20+ end
21+ ```
22+
23+ If a module has no ` export def ` , non-underscore functions are still exported.
24+
25+ ## 2. Use aliases for namespacing
26+
27+ Before:
28+
29+ ``` vibe
30+ fees = require("fees")
31+ fees.apply_fee(amount)
32+ ```
33+
34+ After:
35+
36+ ``` vibe
37+ require("fees", as: "fees")
38+ fees.apply_fee(amount)
39+ ```
40+
41+ Aliases make import intent explicit and reduce global collisions.
42+
43+ ## 3. Plan for conflict behavior
44+
45+ - Existing globals are not overwritten by module exports.
46+ - Access conflicting functions through the returned/aliased module object.
47+ - Alias collisions raise runtime errors (` alias "<name>" already defined ` ).
48+
49+ ## 4. Add policy boundaries in hosts
50+
51+ For long-running or multi-tenant hosts:
52+
53+ - Configure ` ModuleAllowList ` and ` ModuleDenyList ` .
54+ - Use ` engine.ClearModuleCache() ` when module sources may change.
55+
56+ ## 5. Validate with tests
57+
58+ - Add integration tests for required module paths.
59+ - Add negative tests for denied modules and traversal attempts.
60+ - Verify cycle errors are actionable (` a -> b -> a ` ).
You can’t perform that action at this time.
0 commit comments