Imports and Modules
In this chapter you will learn:
- How to import other eucalypt files and data files
- How to scope imports to specific declarations
- How to use named imports for namespacing
- How git imports work for external dependencies
Basic Imports
Import another eucalypt file using the import key in declaration
metadata:
{ import: "helpers.eu" }
# Names from helpers.eu are now available
result: helper-function(42)
When the metadata is at unit level (the first item in the file), the imported names are available throughout the entire file.
Scoped Imports
Imports can be scoped to a specific declaration, limiting where the imported names are visible:
` { import: "math.eu" }
calculations: {
# Names from math.eu are available only within this block
result: advanced-calculation(10)
}
# math.eu names are NOT available here
Named Imports
Give an import a name to access its contents under a namespace:
{ import: "cfg=config.eu" }
host: cfg.host
port: cfg.port
This is especially useful when importing data files that might contain names which clash with your own:
{ import: "prod=production.yaml" }
url: "https://{prod.host}:{prod.port}"
Importing Multiple Files
Supply a list to import several files at once:
{ import: ["helpers.eu", "config.eu"] }
result: helper(config-value)
Named and unnamed imports can be mixed:
{ import: ["helpers.eu", "cfg=config.eu"] }
result: helper(cfg.port)
Importing Data Files
Eucalypt can import files in any supported format. The format is inferred from the file extension:
{ import: "data=records.yaml" }
first-record: data head
You can override the format when the extension is misleading:
{ import: "data=yaml@records.txt" }
Formats That Return Lists
Some formats (CSV, JSON Lines, text) produce lists rather than blocks. These must be given a name:
{ import: "rows=transactions.csv" }
total: rows map(.amount num) foldl(+, 0)
Nested Imports
Imports can be placed at any level of nesting:
deep: {
nested: {
` { import: "local-config.eu" }
config: {
value: local-setting
}
}
}
Import Resolution Order
When an import path is a relative string (e.g. "helpers.eu" rather than an
absolute path or a git import), eucalypt resolves it by searching in this order:
- Source-relative directory — the directory containing the file that
contains the
importdeclaration. - Global lib path — the directories supplied via
-Lflags and the current working directory, searched in the order they were specified.
If the file is found in the source-relative directory it is used immediately and the global lib path is not consulted.
Transitive resolution
Resolution is always relative to the importing file, not to the entry-point file passed on the command line. This means:
main.euimports"lib/utils.eu"→ resolved as<dir-of-main>/lib/utils.eulib/utils.euimports"helpers/misc.eu"→ resolved as<dir-of-main>/lib/helpers/misc.eu(relative toutils.eu, not tomain.eu)lib/helpers/misc.euimports"sub/detail.eu"→ resolved as<dir-of-main>/lib/helpers/sub/detail.eu
Each file sees its own directory as the base for relative imports, no matter how deep the chain goes.
Practical example
Suppose your project is laid out as follows:
project/
main.eu
lib/
utils.eu
helpers/
misc.eu
main.eu can import lib/utils.eu using a path relative to itself:
{ import: "lib/utils.eu" }
result: util-function(42)
lib/utils.eu can import from lib/helpers/misc.eu using a path relative to
its own location:
{ import: "helpers/misc.eu" }
util-function(x): misc-helper(x)
No -L flags or absolute paths are needed. If a file is not found
source-relatively, eucalypt falls back to the global lib path, so existing
projects that rely on -L continue to work without modification.
Git Imports
Import eucalypt code directly from a git repository. This is useful for sharing libraries without manually managing local copies:
{ import: { git: "https://github.com/user/eu-lib"
commit: "abc123def456"
import: "lib/helpers.eu" } }
result: lib-function(42)
The commit field is mandatory and should be a full SHA. This ensures
the import is repeatable and cacheable.
Multiple git imports can be listed alongside simple imports:
{ import: [
"local.eu",
{ git: "https://github.com/user/lib"
commit: "abc123"
import: "helpers.eu" }
] }
Streaming Imports
For large files, streaming imports read data lazily:
{ import: "events=jsonl-stream@events.jsonl" }
recent: events take(100)
Available streaming formats:
| Format | Description |
|---|---|
jsonl-stream | JSON Lines (one object per line) |
csv-stream | CSV with headers |
text-stream | Plain text (one string per line) |
Combining Imports with the Command Line
Imports in .eu files complement the command line input system. You
can use both together:
eu data.yaml transform.eu
Here data.yaml is a command-line input and transform.eu can also
have its own { import: ... } declarations for helpers or
configuration.
See The Command Line for details on the input system.
Practical Example: Configuration Layering
# base.eu
defaults: {
host: "0.0.0.0"
port: 8080
workers: 4
}
# deploy.eu
{ import: "base.eu" }
production: defaults << {
workers: 16
host: "prod.example.com"
}
staging: defaults << {
host: "staging.example.com"
}
Running eu deploy.eu produces layered configuration with shared
defaults.
Key Concepts
- Use
{ import: "file.eu" }in metadata to import files - Named imports (
"name=file") provide namespace isolation - Imports can be scoped to individual declarations
- Data files (YAML, JSON, CSV, etc.) can be imported like code
- Relative paths resolve against the importing file's directory first,
then the global lib path — no
-Lflags needed for co-located helpers - Git imports pull code directly from repositories at a specific commit
- Streaming imports (
jsonl-stream@,csv-stream@,text-stream@) handle large files lazily