diff --git a/.gitignore b/.gitignore index 5c592aed14..0926825d24 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,9 @@ yarn-error.log # User specific bazel settings .bazelrc.user +# User specific ng-dev settings +.ng-dev.user* + .notes.md baseline.json diff --git a/dev-infra/utils/config.ts b/dev-infra/utils/config.ts index bd08ee68e4..8b8dff92ba 100644 --- a/dev-infra/utils/config.ts +++ b/dev-infra/utils/config.ts @@ -9,7 +9,7 @@ import {existsSync} from 'fs'; import {dirname, join} from 'path'; -import {error} from './console'; +import {debug, error} from './console'; import {exec} from './shelljs'; import {isTsNodeAvailable} from './ts-node'; @@ -51,6 +51,15 @@ const CONFIG_FILE_PATH = '.ng-dev/config'; /** The configuration for ng-dev. */ let CONFIG: {}|null = null; +/** + * The filename expected for local user config, without the file extension to allow a typescript, + * javascript or json file to be used. + */ +const USER_CONFIG_FILE_PATH = '.ng-dev.user'; + +/** The local user configuration for ng-dev. */ +let USER_CONFIG: {[key: string]: any}; + /** * Get the configuration from the file system, returning the already loaded * copy if it is defined. @@ -86,8 +95,11 @@ function validateCommonConfig(config: Partial) { return config as NgDevConfig; } -/** Resolves and reads the specified configuration file. */ -function readConfigFile(configPath: string): object { +/** + * Resolves and reads the specified configuration file, optionally returning an empty object if the + * configuration file cannot be read. + */ +function readConfigFile(configPath: string, returnEmptyObjectOnError = false): object { // If the the `.ts` extension has not been set up already, and a TypeScript based // version of the given configuration seems to exist, set up `ts-node` if available. if (require.extensions['.ts'] === undefined && existsSync(`${configPath}.ts`) && @@ -103,7 +115,12 @@ function readConfigFile(configPath: string): object { try { return require(configPath); } catch (e) { - error('Could not read configuration file.'); + if (returnEmptyObjectOnError) { + debug(`Could not read configuration file at ${configPath}, returning empty object instead.`); + debug(e); + return {}; + } + error(`Could not read configuration file at ${configPath}.'`); error(e); process.exit(1); } @@ -135,3 +152,23 @@ export function getRepoBaseDir() { } return baseRepoDir.trim(); } + +/** + * Get the local user configuration from the file system, returning the already loaded copy if it is + * defined. + * + * @returns The user configuration object, or an empty object if no user configuration file is + * present. The object is a untyped object as there are no required user configurations. + */ +export function getUserConfig() { + // If the global config is not defined, load it from the file system. + if (USER_CONFIG === undefined) { + // The full path to the configuration file. + const configPath = join(getRepoBaseDir(), USER_CONFIG_FILE_PATH); + // Set the global config object. + USER_CONFIG = readConfigFile(configPath, true); + } + // Return a clone of the global config to ensure that a new instance of the config is returned + // each time, preventing unexpected effects of modifications to the config object. + return {...USER_CONFIG}; +}