diff --git a/.changeset/slimy-pillows-change.md b/.changeset/slimy-pillows-change.md new file mode 100644 index 00000000..0c086dce --- /dev/null +++ b/.changeset/slimy-pillows-change.md @@ -0,0 +1,5 @@ +--- +"eslint-plugin-astro": minor +--- + +feat add `astro/missing-client-only-directive-value` rule diff --git a/README.md b/README.md index 2b61c204..df9ca6f0 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,7 @@ These rules relate to possible syntax or logic errors in Astro component code: | Rule ID | Description | | |:--------|:------------|:---| +| [astro/missing-client-only-directive-value](https://ota-meshi.github.io/eslint-plugin-astro/rules/missing-client-only-directive-value/) | the client:only directive is missing the correct component's framework value | ⭐ | | [astro/no-conflict-set-directives](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-conflict-set-directives/) | disallow conflicting set directives and child contents | ⭐ | | [astro/no-deprecated-astro-canonicalurl](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-canonicalurl/) | disallow using deprecated `Astro.canonicalURL` | ⭐ | | [astro/no-deprecated-astro-fetchcontent](https://ota-meshi.github.io/eslint-plugin-astro/rules/no-deprecated-astro-fetchcontent/) | disallow using deprecated `Astro.fetchContent()` | ⭐🔧 | diff --git a/docs/rules.md b/docs/rules.md index d3b156bd..7e604bae 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -13,6 +13,7 @@ These rules relate to possible syntax or logic errors in Astro component code: | Rule ID | Description | | |:--------|:------------|:---| +| [astro/missing-client-only-directive-value](./rules/missing-client-only-directive-value.md) | the client:only directive is missing the correct component's framework value | ⭐ | | [astro/no-conflict-set-directives](./rules/no-conflict-set-directives.md) | disallow conflicting set directives and child contents | ⭐ | | [astro/no-deprecated-astro-canonicalurl](./rules/no-deprecated-astro-canonicalurl.md) | disallow using deprecated `Astro.canonicalURL` | ⭐ | | [astro/no-deprecated-astro-fetchcontent](./rules/no-deprecated-astro-fetchcontent.md) | disallow using deprecated `Astro.fetchContent()` | ⭐🔧 | diff --git a/docs/rules/missing-client-only-directive-value.md b/docs/rules/missing-client-only-directive-value.md new file mode 100644 index 00000000..fe624138 --- /dev/null +++ b/docs/rules/missing-client-only-directive-value.md @@ -0,0 +1,49 @@ +--- +title: "astro/missing-client-only-directive-value" +description: "the client:only directive is missing the correct component's framework value" +--- + +# astro/missing-client-only-directive-value + +> the client:only directive is missing the correct component's framework value + +- ❗ **_This rule has not been released yet._** +- ⚙ This rule is included in `"plugin:astro/recommended"`. + +## 📖 Rule Details + +This rule reports not setting a value for the `client:only` directive. + + + + + +```astro +--- +/* eslint astro/missing-client-only-directive-value: "error" */ + +--- + +{/* ✓ GOOD */} + + +{/* ✗ BAD */} + +``` + + + +## 🔧 Options + +Nothing. + + +## 📚 Further Reading + +- [Astro Documentation | Template Directives Reference > client:only](https://docs.astro.build/en/reference/directives-reference/#clientonly) + +## 🔍 Implementation + +- [Rule source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/src/rules/missing-client-only-directive-value.ts) +- [Test source](https://github.com/ota-meshi/eslint-plugin-astro/blob/main/tests/src/rules/missing-client-only-directive-value.ts) +- [Test fixture sources](https://github.com/ota-meshi/eslint-plugin-astro/tree/main/tests/fixtures/rules/missing-client-only-directive-value) diff --git a/src/configs/flat/recommended.ts b/src/configs/flat/recommended.ts index 6dca72a5..08bd802f 100644 --- a/src/configs/flat/recommended.ts +++ b/src/configs/flat/recommended.ts @@ -7,6 +7,7 @@ export default [ { rules: { // eslint-plugin-astro rules + "astro/missing-client-only-directive-value": "error", "astro/no-conflict-set-directives": "error", "astro/no-deprecated-astro-canonicalurl": "error", "astro/no-deprecated-astro-fetchcontent": "error", diff --git a/src/configs/recommended.ts b/src/configs/recommended.ts index 87b267f7..f029a8b5 100644 --- a/src/configs/recommended.ts +++ b/src/configs/recommended.ts @@ -9,6 +9,7 @@ export = { extends: [baseExtend], rules: { // eslint-plugin-astro rules + "astro/missing-client-only-directive-value": "error", "astro/no-conflict-set-directives": "error", "astro/no-deprecated-astro-canonicalurl": "error", "astro/no-deprecated-astro-fetchcontent": "error", diff --git a/src/rules/missing-client-only-directive-value.ts b/src/rules/missing-client-only-directive-value.ts new file mode 100644 index 00000000..4684223c --- /dev/null +++ b/src/rules/missing-client-only-directive-value.ts @@ -0,0 +1,50 @@ +import type { AST } from "astro-eslint-parser" +import { createRule } from "../utils" +import { getAttributeName, getStaticAttributeValue } from "../utils/ast-utils" +import { getSourceCode } from "../utils/compat" + +export default createRule("missing-client-only-directive-value", { + meta: { + docs: { + description: + "the client:only directive is missing the correct component's framework value", + category: "Possible Errors", + recommended: true, + }, + schema: [], + messages: { + missingValue: "`client:only` directive is missing a value", + }, + type: "problem", + }, + create(context) { + const sourceCode = getSourceCode(context) + if (!sourceCode.parserServices.isAstro) { + return {} + } + + /** VerifyDirectiveValue */ + function verifyDirectiveValue( + attr: AST.JSXAttribute | AST.AstroTemplateLiteralAttribute, + ) { + const directiveName = getAttributeName(attr) + const directiveValue = getStaticAttributeValue(attr) + + if (directiveName !== "client:only") return + + if (directiveValue !== null) { + return + } + + context.report({ + node: attr.name, + messageId: "missingValue", + }) + } + + return { + JSXAttribute: verifyDirectiveValue, + AstroTemplateLiteralAttribute: verifyDirectiveValue, + } + }, +}) diff --git a/src/utils/rules.ts b/src/utils/rules.ts index 76b2bb58..a0aa7e6d 100644 --- a/src/utils/rules.ts +++ b/src/utils/rules.ts @@ -2,6 +2,7 @@ // This file has been automatically generated, // in order to update its content execute "npm run update" import type { RuleModule } from "../types" +import missingClientOnlyDirectiveValue from "../rules/missing-client-only-directive-value" import noConflictSetDirectives from "../rules/no-conflict-set-directives" import noDeprecatedAstroCanonicalurl from "../rules/no-deprecated-astro-canonicalurl" import noDeprecatedAstroFetchcontent from "../rules/no-deprecated-astro-fetchcontent" @@ -19,6 +20,7 @@ import validCompile from "../rules/valid-compile" import { buildA11yRules } from "../a11y" export const rules = [ + missingClientOnlyDirectiveValue, noConflictSetDirectives, noDeprecatedAstroCanonicalurl, noDeprecatedAstroFetchcontent, diff --git a/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte b/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte new file mode 100644 index 00000000..8e5d3132 --- /dev/null +++ b/tests/fixtures/rules/missing-client-only-directive-value/SomeSvelteComponent.svelte @@ -0,0 +1,6 @@ + + +
+

Hello Svelte!

+
diff --git a/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json new file mode 100644 index 00000000..e821bb68 --- /dev/null +++ b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-errors.json @@ -0,0 +1,7 @@ +[ + { + "message": "`client:only` directive is missing a value", + "line": 5, + "column": 22 + } +] diff --git a/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro new file mode 100644 index 00000000..1808b8bf --- /dev/null +++ b/tests/fixtures/rules/missing-client-only-directive-value/invalid/missing-client-only-directive-value-input.astro @@ -0,0 +1,5 @@ +--- +import SomeSvelteComponent from "../SomeSvelteComponent.svelte" +--- + + diff --git a/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro b/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro new file mode 100644 index 00000000..a1aa6a99 --- /dev/null +++ b/tests/fixtures/rules/missing-client-only-directive-value/valid/client-only-value.astro @@ -0,0 +1,9 @@ +--- +import SomeSvelteComponent from "../SomeSvelteComponent.svelte" +let string = "svelte" +--- + + + + + diff --git a/tests/src/rules/missing-client-only-directive-value.ts b/tests/src/rules/missing-client-only-directive-value.ts new file mode 100644 index 00000000..6563abfa --- /dev/null +++ b/tests/src/rules/missing-client-only-directive-value.ts @@ -0,0 +1,16 @@ +import { RuleTester } from "../../utils/eslint-compat" +import rule from "../../../src/rules/missing-client-only-directive-value" +import { loadTestCases } from "../../utils/utils" + +const tester = new RuleTester({ + languageOptions: { + ecmaVersion: 2020, + sourceType: "module", + }, +}) + +tester.run( + "missing-client-only-directive-value", + rule as any, + loadTestCases("missing-client-only-directive-value"), +)