Skip to content

Commit b03edaa

Browse files
committed
Split apart font declaration migration
1 parent aa7b2e3 commit b03edaa

28 files changed

+563
-1383
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import type {FileInfo, API, Options} from 'jscodeshift';
2+
import postcss, {Plugin} from 'postcss';
3+
import valueParser from 'postcss-value-parser';
4+
5+
import {POLARIS_MIGRATOR_COMMENT} from '../../constants';
6+
import {
7+
createInlineComment,
8+
getFunctionArgs,
9+
isSassFunction,
10+
namespace,
11+
NamespaceOptions,
12+
StopWalkingFunctionNodes,
13+
} from '../../utilities/sass';
14+
import {isKeyOf} from '../../utilities/type-guards';
15+
16+
export default function scssReplaceFontFamily(
17+
fileInfo: FileInfo,
18+
_: API,
19+
options: Options,
20+
) {
21+
return postcss(plugin(options)).process(fileInfo.source, {
22+
syntax: require('postcss-scss'),
23+
}).css;
24+
}
25+
26+
const processed = Symbol('processed');
27+
28+
interface PluginOptions extends Options, NamespaceOptions {}
29+
30+
const plugin = (options: PluginOptions = {}): Plugin => {
31+
const namespacedFontFamily = namespace('font-family', options);
32+
33+
return {
34+
postcssPlugin: 'scss-replace-font-family',
35+
Declaration(decl) {
36+
// @ts-expect-error - Skip if processed so we don't process it again
37+
if (decl[processed]) return;
38+
39+
let needsComment = false;
40+
let needsFix = false;
41+
const parsedValue = valueParser(decl.value);
42+
43+
parsedValue.walk((node) => {
44+
if (isSassFunction(namespacedFontFamily, node)) {
45+
const args = getFunctionArgs(node);
46+
47+
// `font-family()` args reference:
48+
// https://github.com/shopify/polaris/blob/2b14c0b60097f75d21df7eaa744dfaf84f8f53f7/documentation/guides/legacy-polaris-v8-public-api.scss#L945
49+
const family = args[0] ?? 'base';
50+
51+
if (!isKeyOf(fontFamilyMap, family)) {
52+
needsComment = true;
53+
return StopWalkingFunctionNodes;
54+
}
55+
56+
const fontFamilyCustomProperty = fontFamilyMap[family];
57+
58+
needsFix = true;
59+
node.value = 'var';
60+
node.nodes = [
61+
{
62+
type: 'word',
63+
value: fontFamilyCustomProperty,
64+
sourceIndex: node.nodes[0]?.sourceIndex ?? 0,
65+
sourceEndIndex: fontFamilyCustomProperty.length,
66+
},
67+
];
68+
69+
return StopWalkingFunctionNodes;
70+
}
71+
});
72+
73+
if (needsComment) {
74+
decl.before(
75+
createInlineComment(POLARIS_MIGRATOR_COMMENT, {prose: true}),
76+
);
77+
decl.before(
78+
createInlineComment(`${decl.prop}: ${parsedValue.toString()};`),
79+
);
80+
}
81+
82+
if (needsFix) {
83+
decl.value = parsedValue.toString();
84+
}
85+
86+
// @ts-expect-error - Mark the declaration as processed
87+
decl[processed] = true;
88+
},
89+
};
90+
};
91+
92+
const fontFamilyMap = {
93+
base: '--p-font-family-sans',
94+
monospace: '--p-font-family-mono',
95+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.font-family {
2+
font-family: sans-serif;
3+
font-family: font-family();
4+
font-family: font-family(base);
5+
font-family: font-family(monospace);
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.font-family {
2+
font-family: sans-serif;
3+
font-family: var(--p-font-family-sans);
4+
font-family: var(--p-font-family-sans);
5+
font-family: var(--p-font-family-mono);
6+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {check} from '../../../utilities/testUtils';
2+
3+
const migration = 'scss-replace-font-family';
4+
const fixtures = ['scss-replace-font-family', 'with-namespace'];
5+
6+
for (const fixture of fixtures) {
7+
check(__dirname, {
8+
fixture,
9+
migration,
10+
extension: 'scss',
11+
options: {
12+
namespace: fixture.includes('with-namespace')
13+
? 'legacy-polaris-v8'
14+
: undefined,
15+
},
16+
});
17+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@use 'global-styles/legacy-polaris-v8';
2+
3+
.font-family {
4+
font-family: sans-serif;
5+
font-family: legacy-polaris-v8.font-family();
6+
font-family: legacy-polaris-v8.font-family(base);
7+
font-family: legacy-polaris-v8.font-family(monospace);
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@use 'global-styles/legacy-polaris-v8';
2+
3+
.font-family {
4+
font-family: sans-serif;
5+
font-family: var(--p-font-family-sans);
6+
font-family: var(--p-font-family-sans);
7+
font-family: var(--p-font-family-mono);
8+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/* eslint-disable line-comment-position */
2+
3+
import type {FileInfo, API, Options} from 'jscodeshift';
4+
import postcss, {Plugin} from 'postcss';
5+
import valueParser from 'postcss-value-parser';
6+
7+
import {POLARIS_MIGRATOR_COMMENT} from '../../constants';
8+
import {
9+
createInlineComment,
10+
getFunctionArgs,
11+
isSassFunction,
12+
namespace,
13+
NamespaceOptions,
14+
StopWalkingFunctionNodes,
15+
} from '../../utilities/sass';
16+
import {isKeyOf} from '../../utilities/type-guards';
17+
18+
export default function scssReplaceFont(
19+
fileInfo: FileInfo,
20+
_: API,
21+
options: Options,
22+
) {
23+
return postcss(plugin(options)).process(fileInfo.source, {
24+
syntax: require('postcss-scss'),
25+
}).css;
26+
}
27+
28+
const processed = Symbol('processed');
29+
30+
interface PluginOptions extends Options, NamespaceOptions {}
31+
32+
const plugin = (options: PluginOptions = {}): Plugin => {
33+
const namespacedFontSize = namespace('font-size', options);
34+
35+
return {
36+
postcssPlugin: 'scss-replace-font',
37+
Declaration(decl) {
38+
// @ts-expect-error - Skip if processed so we don't process it again
39+
if (decl[processed]) return;
40+
41+
let needsFix = false;
42+
let needsComment = false;
43+
const parsedValue = valueParser(decl.value);
44+
45+
parsedValue.walk((node) => {
46+
if (isSassFunction(namespacedFontSize, node)) {
47+
const args = getFunctionArgs(node);
48+
49+
// `font-size()` args reference:
50+
// https://github.com/Shopify/polaris/blob/1738f17c739e06dcde4653a9783ca367e38b4e32/documentation/guides/legacy-polaris-v8-public-api.scss#L977
51+
const styleArg = args[0];
52+
const variantArg = args[1] ?? 'base';
53+
54+
if (
55+
!(
56+
isKeyOf(fontSizeMap, styleArg) &&
57+
isKeyOf(fontSizeMap[styleArg], variantArg)
58+
)
59+
) {
60+
needsComment = true;
61+
return StopWalkingFunctionNodes;
62+
}
63+
64+
needsFix = true;
65+
const fontSizeVariant = fontSizeMap[styleArg][variantArg];
66+
67+
if (fontSizeVariant.startsWith('--')) {
68+
node.value = 'var';
69+
node.nodes = [
70+
{
71+
type: 'word',
72+
value: fontSizeVariant,
73+
sourceIndex: node.nodes[0]?.sourceIndex ?? 0,
74+
sourceEndIndex: fontSizeVariant.length,
75+
},
76+
];
77+
} else {
78+
// @ts-expect-error: We are intentionally changing the node type
79+
node.type = 'word';
80+
node.value = fontSizeVariant;
81+
}
82+
return StopWalkingFunctionNodes;
83+
}
84+
});
85+
86+
if (needsComment) {
87+
decl.before(
88+
createInlineComment(POLARIS_MIGRATOR_COMMENT, {prose: true}),
89+
);
90+
decl.before(
91+
createInlineComment(`${decl.prop}: ${parsedValue.toString()};`),
92+
);
93+
}
94+
95+
if (needsFix) {
96+
decl.value = parsedValue.toString();
97+
}
98+
99+
// @ts-expect-error - Mark the declaration as processed
100+
decl[processed] = true;
101+
},
102+
};
103+
};
104+
105+
const fontSizeMap = {
106+
caption: {
107+
base: '0.8125rem', // 13px
108+
'large-screen': '--p-font-size-75',
109+
},
110+
heading: {
111+
base: '1.0625rem', // 17px
112+
'large-screen': '--p-font-size-200',
113+
},
114+
subheading: {
115+
base: '0.8125rem', // 13px
116+
'large-screen': '--p-font-size-75',
117+
},
118+
input: {
119+
base: '--p-font-size-200',
120+
'large-screen': '--p-font-size-100',
121+
},
122+
body: {
123+
base: '0.9375rem', // 15px
124+
'large-screen': '--p-font-size-100',
125+
},
126+
button: {
127+
base: '0.9375rem', // 15px
128+
'large-screen': '--p-font-size-100',
129+
},
130+
'button-large': {
131+
base: '1.0625rem', // 17px
132+
'large-screen': '--p-font-size-200',
133+
},
134+
'display-x-large': {
135+
base: '1.6875rem', // 27px
136+
'large-screen': '2.625rem', // 42px
137+
},
138+
'display-large': {
139+
base: '--p-font-size-400',
140+
'large-screen': '--p-font-size-500',
141+
},
142+
'display-medium': {
143+
base: '1.3125rem', // 21px
144+
'large-screen': '1.625rem', // 26px
145+
},
146+
'display-small': {
147+
base: '--p-font-size-200',
148+
'large-screen': '--p-font-size-300',
149+
},
150+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.font-size {
2+
font-size: font-size(caption);
3+
font-size: font-size(caption, base);
4+
font-size: font-size(caption, large-screen);
5+
// Comment
6+
font-size: font-size($invalid);
7+
font-size: font-size(caption, $invalid);
8+
font-size: font-size();
9+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.font-size {
2+
font-size: 0.8125rem;
3+
font-size: 0.8125rem;
4+
font-size: var(--p-font-size-75);
5+
// Comment
6+
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
7+
// font-size: font-size($invalid);
8+
font-size: font-size($invalid);
9+
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
10+
// font-size: font-size(caption, $invalid);
11+
font-size: font-size(caption, $invalid);
12+
// polaris-migrator: Unable to migrate the following expression. Please upgrade manually.
13+
// font-size: font-size();
14+
font-size: font-size();
15+
}

polaris-migrator/src/migrations/scss-replace-font/tests/scss-replace-font.test.ts renamed to polaris-migrator/src/migrations/scss-replace-font-size/tests/scss-replace-font-size.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {check} from '../../../utilities/testUtils';
22

3-
const migration = 'scss-replace-font';
4-
const fixtures = ['scss-replace-font', 'with-namespace'];
3+
const migration = 'scss-replace-font-size';
4+
const fixtures = ['scss-replace-font-size', 'with-namespace'];
55

66
for (const fixture of fixtures) {
77
check(__dirname, {

0 commit comments

Comments
 (0)