Skip to content

Commit 129229a

Browse files
authored
refactor(eslint-plugin-mark): redesign text-handler class (#70)
This pull request focuses on refactoring the `TextHandler` to be a class and updating its usage across various files. Additionally, it removes the dependency on `@types/mdast`. ### Refactoring to `TextHandler` class: * [`packages/eslint-plugin-mark/src/core/ast/text-handler/index.js`](diffhunk://#diff-1e470e6a321c1024e1ea996172aeac6edc42e119df9038a8e3a2b672e12d3091L1-R3): Changed `textHandler` to `TextHandler` and updated the export accordingly. * [`packages/eslint-plugin-mark/src/core/ast/text-handler/text-handler.js`](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94L2-R2): Refactored `textHandler` function to `TextHandler` class, including changes to handle `Text` nodes and manage lines as a private property with a getter. [[1]](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94L2-R2) [[2]](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94L17-R24) [[3]](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94L32-R51) [[4]](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94L62-R71) [[5]](diffhunk://#diff-5398916d413200c44e3917c35358e2c837f7784fc9cc58fd6d07831985cb8d94R89-R100) ### Updates in test files: * [`packages/eslint-plugin-mark/src/core/ast/text-handler/text-handler.test.js`](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL14-R21): Updated tests to use `TextHandler` class and modified test descriptions to reflect changes. [[1]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL14-R21) [[2]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL38-L42) [[3]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL60-R59) [[4]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL79-L83) [[5]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL99) [[6]](diffhunk://#diff-deb2a322685569f693a9dab33bee793030a0edd6ae061671186ef32f2c9a107eL115) ### Dependency removal: * [`packages/eslint-plugin-mark/package.json`](diffhunk://#diff-9b9400561d26b4f60200e53b8fbb54b0ef18955f8348a54f396c6ac386667a0dL86): Removed `@types/mdast` dependency. ### Updates in rule files: * [`packages/eslint-plugin-mark/src/rules/no-curly-quote/no-curly-quote.js`](diffhunk://#diff-f07d349d38a9d669eeaf3c2a3d369b95cd858048da1d815e4fcc0cf495525ba0L10-R10): Updated to use `TextHandler` class and modified type definitions accordingly. [[1]](diffhunk://#diff-f07d349d38a9d669eeaf3c2a3d369b95cd858048da1d815e4fcc0cf495525ba0L10-R10) [[2]](diffhunk://#diff-f07d349d38a9d669eeaf3c2a3d369b95cd858048da1d815e4fcc0cf495525ba0L19-R19) [[3]](diffhunk://#diff-f07d349d38a9d669eeaf3c2a3d369b95cd858048da1d815e4fcc0cf495525ba0L92-R94) [[4]](diffhunk://#diff-f07d349d38a9d669eeaf3c2a3d369b95cd858048da1d815e4fcc0cf495525ba0L117-R117) * [`packages/eslint-plugin-mark/src/rules/no-double-space/no-double-space.js`](diffhunk://#diff-279d5c20e3f61c9862ba798b78492df0a43e70bc5d724cc593f914c8b1037c3fL10-R10): Updated to use `TextHandler` class and modified type definitions accordingly. [[1]](diffhunk://#diff-279d5c20e3f61c9862ba798b78492df0a43e70bc5d724cc593f914c8b1037c3fL10-R10) [[2]](diffhunk://#diff-279d5c20e3f61c9862ba798b78492df0a43e70bc5d724cc593f914c8b1037c3fL19-R19) [[3]](diffhunk://#diff-279d5c20e3f61c9862ba798b78492df0a43e70bc5d724cc593f914c8b1037c3fL81-R90) * [`packages/eslint-plugin-mark/src/rules/no-emoji/no-emoji.js`](diffhunk://#diff-707b0f8455353358a18744406669218fbd523185bad4689adc052940f41a8308L12-R12): Updated to use `TextHandler` class and modified type definitions accordingly. [[1]](diffhunk://#diff-707b0f8455353358a18744406669218fbd523185bad4689adc052940f41a8308L12-R12) [[2]](diffhunk://#diff-707b0f8455353358a18744406669218fbd523185bad4689adc052940f41a8308L21-R21) [[3]](diffhunk://#diff-707b0f8455353358a18744406669218fbd523185bad4689adc052940f41a8308L50-R54)
1 parent c0c71f2 commit 129229a

File tree

10 files changed

+97
-110
lines changed

10 files changed

+97
-110
lines changed

package-lock.json

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/eslint-plugin-mark/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@
8383
},
8484
"dependencies": {
8585
"@eslint/markdown": "^6.3.0",
86-
"@types/mdast": "^4.0.4",
8786
"cheerio": "^1.0.0",
8887
"emoji-regex": "^10.4.0"
8988
},
9089
"devDependencies": {
90+
"@types/mdast": "^4.0.4",
9191
"eslint": "^9.23.0"
9292
}
9393
}

