Skip to content

Commit 5b086df

Browse files
authored
Merge pull request #1250 from UnwrittenFun/feat/warn-end-pos
Add end position to warnings and errors
2 parents 10600eb + 8902417 commit 5b086df

File tree

127 files changed

+582
-118
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+582
-118
lines changed

src/css/Selector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export default class Selector {
102102
while (i-- > 1) {
103103
const selector = block.selectors[i];
104104
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
105-
validator.error(`:global(...) must be the first element in a compound selector`, selector.start);
105+
validator.error(`:global(...) must be the first element in a compound selector`, selector);
106106
}
107107
}
108108
});
@@ -120,7 +120,7 @@ export default class Selector {
120120

121121
for (let i = start; i < end; i += 1) {
122122
if (this.blocks[i].global) {
123-
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, this.blocks[i].selectors[0].start);
123+
validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, this.blocks[i].selectors[0]);
124124
}
125125
}
126126
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,4 @@ export function create(source: string, _options: CompileOptions = {}) {
146146
}
147147
}
148148

149-
export { parse, validate, version as VERSION };
149+
export { parse, validate, Stylesheet, version as VERSION };

src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface Parsed {
2929

3030
export interface Warning {
3131
loc?: { line: number; column: number; pos?: number };
32+
end?: { line: number; column: number; };
3233
pos?: number;
3334
message: string;
3435
filename?: string;

src/utils/CompileError.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,28 @@ import getCodeFrame from '../utils/getCodeFrame';
44
export default class CompileError extends Error {
55
frame: string;
66
loc: { line: number; column: number };
7+
end: { line: number; column: number };
78
pos: number;
89
filename: string;
910

1011
constructor(
1112
message: string,
1213
template: string,
13-
index: number,
14-
filename: string
14+
startPos: number,
15+
filename: string,
16+
endPos: number = startPos
1517
) {
1618
super(message);
1719

18-
const { line, column } = locate(template, index);
20+
const start = locate(template, startPos);
21+
const end = locate(template, endPos);
1922

20-
this.loc = { line: line + 1, column };
21-
this.pos = index;
23+
this.loc = { line: start.line + 1, column: start.column };
24+
this.end = { line: end.line + 1, column: end.column };
25+
this.pos = startPos;
2226
this.filename = filename;
2327

24-
this.frame = getCodeFrame(template, line, column);
28+
this.frame = getCodeFrame(template, start.line, start.column);
2529
}
2630

2731
public toString = () => {

src/validate/html/a11y.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default function a11y(
3333
if (name.startsWith('aria-')) {
3434
if (invisibleElements.has(node.name)) {
3535
// aria-unsupported-elements
36-
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute.start);
36+
validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute);
3737
}
3838

3939
const type = name.slice(5);
@@ -42,15 +42,15 @@ export default function a11y(
4242
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
4343
if (match) message += ` (did you mean '${match}'?)`;
4444

45-
validator.warn(message, attribute.start);
45+
validator.warn(message, attribute);
4646
}
4747
}
4848

4949
// aria-role
5050
if (name === 'role') {
5151
if (invisibleElements.has(node.name)) {
5252
// aria-unsupported-elements
53-
validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute.start);
53+
validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute);
5454
}
5555

5656
const value = getStaticAttributeValue(node, 'role');
@@ -59,30 +59,30 @@ export default function a11y(
5959
let message = `A11y: Unknown role '${value}'`;
6060
if (match) message += ` (did you mean '${match}'?)`;
6161

62-
validator.warn(message, attribute.start);
62+
validator.warn(message, attribute);
6363
}
6464
}
6565

6666
// no-access-key
6767
if (name === 'accesskey') {
68-
validator.warn(`A11y: Avoid using accesskey`, attribute.start);
68+
validator.warn(`A11y: Avoid using accesskey`, attribute);
6969
}
7070

7171
// no-autofocus
7272
if (name === 'autofocus') {
73-
validator.warn(`A11y: Avoid using autofocus`, attribute.start);
73+
validator.warn(`A11y: Avoid using autofocus`, attribute);
7474
}
7575

7676
// scope
7777
if (name === 'scope' && node.name !== 'th') {
78-
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute.start);
78+
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute);
7979
}
8080

