or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-lifecycle.mdenvironment-building.mdevents.mdindex.md
tile.json

configuration.mddocs/

0

# Configuration Management

1

2

Configuration file discovery and loading system supporting multiple formats, inheritance through extends, flexible search patterns, and custom file extension loaders.

3

4

## Capabilities

5

6

### Configuration File Discovery

7

8

Discovers configuration files based on search patterns, file extensions, and directory traversal rules defined in the Liftoff instance configuration.

9

10

```javascript { .api }

11

/**

12

* Configuration file search specification

13

* Used in configFiles array to define how to search for config files

14

*/

15

interface ConfigFileSpec {

16

/** Base name of the config file to search for */

17

name?: string;

18

/** Path to search in (required) - can be relative or absolute */

19

path: string;

20

/** File extensions to try when searching */

21

extensions?: string[] | { [ext: string]: string | null };

22

/** Base directory for resolving relative paths */

23

cwd?: string;

24

/** Whether to search up the directory tree until file found */

25

findUp?: boolean;

26

}

27

```

28

29

**Usage Examples:**

30

31

```javascript

32

const Liftoff = require('liftoff');

33

34

// Configuration with multiple config file sources

35

const MyApp = new Liftoff({

36

name: 'myapp',

37

configFiles: [

38

// Look for .myapprc in home directory

39

{

40

name: '.myapprc',

41

path: '~',

42

extensions: { '.json': null, '.js': null }

43

},

44

45

// Look for myappfile.js in current directory and up the tree

46

{

47

name: 'myappfile',

48

path: '.',

49

findUp: true

50

},

51

52

// Look for project-specific config in custom location

53

{

54

name: 'project-config',

55

path: './config',

56

cwd: process.cwd(),

57

extensions: ['.json', '.yaml', '.js']

58

}

59

],

60

extensions: {

61

'.js': null,

62

'.json': null,

63

'.yaml': 'yaml-loader'

64

}

65

});

66

67

const env = MyApp.buildEnvironment();

68

console.log('Found config files:', env.configFiles);

69

// Might output:

70

// [

71

// '/home/user/.myapprc.json',

72

// '/project/myappfile.js',

73

// '/project/config/project-config.yaml'

74

// ]

75

```

76

77

### Configuration Loading and Inheritance

78

79

Loads configuration files and processes inheritance through the `extends` property, with support for deep merging and circular reference detection.

80

81

```javascript { .api }

82

/**

83

* Configuration file structure with inheritance support

84

*/

85

interface ConfigurationFile {

86

/** Inherit from another configuration file */

87

extends?: string | ConfigFileSpec;

88

/** Override the discovered config path */

89

[configName: string]?: string;

90

/** Additional modules to preload */

91

preload?: string | string[];

92

/** Any other configuration properties */

93

[key: string]: any;

94

}

95

```

96

97

**Configuration Inheritance Process:**

98

99

1. **File Loading**: Load the primary configuration file

100

2. **Extends Processing**: If `extends` property exists, load the extended configuration

101

3. **Recursive Loading**: Process extends in the extended file (with circular detection)

102

4. **Deep Merging**: Merge configurations with primary config taking precedence

103

5. **Cleanup**: Remove `extends` property from final configuration

104

105

**Usage Examples:**

106

107

```javascript

108

// Base configuration file: base-config.js

109

module.exports = {

110

mode: 'development',

111

plugins: ['plugin-a', 'plugin-b'],

112

settings: {

113

verbose: true,

114

timeout: 5000

115

}

116

};

117

118

// Main configuration file: myappfile.js

119

module.exports = {

120

extends: './base-config.js',

121

mode: 'production', // Overrides base

122

plugins: ['plugin-c'], // Completely replaces base plugins array

123

settings: {

124

timeout: 10000 // Overrides timeout, keeps verbose: true

125

},

126

additionalSetting: 'value'

127

};

128

129

// Result after loading and merging:

130

// {

131

// mode: 'production',

132

// plugins: ['plugin-c'],

133

// settings: {

134

// verbose: true, // From base

135

// timeout: 10000 // From main config

136

// },

137

// additionalSetting: 'value'

138

// }

139

```

140

141

### Configuration Path Override

142

143

Configuration files can override the discovered configuration path using a property that matches the `configName`.

144

145

```javascript { .api }

146

/**

147

* Configuration path override mechanism

148

* A config file can specify an alternate config file to use

149

*/

150

interface ConfigPathOverride {

151

/** Property name matches the Liftoff configName */

152

[configName: string]: string;

153

}

154

```

155

156

**Usage Examples:**

157

158

```javascript

159

const MyApp = new Liftoff({

160

name: 'myapp', // configName becomes 'myappfile'

161

configFiles: [{ name: '.myapprc', path: '~' }]

162

});

163

164

// .myapprc.json content:

165

// {

166

// "myappfile": "./custom/path/to/config.js",

167

// "otherSettings": "value"

168

// }

169

170

const env = MyApp.buildEnvironment();

171

// env.configPath will be '/full/path/to/custom/path/to/config.js'

172

// The .myapprc config is used to find the real config, then the real config is loaded

173

174

// Relative paths are resolved from the directory containing the override config

175

// {

176

// "myappfile": "../configs/main.js" // Resolved relative to .myapprc location

177

// }

178

```

179

180

### Preload Module Configuration

181

182

Configuration files can specify additional modules to preload before CLI execution through the `preload` property.

