Skip to content

Commit 2247394

Browse files
authored
fix(eslint-plugin): [relative-url-prefix] valid relative urls being reported (#456)
1 parent 0ea02ae commit 2247394

File tree

2 files changed

+62
-89
lines changed

2 files changed

+62
-89
lines changed
Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,21 @@
11
import type { TSESTree } from '@typescript-eslint/experimental-utils';
22
import { createESLintRule } from '../utils/create-eslint-rule';
33
import { COMPONENT_CLASS_DECORATOR } from '../utils/selectors';
4-
import {
5-
getDecoratorProperty,
6-
isArrayExpression,
7-
isLiteralWithStringValue,
8-
} from '../utils/utils';
4+
import { isArrayExpression, isLiteralWithStringValue } from '../utils/utils';
95

106
type Options = [];
117
export type MessageIds = 'relativeUrlPrefix';
128
export const RULE_NAME = 'relative-url-prefix';
139

1410
const STYLE_GUIDE_LINK = 'https://angular.io/styleguide#style-05-04';
15-
const RELATIVE_URL_PREFIX_MATCHER = /^\.{1,2}\/[^./]/;
11+
const RELATIVE_URL_PREFIX_MATCHER = /^\.\.?\/.+/;
1612

1713
export default createESLintRule<Options, MessageIds>({
1814
name: RULE_NAME,
1915
meta: {
2016
type: 'suggestion',
2117
docs: {
22-
description: `The ./ and ../ prefix is standard syntax for relative URLs; don't depend on Angular's current ability to do without that prefix. See more at ${STYLE_GUIDE_LINK}.`,
18+
description: `The ./ and ../ prefix is standard syntax for relative URLs; don't depend on Angular's current ability to do without that prefix. See more at ${STYLE_GUIDE_LINK}`,
2319
category: 'Best Practices',
2420
recommended: false,
2521
},
@@ -31,43 +27,35 @@ export default createESLintRule<Options, MessageIds>({
3127
defaultOptions: [],
3228
create(context) {
3329
return {
34-
[COMPONENT_CLASS_DECORATOR](node: TSESTree.Decorator) {
35-
const templateUrlProperty = getDecoratorProperty(node, 'templateUrl');
36-
if (
37-
templateUrlProperty &&
38-
isLiteralWithStringValue(templateUrlProperty.value)
39-
) {
40-
if (
41-
!RELATIVE_URL_PREFIX_MATCHER.test(templateUrlProperty.value.value)
42-
) {
43-
context.report({
44-
node: templateUrlProperty.value,
45-
messageId: 'relativeUrlPrefix',
46-
});
47-
}
48-
}
30+
[`${COMPONENT_CLASS_DECORATOR} Property[key.name='templateUrl']`]({
31+
value,
32+
}: TSESTree.Property) {
33+
if (!isUrlInvalid(value)) return;
4934

50-
const styleUrlsProperty = getDecoratorProperty(node, 'styleUrls');
51-
if (styleUrlsProperty) {
52-
if (
53-
styleUrlsProperty.value &&
54-
isArrayExpression(styleUrlsProperty.value) &&
55-
styleUrlsProperty.value.elements.length > 0
56-
) {
57-
styleUrlsProperty.value.elements.forEach((e) => {
58-
if (
59-
isLiteralWithStringValue(e) &&
60-
!RELATIVE_URL_PREFIX_MATCHER.test(e.value)
61-
) {
62-
context.report({
63-
node: e,
64-
messageId: 'relativeUrlPrefix',
65-
});
66-
}
67-
});
68-
}
69-
}
35+
context.report({
36+
node: value,
37+
messageId: 'relativeUrlPrefix',
38+
});
39+
},
40+
[`${COMPONENT_CLASS_DECORATOR} Property[key.name='styleUrls']`]({
41+
value,
42+
}: TSESTree.Property) {
43+
if (!isArrayExpression(value)) return;
44+
45+
value.elements.filter(isUrlInvalid).forEach((element) => {
46+
context.report({
47+
node: element,
48+
messageId: 'relativeUrlPrefix',
49+
});
50+
});
7051
},
7152
};
7253
},
7354
});
55+
56+
function isUrlInvalid(node: TSESTree.Property | TSESTree.Property['value']) {
57+
return (
58+
!isLiteralWithStringValue(node) ||
59+
!RELATIVE_URL_PREFIX_MATCHER.test(node.value)
60+
);
61+
}

packages/eslint-plugin/tests/rules/relative-url-prefix.test.ts

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,78 +12,74 @@ import rule, { RULE_NAME } from '../../src/rules/relative-url-prefix';
1212
const ruleTester = new RuleTester({
1313
parser: '@typescript-eslint/parser',
1414
});
15-
1615
const messageId: MessageIds = 'relativeUrlPrefix';
1716

1817
ruleTester.run(RULE_NAME, rule, {
1918
valid: [
2019
`
2120
@Component({
22-
styleUrls: ['./foobar.css']
21+
styleUrls: [
22+
'./foo.css',
23+
'../bar.css',
24+
'../../baz.scss',
25+
'../../../baz.sass',
26+
'./../test.css',
27+
'.././angular.sass'
28+
]
2329
})
2430
class Test {}
25-
`,
31+
`,
2632
`
2733
@Component({
28-
styleUrls: ['../foobar.css']
34+
templateUrl: './foobar.html'
2935
})
3036
class Test {}
31-
`,
37+
`,
3238
`
3339
@Component({
34-
styleUrls: ['./foo.css', './bar.css', './whatyouwant.css']
40+
templateUrl: '../foobar.html'
3541
})
3642
class Test {}
37-
`,
43+
`,
3844
`
3945
@Component({
40-
templateUrl: './foobar.html'
46+
templateUrl: '../../foobar.html'
4147
})
4248
class Test {}
43-
`,
49+
`,
4450
`
4551
@Component({
46-
templateUrl: '../foobar.html'
52+
templateUrl: '../../../foobar.html'
4753
})
4854
class Test {}
49-
`,
55+
`,
56+
`
57+
@Component({
58+
templateUrl: './../foobar.html'
59+
})
60+
class Test {}
61+
`,
62+
`
63+
@Component({
64+
templateUrl: '.././foobar.html'
65+
})
66+
class Test {}
67+
`,
5068
],
5169
invalid: [
5270
convertAnnotatedSourceToFailureCase({
53-
description: `it should fail when a relative URL isn't prefixed with ./`,
71+
description: 'it should fail if one of "styleUrls" is absolute',
5472
annotatedSource: `
5573
@Component({
56-
styleUrls: ['foobar.css']
57-
~~~~~~~~~~~~
58-
})
59-
class Test {}
60-
`,
61-
messageId,
62-
}),
63-
convertAnnotatedSourceToFailureCase({
64-
description: `it should fail when a relative URL isn't prefixed with ./`,
65-
annotatedSource: `
66-
@Component({
67-
styleUrls: ['./../foobar.css']
68-
~~~~~~~~~~~~~~~~~
69-
})
70-
class Test {}
71-
`,
72-
messageId,
73-
}),
74-
convertAnnotatedSourceToFailureCase({
75-
description: `it should fail when one relative URL isn't prefixed with ./`,
76-
annotatedSource: `
77-
@Component({
78-
styleUrls: ['./foo.css', 'bar.css', './whatyouwant.css']
74+
styleUrls: ['./foo.css', 'bar.css', '../baz.scss', '../../test.css']
7975
~~~~~~~~~
8076
})
8177
class Test {}
8278
`,
8379
messageId,
8480
}),
8581
convertAnnotatedSourceToFailureCase({
86-
description: `it should fail when a relative URL isn't prefixed with ./`,
82+
description: 'it should fail if "templateUrl" is absolute',
8783
annotatedSource: `
8884
@Component({
8985
templateUrl: 'foobar.html'
@@ -93,16 +89,5 @@ ruleTester.run(RULE_NAME, rule, {
9389
`,
9490
messageId,
9591
}),
96-
convertAnnotatedSourceToFailureCase({
97-
description: `it should fail when a relative URL isn't prefixed with ./`,
98-
annotatedSource: `
99-
@Component({
100-
templateUrl: '.././foobar.html'
101-
~~~~~~~~~~~~~~~~~~
102-
})
103-
class Test {}
104-
`,
105-
messageId,
106-
}),
10792
],
10893
});

0 commit comments

Comments
 (0)