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

Commit 125a2cc

Browse files
committed
Migrate to Linter v2 API
Move to the v2 Linter API, allowing for cleaner messages and no need for translation of the messages on Linter's part.
1 parent 98febbc commit 125a2cc

File tree

4 files changed

+99
-109
lines changed

4 files changed

+99
-109
lines changed

lib/helpers.js

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { dirname } from 'path';
44
import stylelint from 'stylelint';
55
import assignDeep from 'assign-deep';
6-
import escapeHTML from 'escape-html';
76
import { generateRange } from 'atom-linter';
87
import presetConfig from 'stylelint-config-standard';
98

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

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

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

43-
export function generateHTMLMessage(message) {
44-
if (!message.rule || message.rule === 'CssSyntaxError') {
45-
return escapeHTML(message.text);
46-
}
47-
48-
const ruleParts = message.rule.split('/');
49-
let url;
50-
51-
if (ruleParts.length === 1) {
52-
// Core rule
53-
url = `http://stylelint.io/user-guide/rules/${ruleParts[0]}`;
54-
} else {
55-
// Plugin rule
56-
const pluginName = ruleParts[0];
57-
// const ruleName = ruleParts[1];
58-
59-
switch (pluginName) {
60-
case 'plugin':
61-
url = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs/noRuleNamespace.md';
62-
break;
63-
default:
64-
url = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs/linkingNewRule.md';
65-
}
66-
}
67-
68-
// Escape any HTML in the message, and replace the rule ID with a link
69-
return escapeHTML(message.text).replace(
70-
`(${message.rule})`, `(<a href="${url}">${message.rule}</a>)`
71-
);
72-
}
73-
74-
export const parseResults = (editor, results, filePath, showIgnored) => {
44+
const parseResults = (editor, results, filePath, showIgnored) => {
7545
startMeasure('linter-stylelint: Parsing results');
7646
if (!results) {
7747
endMeasure('linter-stylelint: Parsing results');
@@ -80,38 +50,67 @@ export const parseResults = (editor, results, filePath, showIgnored) => {
8050
}
8151

8252
const invalidOptions = results.invalidOptionWarnings.map(msg => ({
83-
type: 'Error',
8453
severity: 'error',
85-
text: msg.text,
86-
filePath
54+
excerpt: msg.text,
55+
location: {
56+
file: filePath,
57+
position: createRange(editor)
58+
}
8759
}));
8860

8961
const warnings = results.warnings.map((warning) => {
9062
// Stylelint only allows 'error' and 'warning' as severity values
9163
const severity = !warning.severity || warning.severity === 'error' ? 'Error' : 'Warning';
92-
return {
93-
type: severity,
64+
const message = {
9465
severity: severity.toLowerCase(),
95-
html: generateHTMLMessage(warning),
96-
filePath,
97-
range: createRange(editor, warning)
66+
excerpt: warning.text,
67+
location: {
68+
file: filePath,
69+
position: createRange(editor, warning)
70+
}
9871
};
72+
73+
const ruleParts = warning.rule.split('/');
74+
if (ruleParts.length === 1) {
75+
// Core rule
76+
message.url = `http://stylelint.io/user-guide/rules/${ruleParts[0]}`;
77+
} else {
78+
// Plugin rule
79+
const pluginName = ruleParts[0];
80+
// const ruleName = ruleParts[1];
81+
82+
const linterStylelintURL = 'https://github.com/AtomLinter/linter-stylelint/tree/master/docs';
83+
switch (pluginName) {
84+
case 'plugin':
85+
message.url = `${linterStylelintURL}/noRuleNamespace.md`;
86+
break;
87+
default:
88+
message.url = `${linterStylelintURL}/linkingNewRule.md`;
89+
}
90+
}
91+
92+
return message;
9993
});
10094

10195
const deprecations = results.deprecations.map(deprecation => ({
102-
type: 'Warning',
10396
severity: 'warning',
104-
html: `${escapeHTML(deprecation.text)} (<a href="${deprecation.reference}">reference</a>)`,
105-
filePath
97+
excerpt: deprecation.text,
98+
url: deprecation.reference,
99+
location: {
100+
file: filePath,
101+
position: createRange(editor)
102+
}
106103
}));
107104

108105
const ignored = [];
109106
if (showIgnored && results.ignored) {
110107
ignored.push({
111-
type: 'Warning',
112108
severity: 'warning',
113-
text: 'This file is ignored',
114-
filePath
109+
excerpt: 'This file is ignored',
110+
location: {
111+
file: filePath,
112+
position: createRange(editor)
113+
}
115114
});
116115
}
117116

@@ -137,11 +136,12 @@ export const runStylelint = async (editor, stylelintOptions, filePath, settings)
137136
if (error.line) {
138137
endMeasure('linter-stylelint: Lint');
139138
return [{
140-
type: 'Error',
141139
severity: 'error',
142-
text: error.reason || error.message,
143-
filePath,
144-
range: createRange(editor, error)
140+
excerpt: error.reason || error.message,
141+
location: {
142+
file: filePath,
143+
position: createRange(editor, error)
144+
}
145145
}];
146146
}
147147

lib/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default {
8686
name: 'stylelint',
8787
grammarScopes: this.baseScopes,
8888
scope: 'file',
89-
lintOnFly: true,
89+
lintsOnChange: true,
9090
lint: async (editor) => {
9191
// Force the dependencies to load if they haven't already
9292
loadDeps();
@@ -178,10 +178,12 @@ export default {
178178
helpers.endMeasure('linter-stylelint: Lint');
179179
if (this.showIgnored) {
180180
return [{
181-
type: 'Warning',
182181
severity: 'warning',
183-
text: 'This file is ignored',
184-
filePath
182+
excerpt: 'This file is ignored',
183+
location: {
184+
file: filePath,
185+
position: helpers.createRange(editor)
186+
}
185187
}];
186188
}
187189
return [];

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@
5353
"dependencies": {
5454
"assign-deep": "^0.4.5",
5555
"atom-linter": "^10.0.0",
56-
"atom-package-deps": "^4.0.1",
57-
"escape-html": "^1.0.3",
56+
"atom-package-deps": "^4.6.0",
5857
"stylelint": "8.0.0",
5958
"stylelint-config-standard": "^17.0.0"
6059
},
@@ -90,12 +89,12 @@
9089
}
9190
},
9291
"package-deps": [
93-
"linter"
92+
"linter:2.0.0"
9493
],
9594
"providedServices": {
9695
"linter": {
9796
"versions": {
98-
"1.0.0": "provideLinter"
97+
"2.0.0": "provideLinter"
9998
}
10099
}
101100
}

spec/linter-stylelint-spec.js

Lines changed: 36 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const configStandardPath = path.join(fixtures, 'bad', 'stylelint-config-standard
99
const warningPath = path.join(fixtures, 'warn', 'warn.css');
1010
const invalidRulePath = path.join(fixtures, 'invalid-rule', 'styles.css');
1111

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

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

3031
// test only the first error
31-
expect(messages[0].type).toBe('Error');
3232
expect(messages[0].severity).toBe('error');
33-
expect(messages[0].text).not.toBeDefined();
34-
expect(messages[0].html).toBe(blockNoEmpty);
35-
expect(messages[0].filePath).toBe(configStandardPath);
36-
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
33+
expect(messages[0].excerpt).toBe(blockNoEmpty);
34+
expect(messages[0].url).toBe(blockNoEmptyUrl);
35+
expect(messages[0].location.file).toBe(configStandardPath);
36+
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
3737
});
3838

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

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

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

67-
expect(messages[0].type).toBe('Error');
6866
expect(messages[0].severity).toBe('error');
69-
expect(messages[0].text).not.toBeDefined();
70-
expect(messages[0].html).toBe('Unknown word (CssSyntaxError)');
71-
expect(messages[0].filePath).toBe(invalidPath);
72-
expect(messages[0].range).toEqual([[0, 0], [0, 3]]);
67+
expect(messages[0].excerpt).toBe('Unknown word (CssSyntaxError)');
68+
expect(messages[0].location.file).toBe(invalidPath);
69+
expect(messages[0].location.position).toEqual([[0, 0], [0, 3]]);
7370
});
7471

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

81-
expect(messages[0].type).toBe('Error');
8278
expect(messages[0].severity).toBe('error');
83-
expect(messages[0].text).toBe(text);
84-
expect(messages[0].html).not.toBeDefined();
85-
expect(messages[0].filePath).toBe(invalidRulePath);
86-
expect(messages[0].range).not.toBeDefined();
79+
expect(messages[0].excerpt).toBe(text);
80+
expect(messages[0].location.file).toBe(invalidRulePath);
81+
expect(messages[0].location.position).toEqual([[0, 0], [0, 6]]);
8782
});
8883

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

