packages/@aws-cdk/toolkit-lib/lib/api/settings.ts (88 lines of code) (raw):

import * as os from 'os'; import * as fs_path from 'path'; import * as fs from 'fs-extra'; import { ToolkitError } from './toolkit-error'; import * as util from '../util'; export type SettingsMap = { [key: string]: any }; /** * If a context value is an object with this key set to a truthy value, it won't be saved to cdk.context.json */ export const TRANSIENT_CONTEXT_KEY = '$dontSaveContext'; /** * A single bag of settings */ export class Settings { public static mergeAll(...settings: Settings[]): Settings { let ret = new Settings(); for (const setting of settings) { ret = ret.merge(setting); } return ret; } constructor( private settings: SettingsMap = {}, public readonly readOnly = false, ) { } public async save(fileName: string): Promise<this> { const expanded = expandHomeDir(fileName); await fs.writeJson(expanded, stripTransientValues(this.settings), { spaces: 2, }); return this; } public get all(): any { return this.get([]); } public merge(other: Settings): Settings { return new Settings(util.deepMerge(this.settings, other.settings)); } public subSettings(keyPrefix: string[]) { return new Settings(this.get(keyPrefix) || {}, false); } public makeReadOnly(): Settings { return new Settings(this.settings, true); } public clear() { if (this.readOnly) { throw new ToolkitError('Cannot clear(): settings are readonly'); } this.settings = {}; } public get empty(): boolean { return Object.keys(this.settings).length === 0; } public get(path: string[]): any { return util.deepClone(util.deepGet(this.settings, path)); } public set(path: string[], value: any): Settings { if (this.readOnly) { throw new ToolkitError(`Can't set ${path}: settings object is readonly`); } if (path.length === 0) { // deepSet can't handle this case this.settings = value; } else { util.deepSet(this.settings, path, value); } return this; } public unset(path: string[]) { this.set(path, undefined); } } function expandHomeDir(x: string) { if (x.startsWith('~')) { return fs_path.join(os.homedir(), x.slice(1)); } return x; } /** * Return all context value that are not transient context values */ function stripTransientValues(obj: { [key: string]: any }) { const ret: any = {}; for (const [key, value] of Object.entries(obj)) { if (!isTransientValue(value)) { ret[key] = value; } } return ret; } /** * Return whether the given value is a transient context value * * Values that are objects with a magic key set to a truthy value are considered transient. */ function isTransientValue(value: any) { return ( typeof value === 'object' && value !== null && (value as any)[TRANSIENT_CONTEXT_KEY] ); }