CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yeoman-generator

Rails-inspired generator system that provides scaffolding for your apps

Overview
Eval results
Files

configuration.mddocs/

Configuration Management

Persistent storage system for generator configurations, user preferences, and project settings using JSON files with support for namespacing and hierarchical storage.

Capabilities

Storage Class

Core storage class for persistent configuration management.

/**
 * Storage instances handle a JSON file where Generator authors can store data
 * The Generator class instantiates the storage named 'config' by default
 */
class Storage {
  constructor(name: string | undefined, fs: MemFsEditor, configPath: string, options?: StorageOptions);
  constructor(fs: MemFsEditor, configPath: string, options?: StorageOptions);
  
  // File system properties
  readonly path: string;
  readonly name?: string;
  readonly fs: MemFsEditor;
  readonly existed: boolean;
  
  // Configuration options
  readonly lodashPath: boolean;
  readonly disableCache: boolean;
  readonly disableCacheByFile: boolean;
  readonly sorted: boolean;
  readonly indent: number;
}

Usage Example:

export default class MyGenerator extends Generator {
  constructor(args, opts) {
    super(args, opts);
    
    // Storage is automatically available as this.config
    console.log(this.config.path); // ~/.yo-rc.json (or project-specific)
    console.log(this.config.existed); // true if config existed before
  }
}

Data Operations

Core methods for storing and retrieving configuration data.

/**
 * Get a stored value
 * @param key - The key under which the value is stored
 * @returns The stored value (any JSON valid type)
 */
get<T extends StorageValue = StorageValue>(key: string): T;

/**
 * Get a stored value from a lodash path
 * @param path - The path under which the value is stored
 * @returns The stored value (any JSON valid type)
 */
getPath<T extends StorageValue = StorageValue>(path: string): T;

/**
 * Get all the stored values
 * @returns Key-value object with all stored data
 */
getAll(): StorageRecord;

/**
 * Assign a key to a value and schedule a save
 * @param key - The key under which the value is stored
 * @param value - Any valid JSON type value
 * @returns Whatever was passed in as value
 */
set<V = StorageValue>(value: V): V;
set<V = StorageValue>(key: string | number, value?: V): V | undefined;

/**
 * Assign a lodash path to a value and schedule a save
 * @param path - The lodash path under which the value is stored
 * @param value - Any valid JSON type value
 * @returns Whatever was passed in as value
 */
setPath(path: string | number, value: StorageValue): StorageValue;

/**
 * Delete a key from the store and schedule a save
 * @param key - The key under which the value is stored
 */
delete(key: string): void;

/**
 * Save current state to disk
 */
save(): void;

Usage Examples:

export default class MyGenerator extends Generator {
  configuring() {
    // Basic get/set operations
    this.config.set('projectName', this.answers.name);
    this.config.set('version', '1.0.0');
    
    // Get stored values
    const projectName = this.config.get('projectName');
    const allConfig = this.config.getAll();
    
    // Object storage
    this.config.set({
      author: this.answers.author,
      license: this.answers.license,
      features: this.answers.features
    });
    
    // Lodash path operations
    this.config.setPath('database.host', 'localhost');
    this.config.setPath('database.port', 5432);
    const dbHost = this.config.getPath('database.host');
    
    // Delete keys
    this.config.delete('temporaryData');
    
    // Manual save (usually automatic)
    this.config.save();
  }
}

Merge and Defaults

Methods for combining configuration data and setting default values.

/**
 * Setup the store with defaults value and schedule a save
 * If keys already exist, the initial value is kept
 * @param defaults - Key-value object to store
 * @returns The merged options
 */
defaults(defaults: StorageRecord): StorageRecord;

/**
 * Merge source data with existing store data
 * @param source - Key-value object to merge
 * @returns The merged object
 */
merge(source: StorageRecord): StorageRecord;

Usage Examples:

export default class MyGenerator extends Generator {
  configuring() {
    // Set defaults (won't overwrite existing values)
    this.config.defaults({
      version: '1.0.0',
      main: 'index.js',
      scripts: {
        start: 'node index.js',
        test: 'echo "No tests yet"'
      }
    });
    
    // Merge additional configuration  
    this.config.merge({
      keywords: this.answers.keywords?.split(',') || [],
      dependencies: this.answers.dependencies || {},
      author: {
        name: this.answers.authorName,
        email: this.answers.authorEmail
      }
    });
  }
}

Storage Creation and Hierarchy

Create child storage instances and manage storage hierarchies.

/**
 * Return a storage instance for the specified path
 * @param storePath - The path of the JSON file
 * @param options - Storage options or the storage name
 * @returns New Storage instance
 */
createStorage(storePath: string, options?: string | StorageOptions): Storage;

/**
 * Create a child storage
 * @param path - Relative path of the key to create a new storage
 * @returns New Storage instance with namespaced access
 */
