Skip to content

Commit 8823fb3

Browse files
authored
Add cache for template literal parsing (#390)
1 parent bdfef79 commit 8823fb3

File tree

7 files changed

+98
-26
lines changed

7 files changed

+98
-26
lines changed

packages/eslint-plugin/lib/rules/indent/indent.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
* @property {Record<string, number>} [Option2.tagChildrenIndent]
2121
*/
2222

23-
const { parse } = require("@html-eslint/template-parser");
23+
const { getCachedParseResult } = require("../utils/template-cache");
24+
const { traverse } = require("@html-eslint/template-parser/lib/traverser");
2425
const { NODE_TYPES } = require("@html-eslint/parser");
2526
const { RULE_CATEGORY } = require("../../constants");
2627
const {
@@ -386,13 +387,18 @@ module.exports = {
386387
TaggedTemplateExpression(node) {
387388
if (shouldCheckTaggedTemplateExpression(node, context)) {
388389
const base = getTemplateLiteralBaseIndentLevel(node.quasi);
389-
parse(node.quasi, getSourceCode(context), createIndentVisitor(base));
390+
const { ast } = getCachedParseResult(
391+
node.quasi,
392+
getSourceCode(context)
393+
);
394+
traverse(ast, createIndentVisitor(base), null);
390395
}
391396
},
392397
TemplateLiteral(node) {
393398
if (shouldCheckTemplateLiteral(node, context)) {
394399
const base = getTemplateLiteralBaseIndentLevel(node);
395-
parse(node, getSourceCode(context), createIndentVisitor(base));
400+
const { ast } = getCachedParseResult(node, getSourceCode(context));
401+
traverse(ast, createIndentVisitor(base), null);
396402
}
397403
},
398404
};

packages/eslint-plugin/lib/rules/no-duplicate-id.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
* @import {RuleModule} from "../types";
44
*/
55

6-
const { parse } = require("@html-eslint/template-parser");
6+
const { getCachedParseResult } = require("./utils/template-cache");
7+
const { traverse } = require("@html-eslint/template-parser/lib/traverser");
78
const { RULE_CATEGORY } = require("../constants");
89
const { findAttr } = require("./utils/node");
910
const {
@@ -90,18 +91,31 @@ module.exports = {
9091
TaggedTemplateExpression(node) {
9192
const idAttrsMap = new Map();
9293
if (shouldCheckTaggedTemplateExpression(node, context)) {
93-
parse(node.quasi, getSourceCode(context), {
94-
Tag: createTagVisitor(idAttrsMap),
95-
});
94+
const { ast } = getCachedParseResult(
95+
node.quasi,
96+
getSourceCode(context)
97+
);
98+
traverse(
99+
ast,
100+
{
101+
Tag: createTagVisitor(idAttrsMap),
102+
},
103+
null
104+
);
96105
}
97106
report(idAttrsMap);
98107
},
99108
TemplateLiteral(node) {
100109
const idAttrsMap = new Map();
101110
if (shouldCheckTemplateLiteral(node, context)) {
102-
parse(node, getSourceCode(context), {
103-
Tag: createTagVisitor(idAttrsMap),
104-
});
111+
const { ast } = getCachedParseResult(node, getSourceCode(context));
112+
traverse(
113+
ast,
114+
{
115+
Tag: createTagVisitor(idAttrsMap),
116+
},
117+
null
118+
);
105119
}
106120
report(idAttrsMap);
107121
},

packages/eslint-plugin/lib/rules/no-duplicate-in-head.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
* @import {RuleModule} from "../types";
44
*/
55

6-
const { parse } = require("@html-eslint/template-parser");
6+
const { getCachedParseResult } = require("./utils/template-cache");
7+
const { traverse } = require("@html-eslint/template-parser/lib/traverser");
78
const { RULE_CATEGORY } = require("../constants");
89
const { findAttr } = require("./utils/node");
910
const {
@@ -168,7 +169,11 @@ module.exports = {
168169

169170
if (shouldCheckTaggedTemplateExpression(node, context)) {
170171
const visitor = createTagVisitor(tagsMap, headCountRef);
171-
parse(node.quasi, getSourceCode(context), visitor);
172+
const { ast } = getCachedParseResult(
173+
node.quasi,
174+
getSourceCode(context)
175+
);
176+
traverse(ast, visitor, null);
172177
report(tagsMap);
173178
}
174179
},
@@ -179,7 +184,8 @@ module.exports = {
179184

180185
if (shouldCheckTemplateLiteral(node, context)) {
181186
const visitor = createTagVisitor(tagsMap, headCountRef);
182-
parse(node, getSourceCode(context), visitor);
187+
const { ast } = getCachedParseResult(node, getSourceCode(context));
188+
traverse(ast, visitor, null);
183189
report(tagsMap);
184190
}
185191
},

packages/eslint-plugin/lib/rules/no-multiple-empty-lines.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @property {number} Option.max
77
*/
88

9-
const { parse } = require("@html-eslint/template-parser");
9+
const { getCachedParseResult } = require("./utils/template-cache");
1010
const { RULE_CATEGORY } = require("../constants");
1111
const {
1212
shouldCheckTaggedTemplateExpression,
@@ -117,10 +117,9 @@ module.exports = {
117117
},
118118
TaggedTemplateExpression(node) {
119119
if (shouldCheckTaggedTemplateExpression(node, context)) {
120-
const { html, tokens } = parse(
120+
const { html, tokens } = getCachedParseResult(
121121
node.quasi,
122-
getSourceCode(context),
123-
{}
122+
getSourceCode(context)
124123
);
125124
const lines = codeToLines(html);
126125
check(
@@ -133,7 +132,10 @@ module.exports = {
133132
},
134133
TemplateLiteral(node) {
135134
if (shouldCheckTemplateLiteral(node, context)) {
136-
const { html, tokens } = parse(node, getSourceCode(context), {});
135+
const { html, tokens } = getCachedParseResult(
136+
node,
137+
getSourceCode(context)
138+
);
137139
const lines = codeToLines(html);
138140
check(
139141
lines,

packages/eslint-plugin/lib/rules/no-trailing-spaces.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @import {CommentContent, Text} from "@html-eslint/types";
44
*/
55

6-
const { parse } = require("@html-eslint/template-parser");
6+
const { getCachedParseResult } = require("./utils/template-cache");
77
const { RULE_CATEGORY } = require("../constants");
88
const {
99
getTemplateTokens,
@@ -107,10 +107,9 @@ module.exports = {
107107
},
108108
TaggedTemplateExpression(node) {
109109
if (shouldCheckTaggedTemplateExpression(node, context)) {
110-
const { html, tokens } = parse(
110+
const { html, tokens } = getCachedParseResult(
111111
node.quasi,
112-
getSourceCode(context),
113-
{}
112+
getSourceCode(context)
114113
);
115114
const lines = codeToLines(html);
116115
check(
@@ -127,7 +126,10 @@ module.exports = {
127126
},
128127
TemplateLiteral(node) {
129128
if (shouldCheckTemplateLiteral(node, context)) {
130-
const { html, tokens } = parse(node, getSourceCode(context), {});
129+
const { html, tokens } = getCachedParseResult(
130+
node,
131+
getSourceCode(context)
132+
);
131133
const lines = codeToLines(html);
132134
check(
133135
html,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @import {TemplateLiteral} from "@html-eslint/types";
3+
* @import {DocumentNode, AnyToken} from "es-html-parser";
4+
* @import {SourceCode} from "eslint";
5+
*/
6+
7+
const { parse } = require("@html-eslint/template-parser");
8+
9+
/**
10+
* Cache for parsed template literals to avoid re-parsing the same template multiple times.
11+
* Uses WeakMap for automatic garbage collection when nodes are no longer referenced.
12+
* @type {WeakMap<TemplateLiteral, {ast: any, html: string, tokens: any[]}>}
13+
*/
14+
const templateCache = new WeakMap();
15+
16+
/**
17+
* Get or create cached parse result for a template literal.
18+
* @param {TemplateLiteral} node
19+
* @param {SourceCode} sourceCode
20+
* @returns {{ast: DocumentNode, html: string, tokens: AnyToken[]}}
21+
*/
22+
function getCachedParseResult(node, sourceCode) {
23+
// Check if we already have a cached result for this node
24+
const cachedResult = templateCache.get(node);
25+
if (cachedResult) {
26+
return cachedResult;
27+
}
28+
29+
// Parse and cache the result
30+
const result = parse(node, sourceCode, {});
31+
templateCache.set(node, result);
32+
33+
return result;
34+
}
35+
36+
module.exports = { getCachedParseResult };

packages/eslint-plugin/lib/rules/utils/visitors.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ const {
66
shouldCheckTaggedTemplateExpression,
77
shouldCheckTemplateLiteral,
88
} = require("./settings");
9-
const { parse } = require("@html-eslint/template-parser");
9+
const { getCachedParseResult } = require("./template-cache");
10+
const { traverse } = require("@html-eslint/template-parser/lib/traverser");
1011
const { getSourceCode } = require("./source-code");
1112

1213
/**
@@ -18,12 +19,17 @@ function createTemplateVisitors(context, visitors) {
1819
return {
1920
TaggedTemplateExpression(node) {
2021
if (shouldCheckTaggedTemplateExpression(node, context)) {
21-
parse(node.quasi, getSourceCode(context), visitors);
22+
const { ast } = getCachedParseResult(
23+
node.quasi,
24+
getSourceCode(context)
25+
);
26+
traverse(ast, visitors, null);
2227
}
2328
},
2429
TemplateLiteral(node) {
2530
if (shouldCheckTemplateLiteral(node, context)) {
26-
parse(node, getSourceCode(context), visitors);
31+
const { ast } = getCachedParseResult(node, getSourceCode(context));
32+
traverse(ast, visitors, null);
2733
}
2834
},
2935
};

0 commit comments

Comments
 (0)