Allow downstream configuration more control over globs and options.

This commit is contained in:
JD Huntington 2020-10-27 12:34:30 -07:00 коммит произвёл JD Huntington
Родитель 583527933c
Коммит 520017ec2b
4 изменённых файлов: 142 добавлений и 12 удалений

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "minor",
"comment": "Allow downstream configuration more control over globs and options.",
"packageName": "@boll/core",
"email": "jdh@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-10-27T19:34:55.408Z"
}

Просмотреть файл

@ -19,13 +19,24 @@ export class Config {
loadRuleSets(): RuleSet[] {
const config = this.resolvedConfiguration();
return (config.ruleSets || []).map(ruleSetConfig => {
const exclude = [...(ruleSetConfig.exclude || []), ...(config.exclude || [])];
let exclude = [...(ruleSetConfig.exclude || []), ...(config.exclude || [])];
if (
ruleSetConfig.name &&
config.configuration &&
config.configuration.ruleSets &&
(config.configuration.ruleSets as any)[ruleSetConfig.name]
) {
exclude = [...exclude, ...((config.configuration.ruleSets as any)[ruleSetConfig.name].exclude || [])];
}
const glob = ruleSetConfig.fileLocator;
glob.exclude = exclude;
glob.include = ruleSetConfig.include || [];
const checks = (ruleSetConfig.checks || []).map(check =>
this.ruleRegistry.get(check.rule)(this.logger, check.options)
);
const checks = (ruleSetConfig.checks || []).map(check => {
const optionsFromConfig =
(config.configuration && config.configuration.rules && (config.configuration.rules as any)[check.rule]) || {};
const options = { ...check.options, ...optionsFromConfig };
return this.ruleRegistry.get(check.rule)(this.logger, options);
});
return new RuleSet(glob, checks);
});
}
@ -34,12 +45,10 @@ export class Config {
this.configuration = def;
}
// TODO this will need a hand-crafted "deep merge" at some point
resolvedConfiguration(): ConfigDefinition {
return {
...this.resolveParentConfiguration(this.configuration.extends),
...this.configuration
};
const parentConfiguration = this.resolveParentConfiguration(this.configuration.extends);
const finalResult = this.mergeConfigurations(this.configuration, parentConfiguration);
return finalResult;
}
resolveParentConfiguration(baseConfigName: string | null | undefined): ConfigDefinition {
@ -47,9 +56,46 @@ export class Config {
return {};
}
const baseConfig = this.configRegistry.get(baseConfigName);
return {
...this.resolveParentConfiguration(baseConfig.extends),
...baseConfig
const parentConfig = this.resolveParentConfiguration(baseConfig.extends);
return this.mergeConfigurations(parentConfig, baseConfig);
}
private mergeConfigurations(
parentConfiguration: ConfigDefinition,
childConfiguration: ConfigDefinition
): ConfigDefinition {
const obj: ConfigDefinition = {
configuration: {
rules: {},
ruleSets: {}
}
};
if (childConfiguration.name) {
obj.name = childConfiguration.name;
}
obj.ruleSets = [...(parentConfiguration.ruleSets || []), ...(childConfiguration.ruleSets || [])];
obj.exclude = [...(parentConfiguration.exclude || []), ...(childConfiguration.exclude || [])];
if (parentConfiguration.configuration && parentConfiguration.configuration.ruleSets) {
Object.keys(parentConfiguration.configuration.ruleSets).forEach(k => {
(obj.configuration!.ruleSets! as any)[k] = (parentConfiguration.configuration!.ruleSets as any)[k];
});
}
if (parentConfiguration.configuration && parentConfiguration.configuration.rules) {
Object.keys(parentConfiguration.configuration.rules).forEach(k => {
(obj.configuration!.rules! as any)[k] = (parentConfiguration.configuration!.rules as any)[k];
});
}
if (childConfiguration.configuration && childConfiguration.configuration.ruleSets) {
Object.keys(childConfiguration.configuration.ruleSets).forEach(k => {
(obj.configuration!.ruleSets! as any)[k] = (childConfiguration.configuration!.ruleSets as any)[k];
});
}
if (childConfiguration.configuration && childConfiguration.configuration.rules) {
Object.keys(childConfiguration.configuration.rules).forEach(k => {
(obj.configuration!.rules! as any)[k] = (childConfiguration.configuration!.rules as any)[k];
});
}
return obj;
}
}

Просмотреть файл

@ -12,6 +12,9 @@ export const test: any = baretest("Config");
class FakeRule implements PackageRule {
name: string = "fakerule";
constructor(public options: {} = {}) {}
async check(file: any): Promise<Result[]> {
throw new Error("Method not implemented.");
}
@ -68,3 +71,71 @@ test("gives options to factory function", () => {
config.buildSuite();
assert.ok(calledWithCorrectArgs, "Rule factory should have been invoked with correct args when creating suite.");
});
test("downstream rules configuration applies to rules", () => {
const configRegistry = new ConfigRegistry();
configRegistry.register({
name: "base",
ruleSets: [{ fileLocator: new FakeGlob(), checks: [{ rule: "foo", options: { bar: "baz" } }] }]
});
const ruleRegistry = new RuleRegistry();
ruleRegistry.register("foo", (l: any, options: any) => {
return new FakeRule(options);
});
const config = new Config(configRegistry, ruleRegistry, NullLogger);
const myConfig = {
extends: "base",
configuration: {
rules: {
foo: { some: "rule" }
}
}
};
config.load(myConfig);
const suite = config.buildSuite();
const fakeRuleInstance = suite.ruleSets[0].checks[0] as FakeRule;
assert.deepStrictEqual(fakeRuleInstance.options, { bar: "baz", some: "rule" });
});
test("downstream ruleSet configuration applies to ruleSets", () => {
const configRegistry = new ConfigRegistry();
configRegistry.register({
name: "base",
ruleSets: [{ fileLocator: new FakeGlob(), name: "fake", exclude: ["bar"] }],
exclude: ["baz"]
});
const config = new Config(configRegistry, new RuleRegistry(), NullLogger);
const myConfig = {
extends: "base",
exclude: ["foo2"],
configuration: {
ruleSets: {
fake: {
exclude: ["foo"]
}
}
}
};
config.load(myConfig);
const suite = config.buildSuite();
const ruleSet = suite.ruleSets[0];
assert.deepStrictEqual(ruleSet.fileGlob.exclude, ["bar", "foo2", "baz", "foo"]);
});
test("resolveConfiguration merges exclude", () => {
const configRegistry = new ConfigRegistry();
configRegistry.register({
name: "base",
exclude: ["baz"]
});
configRegistry.register({
name: "child",
exclude: ["foo"],
extends: "base"
});
const config = new Config(configRegistry, new RuleRegistry(), NullLogger);
config.load({ extends: "child" });
const result = config.resolvedConfiguration();
assert.deepStrictEqual(result.exclude, ["baz", "foo"]);
});

Просмотреть файл

@ -12,6 +12,7 @@ export interface RuleSetConfiguration {
checks?: CheckConfiguration[];
exclude?: string[];
include?: string[];
name?: string;
}
export interface ConfigDefinition {
@ -19,6 +20,10 @@ export interface ConfigDefinition {
extends?: string;
exclude?: string[];
ruleSets?: RuleSetConfiguration[];
configuration?: {
rules?: {};
ruleSets?: {};
};
}
export interface PackageRule {