183

184

```javascript { .api }

185

/**

186

* Preload specification in configuration files

187

*/

188

interface PreloadConfiguration {

189

/** Single module to preload */

190

preload?: string;

191

/** Multiple modules to preload */

192

preload?: string[];

193

}

194

```

195

196

**Usage Examples:**

197

198

```javascript

199

// Configuration file with preload modules

200

module.exports = {

201

// String format for single module

202

preload: 'coffee-script/register',

203

204

// Other config properties

205

sourceMaps: true,

206

plugins: ['babel-plugin-transform-runtime']

207

};

208

209

// Configuration file with multiple preload modules

210

module.exports = {

211

// Array format for multiple modules

212

preload: [

213

'babel-register',

214

'source-map-support/register',

215

'coffee-script/register'

216

],

217

218

// Babel configuration for the preloaded babel-register

219

babel: {

220

presets: ['env']

221

}

222

};

223

224

// The preload modules are combined with any specified in buildEnvironment options

225

const env = MyApp.buildEnvironment({

226

preload: ['ts-node/register'] // Added to config-based preloads

227

});

228

// env.preload = ['ts-node/register', 'coffee-script/register']

229

```

230

231

### File Extension Support

232

233

Configuration files support custom file extensions through registered loaders, allowing for TypeScript, CoffeeScript, YAML, and other formats.

234

235

```javascript { .api }

236

/**

237

* File extension to loader mapping

238

* Loaders are Node.js modules that register file extension handlers

239

*/

240

interface ExtensionLoaders {

241

/** Extension with no loader (built-in Node.js support) */

242

[extension: string]: null;

243

/** Extension with loader module name */

244

[extension: string]: string;

245

}

246

```

247

248

**Usage Examples:**

249

250

```javascript

251

const MyApp = new Liftoff({

252

name: 'myapp',

253

extensions: {

254

'.js': null, // Native JavaScript

255

'.json': null, // Native JSON

256

'.coffee': 'coffee-script/register', // CoffeeScript

257

'.ts': 'ts-node/register', // TypeScript

258

'.yaml': 'js-yaml-loader', // YAML

259

'.toml': 'toml-require' // TOML

260

},

261

configFiles: [

262

{ name: 'myappfile', path: '.', findUp: true }

263

]

264

});

265

266

// Can now load any of these config files:

267

// - myappfile.js

268

// - myappfile.json

269

// - myappfile.coffee

270

// - myappfile.ts

271

// - myappfile.yaml

272

// - myappfile.toml

273

274

// The appropriate loader will be automatically required and registered

275

MyApp.on('loader:success', function(loaderName, module) {

276

console.log('Loaded extension loader:', loaderName);

277

});

278

279

MyApp.on('loader:failure', function(loaderName, error) {

280

console.error('Failed to load extension loader:', loaderName, error.message);

281

});

282

```

283

284

### Advanced Configuration Patterns

285

286

Complex configuration scenarios including multiple inheritance levels, development vs production configs, and modular configuration structures.

287

288

**Multi-level Inheritance:**

289

290

```javascript

291

// base.js - Foundation configuration

292

module.exports = {

293

core: {

294

timeout: 5000,

295

retries: 3

296

}

297

};

298

299

// development.js - Development overrides

300

module.exports = {

301

extends: './base.js',

302

core: {

303

debug: true,

304

timeout: 1000 // Faster timeout for development

305

},

306

devServer: {

307

port: 3000

308

}

309

};

310

311

// myappfile.js - Project-specific configuration

312

module.exports = {

313

extends: './development.js',

314

project: {

315

name: 'my-project',

316

version: '1.0.0'

317

},

318

core: {

319

retries: 5 // Override retries for this project

320

}

321

};

322

323

// Final merged configuration:

324

// {

325

// core: {

326

// timeout: 1000, // From development.js

327

// retries: 5, // From myappfile.js

328

// debug: true // From development.js

329

// },

330

// devServer: {

331

// port: 3000 // From development.js

332

// },

333

// project: {

334

// name: 'my-project',

335

// version: '1.0.0' // From myappfile.js

336

// }

337

// }

338

```

339

340

**Error Handling and Validation:**

341

342

```javascript

343

const MyApp = new Liftoff({

344

name: 'myapp',

345

configFiles: [{ name: 'myappfile', path: '.', findUp: true }]

346

});

347

348

try {

349

const env = MyApp.buildEnvironment();

350

351

// Validate configuration structure

352

if (env.config.length > 0) {

353

const config = env.config[0];

354

355

// Check for required properties

356

if (!config.mode) {

357

console.warn('No mode specified in configuration');

358

}

359

360

// Validate preload modules

361

if (config.preload && !Array.isArray(config.preload) && typeof config.preload !== 'string') {

362

console.error('Invalid preload configuration: must be string or array');

363

}

364

}

365

366

} catch (error) {

367

if (error.message.includes('circular extend')) {

368

console.error('Circular configuration inheritance detected');

369

console.error('Check your extends chain for loops');

370

} else if (error.message.includes('Unable to locate')) {

371

console.error('Configuration extends references missing file');

372

console.error('Verify the path in your extends property');

373

} else if (error.message.includes('Encountered error when loading')) {

374

console.error('Configuration file has syntax or runtime errors');

375

console.error('Check the configuration file for valid JavaScript/JSON');

376

} else {

377

console.error('Configuration loading failed:', error.message);

378

}

379

}

380

```