Skip to content

Commit 5a2ad23

Browse files
authored
fix: export languages type & remove eslint require (#339)
* fix: export languages type & remove eslint require * Update index.js * Update html-language.test.js * Update html-source-code.test.js
1 parent 69b6cde commit 5a2ad23

File tree

5 files changed

+113
-17
lines changed

5 files changed

+113
-17
lines changed

packages/eslint-plugin/lib/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ const { name, version } = require("../package.json");
66
/**
77
* @typedef {import("./rules")} AllRules
88
* @typedef {import("./configs/recommended")} RecommendedConfig
9+
* @typedef {{name: string, version: string}} PluginMeta
10+
* @typedef {{recommended: RecommendedConfig, "flat/recommended": import("eslint").Linter.FlatConfig }} HtmlESLintConfigs
11+
* @typedef {{html: HTMLLanguage}} Languages
912
*/
1013

1114
/**
12-
* @type {{meta: { name: string, version: string }, rules: AllRules, configs: {recommended: RecommendedConfig, "flat/recommended": import("eslint").Linter.FlatConfig }}}
15+
* @type {{meta: PluginMeta, rules: AllRules, configs: HtmlESLintConfigs, languages: Languages}}
1316
*/
1417
const plugin = {
1518
meta: {

packages/eslint-plugin/lib/languages/html-language.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,24 @@ class HTMLLanguage {
6565
* @param {File} file
6666
* @param {Object} [context]
6767
* @param {LanguageOptions} context.languageOptions
68+
* @returns {{ok: true; ast: any; comments: any[]} | {ok: false, errors: unknown[]}}
6869
*/
6970
parse(file, context) {
7071
const code = /** @type {string} */ (file.body);
7172
const languageOptions = (context && context.languageOptions) || {};
72-
const result = parseForESLint(code, languageOptions);
73-
return {
74-
ok: true,
75-
ast: result.ast,
76-
comments: result.ast.comments,
77-
};
73+
try {
74+
const result = parseForESLint(code, languageOptions);
75+
return {
76+
ok: true,
77+
ast: result.ast,
78+
comments: result.ast.comments,
79+
};
80+
} catch (e) {
81+
return {
82+
ok: false,
83+
errors: [e],
84+
};
85+
}
7886
}
7987

8088
/**

packages/eslint-plugin/lib/languages/html-source-code.js

Lines changed: 89 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,23 @@
55
* @typedef {import("@eslint/core").TraversalStep} TraversalStep
66
* @typedef {import("@html-eslint/types").CommentContent} CommentContent
77
* @typedef {import("@html-eslint/types").AnyHTMLNode} AnyHTMLNode
8+
* @typedef {import("@eslint/core").Position} Position
89
* @typedef {import("../types").BaseNode} BaseNode
910
*/
1011
const {
1112
TextSourceCodeBase,
1213
ConfigCommentParser,
1314
Directive,
1415
} = require("@eslint/plugin-kit");
15-
const { SourceCode } = require("eslint");
1616
const { HTMLTraversalStep, STEP_PHASE } = require("./html-traversal-step");
1717
const { visitorKeys } = require("@html-eslint/parser");
1818

19+
const lineBreakPattern = /\r\n|[\r\n\u2028\u2029]/u;
20+
21+
function createGlobalLinebreakMatcher() {
22+
return new RegExp(lineBreakPattern.source, "gu");
23+
}
24+
1925
const INLINE_CONFIG =
2026
/^\s*(?:eslint(?:-enable|-disable(?:(?:-next)?-line)?)?)(?:\s|$)/u;
2127

@@ -27,10 +33,7 @@ class HTMLSourceCode extends TextSourceCodeBase {
2733
*/
2834
constructor({ ast, text, comments }) {
2935
super({ ast, text });
30-
/**
31-
* @property
32-
*/
33-
this.eslintSourceCode = new SourceCode(text, ast);
36+
3437
/**
3538
* @property
3639
*/
@@ -40,6 +43,14 @@ class HTMLSourceCode extends TextSourceCodeBase {
4043
*/
4144
this.comments = comments;
4245
this.parentsMap = new Map();
46+
47+
this.lineStartIndices = [0];
48+
49+
const lineEndingPattern = createGlobalLinebreakMatcher();
50+
let match;
51+
while ((match = lineEndingPattern.exec(this.text))) {
52+
this.lineStartIndices.push(match.index + match[0].length);
53+
}
4354
}
4455

4556
/**
@@ -62,20 +73,88 @@ class HTMLSourceCode extends TextSourceCodeBase {
6273
return this.lines;
6374
}
6475

76+
// Copied from eslint source code
6577
/**
66-
* @param {import("@eslint/core").Position} loc
67-
* @returns
78+
* @see https://github.com/eslint/eslint/blob/f60f2764971a33e252be13e560dccf21f554dbf1/lib/languages/js/source-code/source-code.js#L745
79+
* @param {Position} loc
80+
* @returns {number}
6881
*/
6982
getIndexFromLoc(loc) {
70-
return this.eslintSourceCode.getIndexFromLoc(loc);
83+
if (
84+
typeof loc !== "object" ||
85+
typeof loc.line !== "number" ||
86+
typeof loc.column !== "number"
87+
) {
88+
throw new TypeError(
89+
"Expected `loc` to be an object with numeric `line` and `column` properties."
90+
);
91+
}
92+
93+
if (loc.line <= 0) {
94+
throw new RangeError(
95+
`Line number out of range (line ${loc.line} requested). Line numbers should be 1-based.`
96+
);
97+
}
98+
99+
if (loc.line > this.lineStartIndices.length) {
100+
throw new RangeError(
101+
`Line number out of range (line ${loc.line} requested, but only ${this.lineStartIndices.length} lines present).`
102+
);
103+
}
104+
105+
const lineStartIndex = this.lineStartIndices[loc.line - 1];
106+
const lineEndIndex =
107+
loc.line === this.lineStartIndices.length
108+
? this.text.length
109+
: this.lineStartIndices[loc.line];
110+
const positionIndex = lineStartIndex + loc.column;
111+
if (
112+
(loc.line === this.lineStartIndices.length &&
113+
positionIndex > lineEndIndex) ||
114+
(loc.line < this.lineStartIndices.length && positionIndex >= lineEndIndex)
115+
) {
116+
throw new RangeError(
117+
`Column number out of range (column ${loc.column} requested, but the length of line ${loc.line} is ${lineEndIndex - lineStartIndex}).`
118+
);
119+
}
120+
121+
return positionIndex;
71122
}
72123

124+
// Copied from eslint source code
73125
/**
126+
* @see https://github.com/eslint/eslint/blob/f60f2764971a33e252be13e560dccf21f554dbf1/lib/languages/js/source-code/source-code.js#L694
74127
* @param {number} index
75-
* @returns {import("@eslint/core").Position}
128+
* @returns {Position}
76129
*/
77130
getLocFromIndex(index) {
78-
return this.eslintSourceCode.getLocFromIndex(index);
131+
if (typeof index !== "number") {
132+
throw new TypeError("Expected `index` to be a number.");
133+
}
134+
135+
if (index < 0 || index > this.text.length) {
136+
throw new RangeError(
137+
`Index out of range (requested index ${index}, but source text has length ${this.text.length}).`
138+
);
139+
}
140+
if (index === this.text.length) {
141+
return {
142+
line: this.lines.length,
143+
// @ts-ignore
144+
column: this.lines.at(-1).length,
145+
};
146+
}
147+
148+
const lineNumber =
149+
// @ts-ignore
150+
index >= this.lineStartIndices.at(-1)
151+
? this.lineStartIndices.length
152+
: this.lineStartIndices.findIndex((el) => index < el);
153+
154+
return {
155+
line: lineNumber,
156+
column: index - this.lineStartIndices[lineNumber - 1],
157+
};
79158
}
80159

81160
getInlineConfigNodes() {

packages/eslint-plugin/tests/languages/html-language.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ describe("HTMLLanguage", () => {
3737
const file = createFile(`<div></div>`);
3838
const result = language.parse(file);
3939
expect(result.ok).toBe(true);
40+
// @ts-ignore
4041
expect(result.ast.type).toBe("Program");
42+
// @ts-ignore
4143
expect(result.ast.body[0].children[0].type).toBe("Tag");
4244
});
4345

@@ -53,6 +55,7 @@ name: value
5355
languageOptions: { frontmatter: true },
5456
});
5557
expect(result.ok).toBe(true);
58+
// @ts-ignore
5659
expect(result.ast.body[0].children[0].type).toBe("Tag");
5760
});
5861

@@ -67,6 +70,7 @@ name: value
6770
},
6871
});
6972
expect(result.ok).toBe(true);
73+
// @ts-ignore
7074
const part = result.ast.body[0].children[0].children[0].parts[0];
7175
expect(part.open.type).toBe("OpenTemplate");
7276
expect(part.close.type).toBe("CloseTemplate");

packages/eslint-plugin/tests/languages/html-source-code.test.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ const createSourceCode = (text) => {
2727
const parsed = language.parse(file);
2828
const sourceCode = new HTMLSourceCode({
2929
text,
30+
// @ts-ignore
3031
ast: parsed.ast,
32+
// @ts-ignore
3133
comments: parsed.comments,
3234
});
3335
return sourceCode;

0 commit comments

Comments
 (0)