Imports
The @import() root decorator allows you to import schema and/or values from other sources (currently just .env files), making it easy to share config across services within a monorepo, split up large schemas, or reuse pre-defined schemas. Multiple @import() calls may be used, and an imported source may itself import more sources.
Basic examples:
# @import(./.env.imported) # import specific file# @import(./env-dir/) # import directory# @import(./.env.partial, KEY1, KEY2) # import specific keys# @import(~/.env.shared) # import from home directoryImport source types
Section titled “Import source types”The first argument to @import() specifies where to look for file(s) to import.
Currently only local file imports are supported, but we plan to support importing over http in a style similar to Deno’s http imports.
For now, all imported files must be .env files (and may contain @env-spec decorators), but in the future, we may also support other formats (e.g., JSON, YAML, etc.) or even JS/TS files.
Single file
Section titled “Single file”- Path must begin with
./,../,/, or~/ - Imported file name must be begin with
.env.
# @import(./.env.common)Directory
Section titled “Directory”- Path must begin with
./,../,/, or~/ - Path must end with a trailing
/ - Multiple
.env.*files will be detected and loaded, based on the current environment flag, similar to what happens in the current directory (see environments guide) - The environment flag value will be inherited, unless another
@currentEnvis defined within the directory’s.env.schema
# @import(../shared-config-dir/)Partial imports
Section titled “Partial imports”By default, all items will be imported, but you may add a list of specific keys to import as additional args after the first.
- If there is a chain of imports, an item is only imported if every ancestor import includes it (or imports all items)
# @import(./.env.imported, KEY1, KEY2)Conditional imports
Section titled “Conditional imports”While you can use the @disable root decorator to disable a file from within that file, you can also use the enabled parameter of the @import() decorator to conditionally load the file.
The enabled parameter accepts any expression that evaluates to a boolean. It can be combined with partial imports to only import specific keys from a file.
Example:
# Combine with partial imports# @import(./.env.features, FEATURE_X, enabled=eq($ENABLE_X, "true"))# ---ENABLE_X=trueOptional imports
Section titled “Optional imports”By default, @import() will cause a loading error if the specified file or directory does not exist. You can use the allowMissing parameter to make an import optional - if the file or directory doesn’t exist, it will be silently skipped without causing an error.
The allowMissing parameter accepts a boolean value (defaults to false). It can be combined with partial imports and the enabled parameter.
Example:
# Import if exists, skip if not# @import(./.env.local, allowMissing=true)Combine with other parameters:
# Optional partial import with conditional loading# @import(./.env.features, FEATURE_X, enabled=true, allowMissing=true)Import precedence and merging multiple sources
Section titled “Import precedence and merging multiple sources”Varlock is designed to load multiple definitions for a single item and merge them together.
The common case would be taking schema info from .env.schema and overriding a value from another source (e.g., .env.local, .env.production, etc.),
but there are many cases where root decorators, item decorators, and descriptions may be merged as well.
To do this, we usually walk our data sources in decreasing order of precedence, until we find something defined for the value/decorator/etc we are evaluating.
Precedence rules are:
- Imported files are processed in order, with later imports overriding previous imports
- Definitions and root decorators in the importing file override those in files it imports
- For a directory, the precedence order is
.env.schema<.env<.env.local<.env.{currentEnv}<.env.{currentEnv}.local
For example, given a .env.local and a .env.schema that imports 2 files:
# @import(./.env.import1)# @import(./.env.import2)The precedence order would be .env.import1 < .env.import2 < .env.schema < .env.local.
Meaning if there was a value for ITEM in all 4 files, the final value used would be the one from .env.local.
More details
Section titled “More details”- Root decorators that affect individual items (e.g.,
@defaultRequired) affect only the items that are defined in the file, not those in imported files - An item with no value at all (e.g.,
ITEM=) will be skipped when looking for a value / function to use, but its presence can be used to add other decorators/description to the item - If an imported file is marked with
@disable, it and any files it imports are skipped entirely