Skip to content

Commit cd52bce

Browse files
committed
Add module layout docs and require migration guide
1 parent 6245a03 commit cd52bce

5 files changed

Lines changed: 143 additions & 6 deletions

File tree

ROADMAP.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff 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

docs/examples/module_require.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ def total_with_fee(amount)
4242
end
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+
4555
When `total_with_fee` runs, `require("fees")` resolves the module relative to
4656
`Config.ModulePaths`, compiles it once, and returns an object containing the
4757
module’s exports. Use `export def` for explicit control; if no explicit exports
@@ -60,6 +70,21 @@ def compute(amount)
6070
end
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+
6388
Relative requires (`./` and `../`) resolve from the requiring module’s
6489
directory and cannot escape the configured module root.
6590

docs/introduction.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff 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.

docs/module_project_layout.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
```

docs/module_require_migration.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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`).

0 commit comments

Comments
 (0)