diff --git a/src/css/Selector.ts b/src/css/Selector.ts
index 658f6dc52dee..fba0386f1f82 100644
--- a/src/css/Selector.ts
+++ b/src/css/Selector.ts
@@ -102,7 +102,7 @@ export default class Selector {
while (i-- > 1) {
const selector = block.selectors[i];
if (selector.type === 'PseudoClassSelector' && selector.name === 'global') {
- validator.error(`:global(...) must be the first element in a compound selector`, selector.start);
+ validator.error(`:global(...) must be the first element in a compound selector`, selector);
}
}
});
@@ -120,7 +120,7 @@ export default class Selector {
for (let i = start; i < end; i += 1) {
if (this.blocks[i].global) {
- 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);
+ validator.error(`:global(...) can be at the start or end of a selector sequence, but not in the middle`, this.blocks[i].selectors[0]);
}
}
}
diff --git a/src/index.ts b/src/index.ts
index be62f0776364..cc3c0681e58c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -146,4 +146,4 @@ export function create(source: string, _options: CompileOptions = {}) {
}
}
-export { parse, validate, version as VERSION };
+export { parse, validate, Stylesheet, version as VERSION };
diff --git a/src/interfaces.ts b/src/interfaces.ts
index 82de8cd04737..de187da71d1b 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -29,6 +29,7 @@ export interface Parsed {
export interface Warning {
loc?: { line: number; column: number; pos?: number };
+ end?: { line: number; column: number; };
pos?: number;
message: string;
filename?: string;
diff --git a/src/utils/CompileError.ts b/src/utils/CompileError.ts
index 16dabc3c13f0..c48edba4abf6 100644
--- a/src/utils/CompileError.ts
+++ b/src/utils/CompileError.ts
@@ -4,24 +4,28 @@ import getCodeFrame from '../utils/getCodeFrame';
export default class CompileError extends Error {
frame: string;
loc: { line: number; column: number };
+ end: { line: number; column: number };
pos: number;
filename: string;
constructor(
message: string,
template: string,
- index: number,
- filename: string
+ startPos: number,
+ filename: string,
+ endPos: number = startPos
) {
super(message);
- const { line, column } = locate(template, index);
+ const start = locate(template, startPos);
+ const end = locate(template, endPos);
- this.loc = { line: line + 1, column };
- this.pos = index;
+ this.loc = { line: start.line + 1, column: start.column };
+ this.end = { line: end.line + 1, column: end.column };
+ this.pos = startPos;
this.filename = filename;
- this.frame = getCodeFrame(template, line, column);
+ this.frame = getCodeFrame(template, start.line, start.column);
}
public toString = () => {
diff --git a/src/validate/html/a11y.ts b/src/validate/html/a11y.ts
index 1e07c0b4d468..e9953d60835f 100644
--- a/src/validate/html/a11y.ts
+++ b/src/validate/html/a11y.ts
@@ -33,7 +33,7 @@ export default function a11y(
if (name.startsWith('aria-')) {
if (invisibleElements.has(node.name)) {
// aria-unsupported-elements
- validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute.start);
+ validator.warn(`A11y: <${node.name}> should not have aria-* attributes`, attribute);
}
const type = name.slice(5);
@@ -42,7 +42,7 @@ export default function a11y(
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
if (match) message += ` (did you mean '${match}'?)`;
- validator.warn(message, attribute.start);
+ validator.warn(message, attribute);
}
}
@@ -50,7 +50,7 @@ export default function a11y(
if (name === 'role') {
if (invisibleElements.has(node.name)) {
// aria-unsupported-elements
- validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute.start);
+ validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute);
}
const value = getStaticAttributeValue(node, 'role');
@@ -59,30 +59,30 @@ export default function a11y(
let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`;
- validator.warn(message, attribute.start);
+ validator.warn(message, attribute);
}
}
// no-access-key
if (name === 'accesskey') {
- validator.warn(`A11y: Avoid using accesskey`, attribute.start);
+ validator.warn(`A11y: Avoid using accesskey`, attribute);
}
// no-autofocus
if (name === 'autofocus') {
- validator.warn(`A11y: Avoid using autofocus`, attribute.start);
+ validator.warn(`A11y: Avoid using autofocus`, attribute);
}
// scope
if (name === 'scope' && node.name !== 'th') {
- validator.warn(`A11y: The scope attribute should only be used with
elements`, attribute.start);
+ validator.warn(`A11y: The scope attribute should only be used with | elements`, attribute);
}
// tabindex-no-positive
if (name === 'tabindex') {
const value = getStaticAttributeValue(node, 'tabindex');
if (!isNaN(value) && +value > 0) {
- validator.warn(`A11y: avoid tabindex values above zero`, attribute.start);
+ validator.warn(`A11y: avoid tabindex values above zero`, attribute);
}
}
@@ -96,13 +96,13 @@ export default function a11y(
attributes.slice(0, -1).join(', ') + ` or ${attributes[attributes.length - 1]}` :
attributes[0];
- validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node.start);
+ validator.warn(`A11y: <${name}> element should have ${article} ${sequence} attribute`, node);
}
}
function shouldHaveContent() {
if (node.children.length === 0) {
- validator.warn(`A11y: <${node.name}> element should have child content`, node.start);
+ validator.warn(`A11y: <${node.name}> element should have child content`, node);
}
}
@@ -110,7 +110,7 @@ export default function a11y(
const href = attributeMap.get(attribute);
const value = getStaticAttributeValue(node, attribute);
if (value === '' || value === '#') {
- validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href.start);
+ validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href);
}
}
@@ -122,7 +122,7 @@ export default function a11y(
// anchor-in-svg-is-valid
shouldHaveValidHref('xlink:href')
} else {
- validator.warn(`A11y: element should have an href attribute`, node.start);
+ validator.warn(`A11y: element should have an href attribute`, node);
}
// anchor-has-content
@@ -141,7 +141,7 @@ export default function a11y(
shouldHaveContent();
if (attributeMap.has('aria-hidden')) {
- validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden').start);
+ validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden'));
}
}
@@ -157,14 +157,14 @@ export default function a11y(
// no-distracting-elements
if (node.name === 'marquee' || node.name === 'blink') {
- validator.warn(`A11y: Avoid <${node.name}> elements`, node.start);
+ validator.warn(`A11y: Avoid <${node.name}> elements`, node);
}
if (node.name === 'figcaption') {
const parent = elementStack[elementStack.length - 1];
if (parent) {
if (parent.name !== 'figure') {
- validator.warn(`A11y: must be an immediate child of |