textlint-rule / textlint-rule-prh

textlint rule for prh.
MIT License
82 stars 6 forks source link

Question: prh.ymlのpatternsに指定した文字列がうまくマッチしない #74

Closed heppokofrontend closed 4 years ago

heppokofrontend commented 4 years ago

はじめまして、textlintでHTML内に含まれる特定の文字列を検出したいと考えているのですが、うまく対象の文字列にヒットしません。

どこに問題があるのかわからず、こちらに質問をさせていただきました。

HTMLのタグ内(属性値など)を含む全コードに対して検査をしたいので、あえて@textlint/textlint-plugin-textを利用しています。

.textlintrc

{
  "filters": {},
  "rules": {
    "prh": {
      "rulePaths": ["./prh.yml"]
    }
  },
  "plugins": {
    "@textlint/text": {
        "extensions": [".html"]
    }
  }
}

prh.yml

version: 1
rules:
  - expected: textlint
    patterns: TextLint

test.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>

本来であれば、9, 10, 11, 12行目に指摘が入るはずだと思うのですが 10 行目にしか指摘が入りません。 なにか改善方法などありましたらご教授いただけないでしょうか。

azu commented 4 years ago

https://textlint.github.io/astexplorer/ パースした結果のASTを見てもちゃんとStr(<p>の中のテキストnode)がそれぞれあるような気がするので、いまいち原因が分からないですね。

htmlプラグインを指定した状態のテストケースを書いて、Str Nodeがそれぞれ渡ってきてるかをちょっと確認しないとルール or プラグインのどっちに原因があるのかはっきりしないかんじですね。

https://github.com/textlint-rule/textlint-rule-prh/blob/7579cf2e938c36221026a2e8284eaff043be420d/src/textlint-rule-prh.js#L208

heppokofrontend commented 4 years ago

forkしてきて見よう見まねで試してみているのですが、うまく.textlintrcを読み込んでくれません。

構造は次の形で問題ないでしょうか。。。

│  .textlintrc
│  package-lock.json
│  package.json
│  prh.yml
│  
└─test
    │  textlint-tester-plugin.ts

textlint-tester-plugin.ts

// LICENSE : MIT
"use strict";

import TextLintTester = require("../src/index");

const htmlPlugin = require("textlint-plugin-html");
const prh = require("textlint-rule-prh");
const tester = new TextLintTester();

tester.run(
    "new-style-of-test: prh",
    {
        plugins: [
            {
                pluginId: "html",
                plugin: htmlPlugin
            }
        ],
        rules: [
            {
                ruleId: "prh",
                rule: prh
            }
        ]
    },
    {
        valid: [
            {
                text: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>textlint</p>
<p>textlintが好きです</p>
<p>私はtextlintが好きです</p>
<p>私は textlint が好きです</p>
</body>
</html>`,
                ext: ".html"
            }
        ],
        invalid: [
            // line, column
            {
                text: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>`,
                ext: ".html",
                errors: [
                    {
                        message: "Document is too long(number of lines: 3).",
                        index: 0,
                        line: 1,
                        column: 1
                    },
                    {
                        message: "Found TODO: 'TODO: no todo'",
                        line: 1,
                        column: 4
                    }
                ]
            }
        ]
    }
);

log

> ./node_modules/.bin/mocha test/textlint-tester-plugin.ts

  new-style-of-test: prh
    1) <!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>
    2) <!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>textlint</p>
<p>textlintが好きです</p>
<p>私はtextlintが好きです</p>
<p>私は textlint が好きです</p>
</body>
</html>

  0 passing (50ms)
  2 failing

  1) new-style-of-test: prh
       <!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>:
     Error: Error while loading rule 'prh': textlint-rule-prh require Rule Options.
Please set .textlintrc:
{
    "rules": {
        "prh": {
            "rulePaths" :["path/to/prh.yml"]
        }
    }
}

      at assertOptions (node_modules\textlint-rule-prh\src\textlint-rule-prh.js:75:15)
      at reporter (node_modules\textlint-rule-prh\src\textlint-rule-prh.js:191:5)
      at TextLintCoreTask.tryToGetRuleObject (node_modules\@textlint\kernel\src\task\textlint-core-task.ts:188:20)
      at TextLintCoreTask.tryToAddListenRule (node_modules\@textlint\kernel\src\task\textlint-core-task.ts:225:24)
      at D:\projects\_test\textlint-test\packages\textlint-tester\node_modules\@textlint\kernel\src\task\linter-task.ts:63:18
      at Array.forEach (<anonymous>)
      at TextLintCoreTask._setupRules (node_modules\@textlint\kernel\src\task\linter-task.ts:54:50)
      at new TextLintCoreTask (node_modules\@textlint\kernel\src\task\linter-task.ts:39:14)
      at LinterProcessor.process (node_modules\@textlint\kernel\src\linter\linter-processor.ts:48:22)
      at TextlintKernel._parallelProcess (node_modules\@textlint\kernel\src\textlint-kernel.ts:155:14)
      at D:\projects\_test\textlint-test\packages\textlint-tester\node_modules\@textlint\kernel\src\textlint-kernel.ts:83:25

  2) new-style-of-test: prh
       <!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>textlint</p>
