10000 Reinstate and document workaround feature (#4592) · stylelint/stylelint@36e2dbb · GitHub
[go: up one dir, main page]

Skip to content

Commit 36e2dbb

Browse files
authored
Reinstate and document workaround feature (#4592)
1 parent 20d1a96 commit 36e2dbb

File tree

9 files changed

+187
-18
lines changed

9 files changed

+187
-18
lines changed

docs/about/syntaxes.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
# Syntaxes
22

3-
There are many styling languages, ranging from CSS language extension like SCSS to entirely different notations like CSS-in-JS objects. These styling languages can be then be embedded within other languages, including HTML `<style>` tags, markdown fences and JavaScript variables.
3+
There are many styling languages, ranging from CSS language extensions like SCSS to entirely different notations, e.g. CSS-in-JS objects.
44

5-
We aim to support all these use cases, but it's a complicated task.
5+
These styling languages can be embedded within other languages too. For example:
66

7-
We use [PostCSS syntaxes](https://github.com/postcss/postcss#syntaxes) to transform these languages into something that resembles CSS, which is the language that:
7+
- HTML `<style>` tags
8+
- markdown fences
9+
- JavaScript template literals
810

9-
- underpins all the other styling languages
10-
- is best understood by stylelint
11+
We aim to support all these use cases in stylelint, but it's a complicated endeavor.
12+
13+
We lean on [PostCSS syntaxes](https://github.com/postcss/postcss#syntaxes) to help us with this task. We use them to transform these languages into something that resembles CSS, which is the language that:
1114

12-
We need your help to [support and improve](https://github.com/postcss/postcss/blob/master/docs/syntax.md) the following PostCSS syntaxes:
15+
- underpins all the other styling languages
16+
- is best understood by rules built into stylelint
1317

14-
- [postcss-css-in-js](https://github.com/stylelint/postcss-css-in-js)
15-
- [postcss-html](https://github.com/gucong3000/postcss-html)
16-
- [postcss-less](https://github.com/webschik/postcss-less)
17-
- [postcss-markdown](https://github.com/stylelint/postcss-markdown)
18-
- [postcss-sass](https://github.com/AleshaOleg/postcss-sass)
19-
- [postcss-scss](https://github.com/postcss/postcss-scss)
18+
If you write your styles in anything other than CSS, please consider [contributing to these syntaxes](../developer-guide/syntaxes.md) so that they can remain compatible with stylelint.

docs/developer-guide/syntaxes.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Working on syntaxes
2+
3+
Please help us enhance and debug the [syntaxes](../about/syntaxes.md) we use in stylelint:
4+
5+
- [postcss-css-in-js](https://github.com/stylelint/postcss-css-in-js)
6+
- [postcss-html](https://github.com/gucong3000/postcss-html)
7+
- [postcss-less](https://github.com/webschik/postcss-less)
8+
- [postcss-markdown](https://github.com/stylelint/postcss-markdown)
9+
- [postcss-sass](https://github.com/AleshaOleg/postcss-sass)
10+
- [postcss-scss](https://github.com/postcss/postcss-scss)
11+
12+
To contribute to a syntax, you should:
13+
14+
1. Familiarize yourself with PostCSS's [how to write custom syntax](https://github.com/postcss/postcss/blob/master/docs/syntax.md) guide.
15+
2. Use the [`syntax: *` labels](https://github.com/stylelint/stylelint/labels?utf8=%E2%9C%93&q=syntax%3A) to identify which syntax is behind an issue.
16+
3. Go to the repository for that syntax.
17+
4. Read their contributing guidelines.
18+
19+
## Workarounds
20+
21+
Fixing bugs in syntaxes can take time. stylelint can work around these bug by turning off autofix for incompatible sources. Autofix can then remain safe to use while contributors try to fix the underlying issue.
22+
23+
### Current workarounds
24+
25+
stylelint currently turns off autofix for sources that contain:
26+
27+
- nested tagged template literals ([issue #4119](https://github.com/stylelint/stylelint/issues/4119))
28+
29+
### Add a workaround
30+
31+
To add a new workaround, you should:
32+
33+
1. Add code to [`lib/lintSource.js`](https://github.com/stylelint/stylelint/blob/master/lib/lintSource.js) to detect the incompatible pattern.
34+
2. Add a corresponding test to [`system-tests/fix/fix.test.js`](https://github.com/stylelint/stylelint/blob/master/system-tests/fix/fix.test.js).
35+
3. Document the workaround in [`docs/developer-guides/syntaxes.md`](https://github.com/stylelint/stylelint/blob/master/docs/developer-guide/syntaxes.md).

docs/toc.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [Errors](user-guide/errors.md)
2222
- Developer guide
2323
- [Rules](developer-guide/rules.md)
24+
- [Syntaxes](developer-guide/syntaxes.md)
2425
- [Plugins](developer-guide/plugins.md)
2526
- [Formatters](developer-guide/formatters.md)
2627
- [System tests](developer-guide/system-tests.md)

docs/user-guide/usage/options.md

Copy file name to clipboard
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Automatically fix, where possible, violations reported by rules.
3030

3131
For CSS with standard syntax, stylelint uses [postcss-safe-parser](https://github.com/postcss/postcss-safe-parser) to fix syntax errors.
3232

33-
**Note:** This is an _experimental_ feature. It currently does not respect special comments for disabling stylelint within sources (e. g. `/* stylelint-disable */`). Autofixing is applied regardless of these comments.
33+
_`fix` is an experimental feature that currently ignores sources with [`/* stylelint-disable */` comments](../ignore-code.md)._
3434

3535
## `formatter`
3636

lib/lintSource.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ function lintPostcssResult(stylelint, postcssResult, config) {
146146
postcssResult.stylelint.ignoreDisables = true;
147147
}
148148

149+
const isFileFixCompatible = isFixCompatible(postcssResult);
150+
151+
if (!isFileFixCompatible) {
152+
postcssResult.stylelint.disableWritingFix = true;
153+
}
154+
149155
const postcssRoots =
150156
/** @type {import('postcss').Root[]} */ (postcssDoc &&
151157
postcssDoc.constructor.name === 'Document'
@@ -200,7 +206,7 @@ function lintPostcssResult(stylelint, postcssResult, config) {
200206
Promise.all(
201207
postcssRoots.map((postcssRoot) =>
202208
ruleFunction(primaryOption, secondaryOptions, {
203-
fix: stylelint._options.fix,
209+
fix: stylelint._options.fix && isFileFixCompatible,
204210
newline,
205211
})(postcssRoot, postcssResult),
206212
),
@@ -241,3 +247,58 @@ function createEmptyPostcssResult 10000 (filePath) {
241247
warn: () => {},
242248
};
243249
}
250+
251+
/**
252+
* There are currently some bugs in the autofixer of Stylelint.
253+
* The autofixer does not yet adhere to stylelint-disable comments, so if there are disabled
254+
* ranges we can not autofix this document. More info in issue #2643.
255+
* Also, if this document is parsed with postcss-jsx and there are nested template
256+
* literals, it will duplicate some code. More info in issue #4119.
257+
*
258+
* @param {PostcssResult} postcssResult
259+
* @returns {boolean}
260+
*/
261+
function isFixCompatible({ stylelint, root }) {
262+
// Check for issue #2643
263+
if (stylelint.disabledRanges.all.length) return false;
264+
265+
// Check for issue #4119
266+
if (root && root.source && root.source.lang === 'jsx' && root.nodes) {
267+
// Gather all locations of template literals
268+
/**
269+
* @typedef TemplateLiteralLocation
270+
* @type {object}
271+
* @property {number} startLine - Start of the template literal.
272+
* @property {number} endLine - End of the template literal
273+
*/
274+
/** @type {Array<TemplateLiteralLocation>} */
275+
const templateLiteralLocations = [];
276+
277+
root.nodes.forEach((n) => {
278+
if (n.source && n.source.start && n.source.input.css !== undefined) {
279+
templateLiteralLocations.push({
280+
startLine: n.source.start.line,
281+
endLine: n.source.start.line + n.source.input.css.split('\n').length,
282+
});
283+
}
284+
});
285+
286+
// Compare all different template literal locations with eachother
287+
for (const location1 of templateLiteralLocations) {
288+
for (const location2 of templateLiteralLocations) {
289+
// Make sure it F987 9;s not the same template literal
290+
if (location1 !== location2) {
291+
// The first location should be before or after the second location.
292+
// If not, it's not compatible.
293+
if (
294+
!(location1.endLine < location2.startLine || location2.endLine < location1.startLine)
295+
) {
296+
return false;
297+
}
298+
}
299+
}
300+
}
301+
}
302+
303+
return true;
304+
}

lib/standalone.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,13 @@ module.exports = function(options) {
165165
const returnValue = prepareReturnValue([stylelintResult]);
166166

167167
if (options.fix && postcssResult && !postcssResult.stylelint.ignored) {
168-
// If we're fixing, the output should be the fixed code
169-
returnValue.output = postcssResult.root.toString(postcssResult.opts.syntax);
168+
if (!postcssResult.stylelint.disableWritingFix) {
169+
// If we're fixing, the output should be the fixed code
170+
returnValue.output = postcssResult.root.toString(postcssResult.opts.syntax);
171+
} else {
172+
// If the writing of the fix is disabled, the input code is returned as-is
173+
returnValue.output = code;
174+
}
170175
}
171176

172177
return returnValue;
@@ -247,7 +252,8 @@ module.exports = function(options) {
247252
postcssResult.root &&
248253
postcssResult.opts &&
249254
!postcssResult.stylelint.ignored &&
250-
options.fix
255+
options.fix &&
256+
!postcssResult.stylelint.disableWritingFix
251257
) {
252258
// @ts-ignore TODO TYPES toString accepts 0 arguments
253259
const fixedCss = postcssResult.root.toString(postcssResult.opts.syntax);

system-tests/fix/fix.test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,58 @@ describe('fix', () => {
150150
);
151151
});
152152
});
153+
154+
it("doesn't fix with stylelint-disable commands", () => {
155+
const code = `
156+
/* stylelint-disable */
157+
a {
158+
color: red;
159+
}
160+
`;
161+
162+
return stylelint
163+
.lint({
164+
code,
165+
config: {
166+
rules: {
167+
indentation: 2,
168+
},
169+
},
170+
fix: true,
171+
})
172+
.then((result) => {
173+
expect(result.output).toBe(code);
174+
});
175+
});
176+
177+
it("doesn't fix with nested template literals", () => {
178+
const code = `
179+
import styled, { css } from 'styled-components';
180+
181+
const Component = styled.div\`
182+
padding: 10px;
183+
\${() => css\`
184+
color: #b02d00;
185+
\`}
186+
\`;
187+
`;
188+
189+
return stylelint
190+
.lint({
191+
code,
192+
syntax: 'css-in-js',
193+
config: {
194+
rules: {
195+
indentation: 2,
196+
},
197+
},
198+
fix: true,
199+
})
200+
.then((result) => {
201+
expect(result.errored).toBe(true);
202+
expect(result.output).toBe(code);
203+
});
204+
});
153205
});
154206

155207
describe('fix with BOM', () => {

types/postcss/extensions.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as postcss from 'postcss';
2+
declare module 'postcss' {
3+
4+
interface NodeSource {
5+
lang?: string;
6+
7+
}
8+
9+
interface Input {
10+
css?: string;
11+
}
12+
}

types/stylelint/index.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,15 @@ declare module 'stylelint' {
4949
disabledRanges: DisabledRangeObject,
5050
ignored?: boolean,
5151
ignoreDisables?: boolean,
52-
stylelintError?: boolean
52+
stylelintError?: boolean,
53+
disableWritingFix?: boolean
5354
};
5455

5556
type EmptyResult = {
5657
root: {
58+
nodes?: undefined,
5759
source: {
60+
lang?: undefined,
5861
input: {
5962
file?: string
6063
}

0 commit comments

Comments
 (0)
0