-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from radiovisual/20-no-extra-whitespace
Add `no-extra-whitespace` rule. Closes #20
- Loading branch information
Showing
13 changed files
with
398 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# no-extra-whitespace | ||
|
||
## Rule Details | ||
|
||
**Default Severity**: `error` | ||
**Rule Type**: `validation` | ||
|
||
Messages in your translation files should not contain any whitespace before or after the message, or more than 1 contiguous whitespace character inside the message. | ||
|
||
## Why? | ||
|
||
Whitespace is sometimes deliberately added to the messages in your application to help solve some styling or layout issues. This is not recommended since it becomes hard to maintain consistent whitespace across all your translated strings and is an easy source of layout-related bugs in your application. It is safer and cleaner to let your application make layout decisions, and treat extraneous whitespace in your translated strings as unintended/accidental. | ||
|
||
## Examples | ||
|
||
Assuming `en.json` is where your default language is (the source file): | ||
|
||
❌ Example of **incorrect** setup for this rule (leading and trailing whitespace found): | ||
|
||
```js | ||
en.json: { 'hello': ' Hi, <b>{firstName}</b> ' } | ||
fr.json: { 'hello': ' Salut, <b>{firstName}</b> ' } | ||
de.json: { 'hello': ' Hallo, <b>{firstName}</b> ' } | ||
``` | ||
|
||
❌ Example of **incorrect** setup for this rule (too much internal whitespace found): | ||
|
||
```js | ||
en.json: { 'hello': 'Hi, <b>{firstName}</b> ' } | ||
fr.json: { 'hello': 'Salut, <b>{firstName}</b> ' } | ||
de.json: { 'hello': 'Hallo, <b>{firstName}</b> ' } | ||
``` | ||
|
||
✅ Examples of a **correct** setup for this rule (no whitespace padding is found): | ||
|
||
```js | ||
en.json: { 'hello': 'Hi, {firstName}' } | ||
fr.json: { 'hello': 'Salut, {firstName}' } | ||
de.json: { 'hello': 'Hallo, {firstName}' } | ||
``` | ||
|
||
## Example Configuration | ||
|
||
Simple configuration where you just supply the severity level of `error` | `warn` | `off`: | ||
|
||
```json | ||
{ | ||
"rules": { | ||
"no-extra-whitespace": "error" | ||
} | ||
} | ||
``` | ||
|
||
## Advanced configuration options | ||
|
||
This rule supports some advanced configuration. | ||
|
||
Note that when you use the advanced configuration option you need to set the severity level using the `severity` property, otherwise the rule's default severity will apply. | ||
|
||
### ignoreKeys | ||
|
||
To disable the check for this rule for specific keys, you can pass in the name of the keys where you don't want this rule to run in the `ignoreKeys` array. | ||
|
||
```json | ||
{ | ||
"rules": { | ||
"no-extra-whitespace": { | ||
"severity": "error", | ||
"ignoreKeys": ["foo", "bar"] | ||
} | ||
} | ||
} | ||
``` | ||
|
||
> [!IMPORTANT] | ||
> Be careful when using the `ignoreKeys` array: ignoring keys means means potentially ignoring real problems that can affect the UI/UX and reliability of your application. | ||
## Version | ||
|
||
This rule was introduced in keeli v1.0.0. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { noExtraWhitespace } from "./no-extra-whitespace"; |
172 changes: 172 additions & 0 deletions
172
src/rules/no-extra-whitespace/no-extra-whitespace.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import { createMockProblemReporter } from "../../utils/test-helpers.ts"; | ||
import { | ||
Config, | ||
RuleContext, | ||
RuleSeverity, | ||
TranslationFiles, | ||
} from "../../types.js"; | ||
import { noExtraWhitespace } from "./no-extra-whitespace.ts"; | ||
import { getExtraWhitespaceFoundInMessageProblem } from "./problems.ts"; | ||
|
||
const ruleMeta = noExtraWhitespace.meta; | ||
const rule = noExtraWhitespace; | ||
|
||
const defaultLocale = "en"; | ||
|
||
const baseConfig: Config = { | ||
defaultLocale, | ||
sourceFile: "en.json", | ||
translationFiles: { fr: "fr.json" }, | ||
pathToTranslatedFiles: "i18n", | ||
rules: { | ||
"no-extra-whitespace": "error", | ||
}, | ||
dryRun: false, | ||
enabled: true, | ||
}; | ||
|
||
describe.each([["error"], ["warning"]])(`${rule.meta.name}`, (severityStr) => { | ||
const severity = severityStr as unknown as RuleSeverity; | ||
|
||
const context: RuleContext = { | ||
severity, | ||
ignoreKeys: [], | ||
}; | ||
|
||
it(`should report html in messages with ${severity}`, () => { | ||
const problemStore = createMockProblemReporter(); | ||
|
||
const translationFiles: TranslationFiles = { | ||
en: { | ||
greeting: " [EN] hello ", | ||
farewell: "[EN] goodbye", | ||
}, | ||
fr: { | ||
greeting: " [FR] hello ", | ||
farewell: "[FR] goodbye", | ||
}, | ||
}; | ||
|
||
rule.run(translationFiles, baseConfig, problemStore, context); | ||
|
||
const expected1 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "greeting", | ||
locale: "en", | ||
severity, | ||
ruleMeta, | ||
isIgnored: false, | ||
}); | ||
|
||
const expected2 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "farewell", | ||
locale: "en", | ||
severity, | ||
ruleMeta, | ||
isIgnored: false, | ||
}); | ||
|
||
const expected3 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "greeting", | ||
locale: "fr", | ||
severity, | ||
ruleMeta, | ||
isIgnored: false, | ||
}); | ||
|
||
const expected4 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "farewell", | ||
locale: "fr", | ||
severity, | ||
ruleMeta, | ||
isIgnored: false, | ||
}); | ||
|
||
expect(problemStore.report).toHaveBeenCalledTimes(4); | ||
expect(problemStore.report).toHaveBeenCalledWith(expected1); | ||
expect(problemStore.report).toHaveBeenCalledWith(expected2); | ||
expect(problemStore.report).toHaveBeenCalledWith(expected3); | ||
expect(problemStore.report).toHaveBeenCalledWith(expected4); | ||
}); | ||
|
||
it(`should ignore keys in ignoreKeys with severity ${severity}`, () => { | ||
const problemStore = createMockProblemReporter(); | ||
|
||
const translationFiles: TranslationFiles = { | ||
en: { | ||
greeting: " [EN] hello ", | ||
farewell: "[EN] goodbye", | ||
}, | ||
fr: { | ||
greeting: " [FR] hello ", | ||
farewell: "[FR] goodbye", | ||
}, | ||
}; | ||
|
||
const ignored1 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "greeting", | ||
locale: "en", | ||
severity, | ||
ruleMeta, | ||
isIgnored: true, | ||
}); | ||
|
||
const ignored2 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "farewell", | ||
locale: "en", | ||
severity, | ||
ruleMeta, | ||
isIgnored: true, | ||
}); | ||
|
||
const ignored3 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "greeting", | ||
locale: "fr", | ||
severity, | ||
ruleMeta, | ||
isIgnored: true, | ||
}); | ||
|
||
const ignored4 = getExtraWhitespaceFoundInMessageProblem({ | ||
key: "farewell", | ||
locale: "fr", | ||
severity, | ||
ruleMeta, | ||
isIgnored: true, | ||
}); | ||
|
||
const ignoreKeysContext = { | ||
...context, | ||
ignoreKeys: ["farewell", "greeting"], | ||
}; | ||
|
||
rule.run(translationFiles, baseConfig, problemStore, ignoreKeysContext); | ||
|
||
expect(problemStore.report).toHaveBeenCalledWith(ignored1); | ||
expect(problemStore.report).toHaveBeenCalledWith(ignored2); | ||
expect(problemStore.report).toHaveBeenCalledWith(ignored3); | ||
expect(problemStore.report).toHaveBeenCalledWith(ignored4); | ||
}); | ||
}); | ||
|
||
describe(`${rule.meta.name}: off`, () => { | ||
const problemStore = createMockProblemReporter(); | ||
|
||
const context: RuleContext = { | ||
severity: "off", | ||
ignoreKeys: [], | ||
}; | ||
|
||
const translationFiles: TranslationFiles = { | ||
en: { | ||
greeting: " [EN] hello ", | ||
farewell: "[EN] goodbye", | ||
}, | ||
fr: { | ||
greeting: " [FR] hello ", | ||
farewell: "[FR] goodbye", | ||
}, | ||
}; | ||
|
||
rule.run(translationFiles, baseConfig, problemStore, context); | ||
expect(problemStore.report).not.toHaveBeenCalled(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { SEVERITY_LEVEL } from "../../constants.ts"; | ||
import { | ||
Config, | ||
Rule, | ||
RuleContext, | ||
RuleMeta, | ||
TranslationFiles, | ||
} from "../../types.ts"; | ||
import { | ||
stringHasExtraneousWhitespace, | ||
stringHasWhitespacePadding, | ||
} from "../../utils/string-helpers.ts"; | ||
|
||
import { getExtraWhitespaceFoundInMessageProblem } from "./problems.ts"; | ||
|
||
const ruleMeta: RuleMeta = { | ||
name: "no-extra-whitespace", | ||
description: `Whitespace should not appear before or after a message, or have more than 1 contiguous whitespace character internally.`, | ||
url: "TBD", | ||
type: "validation", | ||
defaultSeverity: "error", | ||
}; | ||
|
||
const noExtraWhitespace: Rule = { | ||
meta: ruleMeta, | ||
run: ( | ||
translationFiles: TranslationFiles, | ||
config: Config, | ||
problemStore, | ||
context: RuleContext | ||
) => { | ||
const { severity, ignoreKeys } = context; | ||
|
||
if (severity === SEVERITY_LEVEL.off) { | ||
return; | ||
} | ||
|
||
for (let [locale, data] of Object.entries(translationFiles)) { | ||
for (let [key, message] of Object.entries(data)) { | ||
const isIgnored = ignoreKeys.includes(key); | ||
|
||
if ( | ||
stringHasExtraneousWhitespace(message) || | ||
stringHasWhitespacePadding(message) | ||
) { | ||
problemStore.report( | ||
getExtraWhitespaceFoundInMessageProblem({ | ||
severity, | ||
locale, | ||
ruleMeta, | ||
key, | ||
isIgnored, | ||
}) | ||
); | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
|
||
export { noExtraWhitespace }; |
Oops, something went wrong.