<p>textlintが好きです</p>
<p>私はtextlintが好きです</p>
<p>私は textlint が好きです</p>
</body>
</html>:
     Error: Error while loading rule 'prh': textlint-rule-prh require Rule Options.
Please set .textlintrc:
{
    "rules": {
        "prh": {
            "rulePaths" :["path/to/prh.yml"]
        }
    }
}

      at assertOptions (node_modules\textlint-rule-prh\src\textlint-rule-prh.js:75:15)
      at reporter (node_modules\textlint-rule-prh\src\textlint-rule-prh.js:191:5)
      at TextLintCoreTask.tryToGetRuleObject (node_modules\@textlint\kernel\src\task\textlint-core-task.ts:188:20)
      at TextLintCoreTask.tryToAddListenRule (node_modules\@textlint\kernel\src\task\textlint-core-task.ts:225:24)
      at D:\projects\_test\textlint-test\packages\textlint-tester\node_modules\@textlint\kernel\src\task\linter-task.ts:63:18
      at Array.forEach (<anonymous>)
      at TextLintCoreTask._setupRules (node_modules\@textlint\kernel\src\task\linter-task.ts:54:50)
      at new TextLintCoreTask (node_modules\@textlint\kernel\src\task\linter-task.ts:39:14)
      at LinterProcessor.process (node_modules\@textlint\kernel\src\linter\linter-processor.ts:48:22)
      at TextlintKernel._parallelProcess (node_modules\@textlint\kernel\src\textlint-kernel.ts:155:14)
      at D:\projects\_test\textlint-test\packages\textlint-tester\node_modules\@textlint\kernel\src\textlint-kernel.ts:83:25
azu commented 4 years ago

tester内では自動的に.textlintrcは読みこまないので、 options で明示的にルールのオプションを指定します。

https://github.com/textlint-rule/textlint-rule-prh/blob/7579cf2e938c36221026a2e8284eaff043be420d/test/prh-rule-tester-test.js#L24-L30


// LICENSE : MIT
"use strict";
var TextLintTester = require("textlint-tester");
var tester = new TextLintTester();
// rule
import rule from "../src/textlint-rule-prh";

const htmlPlugin = require("textlint-plugin-html");
// ruleName, rule, { valid, invalid }
tester.run("prh@html",
    {
        plugins: [
            {
                pluginId: "html",
                plugin: htmlPlugin
            }
        ],
        rules: [
            {
                ruleId: "prh",
                rule: rule,
                options: {
                    rulePaths: [__dirname + "/fixtures/textlint.yml"]
                }
            }
        ]
    }, {
        valid: [
            {
                text: "<p>JavaScript library</p>",
                ext: ".html"
            }
        ],
        invalid: [
            {
                text: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>`,
                output: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>textlint</p>
<p>textlintが好きです</p>
<p>私はtextlintが好きです</p>
<p>私は textlint が好きです</p>
</body>
</html>`,
                ext: ".html",
                errors: [
                    {
                        message: "TextLint => textlint",
                        line: 9
                    },
                    {
                        message: "TextLint => textlint",
                        line: 10
                    },
                    {
                        message: "TextLint => textlint",
                        line: 11
                    },
                    {
                        message: "TextLint => textlint",
                        line: 12
                    }
                ]
            }
        ]
    });

みたいな感じでtextlintrcで書いてるprhに対するオプションだけを指定します。

heppokofrontend commented 4 years ago

ありがとうございます。 作業場所を出なければならないので、明日引き続き試してみます!

azu commented 4 years ago

HTMLのタグ内(属性値など)を含む全コードに対して検査をしたいので、あえて@textlint/textlint-plugin-textを利用しています。

あー、見逃してました。

ちょっとあんまり想定してない使い方ですが、次のような感じのテストコードですね。 うーん、テストコード上はちゃんと4回エラーでますね。

// LICENSE : MIT
"use strict";
import TextLintTester from "textlint-tester";
import textPlugin from "@textlint/textlint-plugin-text";
import rule from "../src/textlint-rule-prh";

