Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check for valid rule names in the config file. Closes #12 #29

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,24 @@ export const keyNamingConventions = {
};

export const validKeyNamingConventions = Object.values(keyNamingConventions);

/**
* Export a list of the rule names that are configurable
*/
export const configurableRuleNames: string[] = [
"no-empty-messages",
"no-untranslated-messages",
"no-invalid-variables",
"no-html-messages",
"no-missing-keys",
"no-malformed-keys",
"no-extra-whitespace",
];

/**
* Export a list of the rule names that are NOT configurable
*/
export const unConfigurableRuleNames: string[] = [
"no-invalid-configuration",
"no-invalid-severity",
];
1 change: 1 addition & 0 deletions src/rules/no-empty-messages/no-empty-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noEmptyMessages: Rule = {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-extra-whitespace/no-extra-whitespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noExtraWhitespace: Rule = {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-html-messages/no-html-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noHtmlMessages: Rule = {
Expand Down
8 changes: 6 additions & 2 deletions src/rules/no-invalid-configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ This rule makes the following checks that your configuration is ready to go.
2. A valid `sourceFile`
3. Valid `translationFiles` object
4. A valid `pathToTranslatedFiles` string
5. Only known rules can be passed into the `rules` section

✅ Examples of a **correct** setup for this rule (all messages are non-empty):
✅ Examples of a **correct** setup for this rule (all messages are non-empty, and valid rules are used):

```json
{
Expand All @@ -25,7 +26,10 @@ This rule makes the following checks that your configuration is ready to go.
"translationFiles": {
"locale": "filename-of-this-locales-messages"
},
"pathToTranslatedFiles": "path/to/translation/files/directory"
"pathToTranslatedFiles": "path/to/translation/files/directory",
"rules": {
"no-html-messages": "warn"
}
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
getInvalidTranslationFilesProblem,
getMissingDefaultLocaleProblem,
getMissingSourceFileProblem,
getUnConfigurableRuleFoundInConfigProblem,
getUnknownRuleConfigurationProblem,
} from "./problems.ts";

const ruleMeta = noInvalidConfiguration.meta;
Expand Down Expand Up @@ -257,4 +259,56 @@ describe(`${rule.meta.name}`, () => {
expect(problemStore.report).toHaveBeenCalledWith(expectedProblem);
expect(problemStore.report).toHaveBeenCalledTimes(1);
});

it(`should report unknown rules in configuration file`, () => {
const problemStore = createMockProblemReporter();

const { severity } = context;

const config: Config = {
...baseConfig,
rules: {
"this-is-an-unknown-rule": "error",
},
};

rule.run(translationFiles, config, problemStore, context);

const expectedProblem = getUnknownRuleConfigurationProblem(
{
severity: severity as RuleSeverity,
ruleMeta,
},
"this-is-an-unknown-rule"
);

expect(problemStore.report).toHaveBeenCalledWith(expectedProblem);
expect(problemStore.report).toHaveBeenCalledTimes(1);
});

it(`should report when un-configurable rules find configuration in the config file`, () => {
const problemStore = createMockProblemReporter();

const { severity } = context;

const config: Config = {
...baseConfig,
rules: {
"no-invalid-configuration": "off",
},
};

rule.run(translationFiles, config, problemStore, context);

const expectedProblem = getUnConfigurableRuleFoundInConfigProblem(
{
severity: severity as RuleSeverity,
ruleMeta,
},
"no-invalid-configuration"
);

expect(problemStore.report).toHaveBeenCalledWith(expectedProblem);
expect(problemStore.report).toHaveBeenCalledTimes(1);
});
});
34 changes: 33 additions & 1 deletion src/rules/no-invalid-configuration/no-invalid-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@ import {
TranslationFiles,
} from "../../types.ts";
import { isEmptyString } from "../../utils/string-helpers.ts";
import {
configurableRuleNames,
unConfigurableRuleNames,
} from "../../constants";
import {
getInvalidTranslationFilesProblem,
getMissingSourceFileProblem,
getMissingDefaultLocaleProblem,
getInvalidPathToTranslatedFilesProblem,
getUnknownRuleConfigurationProblem,
getUnConfigurableRuleFoundInConfigProblem,
} from "./problems.ts";

const ruleMeta: RuleMeta = {
Expand All @@ -19,6 +25,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "configuration",
defaultSeverity: "error",
configurable: false,
};

const noInvalidConfiguration: Rule = {
Expand All @@ -30,7 +37,7 @@ const noInvalidConfiguration: Rule = {
context: RuleContext
) => {
const { defaultLocale, sourceFile, pathToTranslatedFiles } = config;
const { severity } = context;
const severity = ruleMeta.defaultSeverity;

// Look for missing or invalid 'defaultLocale' in the configuration
if (!defaultLocale || isEmptyString(defaultLocale)) {
Expand Down Expand Up @@ -74,6 +81,31 @@ const noInvalidConfiguration: Rule = {
getInvalidPathToTranslatedFilesProblem({ ruleMeta, severity })
);
}

// Look for unexpected rule names in the configuration
Object.keys(config?.rules).forEach((ruleName) => {
if (
!configurableRuleNames.includes(ruleName) &&
!unConfigurableRuleNames.includes(ruleName)
) {
const problem = getUnknownRuleConfigurationProblem(
{ ruleMeta, severity },
ruleName
);
problemStore.report(problem);
}
});

// Look for un-configurable rule names in the configuration
Object.keys(config?.rules).forEach((ruleName) => {
if (unConfigurableRuleNames.includes(ruleName)) {
const problem = getUnConfigurableRuleFoundInConfigProblem(
{ ruleMeta, severity },
ruleName
);
problemStore.report(problem);
}
});
},
};

Expand Down
30 changes: 30 additions & 0 deletions src/rules/no-invalid-configuration/problems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,33 @@ export function getInvalidPathToTranslatedFilesProblem(
)
.build();
}

export function getUnknownRuleConfigurationProblem(
problemContext: ProblemContext,
unknownRuleName: string
): Problem {
const { severity, ruleMeta } = problemContext;

return Problem.Builder.withRuleMeta(ruleMeta)
.withRuleMeta(ruleMeta)
.withSeverity(severity)
.withMessage(
`Unknown rule '${unknownRuleName}' found in configuration. Remove or update the rule entry.`
)
.build();
}

export function getUnConfigurableRuleFoundInConfigProblem(
problemContext: ProblemContext,
ruleName: string
): Problem {
const { severity, ruleMeta } = problemContext;

return Problem.Builder.withRuleMeta(ruleMeta)
.withRuleMeta(ruleMeta)
.withSeverity(severity)
.withMessage(
`The rule '${ruleName}' is not configurable. Remove it from your config's "rules" section.`
)
.build();
}
3 changes: 2 additions & 1 deletion src/rules/no-invalid-severity/no-invalid-severity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "configuration",
defaultSeverity: "error",
configurable: false,
};

