CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-liftoff

Launch your command line tool with ease by handling configuration discovery, local module resolution, and process management.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

environment-building.mddocs/

Environment Building

Environment building functionality that discovers configuration files, resolves module paths, and prepares the complete runtime context with all necessary metadata for CLI tool execution.

Capabilities

Build Environment Method

Builds the execution environment by discovering configuration files, resolving local module paths, and preparing all runtime metadata needed for CLI tool execution.

/**
 * Builds the execution environment with configuration and module resolution
 * @param opts - Options for environment building
 * @returns Environment object with discovered paths and configuration
 */
buildEnvironment(opts?: EnvironmentOptions): Environment;

interface EnvironmentOptions {
  /** Override current working directory for this execution */
  cwd?: string;
  /** Explicit path to configuration file (skips discovery) */
  configPath?: string;
  /** Modules to preload before execution (string or array) */
  preload?: string | string[];
  /** Completion data for shell completions */
  completion?: any;
}

interface Environment {
  /** Current working directory resolved for this execution */
  cwd: string;
  /** Array of module names that will be preloaded */
  preload: string[];
  /** Regex or array of config file names that were searched for */
  configNameSearch: RegExp | string[];
  /** Full absolute path to found configuration file */
  configPath: string | null;
  /** Directory containing the configuration file */
  configBase: string | null;
  /** Full absolute path to the local module */
  modulePath: string | null;
  /** Contents of the local module's package.json */
  modulePackage: object;
  /** Array of found configuration file paths (null for not found) */
  configFiles: (string | null)[];
  /** Array of loaded configuration objects in same order as configFiles */
  config: object[];
}

Usage Examples:

const Liftoff = require('liftoff');

const MyApp = new Liftoff({
  name: 'myapp',
  configFiles: [
    { name: 'myappfile', path: '.', findUp: true },
    { name: '.myapprc', path: '~' }
  ],
  extensions: {
    '.js': null,
    '.json': null,
    '.yaml': 'js-yaml-loader'
  }
});

// Build environment with defaults
const env = MyApp.buildEnvironment();
console.log('Current directory:', env.cwd);
console.log('Config found at:', env.configPath);
console.log('Local module at:', env.modulePath);

// Build environment with custom options
const customEnv = MyApp.buildEnvironment({
  cwd: './my-project',
  preload: ['babel-register', 'source-map-support/register'],
  configPath: './custom-config.js'
});

console.log('Custom environment:', customEnv);

Configuration Discovery

The environment building process automatically discovers configuration files based on the configured search patterns and file extensions.

Configuration File Search Process:

  1. Search Path Resolution: Uses searchPaths from constructor options, with cwd added first
  2. File Name Generation: Creates file name patterns from configName + extensions
  3. File System Search: Searches each path for matching configuration files
  4. Path Resolution: Resolves found files to absolute paths
  5. Loader Registration: Registers appropriate loaders for found file extensions

Configuration Loading Process:

  1. File Reading: Loads each found configuration file
  2. Extension Processing: Applies registered loaders for custom extensions
  3. Inheritance Handling: Processes extends properties for configuration inheritance
  4. Circular Detection: Prevents infinite loops in configuration inheritance
  5. Merge Strategy: Deep merges configurations with later configs taking precedence
// Example of discovered configuration structure
const env = MyApp.buildEnvironment();

// env.configFiles might contain:
// [
//   '/path/to/project/myappfile.js',      // Primary config
//   '/home/user/.myapprc.json',           // User config
//   null                                  // Config not found
// ]

// env.config contains loaded config objects:
// [
//   { setting: 'value', extends: './base.js' },  // Loaded and processed
//   { userSetting: 'userValue' },                // User config
//   {}                                           // Empty for missing config
// ]

Module Resolution

The environment building process locates and resolves local modules that correspond to the CLI tool, enabling version-specific execution.

Module Resolution Process:

  1. Base Directory: Uses configBase (config file directory) or cwd as starting point
  2. Module Search: Uses Node.js resolve.sync() to find the local module
  3. Package Discovery: Locates and loads the module's package.json
  4. Path Resolution: Resolves to absolute paths for module and package
  5. Development Mode: Handles cases where config and module are in same directory (self-development)
// Example module resolution results
const env = MyApp.buildEnvironment();

console.log(env.modulePath);    // '/project/node_modules/myapp/index.js'
console.log(env.modulePackage); // Contents of myapp's package.json
// {
//   "name": "myapp",
//   "version": "2.1.0",
//   "main": "index.js",
//   "dependencies": { ... }
// }

Working Directory Resolution

Determines the appropriate working directory for CLI tool execution based on configuration file location and explicit options.

Working Directory Resolution Rules:

  1. Explicit CWD: If opts.cwd provided, use as-is for config search but preserve for execution
  2. Config-based CWD: If config found and no explicit CWD, use config's directory
  3. Default CWD: Use process.cwd() if no config found and no explicit CWD
  4. Search Paths: When explicit CWD provided, only search in that directory
const MyApp = new Liftoff({ name: 'myapp' });

// Working directory follows config file
const env1 = MyApp.buildEnvironment();
// If config found at /project/subdir/myappfile.js
// Then env1.cwd === '/project/subdir'

// Explicit working directory
const env2 = MyApp.buildEnvironment({ cwd: '/custom/path' });
// env2.cwd === '/custom/path' (absolute)
// Config search limited to /custom/path only

// Relative working directory
const env3 = MyApp.buildEnvironment({ cwd: './relative' });
// env3.cwd === path.resolve(process.cwd(), './relative')

Preload Module Handling

Manages the list of modules that should be preloaded before CLI tool execution, combining options from multiple sources.

Preload Sources (in priority order):

  1. Environment Options: Modules specified in buildEnvironment() options
  2. Configuration Files: Modules specified in config file preload property
  3. Default: Empty array if no preloads specified

Preload Processing:

  • Converts string values to single-item arrays
  • Concatenates arrays from different sources
  • Filters for unique module names
  • Validates that config-based preloads are strings or string arrays
const MyApp = new Liftoff({
  name: 'myapp',
  configFiles: [{ name: 'myappfile', path: '.' }]
});

// Config file contains: { preload: ['coffee-script/register'] }

const env = MyApp.buildEnvironment({
  preload: ['babel-register', 'source-map-support/register']
});

// env.preload will be:
// ['babel-register', 'source-map-support/register', 'coffee-script/register']

Error Handling

Environment building includes comprehensive error handling for common failure scenarios while maintaining graceful degradation.

Handled Error Scenarios:

  • Missing Configuration: Returns null for config paths when files not found
  • Module Resolution Failure: Returns null for module paths when local module not found
  • Invalid Extensions: Gracefully handles unregistered file extensions
  • Circular Extends: Throws descriptive error for circular configuration inheritance
  • Invalid Config Files: Throws error for malformed configuration files
  • Missing Extended Files: Throws error with path information for missing extended configs
const MyApp = new Liftoff({ name: 'myapp' });

try {
  const env = MyApp.buildEnvironment();
  
  // Check for successful discoveries
  if (env.configPath) {
    console.log('Config loaded from:', env.configPath);
  } else {
    console.log('No configuration file found');
  }
  
  if (env.modulePath) {
    console.log('Local module found at:', env.modulePath);
  } else {
    console.log('No local module found, using global');
  }
  
} catch (error) {
  if (error.message.includes('circular extend')) {
    console.error('Configuration has circular inheritance');
  } else if (error.message.includes('Unable to locate')) {
    console.error('Configuration extends missing file');
  } else {
    console.error('Environment building failed:', error.message);
  }
}

docs

configuration.md

core-lifecycle.md

environment-building.md

events.md

index.md

tile.json