const tester = new TextLintTester();
// ruleName, rule, { valid, invalid }
tester.run("prh@html as text", {
    plugins: [
        {
            pluginId: "text",
            plugin: textPlugin,
            options: {
                extensions: [".html"]
            }
        }
    ],
    rules: [
        {
            ruleId: "prh",
            rule: rule,
            options: {
                rulePaths: [__dirname + "/fixtures/textlint.yml"]
            }
        }
    ]
}, {
    valid: [
        {
            text: "<p>JavaScript library</p>",
            ext: ".html"
        }
    ],
    invalid: [
        {
            text: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>TextLint</p>
<p>TextLintが好きです</p>
<p>私はTextLintが好きです</p>
<p>私は TextLint が好きです</p>
</body>
</html>`,
            output: `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p>textlint</p>
<p>textlintが好きです</p>
<p>私はtextlintが好きです</p>
<p>私は textlint が好きです</p>
</body>
</html>`,
            ext: ".html",
            errors: [
                {
                    message: "TextLint => textlint",
                    line: 9
                },
                {
                    message: "TextLint => textlint",
                    line: 10
                },
                {
                    message: "TextLint => textlint",
                    line: 11
                },
                {
                    message: "TextLint => textlint",
                    line: 12
                }
            ]
        }
    ]
});

再現リポジトリって作成できますか? https://github.com/textlint-rule/textlint-rule-prh/issues/74#issue-553568450 の設定で git cloneして npm installしたら動かせるようなリポジトリ。 あんまり想定しない使い方だったので、textlint本体に問題があるのかもしれません。

heppokofrontend commented 4 years ago

repository用意してみました。 こちらでよろしいでしょうか…?

https://github.com/Soten-Bluesky/textlint-test/tree/master

こちらのサンプルの場合はtextlintを動かすと4つしかエラーが出ませんが、想定では次の8項目にエラーが発生する想定です。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>textlint</title><!-- 小文字のtextlint -->
</head>
<body data-test="2"><!-- 全角数字 -->
<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>
<p><a href="​https://example.com/">href属性値にゼロ幅スペースを含むリンク</a></p><!-- ゼロ幅スペース -->
<p>textlint</p><!-- 小文字のtextlint -->
<p>textlintが好きです</p><!-- 小文字のtextlint -->
<p>私はtextlintが好きです</p><!-- 小文字のtextlint -->
<p>私は textlint が好きです</p><!-- 小文字のtextlint -->
<p>ここに→"​"←ゼロ幅スペース文字がいます</p><!-- ゼロ幅スペース -->
</body>
</html>

実際のログ

PS D:\dummy-path\textlint-test> ./node_modules/.bin/textlint test.html

D:\dummy-path\textlint-test\test.html
   5:8   ✓ error  textlint => TEXTLINT          prh
   7:18  ✓ error  2 => 半角数字
半角数字を利用してください    prh
   9:13  ✓ error  ​ =>
ゼロ幅スペースを検出しました  prh
  14:9   ✓ error  ​ =>
ゼロ幅スペースを検出しました  prh

✖ 4 problems (4 errors, 0 warnings)
✓ 4 fixable problems.
Try to run: $ textlint --fix [file]

お手数をおかけしてすみませんが、よろしくお願いいたします。。。

azu commented 4 years ago

ちょっと今週忙しいので来週ぐらいになるかもしれないですが、後で見ますー (忘れてそうならremindしてください)

heppokofrontend commented 4 years ago

お忙しい中恐れ入ります…

どうぞよろしくお願いいたします。

heppokofrontend commented 4 years ago

@azu さん、お世話になっております…本件その後いかがでしょうか。。。

お忙しいところ恐れ入りますがどうぞよろしくお願いいたします。

azu commented 4 years ago

再現プロジェクトの以下でログを吐いてみると getSource の結果がなぜかおかしいですね。

      const text = getSource(node); // to get position from index
+     console.log(node, text);
+     console.log("---------");

https://github.com/textlint-rule/textlint-rule-prh/blob/7579cf2e938c36221026a2e8284eaff043be420d/src/textlint-rule-prh.js#L212


{
  type: 'Str',
  raw: '<!DOCTYPE html>',
  value: '<!DOCTYPE html>',
  range: [ 0, 15 ],
  loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 15 } }
} <!DOCTYPE html>
---------
{
  type: 'Str',
  raw: '<html lang="ja">',
  value: '<html lang="ja">',
  range: [ 16, 32 ],
  loc: { start: { line: 2, column: 0 }, end: { line: 2, column: 16 } }
} 
<html lang="ja"
---------
{
  type: 'Str',
  raw: '<head>',
  value: '<head>',
  range: [ 33, 39 ],
  loc: { start: { line: 3, column: 0 }, end: { line: 3, column: 6 } }
} 
<hea
---------
{
  type: 'Str',
  raw: '<meta charset="UTF-8">',
  value: '<meta charset="UTF-8">',
  range: [ 40, 62 ],
  loc: { start: { line: 4, column: 0 }, end: { line: 4, column: 22 } }
} >
<meta charset="UTF-
---------
{
  type: 'Str',
  raw: '<title>textlint</title>',
  value: '<title>textlint</title>',
  range: [ 63, 86 ],
  loc: { start: { line: 5, column: 0 }, end: { line: 5, column: 23 } }
} ">
<title>textlint</ti
---------
{
  type: 'Str',
  raw: '</head>',
  value: '</head>',
  range: [ 87, 94 ],
  loc: { start: { line: 6, column: 0 }, end: { line: 6, column: 7 } }
} le>
</
---------
{
  type: 'Str',
  raw: '<body data-test="2">',
  value: '<body data-test="2">',
  range: [ 95, 115 ],
  loc: { start: { line: 7, column: 0 }, end: { line: 7, column: 20 } }
} ead>
<body data-tes
---------
{
  type: 'Str',
  raw: '<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>',
  value: '<p>テキストテキストテキストテキストテキストテキストテキストテキスト</p>',
  range: [ 116, 155 ],
  loc: { start: { line: 8, column: 0 }, end: { line: 8, column: 39 } }
} ="2">
<p>テキストテキストテキストテキストテキストテキストテキストテ
---------
{
  type: 'Str',
  raw: '<p><a href="​https://example.com/">href属性値にゼロ幅スペースを含むリンク</a></p>',
  value: '<p><a href="​https://example.com/">href属性値にゼロ幅スペースを含むリンク</a></p>',
  range: [ 156, 220 ],
  loc: { start: { line: 9, column: 0 }, end: { line: 9, column: 64 } }
} スト</p>
<p><a href="​https://example.com/">href属性値にゼロ幅スペースを含むリンク
---------
{
  type: 'Str',
  raw: '<p>textlint</p>',
  value: '<p>textlint</p>',
  range: [ 221, 236 ],
  loc: { start: { line: 10, column: 0 }, end: { line: 10, column: 15 } }
} /a></p>
<p>tex
---------
{
  type: 'Str',
  raw: '<p>textlintが好きです</p>',
  value: '<p>textlintが好きです</p>',
  range: [ 237, 257 ],
  loc: { start: { line: 11, column: 0 }, end: { line: 11, column: 20 } }
} lint</p>
<p>textlin
---------
{
  type: 'Str',
  raw: '<p>私はtextlintが好きです</p>',
  value: '<p>私はtextlintが好きです</p>',
  range: [ 258, 280 ],
  loc: { start: { line: 12, column: 0 }, end: { line: 12, column: 22 } }
} が好きです</p>
<p>私はtextli
---------
{
  type: 'Str',
  raw: '<p>私は textlint が好きです</p>',
  value: '<p>私は textlint が好きです</p>',
  range: [ 281, 305 ],
  loc: { start: { line: 13, column: 0 }, end: { line: 13, column: 24 } }
} tが好きです</p>
<p>私は textli
---------
{
  type: 'Str',
  raw: '<p>ここに→"​"←ゼロ幅スペース文字がいます</p>',
  value: '<p>ここに→"​"←ゼロ幅スペース文字がいます</p>',
  range: [ 306, 334 ],
  loc: { start: { line: 14, column: 0 }, end: { line: 14, column: 28 } }
} t が好きです</p>
<p>ここに→"​"←ゼロ幅ス
---------
{
  type: 'Str',
  raw: '</body>',
  value: '</body>',
  range: [ 335, 342 ],
  loc: { start: { line: 15, column: 0 }, end: { line: 15, column: 7 } }
} ース文字がいま
---------
{
  type: 'Str',
  raw: '</html>',
  value: '</html>',
  range: [ 343, 350 ],
  loc: { start: { line: 16, column: 0 }, end: { line: 16, column: 7 } }
} </p>
<
---------
azu commented 4 years ago

原因は@textlint/textlint-plugin-textが改行コードがLFなら意図通り動いてて、改行コードがCRLFだと1つとカウントしてしまって、1文字分ずつずれてしまってる感じがします。

暫定のワークアラウンドとしてはファイルの改行コードをLFにすることで解決します。

azu commented 4 years ago

具体的な原因は https://github.com/textlint/textlint/blob/45add3ef65ebfdfc79b1890822260587bfe77cf7/packages/%40textlint/text-to-ast/src/plaintext-parser.js#L32 で改行を \n の決め打ちにしているからですね。

azu commented 4 years ago

text plugin側の問題なので別途Issueを作りました https://github.com/textlint/textlint/issues/656

heppokofrontend commented 4 years ago

遅い時間にありがとうございます…! 無事原因がわかったようでよかったです…

解決されるのを心待ちにしておりますね。

azu commented 4 years ago

https://github.com/textlint/textlint/releases/tag/textlint%4011.6.2 で修正できたと思います

heppokofrontend commented 4 years ago

無事に動作することを確認しました! @azu さん、ありがとうございました!!