|
1 | | -import type { TextlintRuleModule } from "@textlint/types"; |
2 | | - |
3 | | -export interface Options { |
4 | | - // If node's text includes allowed text, does not report. |
5 | | - allows?: string[]; |
6 | | - // Disable specific pattern checks |
7 | | - disableBoldListItems?: boolean; |
8 | | - disableEmojiListItems?: boolean; |
9 | | -} |
10 | | - |
11 | | -const report: TextlintRuleModule<Options> = (context, options = {}) => { |
12 | | - const { Syntax, RuleError, report, getSource, locator } = context; |
13 | | - const allows = options.allows ?? []; |
14 | | - const disableBoldListItems = options.disableBoldListItems ?? false; |
15 | | - const disableEmojiListItems = options.disableEmojiListItems ?? false; |
16 | | - |
17 | | - // AI-like emoji patterns commonly used in lists |
18 | | - const emojiPatterns = [ |
19 | | - "✅", |
20 | | - "❌", |
21 | | - "⭐", |
22 | | - "💡", |
23 | | - "🔥", |
24 | | - "📝", |
25 | | - "⚡", |
26 | | - "🎯", |
27 | | - "🚀", |
28 | | - "🎉", |
29 | | - "📌", |
30 | | - "🔍", |
31 | | - "💰", |
32 | | - "📊", |
33 | | - "🔧", |
34 | | - "⚠️", |
35 | | - "❗", |
36 | | - "💻", |
37 | | - "📱", |
38 | | - "🌟" |
39 | | - ]; |
40 | | - |
41 | | - return { |
42 | | - [Syntax.ListItem](node) { |
43 | | - const text = getSource(node); |
44 | | - |
45 | | - if (allows.some((allow) => text.includes(allow))) { |
46 | | - return; |
47 | | - } |
48 | | - |
49 | | - // Check for bold list item pattern: - **text**: description |
50 | | - if (!disableBoldListItems) { |
51 | | - const boldListPattern = /^[\s]*[-*+]\s+\*\*([^*]+)\*\*\s*:/; |
52 | | - const boldMatch = text.match(boldListPattern); |
53 | | - if (boldMatch) { |
54 | | - const matchStart = boldMatch.index ?? 0; |
55 | | - const matchEnd = matchStart + boldMatch[0].length; |
56 | | - const matchRange = [matchStart, matchEnd] as const; |
57 | | - const ruleError = new RuleError( |
58 | | - "リストアイテムで強調(**)とコロン(:)の組み合わせはAIっぽい記述です。より自然な表現を使用してください。", |
59 | | - { |
60 | | - padding: locator.range(matchRange) |
61 | | - } |
62 | | - ); |
63 | | - report(node, ruleError); |
64 | | - } |
65 | | - } |
66 | | - |
67 | | - // Check for emoji list items |
68 | | - if (!disableEmojiListItems) { |
69 | | - for (const emoji of emojiPatterns) { |
70 | | - const emojiIndex = text.indexOf(emoji); |
71 | | - if (emojiIndex !== -1) { |
72 | | - const matchRange = [emojiIndex, emojiIndex + emoji.length] as const; |
73 | | - const ruleError = new RuleError( |
74 | | - `リストアイテムで絵文字「${emoji}」を使用するのはAIっぽい記述です。テキストベースの表現を使用してください。`, |
75 | | - { |
76 | | - padding: locator.range(matchRange) |
77 | | - } |
78 | | - ); |
79 | | - report(node, ruleError); |
80 | | - break; // Only report the first emoji found in each list item |
81 | | - } |
82 | | - } |
83 | | - } |
84 | | - } |
85 | | - }; |
| 1 | +import noAiListFormatting from "./rules/no-ai-list-formatting"; |
| 2 | +import noAiFormalExpressions from "./rules/no-ai-formal-expressions"; |
| 3 | + |
| 4 | +const preset = { |
| 5 | + rules: { |
| 6 | + "no-ai-list-formatting": noAiListFormatting, |
| 7 | + "no-ai-formal-expressions": noAiFormalExpressions |
| 8 | + }, |
| 9 | + rulesConfig: { |
| 10 | + "no-ai-list-formatting": true, |
| 11 | + "no-ai-formal-expressions": true |
| 12 | + } |
86 | 13 | }; |
87 | 14 |
|
88 | | -export default report; |
| 15 | +export default preset; |
0 commit comments