141-
expect(messages[0].type).toBe('Warning');
142136
expect(messages[0].severity).toBe('warning');
143-
expect(messages[0].text).toBe('This file is ignored');
144-
expect(messages[0].html).not.toBeDefined();
145-
expect(messages[0].filePath).toBe(ignorePath);
146-
expect(messages[0].range).not.toBeDefined();
137+
expect(messages[0].excerpt).toBe('This file is ignored');
138+
expect(messages[0].location.file).toBe(ignorePath);
139+
expect(messages[0].location.position).toEqual([[0, 0], [0, 7]]);
147140
});
148141

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

167160
// test only the first error
168-
expect(messages[0].type).toBe('Warning');
169161
expect(messages[0].severity).toBe('warning');
170-
expect(messages[0].text).not.toBeDefined();
171-
expect(messages[0].html).toBe(blockNoEmpty);
172-
expect(messages[0].filePath).toBe(warningPath);
173-
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
162+
expect(messages[0].excerpt).toBe(blockNoEmpty);
163+
expect(messages[0].url).toBe(blockNoEmptyUrl);
164+
expect(messages[0].location.file).toBe(warningPath);
165+
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
174166
});
175167

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

190182
// test only the first error
191-
expect(messages[0].type).toBe('Error');
192183
expect(messages[0].severity).toBe('error');
193-
expect(messages[0].text).not.toBeDefined();
194-
expect(messages[0].html).toBe(blockNoEmpty);
195-
expect(messages[0].filePath).toBe(badLess);
196-
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
184+
expect(messages[0].excerpt).toBe(blockNoEmpty);
185+
expect(messages[0].url).toBe(blockNoEmptyUrl);
186+
expect(messages[0].location.file).toBe(badLess);
187+
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
197188
});
198189

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

