Skip to content

feat: Added the block-lang rule #389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 28 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bbdf0bb
chore: minor refactor
ota-meshi Feb 9, 2023
9c9394c
Merge branch 'main' of github.com:ota-meshi/eslint-plugin-svelte
marekdedic Feb 9, 2023
a74299e
Merge branch 'main' of github.com:ota-meshi/eslint-plugin-svelte
marekdedic Feb 10, 2023
a0e915a
Merge branch 'main' of github.com:ota-meshi/eslint-plugin-svelte
marekdedic Mar 1, 2023
1745a96
test(block-lang): Added rule tests
marekdedic Mar 1, 2023
04370a8
feat(block-lang): added the first version of the rule
marekdedic Mar 1, 2023
0176939
test(block-lang): removed incorrect test
marekdedic Mar 1, 2023
a35c780
test(block-lang): fixed test configuration being too strict for the u…
marekdedic Mar 1, 2023
ceecffc
feat(block-lang): requiring at least one item for each language
marekdedic Mar 1, 2023
68d9fd6
feat(block-lang): good error messages
marekdedic Mar 1, 2023
831ca7f
docs(block-lang): proper rule description
marekdedic Mar 1, 2023
348005b
test(block-lang): removed double errors, part 1
marekdedic Mar 1, 2023
6a42220
test(block-lang): removed double errors, part 2
marekdedic Mar 1, 2023
3435675
feat(block-lang): using block isntead of element
marekdedic Mar 1, 2023
6ae0c5f
test(block-lang): added baselines
marekdedic Mar 1, 2023
a12972e
fix(block-lang): added missing export
marekdedic Mar 1, 2023
6b8941f
docs(block-lang): added the rule to the rule list
marekdedic Mar 1, 2023
8bcd632
chore(block-lang): added a changeset
marekdedic Mar 1, 2023
fa7a413
chore(block-lang): lint fix
marekdedic Mar 1, 2023
47a86a6
chore(block-lang): deduplicated quoting of langs
marekdedic Mar 1, 2023
deb49d6
docs(block-lang): added docs
marekdedic Mar 1, 2023
b48bfaa
test(block-lang): refocused tests on missing lang attribute
marekdedic Mar 1, 2023
4efeab8
test(block-lang): removed tests with missing blocks
marekdedic Mar 1, 2023
26e2755
test(block-lang): added tests for block presence enforcing
marekdedic Mar 1, 2023
3a5eece
feat(block-lang): added enforcing
marekdedic Mar 1, 2023
1b9074b
docs(block-lang): added enforcing
marekdedic Mar 1, 2023
2e61c7a
Merge branch 'main' of github.com:ota-meshi/eslint-plugin-svelte
marekdedic Mar 1, 2023
5445433
Merge branch 'main' into block-lang
marekdedic Mar 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/slimy-donkeys-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: added the `svelte/block-lang` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ These rules relate to better ways of doing things to help you avoid problems:

| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/block-lang](https://ota-meshi.github.io/eslint-plugin-svelte/rules/block-lang/) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | |
| [svelte/button-has-type](https://ota-meshi.github.io/eslint-plugin-svelte/rules/button-has-type/) | disallow usage of button without an explicit type attribute | |
| [svelte/no-at-debug-tags](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-at-debug-tags/) | disallow the use of `{@debug}` | :star: |
| [svelte/no-reactive-functions](https://ota-meshi.github.io/eslint-plugin-svelte/rules/no-reactive-functions/) | it's not necessary to define functions in reactive statements | :bulb: |
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ These rules relate to better ways of doing things to help you avoid problems:

| Rule ID | Description | |
|:--------|:------------|:---|
| [svelte/block-lang](./rules/block-lang.md) | disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks. | |
| [svelte/button-has-type](./rules/button-has-type.md) | disallow usage of button without an explicit type attribute | |
| [svelte/no-at-debug-tags](./rules/no-at-debug-tags.md) | disallow the use of `{@debug}` | :star: |
| [svelte/no-reactive-functions](./rules/no-reactive-functions.md) | it's not necessary to define functions in reactive statements | :bulb: |
Expand Down
88 changes: 88 additions & 0 deletions docs/rules/block-lang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
pageClass: "rule-details"
sidebarDepth: 0
title: "svelte/block-lang"
description: "disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks."
since: "v2.18.0"
---

# svelte/block-lang

> disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks.

## :book: Rule Details

This rule enforces all svelte components to use the same set of languages for their scripts and styles.

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<!-- ✓ GOOD -->
<script lang="ts">
/* eslint svelte/block-lang: {"error", { "script": "ts" }} */
</script>
```

</ESLintCodeBlock>

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<!-- ✓ GOOD -->
<script>
/* eslint svelte/block-lang: {"error", { "script": ["ts", null], "style": "scss" }} */
</script>

<style lang="scss">

</style>
```

</ESLintCodeBlock>

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<!-- ✗ BAD -->
<script>
/* eslint svelte/block-lang: {"error", { "script": ["ts"] }} */
</script>
```

</ESLintCodeBlock>

## :wrench: Options

```json
{
"svelte/block-lang": [
"error",
{
"enforceScriptPresent": true,
"enforceStylePresent": false,
"script": ["ts", null], // a list of languages or null to signify no language specified
"style": "scss" // same as for script, a single value can be used instead of an array.
}
]
}
```

- `enforceScriptPresent` ... Whether to enforce the presence of a `<script>` block with one of the given languages. This may be useful as for example TypeScript checks some uses of a component if it is defined as being TypeScript. Default `false`.
- `enforceStylePresent` ... Whether to enforce the presence of a `<style>` block with one of the given languages. Default `false`.
- `script` ... A list of languages allowed for the `<script>` block. If `null` is included, no `lang` attribute is also allowed. A plain string or `null` can be used instead of one-item array. Default `null`.
- `style` ... A list of languages allowed for the `<style>` block. If `null` is included, no `lang` attribute is also allowed. A plain string or `null` can be used instead of one-item array. Default `null`.

## :rocket: Version

This rule was introduced in eslint-plugin-svelte v2.18.0

## :mag: Implementation

- [Rule source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/src/rules/block-lang.ts)
- [Test source](https://github.com/ota-meshi/eslint-plugin-svelte/blob/main/tests/src/rules/block-lang.ts)
152 changes: 152 additions & 0 deletions src/rules/block-lang.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { createRule } from "../utils"
import { getLangValue } from "../utils/ast-utils"
import type {
SvelteScriptElement,
SvelteStyleElement,
} from "svelte-eslint-parser/lib/ast"

export default createRule("block-lang", {
meta: {
docs: {
description:
"disallows the use of languages other than those specified in the configuration for the lang attribute of `<script>` and `<style>` blocks.",
category: "Best Practices",
recommended: false,
},
schema: [
{
type: "object",
properties: {
enforceScriptPresent: {
type: "boolean",
},
enforceStylePresent: {
type: "boolean",
},
script: {
oneOf: [
{
type: ["string", "null"],
},
{
type: "array",
items: {
type: ["string", "null"],
},
minItems: 1,
},
],
},
style: {
oneOf: [
{
type: ["string", "null"],
},
{
type: "array",
items: {
type: ["string", "null"],
},
minItems: 1,
},
],
},
},
additionalProperties: false,
},
],
messages: {},
type: "suggestion",
},
create(context) {
const enforceScriptPresent: boolean =
context.options[0]?.enforceScriptPresent ?? false
const enforceStylePresent: boolean =
context.options[0]?.enforceStylePresent ?? false

const scriptOption: string | null | (string | null)[] =
context.options[0]?.script ?? null
const allowedScriptLangs: (string | null)[] = Array.isArray(scriptOption)
? scriptOption
: [scriptOption]
let scriptLang: string | null = null
let scriptNode: SvelteScriptElement | undefined = undefined

const styleOption: string | null | (string | null)[] =
context.options[0]?.style ?? null
const allowedStyleLangs: (string | null)[] = Array.isArray(styleOption)
? styleOption
: [styleOption]
let styleLang: string | null = null
let styleNode: SvelteStyleElement | undefined = undefined

return {
SvelteScriptElement(node) {
scriptNode = node
scriptLang = getLangValue(node)?.toLowerCase() ?? null
},
SvelteStyleElement(node) {
styleNode = node
styleLang = getLangValue(node)?.toLowerCase() ?? null
},
"Program:exit"() {
if (!allowedScriptLangs.includes(scriptLang)) {
if (scriptNode !== undefined) {
context.report({
node: scriptNode,
message: `The lang attribute of the <script> block should be ${prettyPrintLangs(
allowedScriptLangs,
)}.`,
})
}
}
if (scriptNode === undefined && enforceScriptPresent) {
context.report({
loc: { line: 1, column: 1 },
message: `The <script> block should be present and its lang attribute should be ${prettyPrintLangs(
allowedScriptLangs,
)}.`,
})
}
if (!allowedStyleLangs.includes(styleLang)) {
if (styleNode !== undefined) {
context.report({
node: styleNode,
message: `The lang attribute of the <style> block should be ${prettyPrintLangs(
allowedStyleLangs,
)}.`,
})
}
}
if (styleNode === undefined && enforceStylePresent) {
context.report({
loc: { line: 1, column: 1 },
message: `The <style> block should be present and its lang attribute should be ${prettyPrintLangs(
allowedStyleLangs,
)}.`,
})
}
},
}
},
})

/**
* Prints the list of allowed languages, with special handling of the `null` option.
*/
function prettyPrintLangs(langs: (string | null)[]): string {
const hasNull = langs.includes(null)
const nonNullLangs = langs
.filter((lang) => lang !== null)
.map((lang) => `"${lang}"`)
if (nonNullLangs.length === 0) {
// No special behaviour for `hasNull`, because that can never happen.
return "omitted"
}
const hasNullText = hasNull ? "either omitted or " : ""
const nonNullText =
nonNullLangs.length === 1
? nonNullLangs[0]
: `one of ${nonNullLangs.join(", ")}`
return hasNullText + nonNullText
}
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RuleModule } from "../types"
import typescriptEslintNoUnnecessaryCondition from "../rules/@typescript-eslint/no-unnecessary-condition"
import blockLang from "../rules/block-lang"
import buttonHasType from "../rules/button-has-type"
import commentDirective from "../rules/comment-directive"
import derivedHasSameInputsOutputs from "../rules/derived-has-same-inputs-outputs"
Expand Down Expand Up @@ -54,6 +55,7 @@ import validPropNamesInKitPages from "../rules/valid-prop-names-in-kit-pages"

export const rules = [
typescriptEslintNoUnnecessaryCondition,
blockLang,
buttonHasType,
commentDirective,
derivedHasSameInputsOutputs,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"options": [{ "enforceScriptPresent": true }]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- message: The <script> block should be present and its lang attribute should be
omitted.
line: 1
column: 2
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<b>Hello World!</b>

<style></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"options": [{ "script": ["javascript"], "style": ["javascript", null] }]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "javascript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script></script>

<style lang="javascript"></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "javascript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="js"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "javascript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "javascript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="ts"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "javascript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="typescript"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"options": [{ "script": ["js"], "style": ["js", null] }]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "js".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="javascript"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "js".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script></script>

<style lang="js"></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "js".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "js".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="ts"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- message: The lang attribute of the <script> block should be "js".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="typescript"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"options": [
{
"script": ["ts", "typescript", null],
"style": ["ts", "typescript", null]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- message: The lang attribute of the <script> block should be either omitted or
one of "ts", "typescript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="javascript"></script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- message: The lang attribute of the <script> block should be either omitted or
one of "ts", "typescript".
line: 1
column: 1
suggestions: null
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script lang="js"></script>
Loading