Skip to content

Commit 981956e

Browse files
committed
More robust stylelint shim to support reporting
1 parent ce3a751 commit 981956e

File tree

5 files changed

+190
-86
lines changed

5 files changed

+190
-86
lines changed

polaris-migrator/README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -278,37 +278,47 @@ migrations
278278

279279
#### The SASS migration function
280280

281-
Each migrator has a default export adhering to the [PostCSS Plugin API](https://github.com/postcss/postcss/blob/main/docs/writing-a-plugin.md) with one main difference: events are only executed once.
281+
Each migrator has a default export adhering to the [Stylelint Rule API](https://github.com/postcss/postcss/blob/main/docs/writing-a-plugin.md). A PostCSS AST is passed as the `root` and can be mutated inline, or emit warning/error reports.
282282

283283
Continuing the example, here is what the migration may look like if our goal is to replace the Sass function `hello()` with `world()`.
284284

285285
```ts
286286
// polaris-migrator/src/migrations/replace-sass-function/replace-sass-function.ts
287-
import valueParser from 'postcss-value-parser';
288-
289287
import {
290288
isSassFunction,
291-
StopWalkingFunctionNodes,
292289
createSassMigrator,
290+
StopWalkingFunctionNodes,
293291
} from '../../utilities/sass';
292+
import type {PolarisMigrator} from '../../utilities/sass';
294293

295-
// options can be passed in from cli / config.
296-
export default createSassMigrator('replace-sass-function', (/* options */) => {
297-
return {
298-
Declaration(decl) {
294+
const replaceHelloWorld: PolarisMigrator = (_, {methods}, context) => {
295+
return (root) => {
296+
methods.walkDecls(root, (decl) => {
299297
const parsedValue = valueParser(decl.value);
300-
301298
parsedValue.walk((node) => {
302299
if (isSassFunction('hello', node)) {
303-
node.value = 'world';
300+
if (context.fix) {
301+
node.value = 'world';
302+
} else {
303+
methods.report({
304+
node: decl,
305+
severity: 'error',
306+
message:
307+
'Method hello() is no longer supported. Please migrate to world().',
308+
});
309+
}
310+
304311
return StopWalkingFunctionNodes;
305312
}
306313
});
307-
308-
decl.value = parsedValue.toString();
309-
},
314+
if (context.fix) {
315+
decl.value = parsedValue.toString();
316+
}
317+
});
310318
};
311-
});
319+
};
320+
321+
export default createSassMigrator('replace-hello-world', replaceHelloWorld);
312322
```
313323

314324
A more complete example can be seen in [`replace-spacing-lengths.ts`](https://github.com/Shopify/polaris/blob/main/polaris-migrator/src/migrations/replace-spacing-lengths/replace-spacing-lengths.ts).

polaris-migrator/src/migrations/replace-spacing-lengths/replace-spacing-lengths.ts

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import valueParser from 'postcss-value-parser';
22

3-
import {POLARIS_MIGRATOR_COMMENT} from '../../constants';
43
import {
5-
createInlineComment,
64
getFunctionArgs,
75
isNumericOperator,
86
isSassFunction,
@@ -16,39 +14,43 @@ import {isKeyOf} from '../../utilities/type-guards';
1614

1715
export default createSassMigrator(
1816
'replace-sass-space',
19-
(_, options, context) => {
17+
(_, {methods, options}, context) => {
2018
const namespacedRem = namespace('rem', options);
2119

2220
return (root) => {
2321
root.walkDecls((decl) => {
2422
if (!spaceProps.has(decl.prop)) return;
2523

26-
/**
27-
* A collection of transformable values to migrate (e.g. decl lengths, functions, etc.)
28-
*
29-
* Note: This is evaluated at the end of each visitor execution to determine whether
30-
* or not to replace the declaration or insert a comment.
31-
*/
32-
const targets: {replaced: boolean}[] = [];
3324
let hasNumericOperator = false;
25+
3426
const parsedValue = valueParser(decl.value);
3527

3628
handleSpaceProps();
3729

38-
if (targets.some(({replaced}) => !replaced || hasNumericOperator)) {
39-
decl.before(
40-
createInlineComment(POLARIS_MIGRATOR_COMMENT, {prose: true}),
41-
);
42-
decl.before(
43-
createInlineComment(`${decl.prop}: ${parsedValue.toString()};`),
44-
);
45-
} else if (context.fix) {
30+
if (hasNumericOperator) {
31+
methods.report({
32+
node: decl,
33+
severity: 'warning',
34+
message: 'Numeric operator detected.',
35+
});
36+
}
37+
38+
if (context.fix && !methods.getReportsForNode(decl)) {
4639
decl.value = parsedValue.toString();
40+
} else {
41+
// The "partial fix" case means we want to insert a suggestion if
42+
// there were changes, but not actually change the line of code.
43+
const newValue = parsedValue.toString();
44+
if (newValue !== decl.value) {
45+
methods.report({
46+
node: decl,
47+
severity: 'suggestion',
48+
message: `${decl.prop}: ${parsedValue.toString()}`,
49+
});
50+
}
4751
}
4852

49-
//
50-
// Handlers
51-
//
53+
methods.flushReports();
5254

5355
function handleSpaceProps() {
5456
parsedValue.walk((node) => {
@@ -64,41 +66,71 @@ export default createSassMigrator(
6466

6567
if (!isTransformableLength(dimension)) return;
6668

67-
targets.push({replaced: false});
68-
6969
const valueInPx = toTransformablePx(node.value);
7070

71-
if (!isKeyOf(spaceMap, valueInPx)) return;
71+
if (!isKeyOf(spaceMap, valueInPx)) {
72+
methods.report({
73+
node: decl,
74+
severity: 'error',
75+
message: `Non-tokenizable value '${node.value}'`,
76+
});
77+
return;
78+
}
7279

73-
targets[targets.length - 1]!.replaced = true;
80+
if (context.fix) {
81+
node.value = `var(${spaceMap[valueInPx]})`;
82+
return;
83+
}
7484

75-
node.value = `var(${spaceMap[valueInPx]})`;
85+
methods.report({
86+
node: decl,
87+
severity: 'error',
88+
message: `Prefer var(${spaceMap[valueInPx]}) Polaris token.`,
89+
});
7690
return;
7791
}
7892

7993
if (node.type === 'function') {
8094
if (isSassFunction(namespacedRem, node)) {
81-
targets.push({replaced: false});
82-
8395
const args = getFunctionArgs(node);
8496

85-
if (args.length !== 1) return;
97+
if (args.length !== 1) {
98+
methods.report({
99+
node: decl,
100+
severity: 'error',
101+
message: `Expected 1 argument, got ${args.length}`,
102+
});
103+
return;
104+
}
86105

87106
const valueInPx = toTransformablePx(args[0]);
88107

89-
if (!isKeyOf(spaceMap, valueInPx)) return;
90-
91-
targets[targets.length - 1]!.replaced = true;
92-
93-
node.value = 'var';
94-
node.nodes = [
95-
{
96-
type: 'word',
97-
value: spaceMap[valueInPx],
98-
sourceIndex: node.nodes[0]?.sourceIndex ?? 0,
99-
sourceEndIndex: spaceMap[valueInPx].length,
100-
},
101-
];
108+
if (!isKeyOf(spaceMap, valueInPx)) {
109+
methods.report({
110+
node: decl,
111+
severity: 'error',
112+
message: `Non-tokenizable value '${args[0].trim()}'`,
113+
});
114+
return;
115+
}
116+
117+
if (context.fix) {
118+
node.value = 'var';
119+
node.nodes = [
120+
{
121+
type: 'word',
122+
value: spaceMap[valueInPx],
123+
sourceIndex: node.nodes[0]?.sourceIndex ?? 0,
124+
sourceEndIndex: spaceMap[valueInPx].length,
125+
},
126+
];
127+
return;
128+
}
129+
methods.report({
130+
node: decl,
131+
severity: 'error',
132+
message: `Prefer var(${spaceMap[valueInPx]}) Polaris token.`,
133+
});
102134
}
103135

104136
return StopWalkingFunctionNodes;

polaris-migrator/src/migrations/replace-spacing-lengths/tests/replace-spacing-lengths.output.scss

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
padding: 0;
1616
padding: 1;
1717
padding: 2;
18+
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
19+
// warning: Numeric operator detected.
1820
padding: #{16px + 16px};
1921
padding: layout-width(nav);
2022
padding: 10em;
@@ -23,55 +25,65 @@
2325
padding: var(--p-space-4, 16px);
2426
// Comment
2527
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
26-
// padding: 10px;
28+
// error: Non-tokenizable value '10px'
2729
padding: 10px;
2830
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
29-
// padding: 10rem;
31+
// error: Non-tokenizable value '10rem'
3032
padding: 10rem;
3133
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
32-
// padding: 10px 10px;
34+
// error: Non-tokenizable value '10px'
3335
padding: 10px 10px;
3436
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
37+
// error: Non-tokenizable value '10px'
3538
// padding: var(--p-space-4) 10px;
3639
padding: 16px 10px;
3740
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
38-
// padding: 10rem 10rem;
41+
// error: Non-tokenizable value '10rem'
3942
padding: 10rem 10rem;
4043
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
44+
// error: Non-tokenizable value '10rem'
4145
// padding: var(--p-space-4) 10rem;
4246
padding: 1rem 10rem;
4347
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
44-
// padding: 10px 10rem;
48+
// error: Non-tokenizable value '10px'
49+
// error: Non-tokenizable value '10rem'
4550
padding: 10px 10rem;
4651
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
52+
// error: Non-tokenizable value '10rem'
4753
// padding: var(--p-space-4) 10rem;
4854
padding: 16px 10rem;
4955
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
56+
// error: Non-tokenizable value '10px'
5057
// padding: 10px var(--p-space-4);
5158
padding: 10px 1rem;
5259
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
60+
// error: Non-tokenizable value '-16px'
5361
// padding: var(--p-space-4) -16px;
5462
padding: 16px -16px;
5563
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
64+
// warning: Numeric operator detected.
5665
// padding: var(--p-space-4) + var(--p-space-4);
5766
padding: 16px + 16px;
5867
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
68+
// warning: Numeric operator detected.
5969
// padding: var(--p-space-4) + var(--p-space-4) var(--p-space-4);
6070
padding: 16px + 16px 16px;
6171
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
72+
// warning: Numeric operator detected.
6273
// padding: $var + var(--p-space-4);
6374
padding: $var + 16px;
6475
padding: calc(16px + 16px);
6576
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
77+
// warning: Numeric operator detected.
6678
// padding: var(--p-space-4) + #{16px + 16px};
6779
padding: 16px + #{16px + 16px};
6880
// Skip negative lengths. Need to discuss replacement strategy e.g.
6981
// calc(-1 * var(--p-space-*)) vs var(--p-space--*)
7082
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
71-
// padding: -16px;
83+
// error: Non-tokenizable value '-16px'
7284
padding: -16px;
7385
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
74-
// padding: -10px;
86+
// error: Non-tokenizable value '-10px'
7587
padding: -10px;
7688

7789
// REM FUNCTION
@@ -86,21 +98,26 @@
8698
padding: calc(10rem + var(--p-choice-size, #{rem(10px)}));
8799
// Comment
88100
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
89-
// padding: rem(10px);
101+
// error: Non-tokenizable value '10px'
90102
padding: rem(10px);
91103
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
92-
// padding: rem(10px) rem(10px);
104+
// error: Non-tokenizable value '10px'
93105
padding: rem(10px) rem(10px);
94106
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
107+
// error: Non-tokenizable value '10px'
95108
// padding: var(--p-space-4) rem(10px);
96109
padding: rem(16px) rem(10px);
97110
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
111+
// error: Non-tokenizable value '-16px'
98112
// padding: var(--p-space-4) -16px;
99113
padding: rem(16px) -16px;
100114
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
115+
// warning: Numeric operator detected.
101116
// padding: var(--p-space-4) + var(--p-space-4);
102117
padding: rem(16px) + 16px;
103118
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
119+
// error: Non-tokenizable value '$var \* 16px'
120+
// warning: Numeric operator detected.
104121
// padding: rem($var * var(--p-space-4));
105122
padding: rem($var * 16px);
106123
}

0 commit comments

Comments
 (0)