Skip to content

Commit

Permalink
add codes to warnings (#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Apr 15, 2018
1 parent 1c4b166 commit f0b2cb9
Show file tree
Hide file tree
Showing 44 changed files with 175 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/css/Stylesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ export default class Stylesheet {
const message = `Unused CSS selector`;

onwarn({
code: `css-unused-selector`,
message,
frame,
loc: { line: line + 1, column },
Expand Down
1 change: 1 addition & 0 deletions src/generators/wrapModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ function getGlobals(dependencies: Dependency[], options: CompileOptions) {
onerror(error);
} else {
const warning = {
code: `options-missing-globals`,
message: `No name was supplied for imported module '${d.source}'. Guessing '${d.name}', but you should use options.globals`,
};

Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Warning {
loc?: { line: number; column: number; pos?: number };
end?: { line: number; column: number; };
pos?: number;
code: string;
message: string;
filename?: string;
frame?: string;
Expand Down
80 changes: 64 additions & 16 deletions src/validate/html/a11y.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ 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);
validator.warn(attribute, {
code: `a11y-aria-attributes`,
message: `A11y: <${node.name}> should not have aria-* attributes`
});
}

const type = name.slice(5);
Expand All @@ -44,15 +47,21 @@ export default function a11y(
let message = `A11y: Unknown aria attribute 'aria-${type}'`;
if (match) message += ` (did you mean '${match}'?)`;

validator.warn(message, attribute);
validator.warn(attribute, {
code: `a11y-unknown-aria-attribute`,
message
});
}
}

// aria-role
if (name === 'role') {
if (invisibleElements.has(node.name)) {
// aria-unsupported-elements
validator.warn(`A11y: <${node.name}> should not have role attribute`, attribute);
validator.warn(attribute, {
code: `a11y-misplaced-role`,
message: `A11y: <${node.name}> should not have role attribute`
});
}

const value = getStaticAttributeValue(node, 'role');
Expand All @@ -61,30 +70,45 @@ export default function a11y(
let message = `A11y: Unknown role '${value}'`;
if (match) message += ` (did you mean '${match}'?)`;

validator.warn(message, attribute);
validator.warn(attribute, {
code: `a11y-unknown-role`,
message
});
}
}

// no-access-key
if (name === 'accesskey') {
validator.warn(`A11y: Avoid using accesskey`, attribute);
validator.warn(attribute, {
code: `a11y-accesskey`,
message: `A11y: Avoid using accesskey`
});
}

// no-autofocus
if (name === 'autofocus') {
validator.warn(`A11y: Avoid using autofocus`, attribute);
validator.warn(attribute, {
code: `a11y-autofocus`,
message: `A11y: Avoid using autofocus`
});
}

// scope
if (name === 'scope' && node.name !== 'th') {
validator.warn(`A11y: The scope attribute should only be used with <th> elements`, attribute);
validator.warn(attribute, {
code: `a11y-misplaced-scope`,
message: `A11y: The scope attribute should only be used with <th> elements`
});
}

// 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);
validator.warn(attribute, {
code: `a11y-positive-tabindex`,
message: `A11y: avoid tabindex values above zero`
});
}
}

Expand All @@ -98,21 +122,30 @@ 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);
validator.warn(node, {
code: `a11y-missing-attribute`,
message: `A11y: <${name}> element should have ${article} ${sequence} attribute`
});
}
}

function shouldHaveContent() {
if (node.children.length === 0) {
validator.warn(`A11y: <${node.name}> element should have child content`, node);
validator.warn(node, {
code: `a11y-missing-content`,
message: `A11y: <${node.name}> element should have child content`
});
}
}

function shouldHaveValidHref (attribute) {
const href = attributeMap.get(attribute);
const value = getStaticAttributeValue(node, attribute);
if (value === '' || value === '#') {
validator.warn(`A11y: '${value}' is not a valid ${attribute} attribute`, href);
validator.warn(href, {
code: `a11y-invalid-attribute`,
message: `A11y: '${value}' is not a valid ${attribute} attribute`
});
}
}

