PageSourceSearch

https://textlint.org/assets/js/2c147dc4.647e2693.js

js textlint.org collected 2026-10-03 23:18:24 UTC 47,140 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunktextlint_website=globalThis.webpackChunktextlint_website||[]).push([[3025],{858:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>o,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rule","title":"Creating Rules","description":"textlint\'s AST(Abstract Syntax Tree) is defined at the page.","source":"@site/../docs/rule.md","sourceDirName":".","slug":"/rule","permalink":"/docs/rule","draft":false,"unlisted":false,"editUrl":"https://github.com/textlint/textlint/edit/master/docs/../docs/rule.md","tags":[],"version":"current","lastUpdatedBy":"renovate[bot]","lastUpdatedAt":1790960936000,"frontMatter":{"id":"rule","title":"Creating Rules"},"sidebar":"docs","previous":{"title":"textlint MCP Server Setup","permalink":"/docs/mcp"},"next":{"title":"Creating Filter Rule","permalink":"/docs/filter-rule"}}');var i=t(1058),l=t(5175);const s={id:"rule",title:"Creating Rules"},o=void 0,d={},c=[{value:"RuleContext API",id:"rulecontext-api",level:2},{value:"RuleError",id:"ruleerror",level:2},{value:"\u26a0\ufe0f Caution \u26a0\ufe0f",id:"warning-caution-warning",level:3},{value:"Report error",id:"report-error",level:2},{value:"How to write async task in the rule",id:"how-to-write-async-task-in-the-rule",level:3},{value:"Example: creating <code>no-todo</code> rules.",id:"example-creating-no-todo-rules",level:2},{value:"Setup for creating rule",id:"setup-for-creating-rule",level:3},{value:"Build",id:"build",level:4},{value:"Tests",id:"tests",level:4},{value:"Let&#39;s create <code>no-todo</code> rule",id:"lets-create-no-todo-rule",level:3},{value:"Advanced rules",id:"advanced-rules",level:3},{value:"Case: ignore child node of <code>Link</code>, <code>Image</code> or <code>BlockQuote</code>.",id:"case-ignore-child-node-of-link-image-or-blockquote",level:4},{value:"How to test the rule?",id:"how-to-test-the-rule",level:3},{value:"Manually Installation",id:"manually-installation",level:3},{value:"Usage of textlint-tester",id:"usage-of-textlint-tester",level:4},{value:"Rule options",id:"rule-options",level:3},{value:"Advanced example",id:"advanced-example",level:2},{value:"Publishing",id:"publishing",level:2},{value:"Package Naming Conventions",id:"package-naming-conventions",level:3},{value:"Rule Naming Conventions",id:"rule-naming-conventions",level:3},{value:"Keywords",id:"keywords",level:3},{value:"FAQ: Publishing",id:"faq-publishing",level:3},{value:"Q. <code>textlint @ x.y.z</code> has new feature. My rule package require it",id:"q-textlint--xyz-has-new-feature-my-rule-package-require-it",level:4},{value:"Q. <code>textlint</code> does major update. Do my rule package major update?",id:"q-textlint-does-major-update-do-my-rule-package-major-update",level:4},{value:"Performance",id:"performance",level:2},{value:"Rule Performance",id:"rule-performance",level:3},{value:"Implementation Notes \ud83d\udcdd",id:"implementation-notes-memo",level:2},{value:"Community Notes",id:"community-notes",level:2}];function a(e){const n={a:"a",code:"code",details:"details",em:"em",h2:"h2",h3:"h3",h4:"h4",hr:"hr",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",summary:"summary",ul:"ul",...(0,l.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.p,{children:"textlint's AST(Abstract Syntax Tree) is defined at the page."}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/docs/txtnode",children:"txtnode.md"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If you want to know AST of a text, use ",(0,i.jsx)(n.a,{href:"/docs/txtnode#online-parsing-demo",children:"Online Parsing Demo"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Each rule are represented by an object with some properties.\nThe properties are equivalent to AST node types from TxtNode.\nIn TypeScript, built-in rule visitors keyed by ",(0,i.jsx)(n.code,{children:"context.Syntax"})," (TxtNode syntax types) receive readonly AST nodes.\nRules should not mutate nodes directly, such as assigning to ",(0,i.jsx)(n.code,{children:"node.value"})," or pushing to ",(0,i.jsx)(n.code,{children:"node.children"}),".\nUse ",(0,i.jsx)(n.code,{children:"context.report()"})," to report issues and ",(0,i.jsx)(n.code,{children:"context.fixer"})," to describe text changes."]}),"\n",(0,i.jsx)(n.p,{children:"The basic source code format for a rule is:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'/**\n * @param {RuleContext} context\n */\nexport default function (context) {\n    // rule object\n    return {\n        [context.Syntax.Document](node) {},\n\n        [context.Syntax.Paragraph](node) {},\n\n        [context.Syntax.Str](node) {\n            const text = context.getSource(node);\n            if (/found wrong use-case/.test(text)) {\n                // report error\n                context.report(node, new context.RuleError("Found wrong"));\n            }\n        },\n\n        [context.Syntax.Break](node) {}\n    };\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["If your rule wants to know when an ",(0,i.jsx)(n.code,{children:"Str"})," node is found in the AST, then add a method called ",(0,i.jsx)(n.code,{children:"context.Syntax.Str"}),", such as:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"// ES2015+\nexport default function (context) {\n    const { Syntax } = context;\n    return {\n        [Syntax.Str](node) {\n            // this method is called\n        }\n    };\n}\n// or ES5\nmodule.exports = function (context) {\n    const exports = {};\n    exports[context.Syntax.Str] = function (node) {\n        // this method is called\n    };\n    return exports;\n};\n"})}),"\n",(0,i.jsxs)(n.p,{children:["By default, the method matching a node name is called during the traversal when the node is first encountered(This is called ",(0,i.jsx)(n.strong,{children:"Enter"}),"), on the way down the AST."]}
1),"\n",(0,i.jsxs)(n.p,{children:["You can also specify to visit the node on the other side of the traversal, as it comes back up the tree(This is called ",(0,i.jsx)(n.strong,{children:"Leave"}),"), but adding ",(0,i.jsx)(n.code,{children:"Exit"})," to the end of the node type, such as:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"export default function (context) {\n    return {\n        // Str:exit\n        [context.Syntax.StrExit](node) {\n            // this method is called\n        }\n    };\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Note: [email protected]+ support ",(0,i.jsx)(n.code,{children:"*Exit"})," constant value like ",(0,i.jsx)(n.code,{children:"Syntax.DocumentExit"}),".\nIn [email protected]<=, you had to write ",(0,i.jsx)(n.code,{children:'[Syntax.Document + ":exit"]'}),"."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://github.com/azu/visualize-txt-traverse",title:"azu/visualize-txt-traverse",children:"visualize-txt-traverse"})," help you better understand this traversing."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://textlint.org/astexplorer/",title:"AST explorer for textlint",children:"AST explorer for textlint"})," help you better understand ",(0,i.jsx)(n.a,{href:"/docs/txtnode",children:"TxtAST"}),"."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.a,{href:"https://textlint.org/astexplorer/",children:(0,i.jsx)(n.img,{alt:"ast-explorer fork",src:t(8779).A+"",width:"1410",height:"757"})})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Related information:"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/azu/visualize-txt-traverse",title:"azu/visualize-txt-traverse",children:"azu/visualize-txt-traverse"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/tree/master/packages/@textlint/ast-traverse",title:"packages/@textlint/ast-traverse",children:"packages/@textlint/ast-traverse"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/estools/estraverse",title:"estools/estraverse",children:"estools/estraverse"})}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"rulecontext-api",children:"RuleContext API"}),"\n",(0,i.jsx)(n.p,{children:"RuleContext object has following property:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"Syntax.*"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["This is const values of ",(0,i.jsx)(n.a,{href:"/docs/txtnode",children:"TxtNode type"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["e.g.) ",(0,i.jsx)(n.code,{children:"context.Syntax.Str"})]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/blob/master/packages/%40textlint/ast-node-types/src/index.ts",children:"packages/@textlint/ast-node-types/src/index.ts"})}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"report(<node>, <ruleError>): void"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"This method is a method that reports a message from one of the rules."}),"\n",(0,i.jsxs)(n.li,{children:["e.g.) ",(0,i.jsx)(n.code,{children:'context.report(node, new context.RuleError("found rule error"));'})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"getSource(<node>): string"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"This method is a method gets the source code for the given node."}),"\n",(0,i.jsxs)(n.li,{children:["e.g.) ",(0,i.jsx)(n.code,{children:'context.getSource(node); // => "text"'})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"getFilePath(): string | undefined"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"This method return file path that is linting target."}),"\n",(0,i.jsxs)(n.li,{children:["e.g.) ",(0,i.jsx)(n.code,{children:"context.getFilePath(): // => /path/to/file.md or undefined"})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"getConfigBaseDir(): string | undefined"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["New in ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/releases/tag/textlint%409.0.0",t
1itle:"9.0.0",children:"9.0.0"})]}),"\n",(0,i.jsxs)(n.li,{children:["Available ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/get-config-base-dir",title:"@textlint/get-config-base-dir",children:"@textlint/get-config-base-dir"})," polyfill for backward compatibility"]}),"\n",(0,i.jsxs)(n.li,{children:["This method return config base directory path that is the place of ",(0,i.jsx)(n.code,{children:".textlintrc"})]}),"\n",(0,i.jsxs)(n.li,{children:["e.g.) ",(0,i.jsx)(n.code,{children:"/path/to/dir/.textlintrc"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"getConfigBaseDir()"})," return ",(0,i.jsx)(n.code,{children:'"/path/to/dir/"'}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"fixer"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"This is creator object of fix command."}),"\n",(0,i.jsxs)(n.li,{children:["See ",(0,i.jsx)(n.a,{href:"/docs/rule-fixable",children:"How to create Fixable Rule?"})," for more details"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"locator"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["New in ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/releases/tag/v12.2.0",children:"12.2.0"})]}),"\n",(0,i.jsxs)(n.li,{children:["This utility provide range methods for ",(0,i.jsx)(n.code,{children:"padding"})," property to ",(0,i.jsx)(n.code,{children:"RuleError"})]}),"\n",(0,i.jsxs)(n.li,{children:["See ",(0,i.jsx)(n.a,{href:"#RuleError",children:"RuleError"})," for more details"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"ruleerror",children:"RuleError"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"RuleError"})," is an Error object for reporting.\nUse it with ",(0,i.jsx)(n.code,{children:"report"})," function."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"report(node, new RuleError(<message>))"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"report new error"}),"\n",(0,i.jsxs)(n.li,{children:["textlint show the ",(0,i.jsx)(n.code,{children:"<message>"})," against ",(0,i.jsx)(n.code,{children:"node"}),"'s range."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"report(node, new RuleError(<message>, { padding }))"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["report new error with ",(0,i.jsx)(n.code,{children:"padding"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["textlint show the ",(0,i.jsx)(n.code,{children:"<message>"})," against ",(0,i.jsx)(n.code,{children:"node"}),"'s range + ",(0,i.jsx)(n.code,{children:"padding"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["You can control correct error range by ",(0,i.jsx)(n.code,{children:"padding"})," property of ",(0,i.jsx)(n.code,{children:"RuleError"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Example: report the ",(0,i.jsx)(n.code,{children:"Str"})," node that is typo."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"This is typo.\n^^^^^^^^^^^^^\n      |\n  Found a typo.\n"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'export default function (context) {\n    const { Syntax, report, RuleError, getSource } = context;\n    return {\n        [Syntax.Str](node) {\n            // get source code of this `node`\n            const text = getSource(node);\n            if (/typo/.test(text)) {\n                // report error\n                report(node, new RuleError("Found a typo"));\n            }\n        }\n    };\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Example: report ",(0,i.jsx)(n.code,{children:"typo"})," string that is typo using ",(0,i.jsx)(n.code,{children:"padding"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"This is typo.\n        ^^^^\n          |\n        Found a typo.\n"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"export default function (context) {\n    const { Syntax, report, RuleError, getSource, locator } = context;\n    return {\n        [Syntax.Str](node) {\n            // get source code of this `node`\n            const text = getSource(node);\n            const match = text.match(/typo/);\n            if (match) {\n                // report error with padding\n                // node's start + padding's range\n                // As a result, report the error that is [node.range[0] + match.index, node.range[1] + match.index + match[0].length]\n                report(\n                    node,\n                    new RuleError(\"Found a typo\", {\n                        padding: locator.range([match.index, match.index + match[0].length])\n                    })\n                );\n            }\n        }\n    };\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"context.locator"})," object has the following methods:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"locator.at(index)"})," - return padding value that is relative index from node's start index.\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The ",(0,i.jsx)(n.code,{children:"index"})," value is 0-based value"]}),"\n",(0,i.jsxs)(n.li,{children:["This method is alias to ",(0,i.jsx)(n.code,{children:"locator.range([index, index + 1])"})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"locator.range([startIndex, endIndex])"})," - return padding value that is relative range from node's start index\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Each ",(0,i.jsx)(n.code,{children:"index"})," value is 0-based value"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"locator.loc({ start: { line, column }, end: { line, column })"})," - return padding value that is relative location from node's start loc\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\ud83d\udcdd This ",(0,i.jsx)(n.code,{children:"line"})," and ",(0,i.jsx)(n.code,{children:"column"})," is relative value. It is not absolute value. So, These are 0-based value."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["\ud83d\udcdd ",(0,i.jsx)(n.code,{children:"context.locator"})," object in introduced in ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/releases/tag/v12.2.0",children:"v12.2.0"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"export type TextlintRulePaddingLocator = {\n    /**\n     * at's index is 0-based value.\n     * It is alias to `range([index, index + 1])`\n     * @param index relative index from node's start position\n     */\n    at(index: number): TextlintRuleErrorPaddingLocation;
1\n    /**\n     * range's index is 0-based value\n     * @param range relative range from node's start position\n     */\n    range(range: readonly [startIndex: number, endIndex: number]): TextlintRuleErrorPaddingLocation;\n    /**\n     * line is relative padding value from node's start position\n     * column is relative padding value from node's start position\n     * @param location relative location from node's start position\n     */\n    loc(location: {\n        start: {\n            line: number;\n            column: number;\n        };\n        end: {\n            line: number;\n            column: number;\n        };\n    }): TextlintRuleErrorPaddingLocation;\n};\n"})}),"\n",(0,i.jsx)(n.p,{children:"Examples"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-ts",children:"// locator.at\nreport(node, new RuleError(message, {\n  padding: locator.at(11)\n}));\n// locator.range\nreport(node, new RuleError(message, {\n  padding: locator.range([5, 10])\n}));\n// locator.loc\nreport(node, new RuleError(message, {\n  padding: locator.loc({\n      start: {\n          line: 1,\n          column: 1\n      },\n      end: {\n          line: 2,\n          column: 2\n      }\n  })\n}));\n"})}),"\n",(0,i.jsxs)(n.p,{children:["\ud83d\udcdd ",(0,i.jsx)(n.code,{children:"padding"})," option and ",(0,i.jsx)(n.code,{children:"locator"})," object are introduced in textlint v12.2.0+."]}),"\n",(0,i.jsxs)(n.details,{children:["\n",(0,i.jsx)(n.summary,{children:"Deprecated: `line, column` and `index` properties"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"{ line, column }"})," and ",(0,i.jsx)(n.code,{children:"{ index }"})," properties are deprecated.\nInstead of these, textlint v12.2.0 introduce ",(0,i.jsx)(n.code,{children:"padding"})," property and ",(0,i.jsx)(n.code,{children:"locator"})," object."]}),"\n",(0,i.jsx)(n.p,{children:"Deprecated way:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'// report without padding\nconst error = new RuleError("message");\n// OR\n// report with location-based padding\nconst paddingLine = 1;\nconst paddingColumn = 1;\nconst errorWithPadding = new RuleError("message", {\n    line: paddingLine, // padding line number from node.loc.start.line. default: 0\n    column: paddingColumn // padding column number from node.loc.start.column. default: 0\n});\n// OR\n// report with index-based padding\nconst paddingIndex = 1;\nconst errorWithPaddingIndex = new RuleError("message", {\n    index: paddingIndex // padding index value from `node.range[0]`. default: 0\n});\n'})}),"\n",(0,i.jsx)(n.p,{children:"Current way:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'// No padding information\n// It means, this error point out the whole node\nconst error = new RuleError("message");\n// OR\nconst errorWithPadding = new RuleError("message", {\n    padding: locator.loc({\n        start: {\n            line: 1, // padding line number from node.loc.start.line. default: 0\n            column: 1 // padding column number from node.loc.start.column. default: 0\n        },\n        end: {\n            line: 1, // padding line number from node.loc.start.line. default: 0\n            column: 2 // padding column number from node.loc.start.column. default: 0\n        }\n    })\n});\n// OR\nconst paddingIndex = 1;\nconst errorWithPaddingIndex = new RuleError("message", {\n    // padding [startIndex, endIndex] from node\'s start index.\n    padding: locator.range([1, 2])\n});\n'})}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"warning-caution-warning",children:"\u26a0\ufe0f Caution \u26a0\ufe0f"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"index"})," could ",(0,i.jsx)(n.strong,{children:"not"})," used with ",(0,i.jsx)(n.code,{children:"line"})," and ",(0,i.jsx)(n.code,{children:"column"}),".\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["It means that to use ",(0,i.jsx)(n.code,{children:"{ line, column }"})," or ",(0,i.jsx)(n.code,{children:"{ index }"})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"index"}),", ",(0,i.jsx)(n.code,{children:"line"}),", ",(0,i.jsx)(n.code,{children:"column"})," is a ",(0,i.jsx)(n.strong,{children:"relative"})," value from the ",(0,i.jsx)(n.code,{children:"node"})," which is reported."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"report-error",children:"Report error"}),"\n",(0,i.jsxs)(n.p,{children:["You will use mainly method is ",(0,i.jsx)(n.code,{children:"context.report()"}),", which publishes an error (defined in each rules)."]}),"\n",(0,i.jsx)(n.p,{children:"For example:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'export default function (context) {\n    const { Syntax, report, RuleError } = context;\n    return {\n        [Syntax.Str](node) {\n            // get source code of this `node`\n            const text = context.getSource(node);\n            if (/found wrong use-case/.test(text)) {\n                // report error\n                report(node, new RuleError("Found wrong"));\n            }\n        }\n    };\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"how-to-write-async-task-in-the-rule",children:"How to write async task in the rule"}),"\n",(0,i.jsx)(n.p,{children:"Return Promise object in the node function and the rule work asynchronously."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"export default function (context) {\n    const { Syntax } = context;\n    return {\n        [Syntax.Str](node) {\n            // textlint wait for resolved the promise.\n            return new Promise((resolve, reject) => {\n                // async task\n            });\n        }\n    };\n}\n"})}),"\n",(0,i.jsxs)(n.h2,{id:"example-creating-no-todo-rules",children:["Example: creating ",(0,i.jsx)(n.code,{children:"no-todo"})," rules."]}),"\n",(0,i.jsxs)(n.p,{children:["This example aim to create ",(0,i.jsx)(n.code,{children:"no-todo"})," rule that throw error if the text includes ",(0,i.jsx)(n.code,{children:"- [ ]"})," or ",(0,i.jsx)(n.code,{children:"todo:"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"setup-for-creating-rule",children:"Setup for creating rule"}),"\n",(0,i.jsxs)(n.p,{children:["textlint prepare useful generator tool that is ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/create-textlint-rule",children:"create-textlint-rule"})," command."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/create-textlint-rule",children:"textlint/create-textlint-rule: Create textlint rule project with no configuration."})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint-scripts",children:"textlint/textlint-scripts: textlint npm-run-scripts CLI help to create textlint rule."})}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["You can setup textlint rule using ",(0,i.jsx)(n.a,{href:"https://github.com/npm/npx",children:"npx"})," that is included in ",(0,i.jsx)(n.code,{children:"npm"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-sh",children:"# Create `textlint-rule-no-todo` project and setup!\nnpx create-textlint-rule no-todo\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Or use ",(0,i.jsx)(n.code,{children:"npm install"})," command:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-sh",children:"# Install `create-textlint-rule` to global\nnpm install --global create-textlint-rule \n# Create `textlint-rule-no-todo` project and setup!\ncreate-textlint-rule no-todo\n"})}),"\n",(0,i.jsxs)(n.p,{children:["This generated project contains ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint-scripts",children:"textlint-scripts"})," that provide build script and test script."]}),"\n",(0,i.jsxs)(n.p,{children:["\ud83d\udcdd If you want to write TypeScript, Pass ",(0,i.jsx)(n.code,{children:"--typescript"})," flag to ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/create-textlint-rule",children:"create-textlint-rule"}),".\nFor more details, see ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/create-textlint-rule",children:"create-textlint-rule"}),"'s README."]}),"\n",(0,i.jsx)(n.h4,{id:"build",children:"Build"}),"\n",(0,i.jsxs)(n.p,{children:["Builds source codes for publish to the ",(0,i.jsx)(n.code,{children:"lib/"})," folder.\nYou can write ES2015+ source codes in ",(0,i.jsx)(n.code,{children:"src/"})," folder.\nThe source codes in ",(0,i.jsx)(n.code,{children:"src/"})," built by following command."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"npm run build\n"})}),"\n",(0,i.jsx)(n.h4,{id:"tests",children:"Tests"}),"\n",(0,i.jsxs)(n.p,{children:["Run test code in ",(0,i.jsx)(n.code,{children:"test/"})," folder.\nTest textlint rule by ",(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/textlint-tester",title:"textlint-tester",children:"textlint-tester"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"npm test\n"})}),"\n",(0,i.jsxs)(n.h3,{id:"lets-create-no-todo-rule",children:["Let's create ",(0,i.jsx)(n.code,{children:"no-todo"})," rule"]}),"\n",(0,i.jsxs)(n.p,{children:["File Name: ",(0,i.jsx)(n.code,{children:"no-todo.js"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"/**\n * @param {RuleContext} context\n */\nexport default function (context) {\n    const { Syntax, getSource, RuleError, report, locator } = context;\n    return {\n        /*\n            # Header\n            Todo: quick fix this.\n            ^^^^^\n            Hit!\n        */\n        [Syntax.Str](node) {\n            // get text from node\n            const text = getSource(node);\n            // does text contain \"todo:\"?\n            const match = text.match(/todo:/i);\n            if (match) {\n                report(\n                    node,\n                    new RuleError(`Found TODO: '${text}'`, {\n                        padding: locator.range([match.index, match.index + text.length])\n                    })\n                );\n            }\n        },\n        /*\n            # Header\n            - [ ] Todo\n              ^^^\n              Hit!\n        */\n        [Syntax.ListItem](node) {\n            const text = context.getSource(node);\n            const match = text.match(/\\[\\s+\\]\\s/i);\n            if (match) {\n                report(\n                    node,\n                    new context.RuleError(`Found TODO: '${text}'`, {\n                        padding: locator.range([match.index, match.index + text.length])\n                    })\n                );\n            }\n        }\n    };\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"Example text:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-markdown",children:"# Header\n\nthis is Str.\n\nTodo: quick fix this.\n\n- list 1\n- [ ] todo\n"})}),"\n",(0,i.jsx)(n.p,{children:"Run Lint!"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-sh",children:"$ npm run build\n$ textlint --rulesdir lib/ README.md -f pretty-error\n"})}),"\n",(0,i.jsx)(n.h3,{id:"advanced-rules",children:"Advanced rules"}),"\n",(0,i.jsxs)(n.p,{children:["When linting following text with above ",(0,i.jsx)(n.code,{children:"no-todo"})," rule, a result was error."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-markdown",children:"[todo:image](http://example.com)\n"})}),"\n",(0,i.jsxs)(n.h4,{id:"case-ignore-child-node-of-link-image-or-blockquote",children:["Case: ignore child node of ",(0,i.jsx)(n.code,{children:"Link"}),", ",(0,i.jsx)(n.code,{children:"Image"})," or ",(0,i.jsx)(n.code,{children:"BlockQuote"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"You want to ignore this case, and write the following:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"/**\n * Get parents of node.\n * The parent nodes are returned in order from the closest parent to the outer ones.\n * @param node\n * @returns {Array}\n */\nfunction getParents(node) {\n    const result = [];\n    // child node has `parent` property.\n    let parent = node.parent;\n    while (parent != null) {\n        result.push(parent);\n        parent = parent.parent;\n    }\n    return result;\n}\n/**\n * Return true if `node` is wrapped any one of `types`.\n * @param {TxtNode} node is target node\n * @param {string[]} types are wrapped target node\n * @returns {boolean|*}\n */\nfunction isNodeWrapped(node, types) {\n    const parents = getParents(node);\n    const parentsTypes = parents.map(function (parent) {\n        return parent.type;\n    });\n    return types.some(function (type) {\n        return parentsTypes.some(function (parentType) {\n            return parentType === type;\n        });\n    });\n}\nexport default function (context) {\n    const { Syntax, getSource, RuleError, report, locator } = context;\n    return {\n        /*\n        Todo: quick fix this.\n    */\n        [Syntax.Str](node) {\n            // Ignore the node if the node is child of some Node types\n            if (isNodeWrapped(node, [Syntax.Link, Syntax.Image, Syntax.BlockQuote])) {\n                return;\n            }\n            // get text from node\n            const text = getSource(node);\n            // Does the text contain \"todo:\"?\n            const matches = text.matchAll(/todo:/gi);\n            for (const match of matches) {\n                const index = match.index ?? 0;\n                const length = match[0].length;\n                report(\n                    node,\n                    new RuleError(`Found TODO: '${text}'`, {\n                        padding: locator.range([index, index + length])\n                    })\n                );\n            }\n        },\n        /*\n        - [ ] Todo\n    */\n        [Syntax.ListItem](node) {\n            const text = context.getSource(node);\n            // Does the ListItem's text starts with `- [ ]`\n            const match = text.match(/^-\\s\\[\\s+]\\s/i);\n            if (match && match.index !== undefined) {\n                report(\n                    node,\n                    new context.RuleError(`Found TODO: '${text}'`, {\n                        padding: locator.range([match.index, match.index + match[0].length])\n                    })\n                );\n            }\n        }\n    };\n}\n"})}),"\n",(0,i.jsx)(n.p,{children:"As a result, linting following text with modified rule, a result was no error."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-markdown",children:"[todo:image](http://example.com)\n"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The created rule is ",(0,i.jsx)(n.a,{href:"https://github.com/textlint-rule/textlint-rule-no-todo",title:"azu/textlint-rule-no-todo",children:"textlint-rule-no-todo"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["These helper functions like ",(0,i.jsx)(n.code,{children:"getParents"})," are implemented in ",(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint-rule-helper",title:"textlint/textlint-rule-helper",children:"textlint/textlint-rule-helper"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"how-to-test-the-rule",children:"How to test the rule?"}),"\n",(0,i.jsxs)(n.p,{children:["You can already run test by ",(0,i.jsx)(n.code,{children:"npm test"})," command.\n(This test scripts is setup by ",(0,i.jsx)(n.code,{children:"create-textlint-rule"}),")"]}),"\n",(0,i.jsxs)(n.p,{children:["This test script use ",(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/textlint-tester",title:"textlint-tester",children:"textlint-tester"}),"."]}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h3,{id:"manually-installation",children:"Manually Installation"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/textlint-tester",title:"textlint-tester",children:"textlint-tester"})," depend on ",(0,i.jsx)(n.a,{href:"https://mochajs.org/",title:"Mocha",children:"Mocha"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"npm install -D textlint-tester mocha\n"})}),"\n",(0,i.jsx)(n.hr,{}),"\n",(0,i.jsx)(n.h4,{id:"usage-of-textlint-tester",children:"Usage of textlint-tester"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsx)(n.li,{children:"Write tests by using textlint-tester"}),"\n",(0,i.jsx)(n.li,{children:"Run tests by Mocha"}),"
1\n"]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"test/textlint-rule-no-todo-test.js"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'import TextLintTester from "textlint-tester";\nconst tester = new TextLintTester();\n// rule\nimport rule from "../src/no-todo";\n// ruleName, rule, { valid, invalid }\ntester.run("no-todo", rule, {\n    valid: [\n        // no match\n        "text",\n        // partial match\n        "TODOS:",\n        // ignore node\'s type\n        "[TODO: this is todo](http://example.com)",\n        "![TODO: this is todo](http://example.com/img)",\n        "> TODO: this is todo"\n    ],\n    invalid: [\n        // range\n        {\n            text: "- [ ] string",\n            //     ^^^^^^\n            errors: [\n                {\n                    message: "Found TODO: \'- [ ] string\'",\n                    range: [0, 6]\n                }\n            ]\n        },\n        // loc\n        {\n            text: "- [ ] string",\n            //     ^^^^^^\n            errors: [\n                {\n                    message: "Found TODO: \'- [ ] string\'",\n                    loc: {\n                        start: {\n                            line: 1,\n                            column: 1\n                        },\n                        end: {\n                            line: 1,\n                            column: 7\n                        }\n                    }\n                }\n            ]\n        }\n    ]\n});\n'})}),"\n",(0,i.jsx)(n.p,{children:"Run the tests:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"$ npm test\n# or\n$ npx mocha test/\n"})}),"\n",(0,i.jsxs)(n.p,{children:["\u2139\ufe0f Please see ",(0,i.jsx)(n.a,{href:"https://github.com/textlint-rule/textlint-rule-no-todo",children:"textlint-rule-no-todo"})," for details."]}),"\n",(0,i.jsx)(n.h3,{id:"rule-options",children:"Rule options"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:".textlintrc"})," is the config file for textlint."]}),"\n",(0,i.jsxs)(n.p,{children:["For example, ",(0,i.jsx)(n.code,{children:"very-nice-rule"}),"'s option is ",(0,i.jsx)(n.code,{children:'{ "key": "value" }'})," in ",(0,i.jsx)(n.code,{children:".textlintrc.json"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n  "rules": {\n    "very-nice-rule": {\n      "key": "value"\n    }\n  }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"very-nice-rule.js"})," rule get the options defined by the config file."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:'export default function (context, options) {\n    console.log(options);\n    /*\n        {\n          "key": "value"\n        }\n    */\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"options"})," value is ",(0,i.jsx)(n.code,{children:"{}"})," (empty object) by default."]}),"\n",(0,i.jsxs)(n.p,{children:["For example, ",(0,i.jsx)(n.code,{children:"very-nice-rule"}),"'s option is ",(0,i.jsx)(n.code,{children:"true"})," (enable the rule) in ",(0,i.jsx)(n.code,{children:".textlintrc.json"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n  "rules": {\n    "very-nice-rule": true\n  }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"very-nice-rule.js"})," rule get ",(0,i.jsx)(n.code,{children:"{}"})," (empty object) as ",(0,i.jsx)(n.code,{children:"options"}),"."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-js",children:"export default function (context, options) {\n    console.log(options); // {}\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"History"}),": This behavior is changed in textlint@11."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/issues/535",children:"Issue #535"})}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"advanced-example",children:"Advanced example"}),"\n",(0,i.jsx)(n.p,{children:"If you want to know more details, please see other example."}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/docs/rule-advanced",children:"Paragraph rule"})}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"publishing",children:"Publishing"}),"\n",(0,i.jsx)(n.p,{children:"If you want to publish your textlint rule, see following documents."}),"\n",(0,i.jsx)(n.h3,{id:"package-naming-conventions",children:"Package Naming Conventions"}),"\n",(0,i.jsxs)(n.p,{children:["textlint rule package naming should have ",(0,i.jsx)(n.code,{children:"textlint-rule-"})," prefix."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.code,{children:"textlint-rule-<name>"})}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"@scope/textlint-rule-<name>"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["textlint supports ",(0,i.jsx)(n.a,{href:"https://docs.npmjs.com/misc/scope",title:"Scoped packages",children:"Scoped packages"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Example: ",(0,i.jsx)(n.code,{children:"textlint-rule-no-todo"})]}),"\n",(0,i.jsx)(n.p,{children:"textlint user use it following:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n    "rules": {\
1n        "no-todo": true\n    }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Example: ",(0,i.jsx)(n.code,{children:"@scope/textlint-rule-awesome"})]}),"\n",(0,i.jsx)(n.p,{children:"textlint user use it following:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n    "rules": {\n        "@scope/awesome": true\n    }\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"rule-naming-conventions",children:"Rule Naming Conventions"}),"\n",(0,i.jsx)(n.p,{children:"The rule naming conventions for textlint are simple:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If your rule is disallowing something, prefix it with ",(0,i.jsx)(n.code,{children:"no-"}),".\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["For example, ",(0,i.jsx)(n.code,{children:"no-todo"})," disallowing ",(0,i.jsx)(n.code,{children:"TODO:"})," and ",(0,i.jsx)(n.code,{children:"no-exclamation-question-mark"})," for disallowing ",(0,i.jsx)(n.code,{children:"!"})," and ",(0,i.jsx)(n.code,{children:"?"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["If your rule is enforcing the inclusion of something, use a short name without a special prefix.\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If the rule for english, please uf ",(0,i.jsx)(n.code,{children:"textlint-rule-en-"})," prefix."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:"Keep your rule names as short as possible, use abbreviations where appropriate."}),"\n",(0,i.jsxs)(n.li,{children:["Use dashes(",(0,i.jsx)(n.code,{children:"-"}),") between words."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"npm information:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://docs.npmjs.com/files/package.json",title:"package.json | npm Documentation",children:"package.json | npm Documentation"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://www.npmjs.com/search?q=textlint",title:"results for textlint",children:"results for textlint"})}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Example rules:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/wiki/Collection-of-textlint-rule",title:"Collection of textlint rule \xb7 textlint/textlint Wiki",children:"Collection of textlint rule \xb7 textlint/textlint Wiki"})}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"keywords",children:"Keywords"}),"\n",(0,i.jsxs)(n.p,{children:["You should add ",(0,i.jsx)(n.code,{children:"textlintrule"})," to npm's ",(0,i.jsx)(n.code,{children:"keywords"})]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n  "name": "textlint-rule-no-todo",\n  "description": "Your custom rules description",\n  "version": "1.0.0",\n  "homepage": "https://github.com/textlint/textlint-custom-rules/",\n  "keywords": [\n    "textlintrule"\n  ]\n}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"faq-publishing",children:"FAQ: Publishing"}),"\n",(0,i.jsxs)(n.h4,{id:"q-textlint--xyz-has-new-feature-my-rule-package-require-it",children:["Q. ",(0,i.jsx)(n.code,{children:"textlint @ x.y.z"})," has new feature. My rule package require it"]}),"\n",(0,i.jsxs)(n.p,{children:["A. You should add ",(0,i.jsx)(n.code,{children:"textlint >= x.y.z"})," to ",(0,i.jsx)(n.code,{children:"peerDependencies"})," in ",(0,i.jsx)(n.code,{children:"package.json"})]}),"\n",(0,i.jsxs)(n.p,{children:["The recommended way to declare a dependency for future-proof compatibility is with the ",(0,i.jsx)(n.code,{children:'">="'})," range syntax, using the lowest required eslint version. For example:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json5",children:'  "peerDependencies": {\n    "textlint": ">= 5.5.0"\n  }\n'})}),"\n",(0,i.jsxs)(n.p,{children:["See example: ",(0,i.jsx)(n.a,{href:"https://github.com/textlint-rule/textlint-rule-no-todo/blob/50880b4e1c13782874a43714ee69900fc54a5348/package.json",children:"textlint-rule-no-todo/package.json"})]}),"\n",(0,i.jsxs)(n.h4,{id:"q-textlint-does-major-update-do-my-rule-package-major-update",children:["Q. ",(0,i.jsx)(n.code,{children:"textlint"})," does major update. Do my rule package major update?"]}),"\n",(0,i.jsxs)(n.p,{children:["A. If the update contains a breaking change on your rule, should update as ",(0,i.jsx)(n.em,{children:"major"}),".\nIf the update ",(0,i.jsx)(n.strong,{children:"does not"})," contain a breaking change on your rule, update as ",(0,i.jsx)(n.em,{children:"minor"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"performance",children:"Performance"}),"\n",(0,i.jsx)(n.h3,{id:"rule-performance",children:"Rule Performance"}),"\n",(0,i.jsxs)(n.p,{children:["textlint has a built-in method to track performance of individual rules.\nSetting the ",(0,i.jsx)(n.code,{children:"TIMING=1"})," environment variable will trigger the display.\nIt show their individual running time and relative performance impact as a percentage of total rule processing time."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"$ TIMING=1 textlint README.md\nRule                            | Time (ms) | Relative\n:-------------------------------|----------:|--------:\nspellcheck-tech-word            |   124.277 |    70.7%\nprh                             |    18.419 |    10.5%\nno-mix-dearu-desumasu           |    13.965 |     7.9%\nmax-ten                         |    13.246 |     7.5%\nno-start-duplicated-conjunction |     5.911 |     3.4%\n"})}),"\n",(0,i.jsx)(n.h2,{id:"implementation-notes-memo",children:"Implementation Notes \ud83d\udcdd"}),"\n",(0,i.jsx)(n.p,{children:"textlint ignore duplicated message/rules by default."}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If already the rule with config is loaded, Don't load this(same rule with same config).\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/issues/219",title:"Proposal: don't load duplicated rule(ruleConfig) \xb7 Issue #219 \xb7 textlint/textlint",children:"Proposal: don't load duplicated rule(ruleConfig) \xb7 Issue #219 \xb7 textlint/textlint"})}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Duplicated error message is ignored by default\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.em,{children:"Duplicated error messages"})," is that have same range and same message."]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/textlint/textlint/issues/209",title:"Proposal: duplicated messages is ignored by default \xb7 Issue #209 \xb7 textlint/textlint",children:"Proposal: duplicated messages is ignored by default \xb7 Issue #209 \xb7 textlint/textlint"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"community-notes",children:"Community Notes"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Japanese\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://eieito.hatenablog.com/entry/2022/07/29/100000",children:"textlint\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u304b\u3089\u65b0\u3057\u3044\u30eb\u30fc\u30eb\u4f5c\u6210\u307e\u3067\u3084\u3063\u3066\u307f\u305f - \u30a8\u30a4\u30a8\u30a4\u30ec\u30c8\u30ea\u30c3\u30af"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://zenn.dev/shivase/articles/006-how-to-create-new-textlint-plugin-1",children:"textlint\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u4f5c\u308a\u65b9(\u4f8b\uff1a\u30
1aa\u30f3\u30c9\u30a5\u30eb\u8a9e\u5909\u63db) \u6e96\u5099\u7de8"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://efcl.info/2023/02/23/textlint-sentence-rule/",children:"textlint\u3067\u30bb\u30f3\u30c6\u30f3\u30b9\u3092\u6271\u3046\u30eb\u30fc\u30eb\u306e\u66f8\u304d\u65b9 | Web Scratch"})}),"\n"]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(a,{...e})}):a(e)}},5175:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>o});var r=t(3706);const i={},l=r.createContext(i);function s(e){const n=r.useContext(l);return r.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:s(e.components),r.createElement(l.Provider,{value:n},e.children)}},8779:(e,n,t)=>{t.d(n,{A:()=>r});const r=t.p+"assets/images/ast-explorer-1dc880dfc63fd6e3df6edd08f5d1fbea.png"}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.