Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Migrate to Linter v2 API #356

Merged
merged 1 commit into from
Jul 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 54 additions & 54 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { dirname } from 'path';
import stylelint from 'stylelint';
import assignDeep from 'assign-deep';
import escapeHTML from 'escape-html';
import { generateRange } from 'atom-linter';
import presetConfig from 'stylelint-config-standard';

Expand All @@ -30,48 +29,19 @@ export function endMeasure(baseName) {
}

export function createRange(editor, data) {
if (!Object.hasOwnProperty.call(data, 'line') && !Object.hasOwnProperty.call(data, 'column')) {
if (!data ||
(!Object.hasOwnProperty.call(data, 'line') && !Object.hasOwnProperty.call(data, 'column'))
) {
// data.line & data.column might be undefined for non-fatal invalid rules,
// e.g.: "block-no-empty": "foo"
// Return `false` so Linter will ignore the range
return false;
// Return a range encompassing the first line of the file
return generateRange(editor);
}

return generateRange(editor, data.line - 1, data.column - 1);
}

export function generateHTMLMessage(message) {
if (!message.rule || message.rule === 'CssSyntaxError') {
return escapeHTML(message.text);
}

const ruleParts = message.rule.split('/');
let url;

if (ruleParts.length === 1) {
// Core rule
url = `http://stylelint.io/user-guide/rules/${ruleParts[0]}`;
} else {
// Plugin rule
const pluginName = ruleParts[0];
// const ruleName = ruleParts[1];

switch (pluginName) {
case 'plugin':
url = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs/noRuleNamespace.md';
break;
default:
url = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs/linkingNewRule.md';
}
}

// Escape any HTML in the message, and replace the rule ID with a link
return escapeHTML(message.text).replace(
`(${message.rule})`, `(<a href="${url}">${message.rule}</a>)`
);
}

export const parseResults = (editor, results, filePath, showIgnored) => {
const parseResults = (editor, results, filePath, showIgnored) => {
startMeasure('linter-stylelint: Parsing results');
if (!results) {
endMeasure('linter-stylelint: Parsing results');
Expand All @@ -80,38 +50,67 @@ export const parseResults = (editor, results, filePath, showIgnored) => {
}

const invalidOptions = results.invalidOptionWarnings.map(msg => ({
type: 'Error',
severity: 'error',
text: msg.text,
filePath
excerpt: msg.text,
location: {
file: filePath,
position: createRange(editor)
}
}));

const warnings = results.warnings.map((warning) => {
// Stylelint only allows 'error' and 'warning' as severity values
const severity = !warning.severity || warning.severity === 'error' ? 'Error' : 'Warning';
return {
type: severity,
const message = {
severity: severity.toLowerCase(),
html: generateHTMLMessage(warning),
filePath,
range: createRange(editor, warning)
excerpt: warning.text,
location: {
file: filePath,
position: createRange(editor, warning)
}
};

const ruleParts = warning.rule.split('/');
if (ruleParts.length === 1) {
// Core rule
message.url = `http://stylelint.io/user-guide/rules/${ruleParts[0]}`;
} else {
// Plugin rule
const pluginName = ruleParts[0];
// const ruleName = ruleParts[1];

const linterStylelintURL = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs';
switch (pluginName) {
case 'plugin':
message.url = `${linterStylelintURL}/noRuleNamespace.md`;
break;
default:
message.url = `${linterStylelintURL}/linkingNewRule.md`;
}
}

return message;
});

const deprecations = results.deprecations.map(deprecation => ({
type: 'Warning',
severity: 'warning',
html: `${escapeHTML(deprecation.text)} (<a href="${deprecation.reference}">reference</a>)`,
filePath
excerpt: deprecation.text,
url: deprecation.reference,
location: {
file: filePath,
position: createRange(editor)
}
}));

const ignored = [];
if (showIgnored && results.ignored) {
ignored.push({
type: 'Warning',
severity: 'warning',
text: 'This file is ignored',
filePath
excerpt: 'This file is ignored',
location: {
file: filePath,
position: createRange(editor)
}
});
}

Expand All @@ -137,11 +136,12 @@ export const runStylelint = async (editor, stylelintOptions, filePath, settings)
if (error.line) {
endMeasure('linter-stylelint: Lint');
return [{
type: 'Error',
severity: 'error',
text: error.reason || error.message,
filePath,
range: createRange(editor, error)
excerpt: error.reason || error.message,
location: {
file: filePath,
position: createRange(editor, error)
}
}];
}

Expand Down
10 changes: 6 additions & 4 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default {
name: 'stylelint',
grammarScopes: this.baseScopes,
scope: 'file',
lintOnFly: true,
lintsOnChange: true,
lint: async (editor) => {
// Force the dependencies to load if they haven't already
loadDeps();
Expand Down Expand Up @@ -178,10 +178,12 @@ export default {
helpers.endMeasure('linter-stylelint: Lint');
if (this.showIgnored) {
return [{
type: 'Warning',
severity: 'warning',
text: 'This file is ignored',
filePath
excerpt: 'This file is ignored',
location: {
file: filePath,
position: helpers.createRange(editor)
}
}];
}
return [];
Expand Down
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@
"dependencies": {
"assign-deep": "^0.4.5",
"atom-linter": "^10.0.0",
"atom-package-deps": "^4.0.1",
"escape-html": "^1.0.3",
"atom-package-deps": "^4.6.0",
"stylelint": "8.0.0",
"stylelint-config-standard": "^17.0.0"
},
Expand Down Expand Up @@ -90,12 +89,12 @@
}
},
"package-deps": [
"linter"
"linter:2.0.0"
],
"providedServices": {
"linter": {
"versions": {
"1.0.0": "provideLinter"
"2.0.0": "provideLinter"
}
}
}
Expand Down
83 changes: 36 additions & 47 deletions spec/linter-stylelint-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const configStandardPath = path.join(fixtures, 'bad', 'stylelint-config-standard
const warningPath = path.join(fixtures, 'warn', 'warn.css');
const invalidRulePath = path.join(fixtures, 'invalid-rule', 'styles.css');

const blockNoEmpty = 'Unexpected empty block (<a href="http://stylelint.io/user-guide/rules/block-no-empty">block-no-empty</a>)';
const blockNoEmpty = 'Unexpected empty block (block-no-empty)';
const blockNoEmptyUrl = 'http://stylelint.io/user-guide/rules/block-no-empty';

describe('The stylelint provider for Linter', () => {
const lint = require('../lib/index.js').provideLinter().lint;
Expand All @@ -28,12 +29,11 @@ describe('The stylelint provider for Linter', () => {
expect(messages.length).toBeGreaterThan(0);

// test only the first error
expect(messages[0].type).toBe('Error');
expect(messages[0].severity).toBe('error');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(blockNoEmpty);
expect(messages[0].filePath).toBe(configStandardPath);
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
expect(messages[0].excerpt).toBe(blockNoEmpty);
expect(messages[0].url).toBe(blockNoEmptyUrl);
expect(messages[0].location.file).toBe(configStandardPath);
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
});

it('reports rules set as warnings as a Warning', async () => {
Expand All @@ -43,12 +43,11 @@ describe('The stylelint provider for Linter', () => {
expect(messages.length).toBeGreaterThan(0);

// test only the first error
expect(messages[0].type).toBe('Warning');
expect(messages[0].severity).toBe('warning');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(blockNoEmpty);
expect(messages[0].filePath).toMatch(/.+warn\.css$/);
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
expect(messages[0].excerpt).toBe(blockNoEmpty);
expect(messages[0].url).toBe(blockNoEmptyUrl);
expect(messages[0].location.file).toMatch(/.+warn\.css$/);
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
});

it('finds nothing wrong with a valid file', async () => {
Expand All @@ -64,12 +63,10 @@ describe('The stylelint provider for Linter', () => {
const messages = await lint(editor);
expect(messages.length).toBe(1);

expect(messages[0].type).toBe('Error');
expect(messages[0].severity).toBe('error');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe('Unknown word (CssSyntaxError)');
expect(messages[0].filePath).toBe(invalidPath);
expect(messages[0].range).toEqual([[0, 0], [0, 3]]);
expect(messages[0].excerpt).toBe('Unknown word (CssSyntaxError)');
expect(messages[0].location.file).toBe(invalidPath);
expect(messages[0].location.position).toEqual([[0, 0], [0, 3]]);
});

it('shows an error on non-fatal stylelint runtime error', async () => {
Expand All @@ -78,12 +75,10 @@ describe('The stylelint provider for Linter', () => {
const messages = await lint(editor);
expect(messages.length).toBe(1);

expect(messages[0].type).toBe('Error');
expect(messages[0].severity).toBe('error');
expect(messages[0].text).toBe(text);
expect(messages[0].html).not.toBeDefined();
expect(messages[0].filePath).toBe(invalidRulePath);
expect(messages[0].range).not.toBeDefined();
expect(messages[0].excerpt).toBe(text);
expect(messages[0].location.file).toBe(invalidRulePath);
expect(messages[0].location.position).toEqual([[0, 0], [0, 6]]);
});

it('shows an error notification for a fatal stylelint runtime error', async () => {
Expand Down Expand Up @@ -138,12 +133,10 @@ describe('The stylelint provider for Linter', () => {
const messages = await lint(editor);
expect(messages.length).toBe(1);

expect(messages[0].type).toBe('Warning');
expect(messages[0].severity).toBe('warning');
expect(messages[0].text).toBe('This file is ignored');
expect(messages[0].html).not.toBeDefined();
expect(messages[0].filePath).toBe(ignorePath);
expect(messages[0].range).not.toBeDefined();
expect(messages[0].excerpt).toBe('This file is ignored');
expect(messages[0].location.file).toBe(ignorePath);
expect(messages[0].location.position).toEqual([[0, 0], [0, 7]]);
});

it("doesn't show a message when not asked to", async () => {
Expand All @@ -165,12 +158,11 @@ describe('The stylelint provider for Linter', () => {
expect(messages.length).toBeGreaterThan(0);

// test only the first error
expect(messages[0].type).toBe('Warning');
expect(messages[0].severity).toBe('warning');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(blockNoEmpty);
expect(messages[0].filePath).toBe(warningPath);
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
expect(messages[0].excerpt).toBe(blockNoEmpty);
expect(messages[0].url).toBe(blockNoEmptyUrl);
expect(messages[0].location.file).toBe(warningPath);
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
});

describe('works with Less files and', () => {
Expand All @@ -188,12 +180,11 @@ describe('The stylelint provider for Linter', () => {
expect(messages.length).toBeGreaterThan(0);

// test only the first error
expect(messages[0].type).toBe('Error');
expect(messages[0].severity).toBe('error');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(blockNoEmpty);
expect(messages[0].filePath).toBe(badLess);
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
expect(messages[0].excerpt).toBe(blockNoEmpty);
expect(messages[0].url).toBe(blockNoEmptyUrl);
expect(messages[0].location.file).toBe(badLess);
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
});

it('finds nothing wrong with a valid file', async () => {
Expand All @@ -217,12 +208,11 @@ describe('The stylelint provider for Linter', () => {
expect(messages.length).toBeGreaterThan(0);

// test only the first error
expect(messages[0].type).toBe('Error');
expect(messages[0].severity).toBe('error');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(blockNoEmpty);
expect(messages[0].filePath).toBe(issuesPostCSS);
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
expect(messages[0].excerpt).toBe(blockNoEmpty);
expect(messages[0].url).toBe(blockNoEmptyUrl);
expect(messages[0].location.file).toBe(issuesPostCSS);
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
});

it('finds nothing wrong with a valid file', async () => {
Expand All @@ -241,15 +231,14 @@ describe('The stylelint provider for Linter', () => {
});

it('shows lint messages when found', async () => {
const nlzMessage = 'Expected a leading zero (<a href="http://stylelint.io/user-guide/rules/number-leading-zero">number-leading-zero</a>)';
const editor = await atom.workspace.open(badSugarSS);
const messages = await lint(editor);
expect(messages[0].type).toBe('Error');

expect(messages[0].severity).toBe('error');
expect(messages[0].text).not.toBeDefined();
expect(messages[0].html).toBe(nlzMessage);
expect(messages[0].filePath).toBe(badSugarSS);
expect(messages[0].range).toEqual([[1, 38], [1, 40]]);
expect(messages[0].excerpt).toBe('Expected a leading zero (number-leading-zero)');
expect(messages[0].url).toBe('http://stylelint.io/user-guide/rules/number-leading-zero');
expect(messages[0].location.file).toBe(badSugarSS);
expect(messages[0].location.position).toEqual([[1, 38], [1, 40]]);
});

it('finds nothing wrong with a valid file', async () => {
Expand Down