8181
// tabindex-no-positive
8282
if (name === 'tabindex') {
8383
const value = getStaticAttributeValue(node, 'tabindex');
8484
if (!isNaN(value) && +value > 0) {
85-
validator.warn(`A11y: avoid tabindex values above zero`, attribute.start);
85+
validator.warn(`A11y: avoid tabindex values above zero`, attribute);
8686
}
8787
}
8888

@@ -96,21 +96,21 @@ export default function a11y(
9696
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
9797
attributes[0];
9898

99-
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node.start);
99+
validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node);
100100
}
101101
}
102102

103103
function shouldHaveContent() {
104104
if (node.children.length === 0) {
105-
validator.warn(`A11y: <${node.name}> element should have child content`, node.start);
105+
validator.warn(`A11y: <${node.name}> element should have child content`, node);
106106
}
107107
}
108108

109109
function shouldHaveValidHref (attribute) {
110110
const href = attributeMap.get(attribute);
111111
const value = getStaticAttributeValue(node, attribute);
112112
if (value === '' || value === '#') {
113-
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href.start);
113+
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href);
114114
}
115115
}
116116

@@ -122,7 +122,7 @@ export default function a11y(
122122
// anchor-in-svg-is-valid
123123
shouldHaveValidHref('xlink:href')
124124
} else {
125-
validator.warn(`A11y: <a> element should have an href attribute`, node.start);
125+
validator.warn(`A11y: <a> element should have an href attribute`, node);
126126
}
127127

128128
// anchor-has-content
@@ -141,7 +141,7 @@ export default function a11y(
141141
shouldHaveContent();
142142

143143
if (attributeMap.has('aria-hidden')) {
144-
validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden').start);
144+
validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden'));
145145
}
146146
}
147147

@@ -157,14 +157,14 @@ export default function a11y(
157157

158158
// no-distracting-elements
159159
if (node.name === 'marquee' || node.name === 'blink') {
160-
validator.warn(`A11y: Avoid <${node.name}> elements`, node.start);
160+
validator.warn(`A11y: Avoid <${node.name}> elements`, node);
161161
}
162162

163163
if (node.name === 'figcaption') {
164164
const parent = elementStack[elementStack.length - 1];
165165
if (parent) {
166166
if (parent.name !== 'figure') {
167-
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node.start);
167+
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node);
168168
} else {
169169
const children = parent.children.filter(node => {
170170
if (node.type === 'Comment') return false;
@@ -175,7 +175,7 @@ export default function a11y(
175175
const index = children.indexOf(node);
176176

177177
if (index !== 0 && index !== children.length - 1) {
178-
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node.start);
178+
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node);
179179
}
180180
}
181181
}

src/validate/html/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function validateHtml(validator: Validator, html: Node) {
5252

5353
else if (node.type === 'EachBlock') {
5454
if (validator.helpers.has(node.context)) {
55-
let c = node.expression.end;
55+
let c: number = node.expression.end;
5656

5757
// find start of context
5858
while (/\s/.test(validator.source[c])) c += 1;
@@ -61,13 +61,13 @@ export default function validateHtml(validator: Validator, html: Node) {
6161

6262
validator.warn(
6363
`Context clashes with a helper. Rename one or the other to eliminate any ambiguity`,
64-
c
64+
{ start: c, end: c + node.context.length }
6565
);
6666
}
6767
}
6868

6969
if (validator.options.dev && isEmptyBlock(node)) {
70-
validator.warn('Empty block', node.start);
70+
validator.warn('Empty block', node);
7171
}
7272

7373
if (node.children) {
@@ -103,7 +103,7 @@ export default function validateHtml(validator: Validator, html: Node) {
103103
let message = `'refs.${ref}' does not exist`;
104104
if (match) message += ` (did you mean 'refs.${match}'?)`;
105105

106-
validator.error(message, callee.start);
106+
validator.error(message, callee);
107107
}
108108
});
109109
}

0 commit comments

Comments
 (0)