const noInvalidSeverity: Rule = {
Expand All @@ -26,7 +27,7 @@ const noInvalidSeverity: Rule = {
problemStore,
context: RuleContext
) => {
const { severity } = context;
const severity = ruleMeta.defaultSeverity;

// Look for invalid rule severity in the configuration file
Object.entries(config.rules).forEach(([rule, ruleValue]) => {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-invalid-variables/no-invalid-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noInvalidVariables: Rule = {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-malformed-keys/no-malformed-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noMalformedKeys: Rule = {
Expand Down
1 change: 1 addition & 0 deletions src/rules/no-missing-keys/no-missing-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noMissingKeys: Rule = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const ruleMeta: RuleMeta = {
url: "TBD",
type: "validation",
defaultSeverity: "error",
configurable: true,
};

const noUntranslatedMessages: Rule = {
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type RuleMeta = {
description: string;
type: keyof typeof RULE_TYPE;
url: string;
configurable: boolean;
};

export type RuleContext = {
Expand Down
6 changes: 6 additions & 0 deletions src/utils/rules-helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe("getRuleSeverity", () => {
url: "",
defaultSeverity: "warn",
name: "example-rule",
configurable: true,
};

const rule: Partial<Rule> = {
Expand All @@ -37,6 +38,7 @@ describe("getRuleSeverity", () => {
url: "",
defaultSeverity: "error",
name,
configurable: true,
};

const rule: Partial<Rule> = {
Expand All @@ -63,6 +65,7 @@ describe("getRuleSeverity", () => {
url: "",
defaultSeverity: "error",
name,
configurable: true,
};

const rule: Partial<Rule> = {
Expand Down Expand Up @@ -93,6 +96,7 @@ describe("getRuleIgnoreKeys", () => {
url: "",
defaultSeverity: "error",
name,
configurable: true,
};

const rule: Partial<Rule> = {
Expand Down Expand Up @@ -121,6 +125,7 @@ describe("getRuleIgnoreKeys", () => {
url: "",
defaultSeverity: "error",
name,
configurable: true,
};

const rule: Partial<Rule> = {
Expand All @@ -145,6 +150,7 @@ describe("getRuleIgnoreKeys", () => {
url: "",
defaultSeverity: "error",
name,
configurable: true,
};

const rule: Partial<Rule> = {
Expand Down
6 changes: 6 additions & 0 deletions src/utils/rules-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import type { Config, Rule, RuleSeverity } from "../types";
* @returns string
*/
export function getRuleSeverity(config: Config, rule: Rule): RuleSeverity {
const isConfigurable = rule.meta.configurable;

const ruleConfig = config.rules[rule.meta.name];

if (!isConfigurable) {
return rule.meta.defaultSeverity;
}

if (typeof ruleConfig === "string" && validSeverities.includes(ruleConfig)) {
return ruleConfig;
}
Expand Down