219210
// test only the first error
220-
expect(messages[0].type).toBe('Error');
221211
expect(messages[0].severity).toBe('error');
222-
expect(messages[0].text).not.toBeDefined();
223-
expect(messages[0].html).toBe(blockNoEmpty);
224-
expect(messages[0].filePath).toBe(issuesPostCSS);
225-
expect(messages[0].range).toEqual([[0, 5], [0, 7]]);
212+
expect(messages[0].excerpt).toBe(blockNoEmpty);
213+
expect(messages[0].url).toBe(blockNoEmptyUrl);
214+
expect(messages[0].location.file).toBe(issuesPostCSS);
215+
expect(messages[0].location.position).toEqual([[0, 5], [0, 7]]);
226216
});
227217

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

243233
it('shows lint messages when found', async () => {
244-
const nlzMessage = 'Expected a leading zero (<a href="http://stylelint.io/user-guide/rules/number-leading-zero">number-leading-zero</a>)';
245234
const editor = await atom.workspace.open(badSugarSS);
246235
const messages = await lint(editor);
247-
expect(messages[0].type).toBe('Error');
236+
248237
expect(messages[0].severity).toBe('error');
249-
expect(messages[0].text).not.toBeDefined();
250-
expect(messages[0].html).toBe(nlzMessage);
251-
expect(messages[0].filePath).toBe(badSugarSS);
252-
expect(messages[0].range).toEqual([[1, 38], [1, 40]]);
238+
expect(messages[0].excerpt).toBe('Expected a leading zero (number-leading-zero)');
239+
expect(messages[0].url).toBe('http://stylelint.io/user-guide/rules/number-leading-zero');
240+
expect(messages[0].location.file).toBe(badSugarSS);
241+
expect(messages[0].location.position).toEqual([[1, 38], [1, 40]]);
253242
});
254243

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

0 commit comments

Comments
 (0)