feat(dev-infra): Add support for local user ng-dev configuration (#38701)

Create a utility for loading a local user configuration object to describe
local configuration values, such as skipping the commit message wizard.

PR Close #38701
This commit is contained in:
Joey Perrott 2020-09-03 13:53:03 -07:00 committed by atscott
parent 3c4b8b97c1
commit 1ed6913b8b
2 changed files with 44 additions and 4 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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<NgDevConfig>) {
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};
}