Skip to content

Commit 293464d

Browse files
committed
refactor: Convert to TypeScript & update to textlint-script@3
1 parent eabb1f5 commit 293464d

File tree

10 files changed

+2432
-1508
lines changed

10 files changed

+2432
-1508
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,11 @@ Via CLI
101101
- `dictOptions`: `object`
102102
- それぞれの`dict`に対するオプションを指定する
103103
- プロパティに`dict`の【dict[id]】を書き、値には次の辞書オプションを指定する
104-
- 辞書オプション: `object`
105-
- `disbled`: `boolean`
106-
- `true`を指定するdictを無効化
107-
- `allows`: `string[]`
108-
- エラーを無視したいパターンを[正規表現ライクな文字列](https://github.com/textlint/regexp-string-matcher)で指定
104+
- 辞書オプション: `object`
105+
- `disabled`: `boolean`
106+
- `true`を指定するdictを無効化
107+
- `allows`: `string[]`
108+
- エラーを無視したいパターンを[正規表現ライクな文字列](https://github.com/textlint/regexp-string-matcher)で指定
109109

110110
例) [dict1](#dict1)は無効化、[dict5](#dict5)で"議論を行う"をエラーにしない。
111111

package.json

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"main": "lib/index.js",
1616
"scripts": {
1717
"test": "textlint-scripts test",
18-
"docs": "node tools/update-readme.js",
18+
"docs": "node --require ts-node/register tools/update-readme.ts",
1919
"prepublish": "npm run docs && npm run --if-present build",
2020
"build": "textlint-scripts build",
2121
"watch": "textlint-scripts build --watch",
@@ -30,19 +30,24 @@
3030
},
3131
"homepage": "https://github.com/textlint-ja/textlint-rule-ja-no-redundant-expression",
3232
"devDependencies": {
33+
"@textlint/types": "^1.2.2",
34+
"@types/mocha": "^5.2.7",
35+
"@types/node": "^12.11.1",
3336
"add-text-to-markdown": "^2.0.0",
34-
"husky": "^1.3.1",
35-
"lint-staged": "^8.1.0",
36-
"prettier": "^1.15.3",
37-
"textlint-scripts": "^2.1.0"
37+
"husky": "^3.0.9",
38+
"lint-staged": "^9.4.2",
39+
"prettier": "^1.18.2",
40+
"textlint-scripts": "^3.0.0",
41+
"ts-node": "^8.4.1",
42+
"typescript": "^3.6.4"
3843
},
3944
"dependencies": {
4045
"@textlint/regexp-string-matcher": "^1.0.2",
41-
"kuromojin": "^1.3.2",
42-
"morpheme-match": "^1.2.1",
43-
"morpheme-match-all": "^1.2.0",
46+
"kuromojin": "^2.0.0",
47+
"morpheme-match": "^2.0.4",
48+
"morpheme-match-all": "^2.0.5",
4449
"textlint-rule-helper": "^2.1.1",
45-
"textlint-util-to-string": "^2.1.1"
50+
"textlint-util-to-string": "^3.0.0"
4651
},
4752
"keywords": [
4853
"textlintrule"
Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,62 @@ const punctuations = ["、", "、", ",", ","];
55
* _capture_to_expectedで返すことが特殊な値
66
* @type {{STOP_REPLACE: string}}
77
*/
8-
const ExpectedType = {
8+
export const ExpectedType = {
99
// Expectedへの変換自体を取りやめる
1010
"STOP_REPLACE": "STOP_REPLACE"
1111
};
12-
const Dictionary = [
12+
13+
14+
export type ExpectedTokenAdditional = {
15+
_skippable?: boolean;
16+
};
17+
18+
// ExpectedToken is based on Token
19+
// But support array of each properties
20+
export type ExpectedToken = {
21+
// 辞書内での単語ID
22+
word_id?: number | number[];
23+
// 単語タイプ(辞書に登録されている単語ならKNOWN; 未知語ならUNKNOWN)
24+
word_type?: "KNOWN" | "UNKNOWN";
25+
// 表層形
26+
surface_form?: string | string[];
27+
// 品詞
28+
pos?: string | string[];
29+
// 品詞細分類1
30+
pos_detail_1?: string | string[];
31+
// 品詞細分類2
32+
pos_detail_2?: string | string[];
33+
// 品詞細分類3
34+
pos_detail_3?: string | string[];
35+
// 活用型
36+
conjugated_type?: string | string[];
37+
// 活用形
38+
conjugated_form?: string | string[];
39+
// 基本形
40+
basic_form?: string | string[];
41+
// 読み
42+
reading?: string | string[];
43+
// 発音
44+
pronunciation?: string | string[];
45+
// 単語の開始位置
46+
word_position?: number | number[];
47+
}
48+
& ExpectedTokenAdditional
49+
& {
50+
[index: string]: any;
51+
};
52+
export type ExpectedDictionary = {
53+
id: string;
54+
disabled: boolean
55+
allows: string[];
56+
message: string
57+
url: string;
58+
expected?: string;
59+
description?: string;
60+
tokens: ExpectedToken[]
61+
}
62+
63+
export const Dictionary: ExpectedDictionary[] = [
1364
{
1465
// https://azu.github.io/morpheme-match/?text=省略(することが可能)。
1566
id: "dict1",
@@ -87,7 +138,7 @@ const Dictionary = [
87138
{
88139
pos: "助詞",
89140
_capture: "$3",
90-
_capture_to_expected: (actualToken) => {
141+
_capture_to_expected: (actualToken: any) => {
91142
// 誤検知しにくい「が」のみ修正する
92143
if (actualToken.surface_form === "が") {
93144
return "";
@@ -363,6 +414,3 @@ const Dictionary = [
363414
]
364415
}
365416
];
366-
367-
module.exports.Dictionary = Dictionary;
368-
module.exports.ExpectedType = ExpectedType;

src/index.js renamed to src/index.ts

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
"use strict";
33
import { matchPatterns } from "@textlint/regexp-string-matcher";
44
import { wrapReportHandler } from "textlint-rule-helper";
5-
import StringSource from "textlint-util-to-string";
6-
const { Dictionary, ExpectedType } = require("./dictionary.js")
5+
import { StringSource } from "textlint-util-to-string";
6+
import { Dictionary, ExpectedType } from "./dictionary";
77

8-
const tokenize = require("kuromojin").tokenize;
9-
10-
const createMatchAll = require("morpheme-match-all");
8+
import { tokenize, KuromojiToken } from "kuromojin";
9+
import { TextlintRuleModule } from "@textlint/types";
10+
import { createMatcher } from "morpheme-match-all";
1111

1212
/**
1313
* textの中身をすべて置換する
@@ -16,11 +16,14 @@ const createMatchAll = require("morpheme-match-all");
1616
* @param {string} to
1717
* @returns {string}
1818
*/
19-
const replaceAll = (text, from, to) => {
19+
const replaceAll = (text: string, from: string | undefined, to: string): string => {
20+
if (!from) {
21+
return text;
22+
}
2023
return text.split(from).join(to);
2124
};
2225

23-
const replaceTokenWith = (matcherToken, actualToken, specialTo) => {
26+
const replaceTokenWith = (matcherToken: any, actualToken: KuromojiToken, specialTo: string) => {
2427
if (matcherToken[specialTo]) {
2528
return matcherToken[specialTo](actualToken);
2629
}
@@ -32,7 +35,7 @@ const replaceTokenWith = (matcherToken, actualToken, specialTo) => {
3235
* @param tokens
3336
* @returns {string}
3437
*/
35-
const tokensToString = tokens => {
38+
const tokensToString = (tokens: KuromojiToken[]) => {
3639
return tokens.map(token => token.surface_form).join("");
3740
};
3841

@@ -41,7 +44,7 @@ const tokensToString = tokens => {
4144
* @param {*[]} tokens
4245
* @param {string[]} allows
4346
*/
44-
const isTokensAllowed = (tokens, allows) => {
47+
const isTokensAllowed = (tokens: KuromojiToken[], allows: string[]) => {
4548
if (allows.length === 0) {
4649
return false;
4750
}
@@ -59,7 +62,9 @@ const isTokensAllowed = (tokens, allows) => {
5962
* @param {*[]} actualTokens
6063
* @returns {null|string}
6164
*/
62-
const createExpected = ({ expected, matcherTokens, skipped, actualTokens }) => {
65+
const createExpected = ({ expected, matcherTokens, skipped, actualTokens }: {
66+
expected?: string, matcherTokens: any[], skipped: boolean[], actualTokens: KuromojiToken[]
67+
}): null | string => {
6368
if (!expected) {
6469
return null;
6570
}
@@ -84,7 +89,7 @@ const createExpected = ({ expected, matcherTokens, skipped, actualTokens }) => {
8489
return resultText;
8590
};
8691

87-
const createMessage = ({ id, text, matcherTokens, skipped, actualTokens }) => {
92+
const createMessage = ({ id, text, matcherTokens, skipped, actualTokens }: { id: string, text: string, matcherTokens: any[], skipped: boolean[], actualTokens: KuromojiToken[] }) => {
8893
let resultText = text;
8994
let actualTokenIndex = 0;
9095
matcherTokens.forEach((token, index) => {
@@ -103,7 +108,22 @@ const createMessage = ({ id, text, matcherTokens, skipped, actualTokens }) => {
103108
解説: https://github.com/textlint-ja/textlint-rule-ja-no-redundant-expression#${id}`;
104109
};
105110

106-
const reporter = (context, options = {}) => {
111+
export interface Options {
112+
// - それぞれの`dict`に対するオプションを指定する
113+
// - プロパティに`dict`の【dict[id]】を書き、値には次の辞書オプションを指定する
114+
// - 辞書オプション: `object`
115+
dictOptions?: {
116+
[index: string]: {
117+
disabled?: boolean;
118+
allows?: string[]
119+
}
120+
};
121+
// - 無視したいNode typeを配列で指定
122+
// - Node typeは <https://textlint.github.io/docs/txtnode.html#type> を参照
123+
// - デフォルトでは、`["BlockQuote", "Link", "ReferenceDef", "Code"]`を指定し、引用やリンクのテキストは無視する
124+
}
125+
126+
const reporter: TextlintRuleModule<Options> = (context, options = {}) => {
107127
const { Syntax, RuleError, fixer } = context;
108128
const DefaultOptions = {
109129
// https://textlint.github.io/docs/txtnode.html#type
@@ -117,7 +137,7 @@ const reporter = (context, options = {}) => {
117137
const disabled = typeof dictOption.disabled === "boolean" ? dictOption.disabled : dict.disabled;
118138
return !disabled;
119139
});
120-
const matchAll = createMatchAll(enabledDictionaryList);
140+
const matchAll = createMatcher(enabledDictionaryList);
121141
const skipNodeTypes = options.allowNodeTypes || DefaultOptions.allowNodeTypes;
122142
return wrapReportHandler(
123143
context,
@@ -130,9 +150,6 @@ const reporter = (context, options = {}) => {
130150
const source = new StringSource(node);
131151
const text = source.toString();
132152
return tokenize(text).then(currentTokens => {
133-
/**
134-
* @type {MatchResult[]}
135-
*/
136153
const matchResults = matchAll(currentTokens);
137154
matchResults.forEach(matchResult => {
138155
const dictOption = dictOptions[matchResult.dict.id] || {};
@@ -147,10 +164,10 @@ const reporter = (context, options = {}) => {
147164
const lastToken = matchResult.tokens[matchResult.tokens.length - 1];
148165
const firstWordIndex = source.originalIndexFromIndex(
149166
Math.max(firstToken.word_position - 1, 0)
150-
);
167+
) || 0;
151168
const lastWordIndex = source.originalIndexFromIndex(
152169
Math.max(lastToken.word_position - 1, 0)
153-
);
170+
) || 0;
154171
// エラーメッセージ
155172
const message =
156173
createMessage({
@@ -168,7 +185,7 @@ const reporter = (context, options = {}) => {
168185
actualTokens: matchResult.tokens
169186
});
170187
const hasFixableResult = expected && tokensToString(matchResult.tokens) !== expected;
171-
if (hasFixableResult) {
188+
if (expected && hasFixableResult) {
172189
const wordLength = lastToken.surface_form.length;
173190
report(
174191
node,
@@ -195,7 +212,7 @@ const reporter = (context, options = {}) => {
195212
}
196213
);
197214
};
198-
module.exports = {
215+
export default {
199216
linter: reporter,
200217
fixer: reporter
201218
};
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
"use strict";
33
import assert from "assert";
44

5-
import { Dictionary } from "../src/dictionary.js";
6-
describe('dictionary', function() {
5+
import { Dictionary } from "../src/dictionary";
6+
7+
describe("dictionary", function() {
78
it("should not have duplicated id", () => {
89
Dictionary.forEach(item => {
9-
assert.ok(typeof item.id === "string", "should have id property");
10+
assert.strictEqual(typeof item.id, "string", "should have id property");
1011
const sameIdItems = Dictionary.filter(target => target.id === item.id);
1112
assert.ok(sameIdItems.length === 1, "should not have duplicated id item");
1213
});
1314
});
1415
it("should have disabled default value", () => {
1516
Dictionary.forEach(item => {
16-
assert.ok(typeof item.disabled === "boolean", `${item} should have disabled property`);
17+
assert.strictEqual(typeof item.disabled, "boolean", `${item} should have disabled property`);
1718
});
1819
});
1920
it("should have allows default value", () => {
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
const TextLintTester = require("textlint-tester");
2-
const tester = new TextLintTester();
31
// rule
4-
const rule = require("../src/index");
2+
import rule from "../src/index";
3+
4+
import TextLintTester from "textlint-tester";
5+
6+
const tester = new TextLintTester();
57
// ruleName, rule, { valid, invalid }
68
tester.run("textlint-rule-ja-no-redundant-expression", rule, {
79
valid: [

test/mocha.opts

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)