Skip to content

Commit

Permalink
feat: Eslint 9 + flat config support (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
timofei-iatsenko authored Oct 11, 2024
1 parent 0c80feb commit b57329b
Show file tree
Hide file tree
Showing 31 changed files with 5,221 additions and 3,637 deletions.
13 changes: 6 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@ jobs:
validate:
timeout-minutes: 10
runs-on: ubuntu-latest
name: validate (16, ubuntu-latest)
name: validate (20, ubuntu-latest)
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
cache: yarn
node-version: 16
node-version: 20

- name: Install dependencies
run: yarn install
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ jobs:
contents: write

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-node@v3
- name: Enable Corepack
run: corepack enable

- uses: actions/setup-node@v4
with:
node-version: 16
cache: yarn
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ lib
.DS_Store
coverage
.idea
.yarn
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodeLinker: node-modules
203 changes: 45 additions & 158 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,187 +32,74 @@ yarn add eslint-plugin-lingui --dev

**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-lingui` globally.

## Usage
## Flat Config (`eslint.config.js`)

Add `lingui` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:
### Recommended Setup

```json
{
"plugins": ["lingui"]
}
```

Then configure the rules you want to use under the rules section.

```json
{
"rules": {
"lingui/no-unlocalized-strings": 2,
"lingui/t-call-in-function": 2,
"lingui/no-single-variables-to-translate": 2,
"lingui/no-expression-in-message": 2,
"lingui/no-single-tag-to-translate": 2,
"lingui/no-trans-inside-trans": 2,
"lingui/text-restrictions": [
2,
{
"rules": [
{
"patterns": ["''", "", ""],
"message": "Error message"
}
]
}
]
}
}
```

## Supported Rules

## no-unlocalized-strings

Check that code doesn't contain strings/templates/jsxText what should be wrapped into `<Trans>` or `i18n`

### Options

#### ignore

The `ignore` option specifies exceptions not to check for
literal strings that match one of regexp patterns.

Examples of correct code for the `{ "ignore": ["rgba"] }` option:

```jsx
/*eslint lingui/no-unlocalized-strings ["error", {"ignore": ["rgba"]}]*/
const a = <div color="rgba(100, 100, 100, 0.4)"></div>
```

#### ignoreFunction

The `ignoreFunction` option specifies exceptions not check for
function calls whose names match one of regexp patterns.

Examples of correct code for the `{ "ignoreFunction": ["showIntercomMessage"] }` option:
To enable all of the recommended rules for our plugin, add the following config:

```js
/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreFunction": ["showIntercomMessage"] }]*/
const bar = showIntercomMessage('Please, write me')
```

#### ignoreAttribute

The `ignoreAttribute` option specifies exceptions not to check for JSX attributes that match one of ignored attributes.

Examples of correct code for the `{ "ignoreAttribute": ["style"] }` option:
import pluginLingui from 'eslint-plugin-lingui'

```jsx
/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreAttribute": ["style"] }]*/
const element = <div style={{ margin: '1rem 2rem' }} />
export default [
...pluginLingui.configs['flat/recommended'],
// Any other config...
]
```

By default, the following attributes are ignored: `className`, `styleName`, `type`, `id`, `width`, `height`
### Custom setup

#### ignoreProperty
Alternatively, you can load the plugin and configure only the rules you want to use:

The `ignoreProperty` option specifies property names not to check.

Examples of correct code for the `{ "ignoreProperty": ["text"] }` option:

```jsx
/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreProperty": ["text"] }]*/
const test = { text: 'This is ignored' }
```js
import pluginLingui from 'eslint-plugin-lingui'

export default [
{
plugins: {
lingui: pluginLingui,
},
rules: {
'lingui/t-call-in-function': 'error',
},
},
// Any other config...
]
```

By default, the following properties are ignored: `className`, `styleName`, `type`, `id`, `width`, `height`
## Legacy Config (`.eslintrc`)

## t-call-in-function
### Recommended setup

Check that `t` calls are inside `function`. They should not be at the module level otherwise they will not react to language switching.
To enable all of the recommended rules for our plugin, add `plugin:lingui/recommended` in extends:

```jsx
import { t } from '@lingui/macro'

// nope ⛔️
const msg = t`Hello world!`

// ok ✅
function getGreeting() {
return t`Hello world!`
```json
{
"extends": ["plugin:lingui/recommended"]
}
```

Check the [Lingui Docs](https://lingui.dev/tutorials/react-patterns#translations-outside-react-components) for more info.

## no-expression-in-message

Check that `` t` ` `` doesn't contain member or function expressions like `` t`Hello ${user.name}` `` or `` t`Hello ${getName()}` ``

Such expressions would be transformed to its index position such as `Hello {0}` which gives zero to little context for translator.

Use a variable identifier instead.

```jsx
// nope ⛔️
t`Hello ${user.name}` // => 'Hello {0}'

// ok ✅
const userName = user.name
t`Hello ${userName}` // => 'Hello {userName}'
```

## no-trans-inside-trans

Check that no `Trans` inside `Trans` components.

```jsx
// nope ⛔️
<Trans>Hello <Trans>World!</Trans></Trans>

// ok ✅
<Trans>Hello World!</Trans>
```

## no-single-variables-to-translate

Doesn't allow single variables without text to translate like `<Trans>{variable}</Trans>` or `` t`${variable}` ``

Such expression would pollute message catalog with useless string which has nothing to translate.

## text-restrictions

Check that strings/templates/jsxText doesn't contain patterns from the rules.

This rules enforces a consistency rules inside your messages.

### Options
### Custom setup

### rules

`rules` is array of rules when one rule has structure
Alternatively, add `lingui` to the plugins section, and configure the rules you want to use:

```json
{
"patterns": ["first", "second"],
"message": "error message"
"plugins": ["lingui"],
"rules": {
"lingui/t-call-in-function": "error"
}
}
```

each `rule` has a structure:

- `patterns` is an array of regex or strings
- `message` is an error message that will be displayed if restricting pattern matches text
- `flags` is a string with regex flags for patterns
- `isOnlyForTranslation` is a boolean indicating that patterns should be found only inside `Trans` tags or `t` tagged template
## Rules

## no-single-tag-to-translate
✅ - Recommended

Ensures `<Trans></Trans>` isn't wrapping a single element unnecessarily

```jsx
// nope ⛔️
<Trans><strong>Foo bar</strong></Trans>

// ok ✅
<strong><Trans>Foo bar</Trans></strong>
```
-[no-expression-in-message](docs/rules/no-expression-in-message.md)
-[no-single-tag-to-translate](docs/rules/no-single-tag-to-translate.md)
-[no-single-variables-to-translate](docs/rules/no-single-variables-to-translate.md)
-[no-trans-inside-trans](docs/rules/no-trans-inside-trans.md)
-[t-call-in-function](docs/rules/t-call-in-function.md)
- [no-unlocalized-strings](docs/rules/no-unlocalized-strings.md)
- [text-restrictions](docs/rules/text-restrictions.md)
16 changes: 16 additions & 0 deletions docs/rules/no-expression-in-message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# no-expression-in-message

Check that `` t` ` `` doesn't contain member or function expressions like `` t`Hello ${user.name}` `` or `` t`Hello ${getName()}` ``

Such expressions would be transformed to its index position such as `Hello {0}` which gives zero to little context for translator.

Use a variable identifier instead.

```jsx
// nope ⛔️
t`Hello ${user.name}` // => 'Hello {0}'

// ok ✅
const userName = user.name
t`Hello ${userName}` // => 'Hello {userName}'
```
14 changes: 14 additions & 0 deletions docs/rules/no-single-tag-to-translate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# no-single-tag-to-translate

> [!TIP]
> This rule is included into the `lingui/recommended` config
Ensures `<Trans></Trans>` isn't wrapping a single element unnecessarily

```jsx
// nope ⛔️
<Trans><strong>Foo bar</strong></Trans>

// ok ✅
<strong><Trans>Foo bar</Trans></strong>
```
16 changes: 16 additions & 0 deletions docs/rules/no-single-variables-to-translate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# no-single-variables-to-translate

> [!TIP]
> This rule is included into the `lingui/recommended` config
Doesn't allow single variables without text to translate like `<Trans>{variable}</Trans>` or `` t`${variable}` ``

Such expression would pollute message catalog with useless string which has nothing to translate.

```jsx
// nope ⛔️
<Trans>{user}</Trans>

// ok ✅
<Trans>Hello {user}</Trans>
```
14 changes: 14 additions & 0 deletions docs/rules/no-trans-inside-trans.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# no-trans-inside-trans

> [!TIP]
> This rule is included into the `lingui/recommended` config
Check that no `Trans` inside `Trans` components.

```jsx
// nope ⛔️
<Trans>Hello <Trans>World!</Trans></Trans>

// ok ✅
<Trans>Hello World!</Trans>
```
Loading

0 comments on commit b57329b

Please sign in to comment.