Skip to content

Commit c1f9852

Browse files
committed
chore: extracted URL utils and fixed an infinite loop
1 parent 5596c25 commit c1f9852

File tree

3 files changed

+49
-135
lines changed

3 files changed

+49
-135
lines changed

packages/eslint-plugin-svelte/src/rules/no-navigation-without-base.ts

Lines changed: 4 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ReferenceTracker } from '@eslint-community/eslint-utils';
44
import { findVariable } from '../utils/ast-utils.js';
55
import { extractExpressionPrefixVariable } from '../utils/expression-affixes.js';
66
import type { RuleContext } from '../types.js';
7-
import type { AST } from 'svelte-eslint-parser';
7+
import { isAbsoluteURL, isFragmentURL } from '../utils/url-utils.js';
88

99
export default createRule('no-navigation-without-base', {
1010
meta: {
@@ -101,15 +101,15 @@ export default createRule('no-navigation-without-base', {
101101
}
102102
const hrefValue = node.value[0];
103103
if (hrefValue.type === 'SvelteLiteral') {
104-
if (!expressionIsAbsolute(hrefValue) && !expressionIsFragment(hrefValue)) {
104+
if (!isAbsoluteURL(hrefValue) && !isFragmentURL(hrefValue)) {
105105
context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
106106
}
107107
return;
108108
}
109109
if (
110110
!expressionStartsWithBase(context, hrefValue.expression, basePathNames) &&
111-
!expressionIsAbsolute(hrefValue.expression) &&
112-
!expressionIsFragment(hrefValue.expression)
111+
!isAbsoluteURL(hrefValue.expression) &&
112+
!isFragmentURL(hrefValue.expression)
113113
) {
114114
context.report({ loc: hrefValue.loc, messageId: 'linkNotPrefixed' });
115115
}
@@ -242,66 +242,3 @@ function expressionIsEmpty(url: TSESTree.Expression): boolean {
242242
url.quasis[0].value.raw === '')
243243
);
244244
}
245-
246-
function expressionIsAbsolute(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
247-
switch (url.type) {
248-
case 'BinaryExpression':
249-
return binaryExpressionIsAbsolute(url);
250-
case 'Literal':
251-
return typeof url.value === 'string' && urlValueIsAbsolute(url.value);
252-
case 'SvelteLiteral':
253-
return urlValueIsAbsolute(url.value);
254-
case 'TemplateLiteral':
255-
return templateLiteralIsAbsolute(url);
256-
default:
257-
return false;
258-
}
259-
}
260-
261-
function binaryExpressionIsAbsolute(url: TSESTree.BinaryExpression): boolean {
262-
return (
263-
(url.left.type !== 'PrivateIdentifier' && expressionIsAbsolute(url.left)) ||
264-
expressionIsAbsolute(url.right)
265-
);
266-
}
267-
268-
function templateLiteralIsAbsolute(url: TSESTree.TemplateLiteral): boolean {
269-
return (
270-
url.expressions.some(expressionIsAbsolute) ||
271-
url.quasis.some((quasi) => urlValueIsAbsolute(quasi.value.raw))
272-
);
273-
}
274-
275-
function urlValueIsAbsolute(url: string): boolean {
276-
return /^[+a-z]*:/i.test(url);
277-
}
278-
279-
function expressionIsFragment(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
280-
switch (url.type) {
281-
case 'BinaryExpression':
282-
return binaryExpressionIsFragment(url);
283-
case 'Literal':
284-
return typeof url.value === 'string' && urlValueIsFragment(url.value);
285-
case 'SvelteLiteral':
286-
return urlValueIsFragment(url.value);
287-
case 'TemplateLiteral':
288-
return templateLiteralIsFragment(url);
289-
default:
290-
return false;
291-
}
292-
}
293-
294-
function binaryExpressionIsFragment(url: TSESTree.BinaryExpression): boolean {
295-
return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(url.left);
296-
}
297-
298-
function templateLiteralIsFragment(url: TSESTree.TemplateLiteral): boolean {
299-
return (
300-
(url.expressions.length >= 1 && expressionIsFragment(url.expressions[0])) ||
301-
(url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw))
302-
);
303-
}
304-
305-
function urlValueIsFragment(url: string): boolean {
306-
return url.startsWith('#');
307-
}

packages/eslint-plugin-svelte/src/rules/no-navigation-without-resolve.ts

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { createRule } from '../utils/index.js';
33
import { ReferenceTracker } from '@eslint-community/eslint-utils';
44
import { findVariable } from '../utils/ast-utils.js';
55
import type { RuleContext } from '../types.js';
6-
import type { AST } from 'svelte-eslint-parser';
6+
import { isAbsoluteURL, isFragmentURL } from '../utils/url-utils.js';
77

88
export default createRule('no-navigation-without-resolve', {
99
meta: {
@@ -96,11 +96,11 @@ export default createRule('no-navigation-without-resolve', {
9696
}
9797
if (
9898
(node.value[0].type === 'SvelteLiteral' &&
99-
!expressionIsAbsolute(node.value[0]) &&
100-
!expressionIsFragment(node.value[0])) ||
99+
!isAbsoluteURL(node.value[0]) &&
100+
!isFragmentURL(node.value[0])) ||
101101
(node.value[0].type === 'SvelteMustacheTag' &&
102-
!expressionIsAbsolute(node.value[0].expression) &&
103-
!expressionIsFragment(node.value[0].expression) &&
102+
!isAbsoluteURL(node.value[0].expression) &&
103+
!isFragmentURL(node.value[0].expression) &&
104104
!isResolveCall(context, node.value[0].expression, resolveReferences))
105105
) {
106106
context.report({ loc: node.value[0].loc, messageId: 'linkWithoutResolve' });
@@ -251,66 +251,3 @@ function expressionIsEmpty(url: TSESTree.CallExpressionArgument): boolean {
251251
url.quasis[0].value.raw === '')
252252
);
253253
}
254-
255-
function expressionIsAbsolute(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
256-
switch (url.type) {
257-
case 'BinaryExpression':
258-
return binaryExpressionIsAbsolute(url);
259-
case 'Literal':
260-
return typeof url.value === 'string' && urlValueIsAbsolute(url.value);
261-
case 'SvelteLiteral':
262-
return urlValueIsAbsolute(url.value);
263-
case 'TemplateLiteral':
264-
return templateLiteralIsAbsolute(url);
265-
default:
266-
return false;
267-
}
268-
}
269-
270-
function binaryExpressionIsAbsolute(url: TSESTree.BinaryExpression): boolean {
271-
return (
272-
(url.left.type !== 'PrivateIdentifier' && expressionIsAbsolute(url.left)) ||
273-
expressionIsAbsolute(url.right)
274-
);
275-
}
276-
277-
function templateLiteralIsAbsolute(url: TSESTree.TemplateLiteral): boolean {
278-
return (
279-
url.expressions.some(expressionIsAbsolute) ||
280-
url.quasis.some((quasi) => urlValueIsAbsolute(quasi.value.raw))
281-
);
282-
}
283-
284-
function urlValueIsAbsolute(url: string): boolean {
285-
return /^[+a-z]*:/i.test(url);
286-
}
287-
288-
function expressionIsFragment(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
289-
switch (url.type) {
290-
case 'BinaryExpression':
291-
return binaryExpressionIsFragment(url);
292-
case 'Literal':
293-
return typeof url.value === 'string' && urlValueIsFragment(url.value);
294-
case 'SvelteLiteral':
295-
return urlValueIsFragment(url.value);
296-
case 'TemplateLiteral':
297-
return templateLiteralIsFragment(url);
298-
default:
299-
return false;
300-
}
301-
}
302-
303-
function binaryExpressionIsFragment(url: TSESTree.BinaryExpression): boolean {
304-
return url.left.type !== 'PrivateIdentifier' && expressionIsFragment(url.left);
305-
}
306-
307-
function templateLiteralIsFragment(url: TSESTree.TemplateLiteral): boolean {
308-
return (
309-
(url.expressions.length >= 1 && expressionIsFragment(url.expressions[0])) ||
310-
(url.quasis.length >= 1 && urlValueIsFragment(url.quasis[0].value.raw))
311-
);
312-
}
313-
314-
function urlValueIsFragment(url: string): boolean {
315-
return url.startsWith('#');
316-
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { TSESTree } from '@typescript-eslint/types';
2+
import type { AST } from 'svelte-eslint-parser';
3+
import { ASTSearchHelper } from 'src/utils/ast-search-helper.js';
4+
5+
export function isAbsoluteURL(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
6+
return (
7+
ASTSearchHelper(url, {
8+
BinaryExpression: (node, searchAnotherNode) =>
9+
(node.left.type !== 'PrivateIdentifier' && searchAnotherNode(node.left)) ||
10+
searchAnotherNode(node.right),
11+
Literal: (node) => typeof node.value === 'string' && urlValueIsAbsolute(node.value),
12+
SvelteLiteral: (node) => urlValueIsAbsolute(node.value),
13+
TemplateLiteral: (node, searchAnotherNode) =>
14+
node.expressions.some(searchAnotherNode) ||
15+
node.quasis.some((quasi) => urlValueIsAbsolute(quasi.value.raw))
16+
}) ?? false
17+
);
18+
}
19+
20+
function urlValueIsAbsolute(url: string): boolean {
21+
return /^[+a-z]*:/i.test(url);
22+
}
23+
24+
export function isFragmentURL(url: AST.SvelteLiteral | TSESTree.Expression): boolean {
25+
return (
26+
ASTSearchHelper(url, {
27+
BinaryExpression: (node, searchAnotherNode) =>
28+
node.left.type !== 'PrivateIdentifier' && searchAnotherNode(node.left),
29+
Literal: (node) => typeof node.value === 'string' && urlValueIsFragment(node.value),
30+
SvelteLiteral: (node) => urlValueIsFragment(node.value),
31+
TemplateLiteral: (node, searchAnotherNode) =>
32+
(node.expressions.length >= 1 && searchAnotherNode(node.expressions[0])) ||
33+
(node.quasis.length >= 1 && urlValueIsFragment(node.quasis[0].value.raw))
34+
}) ?? false
35+
);
36+
}
37+
38+
function urlValueIsFragment(url: string): boolean {
39+
return url.startsWith('#');
40+
}

0 commit comments

Comments
 (0)