createStorage(path: string): Storage;

/**
 * Creates a proxy object for direct property access
 * @returns Proxy that allows direct property access
 */
createProxy(): StorageRecord;

Usage Examples:

export default class MyGenerator extends Generator {
  constructor(args, opts) {
    super(args, opts);
    
    // Create separate storage files
    this.userPrefs = this.createStorage('.yo-preferences.json', 'userPrefs');
    this.projectMeta = this.createStorage('project-meta.json');
    
    // Create child storage (namespaced within same file)
    this.dbConfig = this.config.createStorage('database');
    this.apiConfig = this.config.createStorage('api');
    
    // Create proxy for direct access
    this.configProxy = this.config.createProxy();
  }
  
  configuring() {
    // Use separate storage instances
    this.userPrefs.set('theme', 'dark');
    this.projectMeta.set('created', new Date().toISOString());
    
    // Use child storage (stored as nested objects)
    this.dbConfig.set('host', 'localhost');
    this.dbConfig.set('port', 5432);
    this.apiConfig.set('version', 'v1');
    
    // Use proxy for direct access
    this.configProxy.projectName = this.answers.name;
    this.configProxy.version = '1.0.0';
  }
}

Package.json Storage

Special storage instance for package.json manipulation.

/**
 * Package.json Storage resolved to this.destinationPath('package.json')
 * Environment watches for package.json changes and triggers package manager install
 */
readonly packageJson: Storage;

Usage Examples:

export default class MyGenerator extends Generator {
  configuring() {
    // Modify package.json using storage interface
    this.packageJson.merge({
      name: this.answers.name,
      version: '1.0.0',
      description: this.answers.description,
      main: 'index.js',
      scripts: {
        start: 'node index.js',
        test: 'npm run test:unit',
        'test:unit': 'jest'
      },
      keywords: this.answers.keywords?.split(','),
      author: this.answers.author,
      license: this.answers.license
    });
    
    // Add dependencies conditionally
    if (this.answers.includeExpress) {
      this.packageJson.merge({
        dependencies: {
          express: '^4.18.0'
        }
      });
    }
    
    if (this.answers.includeTests) {
      this.packageJson.merge({
        devDependencies: {
          jest: '^29.0.0',
          '@types/jest': '^29.0.0'
        }
      });
    }
  }
}

Generator Storage Instances

Built-in storage instances available in generators.

/**
 * Generator config Storage (default: .yo-rc.json)
 */
readonly config: Storage;

/**
 * Package.json Storage resolved to destinationPath('package.json')
 */
readonly packageJson: Storage;

/**
 * Generator-specific configuration (namespaced by generator name)
 */
readonly generatorConfig?: Storage;

/**
 * Instance-specific configuration (namespaced by generator + instance)
 */
readonly instanceConfig?: Storage;

Usage Example:

export default class MyGenerator extends Generator {
  configuring() {
    // Main config (shared across all generators in project)
    this.config.set('projectType', 'library');
    
    // Generator-specific config (only for this generator type)
    this.generatorConfig?.set('templateVersion', '2.1.0');
    
    // Instance-specific config (for this specific generator run)
    this.instanceConfig?.set('runId', Date.now());
    
    // Package.json manipulation
    this.packageJson.set('name', this.answers.name);
  }
}

Storage Options

Configuration options for storage behavior.

interface StorageOptions {
  name?: string;
  lodashPath?: boolean;
  disableCache?: boolean;
  disableCacheByFile?: boolean;
  sorted?: boolean;
}

Usage Example:

export default class MyGenerator extends Generator {
  constructor(args, opts) {
    super(args, opts);
    
    // Storage with custom options
    this.sortedConfig = this.createStorage('sorted-config.json', {
      name: 'sortedConfig',
      sorted: true, // Sort keys alphabetically
      lodashPath: true, // Enable lodash path access
      disableCache: false // Enable caching (default)
    });
  }
  
  configuring() {
    // Keys will be sorted when saved
    this.sortedConfig.set('z-last', 'value');
    this.sortedConfig.set('a-first', 'value');
    
    // Can use lodash paths
    this.sortedConfig.setPath('nested.deep.property', 'value');
  }
}

Types

// Valid JSON storage values
type StorageValue = string | number | boolean | null | undefined | StorageValue[] | StorageRecord;

// Storage record (key-value object)
type StorageRecord = Record<string, StorageValue>;

// Storage configuration options
interface StorageOptions {
  name?: string;
  lodashPath?: boolean;
  disableCache?: boolean;
  disableCacheByFile?: boolean;
  sorted?: boolean;
}

Install with Tessl CLI

npx tessl i tessl/npm-yeoman-generator

docs

command-execution.md

command-line.md

configuration.md

core-generator.md

file-system.md

git-integration.md

index.md

package-management.md

task-lifecycle.md

user-interaction.md

tile.json