0
# Environment Building
1
2
Environment building functionality that discovers configuration files, resolves module paths, and prepares the complete runtime context with all necessary metadata for CLI tool execution.
3
4
## Capabilities
5
6
### Build Environment Method
7
8
Builds the execution environment by discovering configuration files, resolving local module paths, and preparing all runtime metadata needed for CLI tool execution.
9
10
```javascript { .api }
11
/**
12
* Builds the execution environment with configuration and module resolution
13
* @param opts - Options for environment building
14
* @returns Environment object with discovered paths and configuration
15
*/
16
buildEnvironment(opts?: EnvironmentOptions): Environment;
17
18
interface EnvironmentOptions {
19
/** Override current working directory for this execution */
20
cwd?: string;
21
/** Explicit path to configuration file (skips discovery) */
22
configPath?: string;
23
/** Modules to preload before execution (string or array) */
24
preload?: string | string[];
25
/** Completion data for shell completions */
26
completion?: any;
27
}
28
29
interface Environment {
30
/** Current working directory resolved for this execution */
31
cwd: string;
32
/** Array of module names that will be preloaded */
33
preload: string[];
34
/** Regex or array of config file names that were searched for */
35
configNameSearch: RegExp | string[];
36
/** Full absolute path to found configuration file */
37
configPath: string | null;
38
/** Directory containing the configuration file */
39
configBase: string | null;
40
/** Full absolute path to the local module */
41
modulePath: string | null;
42
/** Contents of the local module's package.json */
43
modulePackage: object;
44
/** Array of found configuration file paths (null for not found) */
45
configFiles: (string | null)[];
46
/** Array of loaded configuration objects in same order as configFiles */
47
config: object[];
48
}
49
```
50
51
**Usage Examples:**
52
53
```javascript
54
const Liftoff = require('liftoff');
55
56
const MyApp = new Liftoff({
57
name: 'myapp',
58
configFiles: [
59
{ name: 'myappfile', path: '.', findUp: true },
60
{ name: '.myapprc', path: '~' }
61
],
62
extensions: {
63
'.js': null,
64
'.json': null,
65
'.yaml': 'js-yaml-loader'
66
}
67
});
68
69
// Build environment with defaults
70
const env = MyApp.buildEnvironment();
71
console.log('Current directory:', env.cwd);
72
console.log('Config found at:', env.configPath);
73
console.log('Local module at:', env.modulePath);
74
75
// Build environment with custom options
76
const customEnv = MyApp.buildEnvironment({
77
cwd: './my-project',
78
preload: ['babel-register', 'source-map-support/register'],
79
configPath: './custom-config.js'
80
});
81
82
console.log('Custom environment:', customEnv);
83
```
84
85
### Configuration Discovery
86
87
The environment building process automatically discovers configuration files based on the configured search patterns and file extensions.
88
89
**Configuration File Search Process:**
90
91
1. **Search Path Resolution**: Uses `searchPaths` from constructor options, with `cwd` added first
92
2. **File Name Generation**: Creates file name patterns from `configName` + `extensions`
93
3. **File System Search**: Searches each path for matching configuration files
94
4. **Path Resolution**: Resolves found files to absolute paths
95
5. **Loader Registration**: Registers appropriate loaders for found file extensions
96
97
**Configuration Loading Process:**
98
99
1. **File Reading**: Loads each found configuration file
100
2. **Extension Processing**: Applies registered loaders for custom extensions
101
3. **Inheritance Handling**: Processes `extends` properties for configuration inheritance
102
4. **Circular Detection**: Prevents infinite loops in configuration inheritance
103
5. **Merge Strategy**: Deep merges configurations with later configs taking precedence
104
105
```javascript
106
// Example of discovered configuration structure
107
const env = MyApp.buildEnvironment();
108
109
// env.configFiles might contain:
110
// [
111
// '/path/to/project/myappfile.js', // Primary config
112
// '/home/user/.myapprc.json', // User config
113
// null // Config not found
114
// ]
115
116
// env.config contains loaded config objects:
117
// [
118
// { setting: 'value', extends: './base.js' }, // Loaded and processed
119
// { userSetting: 'userValue' }, // User config
120
// {} // Empty for missing config
121
// ]
122
```
123
124
### Module Resolution
125
126
The environment building process locates and resolves local modules that correspond to the CLI tool, enabling version-specific execution.
127
128
**Module Resolution Process:**
129
130
1. **Base Directory**: Uses `configBase` (config file directory) or `cwd` as starting point
131
2. **Module Search**: Uses Node.js `resolve.sync()` to find the local module
132
3. **Package Discovery**: Locates and loads the module's `package.json`
133
4. **Path Resolution**: Resolves to absolute paths for module and package
134
5. **Development Mode**: Handles cases where config and module are in same directory (self-development)
135
136
```javascript
137
// Example module resolution results
138
const env = MyApp.buildEnvironment();
139
140
console.log(env.modulePath); // '/project/node_modules/myapp/index.js'
141
console.log(env.modulePackage); // Contents of myapp's package.json
142
// {
143
// "name": "myapp",
144
// "version": "2.1.0",
145
// "main": "index.js",
146
// "dependencies": { ... }
147
// }
148
```
149
150
### Working Directory Resolution
151
152
Determines the appropriate working directory for CLI tool execution based on configuration file location and explicit options.
153
154
**Working Directory Resolution Rules:**
155
156
1. **Explicit CWD**: If `opts.cwd` provided, use as-is for config search but preserve for execution
157
2. **Config-based CWD**: If config found and no explicit CWD, use config's directory
158
3. **Default CWD**: Use `process.cwd()` if no config found and no explicit CWD
159
4. **Search Paths**: When explicit CWD provided, only search in that directory
160
161
```javascript
162
const MyApp = new Liftoff({ name: 'myapp' });
163
164
// Working directory follows config file
165
const env1 = MyApp.buildEnvironment();
166
// If config found at /project/subdir/myappfile.js
167
// Then env1.cwd === '/project/subdir'
168
169
// Explicit working directory
170
const env2 = MyApp.buildEnvironment({ cwd: '/custom/path' });
171
// env2.cwd === '/custom/path' (absolute)
172
// Config search limited to /custom/path only
173
174
// Relative working directory
175
const env3 = MyApp.buildEnvironment({ cwd: './relative' });
176
// env3.cwd === path.resolve(process.cwd(), './relative')
177
```
178
179
### Preload Module Handling
180
181
Manages the list of modules that should be preloaded before CLI tool execution, combining options from multiple sources.
182
183
**Preload Sources (in priority order):**
184
185
1. **Environment Options**: Modules specified in `buildEnvironment()` options
186
2. **Configuration Files**: Modules specified in config file `preload` property
187
3. **Default**: Empty array if no preloads specified
188
189
**Preload Processing:**
190
191
- Converts string values to single-item arrays
192
- Concatenates arrays from different sources
193
- Filters for unique module names
194
- Validates that config-based preloads are strings or string arrays
195
196
```javascript
197
const MyApp = new Liftoff({
198
name: 'myapp',
199
configFiles: [{ name: 'myappfile', path: '.' }]
200
});
201
202
// Config file contains: { preload: ['coffee-script/register'] }
203
204
const env = MyApp.buildEnvironment({
205
preload: ['babel-register', 'source-map-support/register']
206
});
207
208
// env.preload will be:
209
// ['babel-register', 'source-map-support/register', 'coffee-script/register']
210
```
211
212
### Error Handling
213
214
Environment building includes comprehensive error handling for common failure scenarios while maintaining graceful degradation.
215
216
**Handled Error Scenarios:**
217
218
- **Missing Configuration**: Returns `null` for config paths when files not found
219
- **Module Resolution Failure**: Returns `null` for module paths when local module not found
220
- **Invalid Extensions**: Gracefully handles unregistered file extensions
221
- **Circular Extends**: Throws descriptive error for circular configuration inheritance
222
- **Invalid Config Files**: Throws error for malformed configuration files
223
- **Missing Extended Files**: Throws error with path information for missing extended configs
224
225
```javascript
226
const MyApp = new Liftoff({ name: 'myapp' });
227
228
try {
229
const env = MyApp.buildEnvironment();
230
231
// Check for successful discoveries
232
if (env.configPath) {
233
console.log('Config loaded from:', env.configPath);
234
} else {
235
console.log('No configuration file found');
236
}
237
238
if (env.modulePath) {
239
console.log('Local module found at:', env.modulePath);
240
} else {
241
console.log('No local module found, using global');
242
}
243
244
} catch (error) {
245
if (error.message.includes('circular extend')) {
246
console.error('Configuration has circular inheritance');
247
} else if (error.message.includes('Unable to locate')) {
248
console.error('Configuration extends missing file');
249
} else {
250
console.error('Environment building failed:', error.message);
251
}
252
}
253
```