Rails-inspired generator system that provides scaffolding for your apps
Persistent storage system for generator configurations, user preferences, and project settings using JSON files with support for namespacing and hierarchical storage.
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
}
}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();
}
}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
}
});
}
}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';
}
}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'
}
});
}
}
}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);
}
}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');
}
}// 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