packages/eslint-plugin-mark/src/core/ast/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
import IgnoredPositions from './ignored-positions/index.js';
44
import ReferenceDefinitionHandler from './reference-definition-handler/index.js';
5-
import textHandler from './text-handler/index.js';
5+
import TextHandler from './text-handler/index.js';
66

7-
export { IgnoredPositions, ReferenceDefinitionHandler, textHandler };
7+
export { IgnoredPositions, ReferenceDefinitionHandler, TextHandler };
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
import textHandler from './text-handler.js';
1+
import TextHandler from './text-handler.js';
22

3-
export default textHandler;
3+
export default TextHandler;
Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* @fileoverview `TextHandler` transforms a `Text` node into a `TextExt` node by adding a `children` property.
2+
* @fileoverview Class to manage `Text` nodes.
33
*/
44

55
// --------------------------------------------------------------------------------
@@ -14,8 +14,14 @@ import { ZERO_TO_ONE_BASED_OFFSET } from '../../constants.js';
1414

1515
/**
1616
* @typedef {import("eslint").Rule.RuleContext} RuleContext
17-
* @typedef {import("../../types.d.ts").TextExt} TextExt
18-
* @typedef {import("../../types.d.ts").TextLine} TextLine
17+
* @typedef {import("mdast").Text} Text
18+
* @typedef {import("unist").Position} Position
19+
*/
20+
21+
/**
22+
* @typedef {object} Line
23+
* @property {string} value The text of the line.
24+
* @property {Position} position The position of the line.
1925
*/
2026

2127
// --------------------------------------------------------------------------------
@@ -29,53 +35,66 @@ const newLineRegex = /\r?\n/;
2935
// --------------------------------------------------------------------------------
3036

