@atlaskit/codemod-cli 0.13.4 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/main.js +1 -1
- package/dist/cjs/presets/css-to-design-tokens/css-to-design-tokens.js +22 -11
- package/dist/cjs/presets/theme-to-design-tokens/theme-to-design-tokens.js +348 -104
- package/dist/cjs/presets/theme-to-design-tokens/utils/ast.js +1 -1
- package/dist/cjs/presets/theme-to-design-tokens/utils/color.js +15 -11
- package/dist/cjs/presets/theme-to-design-tokens/utils/css-utils.js +38 -0
- package/dist/cjs/presets/theme-to-design-tokens/utils/string-utils.js +26 -0
- package/dist/es2019/presets/css-to-design-tokens/css-to-design-tokens.js +18 -8
- package/dist/es2019/presets/theme-to-design-tokens/theme-to-design-tokens.js +191 -32
- package/dist/es2019/presets/theme-to-design-tokens/utils/ast.js +1 -1
- package/dist/es2019/presets/theme-to-design-tokens/utils/color.js +12 -10
- package/dist/es2019/presets/theme-to-design-tokens/utils/css-utils.js +31 -0
- package/dist/es2019/presets/theme-to-design-tokens/utils/string-utils.js +13 -0
- package/dist/esm/main.js +1 -1
- package/dist/esm/presets/css-to-design-tokens/css-to-design-tokens.js +22 -12
- package/dist/esm/presets/theme-to-design-tokens/theme-to-design-tokens.js +346 -100
- package/dist/esm/presets/theme-to-design-tokens/utils/ast.js +1 -1
- package/dist/esm/presets/theme-to-design-tokens/utils/color.js +12 -10
- package/dist/esm/presets/theme-to-design-tokens/utils/css-utils.js +31 -0
- package/dist/esm/presets/theme-to-design-tokens/utils/string-utils.js +17 -0
- package/dist/types/presets/css-to-design-tokens/css-to-design-tokens.d.ts +2 -1
- package/dist/types/presets/theme-to-design-tokens/theme-to-design-tokens.d.ts +1 -1
- package/dist/types/presets/theme-to-design-tokens/utils/ast.d.ts +1 -1
- package/dist/types/presets/theme-to-design-tokens/utils/color.d.ts +2 -1
- package/dist/types/presets/theme-to-design-tokens/utils/css-utils.d.ts +2 -0
- package/dist/types/presets/theme-to-design-tokens/utils/string-utils.d.ts +3 -0
- package/dist/types-ts4.5/presets/css-to-design-tokens/css-to-design-tokens.d.ts +2 -1
- package/dist/types-ts4.5/presets/theme-to-design-tokens/theme-to-design-tokens.d.ts +1 -1
- package/dist/types-ts4.5/presets/theme-to-design-tokens/utils/ast.d.ts +1 -1
- package/dist/types-ts4.5/presets/theme-to-design-tokens/utils/color.d.ts +2 -1
- package/dist/types-ts4.5/presets/theme-to-design-tokens/utils/css-utils.d.ts +2 -0
- package/dist/types-ts4.5/presets/theme-to-design-tokens/utils/string-utils.d.ts +6 -0
- package/package.json +2 -2
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.includesHardCodedColor = void 0;
|
|
7
|
+
exports.isBoldColor = isBoldColor;
|
|
8
|
+
exports.isLegacyNamedColor = exports.isLegacyColor = exports.isHardCodedColor = void 0;
|
|
7
9
|
var _legacyColors = require("./legacy-colors");
|
|
8
10
|
var _namedColors = require("./named-colors");
|
|
9
11
|
var isLegacyColor = function isLegacyColor(value) {
|
|
@@ -14,9 +16,10 @@ var isLegacyNamedColor = function isLegacyNamedColor(value) {
|
|
|
14
16
|
return _legacyColors.legacyColorMixins.includes(value);
|
|
15
17
|
};
|
|
16
18
|
exports.isLegacyNamedColor = isLegacyNamedColor;
|
|
19
|
+
var colorRegexp = /#(?:[a-f0-9]{3}|[a-f0-9]{6}|[a-f0-9]{8})\b|(?:rgb|rgba|hsl|hsla|lch|lab|color)\([^\)]*\)/;
|
|
17
20
|
var includesHardCodedColor = function includesHardCodedColor(raw) {
|
|
18
21
|
var value = raw.toLowerCase();
|
|
19
|
-
if (
|
|
22
|
+
if (colorRegexp.exec(value)) {
|
|
20
23
|
return true;
|
|
21
24
|
}
|
|
22
25
|
for (var i = 0; i < _namedColors.namedColors.length; i++) {
|
|
@@ -27,18 +30,19 @@ var includesHardCodedColor = function includesHardCodedColor(raw) {
|
|
|
27
30
|
return false;
|
|
28
31
|
};
|
|
29
32
|
exports.includesHardCodedColor = includesHardCodedColor;
|
|
30
|
-
var isHardCodedColor = function isHardCodedColor(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (value.startsWith('rgb(') || value.startsWith('rgba(') || value.startsWith('hsl(') || value.startsWith('hsla(') || value.startsWith('lch(') || value.startsWith('lab(') || value.startsWith('color(')) {
|
|
33
|
+
var isHardCodedColor = function isHardCodedColor(raw) {
|
|
34
|
+
var value = raw.toLowerCase();
|
|
35
|
+
if (_namedColors.namedColors.includes(value)) {
|
|
35
36
|
return true;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
value.length === 4 || value.length === 7 || value.length === 9)) {
|
|
38
|
+
var match = value.toLowerCase().match(colorRegexp);
|
|
39
|
+
if (match && match[0] === value) {
|
|
40
40
|
return true;
|
|
41
41
|
}
|
|
42
42
|
return false;
|
|
43
43
|
};
|
|
44
|
-
exports.isHardCodedColor = isHardCodedColor;
|
|
44
|
+
exports.isHardCodedColor = isHardCodedColor;
|
|
45
|
+
function isBoldColor(color) {
|
|
46
|
+
var number = parseInt(color.replace(/^./, ''), 10);
|
|
47
|
+
return number > 300;
|
|
48
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.containsReplaceableCSSDeclarations = containsReplaceableCSSDeclarations;
|
|
7
|
+
exports.findEndIndexOfCSSExpression = findEndIndexOfCSSExpression;
|
|
8
|
+
var _cssToDesignTokens = require("../../css-to-design-tokens/css-to-design-tokens");
|
|
9
|
+
function containsReplaceableCSSDeclarations(input) {
|
|
10
|
+
var cssPattern = /(\S+)\s*:/g;
|
|
11
|
+
var match;
|
|
12
|
+
while ((match = cssPattern.exec(input)) !== null) {
|
|
13
|
+
if ((0, _cssToDesignTokens.isColorProperty)(match[1])) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function findEndIndexOfCSSExpression(text, isAtEndOfInput) {
|
|
20
|
+
// CSS expression can end *on* a semicolon or *before* a brace. In either
|
|
21
|
+
// case we treat the remaining part of the value to cover one character
|
|
22
|
+
// before that symbol.
|
|
23
|
+
var semicolonIndex = text.indexOf(';');
|
|
24
|
+
var braceIndex = text.indexOf('}');
|
|
25
|
+
if (semicolonIndex === -1 && braceIndex === -1) {
|
|
26
|
+
if (isAtEndOfInput) {
|
|
27
|
+
return text.length;
|
|
28
|
+
} else {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
} else if (semicolonIndex === -1) {
|
|
32
|
+
return braceIndex - 1;
|
|
33
|
+
} else if (braceIndex === -1) {
|
|
34
|
+
return semicolonIndex - 1;
|
|
35
|
+
} else {
|
|
36
|
+
return Math.min(semicolonIndex, braceIndex) - 1;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.findFirstNonspaceIndexAfter = findFirstNonspaceIndexAfter;
|
|
7
|
+
exports.kebabize = void 0;
|
|
8
|
+
exports.splitAtIndex = splitAtIndex;
|
|
9
|
+
var kebabize = function kebabize(str) {
|
|
10
|
+
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, function ($, ofs) {
|
|
11
|
+
return (ofs ? '-' : '') + $.toLowerCase();
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
exports.kebabize = kebabize;
|
|
15
|
+
function findFirstNonspaceIndexAfter(text, index) {
|
|
16
|
+
var rest = text.slice(index + 1);
|
|
17
|
+
var indexInRest = rest.search(/\S/);
|
|
18
|
+
if (indexInRest === -1) {
|
|
19
|
+
return text.length;
|
|
20
|
+
} else {
|
|
21
|
+
return index + 1 + indexInRest;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function splitAtIndex(text, index) {
|
|
25
|
+
return [text.slice(0, index), text.slice(index)];
|
|
26
|
+
}
|
|
@@ -6,6 +6,10 @@ import designTokens from '@atlaskit/tokens/token-names';
|
|
|
6
6
|
import Search from '../theme-to-design-tokens/utils/fuzzy-search';
|
|
7
7
|
import { knownVariables, knownColors, knownRawColors } from './utils/legacy-colors';
|
|
8
8
|
import { cleanMeta } from './utils/meta';
|
|
9
|
+
const options = {
|
|
10
|
+
syntax: lessSyntax,
|
|
11
|
+
from: undefined
|
|
12
|
+
};
|
|
9
13
|
const tokens = rawTokens.filter(t => t.attributes.state === 'active').map(t => t.name.replace(/\.\[default\]/g, '')).filter(t => !t.includes('UNSAFE') && !t.includes('interaction'));
|
|
10
14
|
const search = Search(tokens, false);
|
|
11
15
|
function isRule(node) {
|
|
@@ -27,8 +31,8 @@ function stripVar(prop) {
|
|
|
27
31
|
function stripLessVar(prop) {
|
|
28
32
|
return prop.substring(1);
|
|
29
33
|
}
|
|
30
|
-
function isColorProperty(prop) {
|
|
31
|
-
return prop === 'color' || prop === 'background' || prop === 'background-color' || prop === 'box-shadow' || prop === 'border' || prop === 'border-left' || prop === 'border-right' || prop === 'border-top' || prop === 'border-bottom' || prop === 'border-color';
|
|
34
|
+
export function isColorProperty(prop) {
|
|
35
|
+
return prop === 'color' || prop === 'background' || prop === 'background-color' || prop === 'box-shadow' || prop === 'border' || prop === 'border-left' || prop === 'border-right' || prop === 'border-top' || prop === 'border-bottom' || prop === 'border-color' || prop === 'outline';
|
|
32
36
|
}
|
|
33
37
|
function getDeclarationMeta(decl) {
|
|
34
38
|
if (decl.prop === 'color') {
|
|
@@ -77,6 +81,9 @@ const plugin = () => {
|
|
|
77
81
|
if (!isColorProperty(decl.prop)) {
|
|
78
82
|
return;
|
|
79
83
|
}
|
|
84
|
+
if (decl.value === 'none') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
80
87
|
const searchTerms = [getDeclarationMeta(decl), ...getParentSelectors(decl).split(/\-|\.|\,|\ |\:|\&/).filter(el => !!el)];
|
|
81
88
|
let match;
|
|
82
89
|
const cssVarRe = /var\([^\)]+\)/g;
|
|
@@ -95,10 +102,10 @@ const plugin = () => {
|
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
// Less variables
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
105
|
+
const lessVarMatch = decl.value.match(lessVarRe);
|
|
106
|
+
if (lessVarMatch) {
|
|
100
107
|
var _getMetaFromCssVar2;
|
|
101
|
-
match =
|
|
108
|
+
match = lessVarMatch[0];
|
|
102
109
|
searchTerms.push(...((_getMetaFromCssVar2 = getMetaFromCssVar(`--${stripLessVar(match)}`)) !== null && _getMetaFromCssVar2 !== void 0 ? _getMetaFromCssVar2 : []));
|
|
103
110
|
}
|
|
104
111
|
|
|
@@ -137,7 +144,10 @@ const plugin = () => {
|
|
|
137
144
|
};
|
|
138
145
|
};
|
|
139
146
|
export default async function transformer(file) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
const processor = postcss([plugin()]);
|
|
148
|
+
const src = typeof file === 'string' ? file : file.source;
|
|
149
|
+
const {
|
|
150
|
+
css
|
|
151
|
+
} = await processor.process(src, options);
|
|
152
|
+
return css;
|
|
143
153
|
}
|
|
@@ -3,15 +3,13 @@
|
|
|
3
3
|
import { isDecendantOfType, hasImportDeclaration } from '@codeshift/utils';
|
|
4
4
|
import { isDecendantOfToken, isParentOfToken } from './utils/ast';
|
|
5
5
|
import { cleanMeta, getMetaFromAncestors } from './utils/ast-meta';
|
|
6
|
-
import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor } from './utils/color';
|
|
6
|
+
import { includesHardCodedColor, isHardCodedColor, isLegacyColor, isLegacyNamedColor, isBoldColor } from './utils/color';
|
|
7
7
|
import Search from './utils/fuzzy-search';
|
|
8
8
|
import { legacyColorMetaMap } from './utils/legacy-colors';
|
|
9
9
|
import { tokens } from './utils/tokens';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return number > 300;
|
|
14
|
-
}
|
|
10
|
+
import { kebabize, findFirstNonspaceIndexAfter, splitAtIndex } from './utils/string-utils';
|
|
11
|
+
import { containsReplaceableCSSDeclarations, findEndIndexOfCSSExpression } from './utils/css-utils';
|
|
12
|
+
import CSSTransformer from '../css-to-design-tokens/css-to-design-tokens';
|
|
15
13
|
function insertTokenImport(j, source) {
|
|
16
14
|
if (hasImportDeclaration(j, source, '@atlaskit/tokens')) {
|
|
17
15
|
return;
|
|
@@ -23,6 +21,23 @@ function buildToken(j, tokenId, node) {
|
|
|
23
21
|
const callExpr = j.callExpression(j.identifier('token'), [j.stringLiteral(tokenId), node].filter(Boolean));
|
|
24
22
|
return callExpr;
|
|
25
23
|
}
|
|
24
|
+
|
|
25
|
+
// Wrap over the j.templateElement builder to provide a more convenient API.
|
|
26
|
+
function buildTemplateElement(j, text, options = {
|
|
27
|
+
tail: false,
|
|
28
|
+
fromNode: null
|
|
29
|
+
}) {
|
|
30
|
+
let tail;
|
|
31
|
+
if (options.fromNode) {
|
|
32
|
+
tail = options.fromNode.tail;
|
|
33
|
+
} else {
|
|
34
|
+
tail = !!options.tail;
|
|
35
|
+
}
|
|
36
|
+
return j.templateElement({
|
|
37
|
+
raw: text,
|
|
38
|
+
cooked: null
|
|
39
|
+
}, tail);
|
|
40
|
+
}
|
|
26
41
|
function getColorFromIdentifier(expression) {
|
|
27
42
|
let value = '';
|
|
28
43
|
if (expression.type === 'Identifier') {
|
|
@@ -126,10 +141,20 @@ function getTokenFromNode(j, path, value, propertyName) {
|
|
|
126
141
|
}
|
|
127
142
|
function parseCSSPropertyName(cssString) {
|
|
128
143
|
const lastColonIndex = cssString.lastIndexOf(':');
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
if (lastColonIndex === -1) {
|
|
145
|
+
return {
|
|
146
|
+
colonIndex: null,
|
|
147
|
+
cssPropertyName: null
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const propertyNameEndIndex = Math.max(cssString.lastIndexOf(';', lastColonIndex), cssString.lastIndexOf(' ', lastColonIndex), -1);
|
|
151
|
+
const startIndex = propertyNameEndIndex + 1;
|
|
152
|
+
return {
|
|
153
|
+
cssPropertyName: cssString.slice(startIndex, lastColonIndex).trim(),
|
|
154
|
+
colonIndex: lastColonIndex
|
|
155
|
+
};
|
|
131
156
|
}
|
|
132
|
-
export default function transformer(file, api, debug = false) {
|
|
157
|
+
export default async function transformer(file, api, debug = false) {
|
|
133
158
|
const j = api.jscodeshift;
|
|
134
159
|
const source = j(file.source);
|
|
135
160
|
let transformed = false;
|
|
@@ -169,29 +194,6 @@ export default function transformer(file, api, debug = false) {
|
|
|
169
194
|
transformed = true;
|
|
170
195
|
});
|
|
171
196
|
|
|
172
|
-
// Template literals
|
|
173
|
-
source.find(j.TemplateLiteral).forEach(path => {
|
|
174
|
-
function replaceTemplateLiteralExpressions(j, expression, index) {
|
|
175
|
-
if (isDecendantOfToken(j, expression)) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (index >= path.value.quasis.length) {
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
const quasi = path.value.quasis[index];
|
|
182
|
-
const value = getColorFromIdentifier(expression.value);
|
|
183
|
-
if (!value || !includesHardCodedColor(value) && !isHardCodedColor(value) && !isLegacyColor(value) && !isLegacyNamedColor(value)) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const tokenId = getTokenFromNode(j, expression, value, parseCSSPropertyName(quasi.value.cooked || ''));
|
|
187
|
-
insertTokenImport(j, source);
|
|
188
|
-
expression.replace(buildToken(j, tokenId, expression.value));
|
|
189
|
-
}
|
|
190
|
-
j(path).find(j.Identifier).filter(expression => !isDecendantOfType(j, expression, j.MemberExpression)).forEach((expression, i) => replaceTemplateLiteralExpressions(j, expression, i));
|
|
191
|
-
j(path).find(j.MemberExpression).forEach((expression, i) => replaceTemplateLiteralExpressions(j, expression, i));
|
|
192
|
-
transformed = true;
|
|
193
|
-
});
|
|
194
|
-
|
|
195
197
|
// JSX props
|
|
196
198
|
source.find(j.JSXAttribute).forEach(path => {
|
|
197
199
|
var _path$value, _path$value$value;
|
|
@@ -214,5 +216,162 @@ export default function transformer(file, api, debug = false) {
|
|
|
214
216
|
});
|
|
215
217
|
transformed = true;
|
|
216
218
|
});
|
|
219
|
+
|
|
220
|
+
// Strings
|
|
221
|
+
source.find(j.StringLiteral).forEach(path => {
|
|
222
|
+
j(path).filter(expression => !isDecendantOfType(j, expression, j.ObjectExpression)).forEach(path => {
|
|
223
|
+
const value = path.value.value;
|
|
224
|
+
if (replaceStringLiteralIfItConsistsOnlyOfColor(j, path, value)) {
|
|
225
|
+
transformed = true;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
const templateLiteralPaths = source.find(j.TemplateLiteral).paths();
|
|
230
|
+
for (const path of templateLiteralPaths) {
|
|
231
|
+
// Background: a 'type: TemplateLiteral' Node has quasis and expressions
|
|
232
|
+
// (see ast-types/src/gen/namedTypes.ts), and invariant holds that
|
|
233
|
+
// quasis.length === expression.length + 1.
|
|
234
|
+
//
|
|
235
|
+
// eg `${foo}bar` has quasis [Node(''), Node('bar')] and expressions
|
|
236
|
+
// [Node('foo')]. Each quasi has type: 'TemplateElement'; expressions are
|
|
237
|
+
// probably safe to treat as subtypes of Expression, though ast-types
|
|
238
|
+
// codebase has a more involved definition.
|
|
239
|
+
if (path.value.expressions.length === 0) {
|
|
240
|
+
// A single-quasi (equivalently, no-expression) template literal is
|
|
241
|
+
// basically just a string literal, possibly multi-line. We handle the
|
|
242
|
+
// simple `#ababab` case here, and the multi-line case after.
|
|
243
|
+
const text = path.value.quasis[0].value.raw;
|
|
244
|
+
if (replaceStringLiteralIfItConsistsOnlyOfColor(j, path, text)) {
|
|
245
|
+
transformed = true;
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
j(path).find(j.Expression).filter(expressionPath => {
|
|
249
|
+
// jscodeshift walks over the whole tree; we are interested only in
|
|
250
|
+
// the direct children: i.e. top-level expressions appearing in ${}.
|
|
251
|
+
return expressionPath.parent === path;
|
|
252
|
+
}).forEach((expressionPath, expressionIndex) => {
|
|
253
|
+
if (replaceTemplateLiteralExpression(j, path, expressionPath, expressionIndex)) {
|
|
254
|
+
transformed = true;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// No matter if we have one big quasi or many small chunks between
|
|
260
|
+
// expressions (which potentially have been transformed), try to pass them
|
|
261
|
+
// through the CSS transformer; it's robust enough to understand malformed
|
|
262
|
+
// CSS that would result if we split e.g. this template:
|
|
263
|
+
//
|
|
264
|
+
// `${gridSize}px; color: red;`, giving `px; color: red;` as input; or
|
|
265
|
+
// `@media ${mobile} { color: red }`, giving `{ color: red }`.
|
|
266
|
+
const quasiPaths = j(path).find(j.TemplateElement).filter(quasiPath => {
|
|
267
|
+
return quasiPath.parent === path;
|
|
268
|
+
}).paths();
|
|
269
|
+
for (const quasiPath of quasiPaths) {
|
|
270
|
+
const text = quasiPath.value.value.raw;
|
|
271
|
+
if (includesHardCodedColor(text) && containsReplaceableCSSDeclarations(text)) {
|
|
272
|
+
const newCSS = await CSSTransformer(text);
|
|
273
|
+
j(quasiPath).replaceWith(buildTemplateElement(j, newCSS));
|
|
274
|
+
transformed = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function replaceStringLiteralIfItConsistsOnlyOfColor(j, path, value) {
|
|
279
|
+
if (isDecendantOfToken(j, path)) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (isHardCodedColor(value) && !isLegacyColor(value) && !isLegacyNamedColor(value)) {
|
|
283
|
+
const parent = path.parent.value;
|
|
284
|
+
let key = '';
|
|
285
|
+
if (parent.type === 'VariableDeclarator') {
|
|
286
|
+
key = parent.id.name;
|
|
287
|
+
}
|
|
288
|
+
const tokenId = getTokenFromNode(j, path, value, key);
|
|
289
|
+
insertTokenImport(j, source);
|
|
290
|
+
j(path).replaceWith(buildToken(j, tokenId, path.value));
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function replaceTemplateLiteralExpression(j, mainPath, expressionPath, expressionIndex) {
|
|
296
|
+
const expression = expressionPath.value;
|
|
297
|
+
if (!(expression.type === 'MemberExpression' || expression.type === 'Identifier')) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
if (isDecendantOfToken(j, expressionPath)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
const value = getColorFromIdentifier(expression);
|
|
304
|
+
if (!value || !includesHardCodedColor(value) && !isHardCodedColor(value) && !isLegacyColor(value) && !isLegacyNamedColor(value)) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
const precedingQuasi = mainPath.value.quasis[expressionIndex];
|
|
308
|
+
const precedingQuasiText = precedingQuasi.value.raw;
|
|
309
|
+
const {
|
|
310
|
+
cssPropertyName,
|
|
311
|
+
colonIndex
|
|
312
|
+
} = parseCSSPropertyName(precedingQuasiText);
|
|
313
|
+
if (!cssPropertyName) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
const tokenId = getTokenFromNode(j, expressionPath, value, cssPropertyName);
|
|
317
|
+
insertTokenImport(j, source);
|
|
318
|
+
const newQuasis = [...mainPath.value.quasis];
|
|
319
|
+
const newExpressions = [...mainPath.value.expressions];
|
|
320
|
+
if (cssPropertyName !== 'box-shadow') {
|
|
321
|
+
const tokenExpression = buildToken(j, tokenId, expressionPath.value);
|
|
322
|
+
newExpressions[expressionIndex] = tokenExpression;
|
|
323
|
+
} else {
|
|
324
|
+
// box-shadow is a multi-part property where the color can appear at any
|
|
325
|
+
// part position (even though the standard suggests that color comes
|
|
326
|
+
// last, browsers' CSS parsers are more lax). If we get here, then the
|
|
327
|
+
// color part is replaceable. Textually, it's something like:
|
|
328
|
+
//
|
|
329
|
+
// <rules before>; box-shadow: 0 1px ${colors.N50} 2rem; <rules after>
|
|
330
|
+
//
|
|
331
|
+
// the fallback value will be multipart, i.e. the token call is
|
|
332
|
+
//
|
|
333
|
+
// token(<replacedValue>, `0 1px ${colors.N50} 2rem`)
|
|
334
|
+
//
|
|
335
|
+
// and it's wrapped in a substitution like this:
|
|
336
|
+
//
|
|
337
|
+
// <rules before>; box-shadow: ${token(<...>)}; <rules after>
|
|
338
|
+
//
|
|
339
|
+
// We stich the fallback from the last part of preceding quasi (after
|
|
340
|
+
// colon) and the first part of the following quasi (before ';' or '}').
|
|
341
|
+
//
|
|
342
|
+
// If multiple box-shadows are comma-separated but only one of them has a
|
|
343
|
+
// replaceable color and others are hard-coded, this logic would still
|
|
344
|
+
// work. When multiple shadows have expressions, it's unfortunately not
|
|
345
|
+
// possible to proceed because we cannot find where the value ends from a
|
|
346
|
+
// single following quasi.
|
|
347
|
+
const valueStartIndex = findFirstNonspaceIndexAfter(precedingQuasiText, colonIndex);
|
|
348
|
+
const [newPrecedingQuasiText, partialValueBeginning] = splitAtIndex(precedingQuasiText, valueStartIndex);
|
|
349
|
+
const followingQuasi = mainPath.value.quasis[expressionIndex + 1];
|
|
350
|
+
const followingQuasiText = followingQuasi.value.raw;
|
|
351
|
+
const valueEndIndex = findEndIndexOfCSSExpression(followingQuasiText, followingQuasi.tail);
|
|
352
|
+
if (!valueEndIndex) {
|
|
353
|
+
console.warn('cannot find end of box-shadow value, please check manually');
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
const [partialValueEnd, newFollowingQuasiText] = splitAtIndex(followingQuasiText, valueEndIndex + 1);
|
|
357
|
+
const internalQuasis = [buildTemplateElement(j, partialValueBeginning, {
|
|
358
|
+
tail: false
|
|
359
|
+
}), buildTemplateElement(j, partialValueEnd, {
|
|
360
|
+
tail: true
|
|
361
|
+
})];
|
|
362
|
+
const internalExpressions = [expressionPath.value];
|
|
363
|
+
const newFallback = j.templateLiteral(internalQuasis, internalExpressions);
|
|
364
|
+
const newExpression = buildToken(j, tokenId, newFallback);
|
|
365
|
+
newQuasis[expressionIndex] = buildTemplateElement(j, newPrecedingQuasiText, {
|
|
366
|
+
fromNode: newQuasis[expressionIndex]
|
|
367
|
+
});
|
|
368
|
+
newExpressions[expressionIndex] = newExpression;
|
|
369
|
+
newQuasis[expressionIndex + 1] = buildTemplateElement(j, newFollowingQuasiText, {
|
|
370
|
+
fromNode: newQuasis[expressionIndex]
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
mainPath.replace(j.templateLiteral(newQuasis, newExpressions));
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
217
376
|
return transformed ? source.toSource() : file.source;
|
|
218
377
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isDecendantOfType } from '@codeshift/utils';
|
|
2
2
|
export function isDecendantOfToken(j, path) {
|
|
3
|
-
if (path.type === 'CallExpression' && path.callee.type === 'Identifier' && path.callee.name === 'token') {
|
|
3
|
+
if ('type' in path && path.type === 'CallExpression' && path.callee.type === 'Identifier' && path.callee.name === 'token') {
|
|
4
4
|
return true;
|
|
5
5
|
}
|
|
6
6
|
return j(path).closest(j.CallExpression, {
|
|
@@ -2,9 +2,10 @@ import { legacyColorMixins, legacyColors } from './legacy-colors';
|
|
|
2
2
|
import { namedColors } from './named-colors';
|
|
3
3
|
export const isLegacyColor = value => legacyColors.includes(value);
|
|
4
4
|
export const isLegacyNamedColor = value => legacyColorMixins.includes(value);
|
|
5
|
+
const colorRegexp = /#(?:[a-f0-9]{3}|[a-f0-9]{6}|[a-f0-9]{8})\b|(?:rgb|rgba|hsl|hsla|lch|lab|color)\([^\)]*\)/;
|
|
5
6
|
export const includesHardCodedColor = raw => {
|
|
6
7
|
const value = raw.toLowerCase();
|
|
7
|
-
if (
|
|
8
|
+
if (colorRegexp.exec(value)) {
|
|
8
9
|
return true;
|
|
9
10
|
}
|
|
10
11
|
for (let i = 0; i < namedColors.length; i++) {
|
|
@@ -14,17 +15,18 @@ export const includesHardCodedColor = raw => {
|
|
|
14
15
|
}
|
|
15
16
|
return false;
|
|
16
17
|
};
|
|
17
|
-
export const isHardCodedColor =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
if (value.startsWith('rgb(') || value.startsWith('rgba(') || value.startsWith('hsl(') || value.startsWith('hsla(') || value.startsWith('lch(') || value.startsWith('lab(') || value.startsWith('color(')) {
|
|
18
|
+
export const isHardCodedColor = raw => {
|
|
19
|
+
const value = raw.toLowerCase();
|
|
20
|
+
if (namedColors.includes(value)) {
|
|
22
21
|
return true;
|
|
23
22
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
value.length === 4 || value.length === 7 || value.length === 9)) {
|
|
23
|
+
const match = value.toLowerCase().match(colorRegexp);
|
|
24
|
+
if (match && match[0] === value) {
|
|
27
25
|
return true;
|
|
28
26
|
}
|
|
29
27
|
return false;
|
|
30
|
-
};
|
|
28
|
+
};
|
|
29
|
+
export function isBoldColor(color) {
|
|
30
|
+
const number = parseInt(color.replace(/^./, ''), 10);
|
|
31
|
+
return number > 300;
|
|
32
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isColorProperty } from '../../css-to-design-tokens/css-to-design-tokens';
|
|
2
|
+
export function containsReplaceableCSSDeclarations(input) {
|
|
3
|
+
const cssPattern = /(\S+)\s*:/g;
|
|
4
|
+
let match;
|
|
5
|
+
while ((match = cssPattern.exec(input)) !== null) {
|
|
6
|
+
if (isColorProperty(match[1])) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
export function findEndIndexOfCSSExpression(text, isAtEndOfInput) {
|
|
13
|
+
// CSS expression can end *on* a semicolon or *before* a brace. In either
|
|
14
|
+
// case we treat the remaining part of the value to cover one character
|
|
15
|
+
// before that symbol.
|
|
16
|
+
const semicolonIndex = text.indexOf(';');
|
|
17
|
+
const braceIndex = text.indexOf('}');
|
|
18
|
+
if (semicolonIndex === -1 && braceIndex === -1) {
|
|
19
|
+
if (isAtEndOfInput) {
|
|
20
|
+
return text.length;
|
|
21
|
+
} else {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
} else if (semicolonIndex === -1) {
|
|
25
|
+
return braceIndex - 1;
|
|
26
|
+
} else if (braceIndex === -1) {
|
|
27
|
+
return semicolonIndex - 1;
|
|
28
|
+
} else {
|
|
29
|
+
return Math.min(semicolonIndex, braceIndex) - 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const kebabize = str => str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, ($, ofs) => (ofs ? '-' : '') + $.toLowerCase());
|
|
2
|
+
export function findFirstNonspaceIndexAfter(text, index) {
|
|
3
|
+
const rest = text.slice(index + 1);
|
|
4
|
+
const indexInRest = rest.search(/\S/);
|
|
5
|
+
if (indexInRest === -1) {
|
|
6
|
+
return text.length;
|
|
7
|
+
} else {
|
|
8
|
+
return index + 1 + indexInRest;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function splitAtIndex(text, index) {
|
|
12
|
+
return [text.slice(0, index), text.slice(index)];
|
|
13
|
+
}
|
package/dist/esm/main.js
CHANGED
|
@@ -291,7 +291,7 @@ function _main() {
|
|
|
291
291
|
case 4:
|
|
292
292
|
_yield$parseArgs = _context5.sent;
|
|
293
293
|
packages = _yield$parseArgs.packages;
|
|
294
|
-
_process$env$_PACKAGE = "0.
|
|
294
|
+
_process$env$_PACKAGE = "0.14.0", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
|
|
295
295
|
logger.log(chalk.bgBlue(chalk.black("\uD83D\uDCDA Atlassian-Frontend codemod library @ ".concat(_PACKAGE_VERSION_, " \uD83D\uDCDA"))));
|
|
296
296
|
if (packages && packages.length > 0) {
|
|
297
297
|
logger.log(chalk.gray("Searching for codemods for newer versions of the following packages: ".concat(packages.map(function (pkg) {
|
|
@@ -10,6 +10,10 @@ import designTokens from '@atlaskit/tokens/token-names';
|
|
|
10
10
|
import Search from '../theme-to-design-tokens/utils/fuzzy-search';
|
|
11
11
|
import { knownVariables, knownColors, knownRawColors } from './utils/legacy-colors';
|
|
12
12
|
import { cleanMeta } from './utils/meta';
|
|
13
|
+
var options = {
|
|
14
|
+
syntax: lessSyntax,
|
|
15
|
+
from: undefined
|
|
16
|
+
};
|
|
13
17
|
var tokens = rawTokens.filter(function (t) {
|
|
14
18
|
return t.attributes.state === 'active';
|
|
15
19
|
}).map(function (t) {
|
|
@@ -37,8 +41,8 @@ function stripVar(prop) {
|
|
|
37
41
|
function stripLessVar(prop) {
|
|
38
42
|
return prop.substring(1);
|
|
39
43
|
}
|
|
40
|
-
function isColorProperty(prop) {
|
|
41
|
-
return prop === 'color' || prop === 'background' || prop === 'background-color' || prop === 'box-shadow' || prop === 'border' || prop === 'border-left' || prop === 'border-right' || prop === 'border-top' || prop === 'border-bottom' || prop === 'border-color';
|
|
44
|
+
export function isColorProperty(prop) {
|
|
45
|
+
return prop === 'color' || prop === 'background' || prop === 'background-color' || prop === 'box-shadow' || prop === 'border' || prop === 'border-left' || prop === 'border-right' || prop === 'border-top' || prop === 'border-bottom' || prop === 'border-color' || prop === 'outline';
|
|
42
46
|
}
|
|
43
47
|
function getDeclarationMeta(decl) {
|
|
44
48
|
if (decl.prop === 'color') {
|
|
@@ -92,6 +96,9 @@ var plugin = function plugin() {
|
|
|
92
96
|
if (!isColorProperty(decl.prop)) {
|
|
93
97
|
return;
|
|
94
98
|
}
|
|
99
|
+
if (decl.value === 'none') {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
95
102
|
var searchTerms = [getDeclarationMeta(decl)].concat(_toConsumableArray(getParentSelectors(decl).split(/\-|\.|\,|\ |\:|\&/).filter(function (el) {
|
|
96
103
|
return !!el;
|
|
97
104
|
})));
|
|
@@ -112,10 +119,10 @@ var plugin = function plugin() {
|
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
// Less variables
|
|
115
|
-
var
|
|
116
|
-
if (
|
|
122
|
+
var lessVarMatch = decl.value.match(lessVarRe);
|
|
123
|
+
if (lessVarMatch) {
|
|
117
124
|
var _getMetaFromCssVar2;
|
|
118
|
-
match =
|
|
125
|
+
match = lessVarMatch[0];
|
|
119
126
|
searchTerms.push.apply(searchTerms, _toConsumableArray((_getMetaFromCssVar2 = getMetaFromCssVar("--".concat(stripLessVar(match)))) !== null && _getMetaFromCssVar2 !== void 0 ? _getMetaFromCssVar2 : []));
|
|
120
127
|
}
|
|
121
128
|
|
|
@@ -160,16 +167,19 @@ export default function transformer(_x) {
|
|
|
160
167
|
}
|
|
161
168
|
function _transformer() {
|
|
162
169
|
_transformer = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(file) {
|
|
170
|
+
var processor, src, _yield$processor$proc, css;
|
|
163
171
|
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
164
172
|
while (1) switch (_context.prev = _context.next) {
|
|
165
173
|
case 0:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
case
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
processor = postcss([plugin()]);
|
|
175
|
+
src = typeof file === 'string' ? file : file.source;
|
|
176
|
+
_context.next = 4;
|
|
177
|
+
return processor.process(src, options);
|
|
178
|
+
case 4:
|
|
179
|
+
_yield$processor$proc = _context.sent;
|
|
180
|
+
css = _yield$processor$proc.css;
|
|
181
|
+
return _context.abrupt("return", css);
|
|
182
|
+
case 7:
|
|
173
183
|
case "end":
|
|
174
184
|
return _context.stop();
|
|
175
185
|
}
|