Skip to content

Commit

Permalink
feat: add es-roikoren/no-top-level-await rule (#12)
Browse files Browse the repository at this point in the history
* feat: add `es-roikoren/no-top-level-await` rule

* docs: tiny update

* fix: import order

* test: fix

* chore: update watch script

* test: small fix
  • Loading branch information
roikoren755 authored Dec 2, 2021
1 parent ae75c1b commit f54eb1a
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/real-rabbits-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-es-roikoren': patch
---

feat: add `es-roikoren/no-top-level-await` rule
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ There is a config that enables the rules in this category: `plugin:es-roikoren/n
| [es-roikoren/no-object-hasown](./no-object-hasown.md) | disallow the `Object.hasOwn` method. | |
| [es-roikoren/no-private-in](./no-private-in.md) | disallow `#x in obj`. | |
| [es-roikoren/no-regexp-d-flag](./no-regexp-d-flag.md) | disallow RegExp `d` flag. | |
| [es-roikoren/no-top-level-await](./no-top-level-await.md) | disallow top-level `await`. | |

## ES2021

Expand Down
20 changes: 20 additions & 0 deletions docs/rules/no-top-level-await.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# es-roikoren/no-top-level-await
> disallow top-level `await`.
- ✅ The following configurations enable this rule: `plugin:es-roikoren/no-new-in-esnext`

This rule reports ES2022 [Top-level `await`](https://github.com/tc39/proposal-top-level-await) as errors.

## Examples

⛔ Examples of **incorrect** code for this rule:

```js
/*eslint es-roikoren/no-top-level-await: error */
await expr;
```

## 📚 References

- [Rule source](https://github.com/roikoren755/eslint-plugin-es/blob/v0.0.6/src/rules/no-top-level-await.ts)
- [Test source](https://github.com/roikoren755/eslint-plugin-es/blob/v0.0.6/tests/src/rules/no-top-level-await.ts)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"update:doc": "ts-node scripts/update-docs-readme",
"update:index": "ts-node scripts/update-src-index",
"update:ruledocs": "ts-node scripts/update-docs-rules",
"watch": "mocha tests/**/*.js --reporter progress --watch --growl"
"watch": "mocha tests/**/*.ts --reporter progress --watch"
},
"resolutions": {
"prettier": "2.5.0"
Expand Down
1 change: 1 addition & 0 deletions src/configs/no-new-in-esnext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const config: TSESLint.Linter.Config = {
'es-roikoren/no-object-hasown': 'error',
'es-roikoren/no-private-in': 'error',
'es-roikoren/no-regexp-d-flag': 'error',
'es-roikoren/no-top-level-await': 'error',
},
};

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ import noSubclassingBuiltins from './rules/no-subclassing-builtins';
import noSymbol from './rules/no-symbol';
import noSymbolPrototypeDescription from './rules/no-symbol-prototype-description';
import noTemplateLiterals from './rules/no-template-literals';
import noTopLevelAwait from './rules/no-top-level-await';
import noTrailingCommas from './rules/no-trailing-commas';
import noTrailingFunctionCommas from './rules/no-trailing-function-commas';
import noTypedArrays from './rules/no-typed-arrays';
Expand Down Expand Up @@ -344,6 +345,7 @@ export default {
'no-symbol': noSymbol,
'no-symbol-prototype-description': noSymbolPrototypeDescription,
'no-template-literals': noTemplateLiterals,
'no-top-level-await': noTopLevelAwait,
'no-trailing-commas': noTrailingCommas,
'no-trailing-function-commas': noTrailingFunctionCommas,
'no-typed-arrays': noTypedArrays,
Expand Down
37 changes: 37 additions & 0 deletions src/rules/no-top-level-await.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { TSESTree } from '@typescript-eslint/typescript-estree';

import { createRule } from '../util/create-rule';

export const category = 'ES2022';
export default createRule<[], 'forbidden'>({
name: 'no-top-level-await',
meta: {
type: 'problem',
docs: { description: 'disallow top-level `await`.', recommended: false },
schema: [],
messages: { forbidden: "ES2022 top-level 'await' is forbidden." },
},
defaultOptions: [],
create(context) {
let inFunction: TSESTree.FunctionExpression | null = null;

return {
':function'(node: TSESTree.FunctionExpression) {
inFunction = node;
},
':function:exit'(node: TSESTree.FunctionExpression) {
if (inFunction === node) {
inFunction = null;
}
},
'AwaitExpression, ForOfStatement[await=true]'(node: TSESTree.AwaitExpression | TSESTree.ForOfStatement) {
if (inFunction) {
// not top-level
return;
}

context.report({ node, messageId: 'forbidden' });
},
};
},
});
84 changes: 84 additions & 0 deletions tests/src/rules/no-top-level-await.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { TSESLint } from '@typescript-eslint/experimental-utils';
import { AST_NODE_TYPES } from '@typescript-eslint/types';

import { RuleTester } from '../../tester';
import rule from '../../../src/rules/no-top-level-await';

const error = { messageId: 'forbidden' as const, line: 1, column: 1, type: AST_NODE_TYPES.AwaitExpression, data: {} };

if (!RuleTester.isSupported(2022)) {
console.log('Skip the tests of no-top-level-await.');
} else {
new RuleTester({ parserOptions: { sourceType: 'module' } } as TSESLint.RuleTesterConfig).run(
'no-top-level-await',
rule,
{
valid: [
'async function f() { await expr }',
'expr;',
'const f = async function() { await expr }',
'const f = async () => { await expr }',
'({ async method() { await expr } })',
'class A { async method() { await expr } }',
'(class { async method() { await expr } })',
'async function f() { for await (a of b); }',
'async function f() { for await (var a of b); }',
'async function f() { for await (let a of b); }',
'async function f() { for await (const a of b); }',
'function f() { async function f() { await expr } }',
],
invalid: [
{ code: 'await expr', errors: [{ ...error }] },
{ code: 'for await (a of b);', errors: [{ ...error, type: AST_NODE_TYPES.ForOfStatement }] },
{ code: 'for await (var a of b);', errors: [{ ...error, type: AST_NODE_TYPES.ForOfStatement }] },
{ code: 'for await (let a of b);', errors: [{ ...error, type: AST_NODE_TYPES.ForOfStatement }] },
{ code: 'for await (const a of b);', errors: [{ ...error, type: AST_NODE_TYPES.ForOfStatement }] },
{
code: `
await expr
async function f() {
await expr
}
await expr`,
errors: [
{ ...error, line: 2 },
{ ...error, line: 6 },
],
},
{
code: `
await expr
async function f() {
await expr
async function f() {
await expr
}
}
await expr`,
errors: [
{ ...error, line: 2 },
{ ...error, line: 9 },
],
},
{
code: `
let jQuery;
try {
jQuery = await import('https://cdn-a.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.com/jQuery');
}`,
errors: [
{ ...error, line: 4, column: 12 },
{ ...error, line: 6, column: 12 },
],
},
{ code: '{ await expr }', errors: [{ ...error, column: 3 }] },
{ code: '( await expr )', errors: [{ ...error, column: 3 }] },
{ code: 'fn( await expr )', errors: [{ ...error, column: 5 }] },
{ code: 'if (foo) { await expr }', errors: [{ ...error, column: 12 }] },
{ code: 'for (const foo of bar) { await expr }', errors: [{ ...error, column: 26 }] },
],
},
);
}

0 comments on commit f54eb1a

Please sign in to comment.