3137
/**
32-
* `TextHandler` transforms a `Text` node into a `TextExt` node by adding a `children` property.
33-
* During this process, it splits the text into multiple lines and creates `TextLine` nodes for each line.
34-
* The generated `TextLine` nodes then become child elements of the `Text` node.
35-
*
36-
* - NOTE: This function is designed to create **SIDE EFFECTS** by modifying the original `Text` node.
37-
*
38-
* @param {RuleContext} context `import("eslint").Rule.RuleContext`
39-
* @param {TextExt} textNode
38+
* Class to manage `Text` nodes.
39+
* `TextHandler` transforms a multi-line `Text` node into separate single-line nodes,
40+
* each with `value` and `position` properties.
4041
*/
41-
export default function textHandler(context, textNode) {
42-
textNode.children = [];
42+
export default class TextHandler {
43+
// ------------------------------------------------------------------------------
44+
// Private Properties
45+
// ------------------------------------------------------------------------------
4346

44-
/**
45-
* - Supports both LF and CRLF line endings.
46-
* - In JavaScript, `\n` represents a newline character and splits text accordingly.
47-
* However, in Markdown, writing `abcd\ndefg` treats `\n` as plain text (backslash + 'n'), not a newline.
48-
* Only actual line breaks in Markdown (`abcd↵defg`) are stored as `"\n"` and split properly.
49-
*/ // @ts-expect-error -- TODO: https://github.com/eslint/markdown/issues/323
50-
const lines = context.sourceCode.getText(textNode).split(newLineRegex);
51-
52-
lines.forEach((line, lineNumber) => {
53-
const locLine = textNode.position.start.line + lineNumber;
54-
const locColumn =
55-
lineNumber === 0 ? textNode.position.start.column : ZERO_TO_ONE_BASED_OFFSET;
56-
let locOffset = textNode.position.start.offset;
57-
58-
for (let i = 0; i < lineNumber; i++) {
59-
locOffset += lines[i].length + 1; // Add the lengths of previous lines. (`+1` for the newline character.)
60-
}
61-
62-
textNode.children.push(
63-
/** @type {TextLine} */ {
64-
type: 'textLine',
65-
value: line,
66-
position: {
67-
start: {
68-
line: locLine,
69-
column: locColumn,
70-
offset: locOffset,
71-
},
72-
end: {
73-
line: locLine,
74-
column: locColumn + line.length,
75-
offset: locOffset + line.length,
47+
/** @type {Line[]} */
48+
#lines = [];
49+
50+
/** @param {RuleContext} context @param {Text} textNode */
51+
constructor(context, textNode) {
52+
/**
53+
* - Supports both LF and CRLF line endings.
54+
* - In JavaScript, `\n` represents a newline character and splits text accordingly.
55+
* However, in Markdown, writing `abcd\ndefg` treats `\n` as plain text (backslash + 'n'), not a newline.
56+
* Only actual line breaks in Markdown (`abcd↵defg`) are stored as `"\n"` and split properly.
57+
*/ // @ts-expect-error -- TODO: https://github.com/eslint/markdown/issues/323
58+
const lines = context.sourceCode.getText(textNode).split(newLineRegex);
59+
60+
lines.forEach((line, lineNumber) => {
61+
const locLine = textNode.position.start.line + lineNumber;
62+
const locColumn =
63+
lineNumber === 0 ? textNode.position.start.column : ZERO_TO_ONE_BASED_OFFSET;
64+
let locOffset = textNode.position.start.offset;
65+
66+
for (let i = 0; i < lineNumber; i++) {
67+
locOffset += lines[i].length + 1; // Add the lengths of previous lines. (`+1` for the newline character.)
68+
}
69+
70+
this.#lines.push(
71+
/** @type {Line} */ {
72+
value: line,
73+
position: {
74+
start: {
75+
line: locLine,
76+
column: locColumn,
77+
offset: locOffset,
78+
},
79+
end: {
80+
line: locLine,
81+
column: locColumn + line.length,
82+
offset: locOffset + line.length,
83+
},
7684
},
7785
},
78-
},
79-
);
80-
});
86+
);
87+
});
88+
}
89+
90+
// ------------------------------------------------------------------------------
91+
// Getters and Setters
92+
// ------------------------------------------------------------------------------
93+
94+
/**
95+
* Get the lines.
96+
*/
97+
get lines() {
98+
return this.#lines;
99+
}
81100
}

packages/eslint-plugin-mark/src/core/ast/text-handler/text-handler.test.js

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import { deepStrictEqual } from 'node:assert';
1111

1212
import { getFileName } from '../../tests/index.js';
1313

14-
import textHandler from './text-handler.js';
14+
import TextHandler from './text-handler.js';
1515

1616
// --------------------------------------------------------------------------------
1717
// Test
1818
// --------------------------------------------------------------------------------
1919

2020
describe(getFileName(import.meta.url), () => {
21-
it('should return correct `TextExt` node: singleline', () => {
21+
it('should return correct lines: singleline', () => {
2222
const text = 'foo';
2323
const context = {
2424
sourceCode: {
@@ -35,11 +35,10 @@ describe(getFileName(import.meta.url), () => {
3535
},
3636
};
3737

38-
textHandler(context, textNode);
38+
const textHandler = new TextHandler(context, textNode);
3939

40-
deepStrictEqual(textNode.children, [
40+
deepStrictEqual(textHandler.lines, [
4141
{
42-
type: 'textLine',
4342
value: 'foo',
4443
position: {
4544
start: {
@@ -57,7 +56,7 @@ describe(getFileName(import.meta.url), () => {
5756
]);
5857
});
5958

60-
it('should return correct `TextExt` node: multiline', () => {
59+
it('should return correct lines: multiline', () => {
6160
const text = `foo
6261
bar
6362
baz`;
@@ -76,11 +75,10 @@ baz`;
7675
},
7776
};
7877

79-
textHandler(context, textNode);
78+
const textHandler = new TextHandler(context, textNode);
8079

81-
deepStrictEqual(textNode.children, [
80+
deepStrictEqual(textHandler.lines, [
8281
{
83-
type: 'textLine',
8482
value: 'foo',
8583
position: {
8684
start: {
@@ -96,7 +94,6 @@ baz`;
9694
},
9795
},
9896
{
99-
type: 'textLine',
10097
value: ' bar',
10198
position: {
10299
start: {
@@ -112,7 +109,6 @@ baz`;
112109
},
113110
},
114111
{
115-
type: 'textLine',
116112
value: 'baz',
117113
position: {
118114
start: {

packages/eslint-plugin-mark/src/core/types.d.ts

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,6 @@
22
* @fileoverview Define common types.
33
*/
44

5-
// --------------------------------------------------------------------------------
6-
// Import
7-
// --------------------------------------------------------------------------------
8-
9-
import type { Literal, Text } from 'mdast';
10-
115
// --------------------------------------------------------------------------------
126
// Typedefs
137
// --------------------------------------------------------------------------------
14-
15-
/**
16-
* Custom markdown text extension.
17-
*/
18-
export interface TextExt extends Text {
19-
/**
20-
* Node type of custom markdown text extension.
21-
*/
22-
type: 'textExt';
23-
/**
24-
* Children of the node.
25-
*/
26-
children?: TextLine[] | undefined;
27-
}
28-
29-
/**
30-
* Custom markdown text line.
31-
*/
32-
export interface TextLine extends Literal {
33-
/**
34-
* Node type of custom markdown text line.
35-
*/
36-
type: 'textLine';
37-
}

packages/eslint-plugin-mark/src/rules/no-curly-quote/no-curly-quote.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Import
88
// --------------------------------------------------------------------------------
99

10-
import { textHandler } from '../../core/ast/index.js';
10+
import { TextHandler } from '../../core/ast/index.js';
1111
import { URL_RULE_DOCS } from '../../core/constants.js';
1212

1313
// --------------------------------------------------------------------------------
@@ -16,7 +16,7 @@ import { URL_RULE_DOCS } from '../../core/constants.js';
1616

1717
/**
1818
* @typedef {import("@eslint/markdown").RuleModule} RuleModule
19-
* @typedef {import("../../core/types.d.ts").TextExt} TextExt
19+
* @typedef {import("mdast").Text} Text
2020
*/
2121

2222
// --------------------------------------------------------------------------------
@@ -89,9 +89,9 @@ export default {
8989

9090
create(context) {
9191
return {
92-
/** @param {TextExt} node */
92+
/** @param {Text} node */
9393
text(node) {
94-
textHandler(context, node);
94+
const textHandler = new TextHandler(context, node);
9595

9696
const [
9797
{
@@ -114,7 +114,7 @@ export default {
114114

115115
if (!regexString) return;
116116

117-
node.children.forEach(textLineNode => {
117+
textHandler.lines.forEach(textLineNode => {
118118
const matches = [
119119
...textLineNode.value.matchAll(new RegExp(`[${regexString}]`, 'g')),
120120
];

packages/eslint-plugin-mark/src/rules/no-double-space/no-double-space.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Import
88
// --------------------------------------------------------------------------------
99

10-
import { textHandler } from '../../core/ast/index.js';
10+
import { TextHandler } from '../../core/ast/index.js';
1111
import { URL_RULE_DOCS } from '../../core/constants.js';
1212

1313
// --------------------------------------------------------------------------------
@@ -16,7 +16,7 @@ import { URL_RULE_DOCS } from '../../core/constants.js';
1616

1717
/**
1818
* @typedef {import("@eslint/markdown").RuleModule} RuleModule
19-
* @typedef {import("../../core/types.d.ts").TextExt} TextExt
19+
* @typedef {import("mdast").Text} Text
2020
*/
2121

2222
// --------------------------------------------------------------------------------
@@ -78,16 +78,16 @@ export default {
7878

7979
create(context) {
8080
return {
81-
/** @param {TextExt} node */
81+
/** @param {Text} node */
8282
text(node) {
83-
textHandler(context, node);
83+
const textHandler = new TextHandler(context, node);
8484

8585
// @ts-expect-error -- TODO
8686
const [{ multipleSpace }] = context.options;
8787
const spaceRegex = multipleSpace ? multipleSpaceRegex : doubleSpaceRegex;
8888
const messageId = multipleSpace ? 'noMultipleSpace' : 'noDoubleSpace';
8989

90-
node.children.forEach(textLineNode => {
90+
textHandler.lines.forEach(textLineNode => {
9191
const matches = [...textLineNode.value.trim().matchAll(spaceRegex)];
9292

9393
if (matches.length > 0) {

0 commit comments

Comments
 (0)