eslint-plugin-react-google-translate

0.0.85 • Public • Published

eslint-plugin-react-google-translate

ESLint plugin to highlight code patterns in React applications which can lead to browser exceptions while the Google Translate browser extension is in use. This is a common problem for React applications and a known issue to both React and Google.

When active on a page, the Google Translate browser extension is very liberal with its DOM manipulation, notably, replacing text nodes with font tags. This can be a problem for React applications as it can cause exceptions to be thrown when conditionally rendering text nodes with siblings within JSX expressions.

Whilst many proposals have been suggested to avoid browser issues, this ESLint plugin aims to solve the problem far earlier in the development process by highlighting certain code patterns to the developer which can cause a browser exception to be thrown where Google Translate is in use.

Examples of code that can throw:

function SomeComponent({ val }) {
  return (
    <div>
      <p>
        // ❌ foo & bar must be wrapped
        {val ? 'foo' : 'bar'} <span>hello world</span>
      </p>
      <p>
        // ❌ static text nodes must be wrapped when they are preceeded by a
        conditional expression
        {val ? <span>foo</span> : <span>bar</span>} hello world
      </p>
      // ❌ all text must be wrapped in spans ('foo', 'bar' & 'hello world')
      <p>{val ? 'foo' : 'bar'} hello world</p>
      <p>
        // ❌ `val.toLocaleString()` returns a text node and should be wrapped
        {val ? val.toLocaleString() : <span>bar</span>} <span>hello world</span>
      </p>
      <p>
        // ❌ object properties rendering a text node should be wrapped
        {val ? obj.a : <span>bar</span>} <span>hello</span>
      </p>
      <p>
        // ❌ 'foo' needs to be wrapped in a span (expression has a sibling)
        {val && 'foo'}
        <span>hello world</span>
      </p>
      // ✅ conditionally rendered text nodes with no siblings won't throw
      <p>{val || 'bar'}</p>
      // ✅ conditionally rendered text nodes with no siblings won't throw
      <p>{val ? 'foo' : 'bar'}</p>
      // ✅ conditional expression follows the static text node and won't throw
      <p>hello world {val ? <span>foo</span> : <span>bar</span>}</p>
    </div>
  );
}

The safe way to write this code, avoiding browser exceptions, is to wrap each of the conditionally rendered text nodes (with siblings) in an element (for example, a <span>). Static text nodes with preceeding conditionally rendered siblings must also be wrapped:

function SomeComponent({ val }) {
  return (
    <div>
      // ✅ all text nodes are wrapped
      <p>
        {val ? <span>foo</span> : <span>bar</span>}
        <span>hello world</span>
      </p>
      <p>
        // ✅ `val.toLocaleString()` is wrapped
        {val ? <span>{val.toLocaleString()}</span> : <span>bar</span>}
        <span>hello world</span>
      </p>
      <p>
        // ✅ object properties rendering a text node are wrapped
        {val ? <span>{obj.a}</span> : <span>bar</span>} <span>hello</span>
      </p>
      <p>
        // ✅ 'foo' is wrapped (expression has a sibling)
        {val && <span>foo</span>} <span>hello world</span>
      </p>
      // ✅ conditionally rendered text, but no siblings
      <p>{val || 'bar'}</p>
      // ✅ conditionally rendered text, but no siblings
      <p>{val ? 'foo' : 'bar'}</p>
    </div>
  );
}

Installation

You'll first need to install ESLint:

npm i eslint --save-dev

Next, install eslint-plugin-react-google-translate:

npm install eslint-plugin-react-google-translate --save-dev

Usage

Add react-google-translate to the plugins section of your .eslintrc configuration file. You can omit the eslint-plugin- prefix:

{
  "plugins": ["react-google-translate"]
}

Then configure the rule under the rules section.

{
  "rules": {
    "react-google-translate/no-conditional-text-nodes-with-siblings": "error"
  }
}

The rule can also be treated as a warning if an error is deemed too strict.

{
  "rules": {
    "react-google-translate/no-conditional-text-nodes-with-siblings": "warn"
  }
}

Limitations

  • This plugin will highlight instances where a text node is generated by calling toString or toLocaleString, though it is not able to identify text nodes returned from other functions. For example, the following code will not show an error.
<p>
  {val ? getSomeValue() : <span>bar</span>} <span>hello world</span>
</p>
  • Conditional expressions are limited to a single ternary operator. Values derived from nested ternary expressions will not be reported.

  • Object properties are identified by the plugin, though only those which are one level deep.

<p>
  // ✅ `val.a` is only one level deep and will be reported
  {showVal ? val.a : <span>bar</span>} <span>hello world</span>
  // ❌ `val.a.b` is two levels deep and will not be reported
  {showVal ? val.a.b : <span>bar</span>} <span>hello world</span>
</p>
  • Optional chaining is not supported.
<p>
  // ❌ `val?.a` uses optional chaining and will not be reported
  {showVal ? val?.a : <span>bar</span>} <span>hello world</span>
  // ❌ `toLocaleString` being optionally chained will also fail to report
  {showVal ? val?.toLocaleString() : <span>bar</span>} <span>hello world</span>
</p>

Package Sidebar

Install

npm i eslint-plugin-react-google-translate

Weekly Downloads

411

Version

0.0.85

License

ISC

Unpacked Size

14.1 kB

Total Files

5

Last publish

Collaborators

  • alistair-coup