diff --git a/web/eslint-plugins/README.md b/web/eslint-plugins/README.md index 55990ef..1bb3cd5 100644 --- a/web/eslint-plugins/README.md +++ b/web/eslint-plugins/README.md @@ -142,13 +142,21 @@ report-only (`warn`), baseline — 0 срабатываний. списку**: комментарий обязан быть ASCII, поэтому ловятся любые письменности (кириллица, CJK, арабский), а не один конкретный алфавит; осознанные глифы — через опцию `allowChars`. Строковые литералы не трогаем — локализованные - значения легальны в словарях. + значения легальны в словарях. В `.svelte` проверяются оба вида комментариев: + `', + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + }, + { + // A CSS comment at the cap is fine, same as a script comment. + code: "", + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + }, + { + // Script and ', + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + }, + ], + invalid: [ + { + // The template comment the ts pass could never see, over the cap. + code: "\n
Hello
", + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + errors: [{ messageId: "blockTooLong" }], + }, + { + // ", + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + errors: [{ messageId: "blockTooLong", line: 2 }], + }, + { + // A comment nested in markup is collected too (not Program.body). + code: "
\n\t\n
", + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + errors: [{ messageId: "blockTooLong" }], + }, + { + // Template comment lines count toward density: 2 of 4 non-blank. + code: "\n\n

x

\n

y

", + filename: "Component.svelte", + options: [{ maxBlockLines: 5, maxRatio: 0.4 }], + errors: [{ messageId: "tooDense" }], + }, + { + // Script and template comments share one density budget. + code: '\n\n

c

', + filename: "Component.svelte", + options: [{ maxBlockLines: 5, maxRatio: 0.3 }], + errors: [{ messageId: "tooDense" }], + }, + { + // CSS lines count toward density too: 2 of 8 non-blank. Consecutive + // CSS comments stay separate blocks (each one line, under the cap). + code: "

x

\n", + filename: "Component.svelte", + options: [{ maxBlockLines: 5, maxRatio: 0.2 }], + errors: [{ messageId: "tooDense" }], + }, + { + // All three kinds over the cap: one report per block, by position. + code: "\n", + filename: "Component.svelte", + options: [{ maxRatio: 1 }], + errors: [ + { messageId: "blockTooLong", line: 1 }, + { messageId: "blockTooLong", line: 9 }, + ], + }, + { + // Both violations in one file produce two reports. + code: "\n

x

", + filename: "Component.svelte", + options: [{ maxBlockLines: 5, maxRatio: 0.5 }], + errors: [{ messageId: "blockTooLong" }, { messageId: "tooDense" }], + }, + ], +}); diff --git a/web/eslint-plugins/__tests__/comments-english.test.ts b/web/eslint-plugins/__tests__/comments-english.test.ts index 2d1b4a1..04c8521 100644 --- a/web/eslint-plugins/__tests__/comments-english.test.ts +++ b/web/eslint-plugins/__tests__/comments-english.test.ts @@ -3,12 +3,14 @@ // // Pure AST check over comment nodes with an ASCII whitelist; string literals // are ignored by construction (they are not comments), which the cases below -// pin down. +// pin down. .svelte gets its own pass: template `` comments are AST +// nodes (SvelteHTMLComment), not comment tokens, so they need a svelte parser. import { RuleTester } from "eslint"; +import svelteParser from "svelte-eslint-parser"; import tseslint from "typescript-eslint"; import { describe, it } from "vitest"; import commentsEnglish from "../conventions/comments-english.js"; -import { asRuleModule } from "./helpers.js"; +import { asRuleModule, svelteComponent } from "./helpers.js"; RuleTester.describe = describe; RuleTester.it = it; @@ -19,6 +21,13 @@ const tester = new RuleTester({ }, }); +const svelteTester = new RuleTester({ + languageOptions: { + parser: svelteParser, + parserOptions: { parser: tseslint.parser }, + }, +}); + tester.run("comments-english", asRuleModule(commentsEnglish), { valid: [ { code: "// resize the canvas\nconst x = 1;", filename: "a.ts" }, @@ -94,3 +103,77 @@ tester.run("comments-english", asRuleModule(commentsEnglish), { }, ], }); + +// Svelte components: both comment kinds are in scope. `', + filename: "Component.svelte", + errors: [ + { messageId: "nonAscii", line: 1 }, + { messageId: "nonAscii", line: 3 }, + { messageId: "nonAscii", line: 9 }, + ], + }, + { + code: svelteComponent("// ресайз холста"), + filename: "Component.svelte", + errors: [{ messageId: "nonAscii" }], + }, + { + // One report per comment, script and template alike. + code: '\n
два
\n', + filename: "Component.svelte", + errors: [{ messageId: "nonAscii" }, { messageId: "nonAscii" }], + }, + { + // allowChars does not whitelist other glyphs in the same comment. + code: "\n
Hello
", + filename: "Component.svelte", + options: [{ allowChars: ["×"] }], + errors: [{ messageId: "nonAscii" }], + }, + ], +}); diff --git a/web/eslint-plugins/conventions/comment-format.js b/web/eslint-plugins/conventions/comment-format.js index 3960782..0efa61f 100644 --- a/web/eslint-plugins/conventions/comment-format.js +++ b/web/eslint-plugins/conventions/comment-format.js @@ -1,9 +1,14 @@ // Rule: COMMENT FORMAT: bounded blocks and bounded density. // maxBlockLines caps one comment block; maxRatio caps the share of comment -// lines in files with minLines+ non-blank lines. Warn-only tripwires: -// excess means refactor the code, not squeeze the comment. +// lines in files with minLines+ non-blank lines. In .svelte,