Expand All @@ -124,7 +157,10 @@ export default function a11y(
// anchor-in-svg-is-valid
shouldHaveValidHref('xlink:href')
} else {
validator.warn(`A11y: <a> element should have an href attribute`, node);
validator.warn(node, {
code: `a11y-missing-attribute`,
message: `A11y: <a> element should have an href attribute`
});
}

// anchor-has-content
Expand All @@ -143,7 +179,10 @@ export default function a11y(
shouldHaveContent();

if (attributeMap.has('aria-hidden')) {
validator.warn(`A11y: <${node.name}> element should not be hidden`, attributeMap.get('aria-hidden'));
validator.warn(attributeMap.get('aria-hidden'), {
code: `a11y-hidden`,
message: `A11y: <${node.name}> element should not be hidden`
});
}
}

Expand All @@ -159,14 +198,20 @@ export default function a11y(

// no-distracting-elements
if (node.name === 'marquee' || node.name === 'blink') {
validator.warn(`A11y: Avoid <${node.name}> elements`, node);
validator.warn(node, {
code: `a11y-distracting-elements`,
message: `A11y: Avoid <${node.name}> elements`
});
}

if (node.name === 'figcaption') {
const parent = elementStack[elementStack.length - 1];
if (parent) {
if (parent.name !== 'figure') {
validator.warn(`A11y: <figcaption> must be an immediate child of <figure>`, node);
validator.warn(node, {
code: `a11y-structure`,
message: `A11y: <figcaption> must be an immediate child of <figure>`
});
} else {
const children = parent.children.filter(node => {
if (node.type === 'Comment') return false;
Expand All @@ -177,7 +222,10 @@ export default function a11y(
const index = children.indexOf(node);

if (index !== 0 && index !== children.length - 1) {
validator.warn(`A11y: <figcaption> must be first or last child of <figure>`, node);
validator.warn(node, {
code: `a11y-structure`,
message: `A11y: <figcaption> must be first or last child of <figure>`
});
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions src/validate/html/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ export default function validateHtml(validator: Validator, html: Node) {
c += 2;
while (/\s/.test(validator.source[c])) c += 1;

validator.warn(
`Context clashes with a helper. Rename one or the other to eliminate any ambiguity`,
{ start: c, end: c + node.context.length }
);
validator.warn({ start: c, end: c + node.context.length }, {
code: `each-context-clash`,
message: `Context clashes with a helper. Rename one or the other to eliminate any ambiguity`
});
}
}

if (validator.options.dev && isEmptyBlock(node)) {
validator.warn('Empty block', node);
validator.warn(node, {
code: `empty-block`,
message: 'Empty block'
});
}

if (node.children) {
Expand Down
13 changes: 8 additions & 5 deletions src/validate/html/validateElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ export default function validateElement(

if (!isComponent && /^[A-Z]/.test(node.name[0])) {
// TODO upgrade to validator.error in v2
validator.warn(`${node.name} component is not defined`, node);
validator.warn(node, {
code: `missing-component`,
message: `${node.name} component is not defined`
});
}

if (elementStack.length === 0 && validator.namespace !== namespaces.svg && svg.test(node.name)) {
validator.warn(
`<${node.name}> is an SVG element – did you forget to add { namespace: 'svg' } ?`,
node
);
validator.warn(node, {
code: `missing-namespace`,
message: `<${node.name}> is an SVG element – did you forget to add { namespace: 'svg' } ?`
});
}

if (node.name === 'slot') {
Expand Down
13 changes: 8 additions & 5 deletions src/validate/html/validateEventHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export default function validateEventHandlerCallee(

if (name === 'store' && attribute.expression.callee.type === 'MemberExpression') {
if (!validator.options.store) {
validator.warn(
'compile with `store: true` in order to call store methods',
attribute.expression
);
validator.warn(attribute.expression, {
code: `options-missing-store`,
message: 'compile with `store: true` in order to call store methods'
});
}
return;
}
Expand All @@ -59,5 +59,8 @@ export default function validateEventHandlerCallee(
message += `. '${callee.name}' exists on 'helpers', did you put it in the wrong place?`;
}

validator.warn(message, attribute.expression);
validator.warn(attribute.expression, {
code: `invalid-callee`,
message
});
}
12 changes: 7 additions & 5 deletions src/validate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,15 @@ export class Validator {
});
}

warn(message: string, pos: { start: number, end: number }) {
warn(pos: { start: number, end: number }, { code, message }: { code: string, message: string }) {
if (!this.locator) this.locator = getLocator(this.source);
const start = this.locator(pos.start);
const end = this.locator(pos.end);

const frame = getCodeFrame(this.source, start.line, start.column);

this.onwarn({
code,
message,
frame,
loc: { line: start.line + 1, column: start.column },
Expand Down Expand Up @@ -105,6 +106,7 @@ export default function validate(
if (name && /^[a-z]/.test(name)) {
const message = `options.name should be capitalised`;
onwarn({
code: `options-lowercase-name`,
message,
filename,
toString: () => message,
Expand Down Expand Up @@ -148,10 +150,10 @@ export default function validate(
definitions.value.properties.forEach(prop => {
const { name } = prop.key;
if (!validator.used[category].has(name)) {
validator.warn(
`The '${name}' ${categories[category]} is unused`,
prop
);
validator.warn(prop, {
code: `unused-${category.slice(0, -1)}`,
message: `The '${name}' ${categories[category]} is unused`
});
}
});
}
Expand Down
5 changes: 4 additions & 1 deletion src/validate/js/propValidators/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export default function components(validator: Validator, prop: Node) {
}

if (!/^[A-Z]/.test(name)) {
validator.warn(`Component names should be capitalised`, component);
validator.warn(component, {
code: `component-lowercase`,
message: `Component names should be capitalised`
});
}
});
}
8 changes: 4 additions & 4 deletions src/validate/js/propValidators/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ export default function helpers(validator: Validator, prop: Node) {
});

if (prop.value.params.length === 0 && !usesArguments) {
validator.warn(
`Helpers should be pure functions, with at least one argument`,
prop
);
validator.warn(prop, {
code: `impure-helper`,
message: `Helpers should be pure functions, with at least one argument`
});
}
});
}
11 changes: 6 additions & 5 deletions src/validate/js/propValidators/onrender.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import oncreate from './oncreate';
import { Validator } from '../../';
import { Validator } from '../../index';
import { Node } from '../../../interfaces';

export default function onrender(validator: Validator, prop: Node) {
validator.warn(
`'onrender' has been deprecated in favour of 'oncreate', and will cause an error in Svelte 2.x`,
prop
);
validator.warn(prop, {
code: `deprecated-onrender`,
message: `'onrender' has been deprecated in favour of 'oncreate', and will cause an error in Svelte 2.x`
});

oncreate(validator, prop);
}
11 changes: 6 additions & 5 deletions src/validate/js/propValidators/onteardown.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import ondestroy from './ondestroy';
import { Validator } from '../../';
import { Validator } from '../../index';
import { Node } from '../../../interfaces';

export default function onteardown(validator: Validator, prop: Node) {
validator.warn(
`'onteardown' has been deprecated in favour of 'ondestroy', and will cause an error in Svelte 2.x`,
prop
);
validator.warn(prop, {
code: `deprecated-onteardown`,
message: `'onteardown' has been deprecated in favour of 'ondestroy', and will cause an error in Svelte 2.x`
});

ondestroy(validator, prop);
}
1 change: 1 addition & 0 deletions test/css/samples/omit-scoping-element-uppercase/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export default {
cascade: false,

warnings: [{
code: 'missing-component',
message: 'P component is not defined',
loc: {
line: 2,
Expand Down
Loading

0 comments on commit f0b2cb9

Please sign in to comment.