зеркало из https://github.com/microsoft/boll.git
Allow downstream configuration more control over globs and options.
This commit is contained in:
Родитель
583527933c
Коммит
520017ec2b
|
@ -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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче