Skip to content

Commit cd13e03

Browse files
authored
refactor: Rewritten by TypeScript (#16)
* refactor: Rewritten by TypeScript * CI: add GitHub Actions * chore: add Options types
1 parent 6374e53 commit cd13e03

File tree

11 files changed

+2345
-1633
lines changed

11 files changed

+2345
-1633
lines changed

.githooks/pre-commit

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
npx --no-install lint-staged

.github/workflows/test.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: test
2+
on: [push, pull_request]
3+
jobs:
4+
test:
5+
name: "Test on Node.js ${{ matrix.node-version }}"
6+
runs-on: ubuntu-latest
7+
strategy:
8+
matrix:
9+
node-version: [12, 14]
10+
steps:
11+
- name: checkout
12+
uses: actions/checkout@v2
13+
- name: setup Node.js ${{ matrix.node-version }}
14+
uses: actions/setup-node@v2
15+
with:
16+
node-version: ${{ matrix.node-version }}
17+
- name: Install
18+
run: yarn install
19+
- name: Test
20+
run: yarn test

.travis.yml

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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# textlint-rule-sentence-length [![Build Status](https://travis-ci.org/textlint-rule/textlint-rule-sentence-length.svg?branch=master)](https://travis-ci.org/textlint-rule/textlint-rule-sentence-length)
1+
# textlint-rule-sentence-length [![Actions Status: test](https://github.com/textlint-rule/textlint-rule-sentence-length/workflows/test/badge.svg)](https://github.com/textlint-rule/textlint-rule-sentence-length/actions?query=workflow%3A"test")
22

33
[textlint](https://github.com/textlint/textlint "textlint") rule that limit Maximum Length of Sentence.
44

package.json

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,35 @@
2929
"prepublish": "npm run --if-present build",
3030
"test": "textlint-scripts test",
3131
"watch": "textlint-scripts build --watch",
32-
"prettier": "prettier --write **/*.js"
32+
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,css}\"",
33+
"prepare": "git config --local core.hooksPath .githooks"
3334
},
3435
"dependencies": {
3536
"@textlint/regexp-string-matcher": "^1.1.0",
36-
"sentence-splitter": "^3.0.11",
37+
"sentence-splitter": "^3.2.1",
3738
"textlint-rule-helper": "^2.1.1",
38-
"textlint-util-to-string": "^3.0.0"
39+
"textlint-util-to-string": "^3.1.1"
3940
},
4041
"devDependencies": {
41-
"lint-staged": "^9.2.5",
42-
"prettier": "^1.18.2",
42+
"@textlint/types": "^1.5.5",
43+
"@types/node": "^15.0.2",
44+
"lint-staged": "^11.0.0",
45+
"prettier": "^2.2.1",
4346
"textlint-plugin-html": "^0.2.0",
44-
"textlint-scripts": "^2.1.0"
47+
"textlint-scripts": "^3.0.0",
48+
"ts-node": "^9.1.1",
49+
"typescript": "^4.2.4"
4550
},
4651
"email": "[email protected]",
4752
"lint-staged": {
4853
"*.{js,jsx,ts,tsx,css}": [
49-
"git stash --keep-index",
50-
"prettier --write",
51-
"git stash pop"
54+
"prettier --write"
5255
]
5356
},
5457
"prettier": {
58+
"singleQuote": false,
5559
"printWidth": 120,
56-
"tabWidth": 4
60+
"tabWidth": 4,
61+
"trailingComma": "none"
5762
}
5863
}

src/sentence-length.js

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

src/sentence-length.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { splitAST, Syntax as SentenceSyntax } from "sentence-splitter";
2+
import { StringSource } from "textlint-util-to-string";
3+
import { RuleHelper } from "textlint-rule-helper";
4+
import { createRegExp } from "@textlint/regexp-string-matcher";
5+
import { TextlintRuleReporter } from "@textlint/types";
6+
7+
function removeRangeFromString(text: string, regExpStrings: string[]) {
8+
const patterns = regExpStrings.map((pattern) => {
9+
return createRegExp(pattern);
10+
});
11+
let result = text;
12+
patterns.forEach((pattern) => {
13+
result = result.replace(pattern, "");
14+
});
15+
return result;
16+
}
17+
18+
export type Options = {
19+
max?: number;
20+
exclusionPatterns?: string[];
21+
};
22+
const defaultOptions: Required<Options> = {
23+
max: 100,
24+
// The strings that match following patterns is uncount of the sentence
25+
// See https://github.com/textlint/regexp-string-matcher
26+
exclusionPatterns: []
27+
};
28+
29+
const reporter: TextlintRuleReporter<Options> = (context, options = {}) => {
30+
const maxLength = options.max ?? defaultOptions.max;
31+
const exclusionPatterns = options.exclusionPatterns ?? defaultOptions.exclusionPatterns;
32+
const helper = new RuleHelper(context);
33+
const { Syntax, RuleError, report } = context;
34+
// toPlainText
35+
return {
36+
[Syntax.Paragraph](node) {
37+
if (helper.isChildNode(node, [Syntax.BlockQuote])) {
38+
return;
39+
}
40+
// If a single Link node in the paragraph node, should be ignore the link length
41+
const isChildrenSingleLinkNode = node.children.length === 1 && node.children[0].type === Syntax.Link;
42+
if (isChildrenSingleLinkNode) {
43+
return;
44+
}
45+
// empty break line == split sentence
46+
const paragraph = splitAST(node);
47+
paragraph.children
48+
.filter((sentence) => sentence.type === SentenceSyntax.Sentence)
49+
.forEach((sentence) => {
50+
// @ts-expect-error: wrong types
51+
const source = new StringSource(sentence);
52+
const actualText = source.toString();
53+
const sentenceText = removeRangeFromString(actualText, exclusionPatterns);
54+
// larger than > 100
55+
const actualTextLength = actualText.length;
56+
const sentenceLength = sentenceText.length;
57+
if (sentenceLength > maxLength) {
58+
const startLine = sentence.loc.start.line;
59+
report(
60+
sentence,
61+
new RuleError(`Line ${startLine} sentence length(${
62+
sentenceLength !== actualTextLength
63+
? `${sentenceLength}, original:${actualTextLength}`
64+
: sentenceLength
65+
}) exceeds the maximum sentence length of ${maxLength}.
66+
Over ${sentenceLength - maxLength} characters.`)
67+
);
68+
}
69+
});
70+
}
71+
};
72+
};
73+
export default reporter;

test/mocha.opts

Lines changed: 0 additions & 1 deletion
This file was deleted.

test/sentence-length-test.js renamed to test/sentence-length-test.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
// LICENSE : MIT
2-
"use strict";
31
import TextLintTester from "textlint-tester";
4-
5-
const rule = require("../src/sentence-length");
6-
const htmlPlugin = require("textlint-plugin-html");
7-
2+
import rule from "../src/sentence-length";
3+
// @ts-expect-error: no types
4+
import htmlPlugin from "textlint-plugin-html";
85
const tester = new TextLintTester();
96

