Skip to content

Commit

Permalink
feat: add astro/semi rule (#105)
Browse files Browse the repository at this point in the history
* feat: add `astro/semi` rule

* Create little-kangaroos-invent.md
  • Loading branch information
ota-meshi authored Sep 9, 2022
1 parent 6912804 commit 4938a61
Show file tree
Hide file tree
Showing 26 changed files with 534 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/little-kangaroos-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-astro": minor
---

feat: add `astro/semi` rule
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
/tests/fixtures/rules/semi/**/*.astro
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| [astro/prefer-class-list-directive](https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-class-list-directive/) | require `class:list` directives instead of `class` with expressions | :wrench: |
| [astro/prefer-object-class-list](https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-object-class-list/) | require use object instead of ternary expression in `class:list` | :wrench: |
| [astro/prefer-split-class-list](https://ota-meshi.github.io/eslint-plugin-astro/rules/prefer-split-class-list/) | require use split array elements in `class:list` | :wrench: |
| [astro/semi](https://ota-meshi.github.io/eslint-plugin-astro/rules/semi/) | Require or disallow semicolons instead of ASI | :wrench: |

## A11Y Extension Rules

Expand Down
21 changes: 15 additions & 6 deletions docs-build/src/components/eslint/ESLintEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
let messageMap = new Map()
let editor
let lastId = 0
export function setCursorPosition(loc) {
if (editor) {
editor.setCursorPosition(loc)
Expand Down Expand Up @@ -60,6 +62,9 @@
throw new Error()
}
}
const currId = ++lastId
const messages = linter.verify(code, config, options)
const time = Date.now() - start
Expand All @@ -75,12 +80,16 @@
fixedMessages: fixResult.messages,
})
leftMarkers = await Promise.all(
messages.map((m) => messageToMarker(m, messageMap)),
)
rightMarkers = await Promise.all(
fixResult.messages.map((m) => messageToMarker(m)),
)
const marsers = await Promise.all([
Promise.all(messages.map((m) => messageToMarker(m, messageMap))),
Promise.all(fixResult.messages.map((m) => messageToMarker(m))),
])
if (currId !== lastId) {
return
}
;[leftMarkers, rightMarkers] = marsers
}
function applyFix() {
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
| [astro/prefer-class-list-directive](./rules/prefer-class-list-directive.md) | require `class:list` directives instead of `class` with expressions | :wrench: |
| [astro/prefer-object-class-list](./rules/prefer-object-class-list.md) | require use object instead of ternary expression in `class:list` | :wrench: |
| [astro/prefer-split-class-list](./rules/prefer-split-class-list.md) | require use split array elements in `class:list` | :wrench: |
| [astro/semi](./rules/semi.md) | Require or disallow semicolons instead of ASI | :wrench: |

## A11Y Extension Rules

Expand Down
100 changes: 100 additions & 0 deletions docs/rules/semi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
title: "astro/semi"
description: "Require or disallow semicolons instead of ASI"
---

# astro/semi

> Require or disallow semicolons instead of ASI
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
- :wrench: The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.

## :book: Rule Details

This rule enforces consistent use of semicolons.

This rule extends the base ESLint's [semi] rule. The [semi] rule does not understand frontmatter fence tokens (`---`), so using the `never` option in the [semi] rule will result in a false negative.
This rule supports [astro-eslint-parser]'s AST and tokens.

[astro-eslint-parser]: https://github.com/ota-meshi/astro-eslint-parser

Default:

<ESLintCodeBlock fix>

<!--eslint-skip-->

```astro
---
/* eslint astro/semi: ["error"] */
/* ✓ GOOD */
fn();
/* ✗ BAD */
fn()
---
{() => {
/* ✓ GOOD */
fn();
/* ✗ BAD */
fn()
}}
```

</ESLintCodeBlock>

with `"never"` option:

<ESLintCodeBlock fix>

<!--eslint-skip-->

```astro
---
/* eslint astro/semi: ["error", "never"] */
/* ✓ GOOD */
fn()
/* ✗ BAD */
fn();
---
{() => {
/* ✓ GOOD */
fn()
/* ✗ BAD */
fn();
}}
```

</ESLintCodeBlock>

## :wrench: Options

```json
{
"semi": "off", // Don't need ESLint's semi, so turn it off.
"astro/semi": [
"error",
"always", // or "never"
{ "omitLastInOneLineBlock": true }
// or { "beforeStatementContinuationChars": "any" | "always" | "never" }
]
}
```

Same as [semi] rule option. See [here](https://eslint.org/docs/rules/semi#options) for details.

## :couple: Related rules

- [semi]

[semi]: https://eslint.org/docs/rules/semi

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/src/rules/semi.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/tests/src/rules/semi.ts)
- [Test fixture sources](https://github.com/ota-meshi/eslint-plugin-astro/tree/main/tests/fixtures/rules/semi)

<sup>Taken with ❤️ [from ESLint core](https://eslint.org/docs/rules/semi)</sup>
111 changes: 111 additions & 0 deletions src/rules/semi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { AST } from "astro-eslint-parser"
import type { SourceCode } from "../types"
import { createRule } from "../utils"
import { getCoreRule, newProxy } from "../utils/eslint-core"

const coreRule = getCoreRule("semi")
export default createRule("semi", {
meta: {
docs: {
description: coreRule.meta.docs.description,
category: "Stylistic Issues",
recommended: false,
extensionRule: "semi",
},
schema: coreRule.meta.schema,
messages: coreRule.meta.messages,
type: coreRule.meta.type,
fixable: coreRule.meta.fixable,
hasSuggestions: coreRule.meta.hasSuggestions,
},
create(context) {
if (!context.parserServices.isAstro) {
return coreRule.create(context)
}

let sourceCodeWrapper: SourceCode | undefined

return coreRule.create(
newProxy(context, {
getSourceCode() {
if (sourceCodeWrapper) {
return sourceCodeWrapper
}
const sourceCode = context.getSourceCode()

/** Transforms token */
function transformToken(
token: AST.Token | AST.Comment,
): AST.Token | AST.Comment {
return token.value === "---"
? newProxy(token, { value: "___" })
: token
}

return (sourceCodeWrapper = newProxy(sourceCode, {
/* eslint-disable @typescript-eslint/unbound-method -- ignore */
getFirstToken: wrapGetTokenFunction(sourceCode.getFirstToken),
getFirstTokens: wrapGetTokensFunction(sourceCode.getFirstTokens),
getFirstTokenBetween: wrapGetTokenFunction(
sourceCode.getFirstTokenBetween,
),
getFirstTokensBetween: wrapGetTokensFunction(
sourceCode.getFirstTokensBetween,
),
getLastToken: wrapGetTokenFunction(sourceCode.getLastToken),
getLastTokens: wrapGetTokensFunction(sourceCode.getLastTokens),
getLastTokenBetween: wrapGetTokenFunction(
sourceCode.getLastTokenBetween,
),
getLastTokensBetween: wrapGetTokensFunction(
sourceCode.getLastTokensBetween,
),
getTokenBefore: wrapGetTokenFunction(sourceCode.getTokenBefore),
getTokensBefore: wrapGetTokensFunction(sourceCode.getTokensBefore),
getTokenAfter: wrapGetTokenFunction(sourceCode.getTokenAfter),
getTokensAfter: wrapGetTokensFunction(sourceCode.getTokensAfter),
getTokenByRangeStart: wrapGetTokenFunction(
sourceCode.getTokenByRangeStart,
),
getTokens: wrapGetTokensFunction(sourceCode.getTokens),
getTokensBetween: wrapGetTokensFunction(
sourceCode.getTokensBetween,
),
/* eslint-enable @typescript-eslint/unbound-method -- ignore */
}))

/** Wrap token getter function */
function wrapGetTokenFunction<
T extends (
this: SourceCode,
...args: never[]
) => AST.Token | AST.Comment | null,
>(base: T): T {
return function (this: SourceCode, ...args) {
// eslint-disable-next-line no-invalid-this -- is valid
const token = base.apply(this, args)
if (!token) {
return token
}
return transformToken(token)
} as T
}

/** Wrap tokens getter function */
function wrapGetTokensFunction<
T extends (
this: SourceCode,
...args: never[]
) => (AST.Token | AST.Comment)[],
>(base: T): T {
return function (this: SourceCode, ...args) {
// eslint-disable-next-line no-invalid-this -- is valid
const tokens = base.apply(this, args)
return tokens.map(transformToken)
} as T
}
},
}),
)
},
})
Loading

0 comments on commit 4938a61

Please sign in to comment.