107
tester.run("textlint-rule-sentence-length", rule, {
118
valid: [
129
"This is a article",
1310
"Test`code`です。",
1411
{
15-
text: "\"This\" is code.",
12+
text: '"This" is code.',
1613
options: {
1714
max: 15
1815
}
@@ -35,7 +32,7 @@ tester.run("textlint-rule-sentence-length", rule, {
3532
},
3633
{
3734
// == 12345
38-
text: "[123](http://example.com \"123456\")45",
35+
text: '[123](http://example.com "123456")45',
3936
options: {
4037
max: 5
4138
}
@@ -45,9 +42,7 @@ tester.run("textlint-rule-sentence-length", rule, {
4542
text: "1234(56789)",
4643
options: {
4744
max: 5,
48-
exclusionPatterns: [
49-
"/\\(.*\\)$/"
50-
]
45+
exclusionPatterns: ["/\\(.*\\)$/"]
5146
}
5247
},
5348
{
@@ -60,7 +55,7 @@ tester.run("textlint-rule-sentence-length", rule, {
6055
},
6156
{
6257
// List
63-
text: "- [abc](http://example.com \"abc\")de",
58+
text: '- [abc](http://example.com "abc")de',
6459
options: {
6560
max: 5
6661
}
@@ -95,9 +90,10 @@ tester.run("textlint-rule-sentence-length", rule, {
9590
// regression test
9691
// https://github.com/textlint-rule/textlint-rule-sentence-length/issues/13
9792
{
98-
text: "ではみなさんは、そういうふうに川だと云いわれたり、乳の流れたあとだと云われたりしていたこのぼんやりと白いものがほんとうは何かご承知ですか。\n" +
93+
text:
94+
"ではみなさんは、そういうふうに川だと云いわれたり、乳の流れたあとだと云われたりしていたこのぼんやりと白いものがほんとうは何かご承知ですか。\n" +
9995
"\n" +
100-
"ではみなさんは、そういうふうに川だと云いわれたり、乳の流れたあとだと云われたりしていたこの<a href=\"https://www.aozora.gr.jp/cards/000081/files/456_15050.html\" target=\"_blank\" rel=\"noopener noreferrer\">ぼんやりと白いもの</a>がほんとうは何かご承知ですか。\n"
96+
'ではみなさんは、そういうふうに川だと云いわれたり、乳の流れたあとだと云われたりしていたこの<a href="https://www.aozora.gr.jp/cards/000081/files/456_15050.html" target="_blank" rel="noopener noreferrer">ぼんやりと白いもの</a>がほんとうは何かご承知ですか。\n'
10197
}
10298
],
10399
invalid: [
@@ -206,14 +202,15 @@ Over 2 characters.`,
206202
text: "123456789(56789)",
207203
options: {
208204
max: 5,
209-
exclusionPatterns: [
210-
"/\\(.*\\)$/"
211-
]
205+
exclusionPatterns: ["/\\(.*\\)$/"]
212206
},
213-
errors: [{
214-
message: "Line 1 sentence length(9, original:16) exceeds the maximum sentence length of 5.\n" +
215-
"Over 4 characters."
216-
}]
207+
errors: [
208+
{
209+
message:
210+
"Line 1 sentence length(9, original:16) exceeds the maximum sentence length of 5.\n" +
211+
"Over 4 characters."
212+
}
213+
]
217214
}
218215
]
219216
});

tsconfig.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"compilerOptions": {
3+
/* Basic Options */
4+
"module": "commonjs",
5+
"moduleResolution": "node",
6+
"esModuleInterop": true,
7+
"newLine": "LF",
8+
"outDir": "./lib/",
9+
"target": "es5",
10+
"sourceMap": true,
11+
"declaration": true,
12+
"jsx": "preserve",
13+
"lib": [
14+
"esnext",
15+
"dom"
16+
],
17+
/* Strict Type-Checking Options */
18+
"strict": true,
19+
/* Additional Checks */
20+
/* Report errors on unused locals. */
21+
"noUnusedLocals": true,
22+
/* Report errors on unused parameters. */
23+
"noUnusedParameters": true,
24+
/* Report error when not all code paths in function return a value. */
25+
"noImplicitReturns": true,
26+
/* Report errors for fallthrough cases in switch statement. */
27+
"noFallthroughCasesInSwitch": true
28+
},
29+
"include": [
30+
"src/**/*"
31+
],
32+
"exclude": [
33+
".git",
34+
"node_modules"
35+
]
36+
}

0 commit comments

Comments
 (0)