@plumeria/codemod 18.2.19 → 18.2.20
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/README.md +48 -9
- package/dist/cli.js +5 -3
- package/dist/migrate.js +3 -1
- package/dist/release.d.ts +2 -0
- package/dist/release.js +295 -38
- package/dist/transforms/adopt-styles.d.ts +2 -0
- package/dist/transforms/adopt-styles.js +303 -10
- package/dist/transforms/css-modules.d.ts +5 -1
- package/dist/transforms/css-modules.js +59 -5
- package/dist/transforms/from-plumeria.d.ts +17 -1
- package/dist/transforms/from-plumeria.js +276 -10
- package/dist/transforms/release-styles.d.ts +4 -0
- package/dist/transforms/release-styles.js +466 -66
- package/package.json +1 -1
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.adoptStyles = void 0;
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
const resolve_1 = require("../resolve");
|
|
4
39
|
const CORE = '@plumeria/core';
|
|
5
40
|
const readOptions = (context) => {
|
|
6
41
|
const raw = (context.options[0] ?? {});
|
|
@@ -22,6 +57,9 @@ exports.adoptStyles = {
|
|
|
22
57
|
key: 'The generated key for "{{from}}" is "{{to}}".',
|
|
23
58
|
prop: 'Styles are carried by the "{{prop}}" prop.',
|
|
24
59
|
composes: '"{{key}}" composed other classes; pass them as an array.',
|
|
60
|
+
value: 'A style read as a value goes back through `css.use`.',
|
|
61
|
+
joined: 'A joined class list is written as an array.',
|
|
62
|
+
call: 'The custom properties are the arguments of a function style.',
|
|
25
63
|
},
|
|
26
64
|
schema: [
|
|
27
65
|
{
|
|
@@ -39,9 +77,125 @@ exports.adoptStyles = {
|
|
|
39
77
|
const bindings = new Map();
|
|
40
78
|
let hasCore = false;
|
|
41
79
|
let stylesTaken = false;
|
|
80
|
+
let coreLocal;
|
|
81
|
+
let readsValue = false;
|
|
82
|
+
let several = false;
|
|
83
|
+
const stylingPosition = (node) => {
|
|
84
|
+
let current = node;
|
|
85
|
+
while (current.parent &&
|
|
86
|
+
(current.parent.type === 'ArrayExpression' ||
|
|
87
|
+
current.parent.type === 'LogicalExpression' ||
|
|
88
|
+
current.parent.type === 'ConditionalExpression' ||
|
|
89
|
+
current.parent.type === 'JSXExpressionContainer'))
|
|
90
|
+
current = current.parent;
|
|
91
|
+
return (current.parent?.type === 'JSXAttribute' &&
|
|
92
|
+
(current.parent.name?.name === 'className' ||
|
|
93
|
+
current.parent.name?.name === styleProp));
|
|
94
|
+
};
|
|
95
|
+
const joinedElements = (node) => {
|
|
96
|
+
if (node?.type === 'TemplateLiteral' && node.expressions.length > 1) {
|
|
97
|
+
const spaced = node.quasis.every((quasi, index) => index === 0 || index === node.quasis.length - 1
|
|
98
|
+
? quasi.value.cooked === ''
|
|
99
|
+
: quasi.value.cooked === ' ');
|
|
100
|
+
return spaced ? node.expressions : undefined;
|
|
101
|
+
}
|
|
102
|
+
if (node?.type !== 'CallExpression' ||
|
|
103
|
+
node.callee.type !== 'MemberExpression' ||
|
|
104
|
+
node.callee.property?.name !== 'join' ||
|
|
105
|
+
node.arguments.length !== 1 ||
|
|
106
|
+
node.arguments[0].value !== ' ')
|
|
107
|
+
return undefined;
|
|
108
|
+
const joined = node.callee.object;
|
|
109
|
+
if (joined?.type === 'ArrayExpression')
|
|
110
|
+
return joined.elements.filter(Boolean);
|
|
111
|
+
return joined?.type === 'CallExpression' &&
|
|
112
|
+
joined.callee.type === 'MemberExpression' &&
|
|
113
|
+
joined.callee.property?.name === 'filter' &&
|
|
114
|
+
joined.callee.object?.type === 'ArrayExpression'
|
|
115
|
+
? joined.callee.object.elements.filter(Boolean)
|
|
116
|
+
: undefined;
|
|
117
|
+
};
|
|
118
|
+
const intrinsic = (element) => {
|
|
119
|
+
const name = element?.name;
|
|
120
|
+
return name?.type === 'JSXIdentifier' && /^[a-z]/.test(String(name.name));
|
|
121
|
+
};
|
|
122
|
+
const styleRead = (node) => node?.type === 'LogicalExpression'
|
|
123
|
+
? styleRead(node.right)
|
|
124
|
+
: node?.type === 'MemberExpression'
|
|
125
|
+
? node
|
|
126
|
+
: undefined;
|
|
127
|
+
const insideUse = (node) => {
|
|
128
|
+
let current = node;
|
|
129
|
+
while (current.parent &&
|
|
130
|
+
(current.parent.type === 'LogicalExpression' ||
|
|
131
|
+
current.parent.type === 'ConditionalExpression' ||
|
|
132
|
+
current.parent.type === 'ArrayExpression'))
|
|
133
|
+
current = current.parent;
|
|
134
|
+
return (current.parent?.type === 'CallExpression' &&
|
|
135
|
+
current.parent.callee?.type === 'MemberExpression' &&
|
|
136
|
+
current.parent.callee.property?.name === 'use');
|
|
137
|
+
};
|
|
42
138
|
return {
|
|
43
139
|
Program(program) {
|
|
44
140
|
hasCore = program.body.some((n) => n.type === 'ImportDeclaration' && n.source.value === CORE);
|
|
141
|
+
const locals = new Set();
|
|
142
|
+
let seen = 0;
|
|
143
|
+
for (const statement of program.body) {
|
|
144
|
+
if (statement.type !== 'ImportDeclaration')
|
|
145
|
+
continue;
|
|
146
|
+
if (statement.source.value === CORE) {
|
|
147
|
+
for (const specifier of statement.specifiers)
|
|
148
|
+
if (specifier.type === 'ImportNamespaceSpecifier' ||
|
|
149
|
+
(specifier.type === 'ImportSpecifier' &&
|
|
150
|
+
(specifier.imported.name ?? specifier.imported.value) ===
|
|
151
|
+
'css'))
|
|
152
|
+
coreLocal = specifier.local.name;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const target = (0, resolve_1.resolveSourcePath)(String(statement.source.value), context.filename);
|
|
156
|
+
if (!target || !modules[target])
|
|
157
|
+
continue;
|
|
158
|
+
for (const specifier of statement.specifiers)
|
|
159
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
160
|
+
locals.add(specifier.local.name);
|
|
161
|
+
seen += 1;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
several = seen > 1;
|
|
165
|
+
const visit = (node, styling) => {
|
|
166
|
+
if (!node || typeof node !== 'object')
|
|
167
|
+
return;
|
|
168
|
+
if (Array.isArray(node)) {
|
|
169
|
+
for (const child of node)
|
|
170
|
+
visit(child, styling);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (typeof node.type !== 'string')
|
|
174
|
+
return;
|
|
175
|
+
const inside = styling ||
|
|
176
|
+
(node.type === 'JSXAttribute' &&
|
|
177
|
+
(node.name?.name === 'className' ||
|
|
178
|
+
node.name?.name === styleProp));
|
|
179
|
+
if (!inside &&
|
|
180
|
+
node.type === 'MemberExpression' &&
|
|
181
|
+
locals.has(node.object?.name))
|
|
182
|
+
readsValue = true;
|
|
183
|
+
if (node.type === 'JSXAttribute' &&
|
|
184
|
+
node.name?.name === 'className' &&
|
|
185
|
+
node.parent?.name?.type === 'JSXIdentifier' &&
|
|
186
|
+
/^[A-Z]/.test(String(node.parent.name.name)) &&
|
|
187
|
+
node.value?.type === 'JSXExpressionContainer' &&
|
|
188
|
+
joinedElements(node.value.expression))
|
|
189
|
+
readsValue = true;
|
|
190
|
+
for (const key of Object.keys(node)) {
|
|
191
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
192
|
+
continue;
|
|
193
|
+
visit(node[key], inside);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
visit(program.body, false);
|
|
197
|
+
if (readsValue && !coreLocal)
|
|
198
|
+
coreLocal = 'css';
|
|
45
199
|
stylesTaken = program.body.some((n) => {
|
|
46
200
|
if (n.type === 'VariableDeclaration') {
|
|
47
201
|
return n.declarations.some((d) => d.id?.name === 'styles');
|
|
@@ -56,28 +210,55 @@ exports.adoptStyles = {
|
|
|
56
210
|
},
|
|
57
211
|
ImportDeclaration(node) {
|
|
58
212
|
const specifier = String(node.source.value);
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
213
|
+
const resolved = (0, resolve_1.resolveSourcePath)(specifier, context.filename);
|
|
214
|
+
const named = Object.entries(modules).filter(([key]) => key === specifier ||
|
|
215
|
+
path.basename(key) === path.basename(specifier));
|
|
216
|
+
const entry = (resolved ? modules[resolved] : undefined) ??
|
|
217
|
+
(named.length === 1 ? named[0][1] : undefined);
|
|
218
|
+
if (!entry) {
|
|
219
|
+
const adopted = resolved &&
|
|
220
|
+
Object.values(modules).find((candidate) => candidate.target === resolved);
|
|
221
|
+
if (!adopted)
|
|
222
|
+
return;
|
|
223
|
+
const already = node.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
224
|
+
(specifier.imported.name ?? specifier.imported.value) ===
|
|
225
|
+
'styles');
|
|
226
|
+
if (already)
|
|
227
|
+
bindings.set(already.local.name, {
|
|
228
|
+
entry: adopted,
|
|
229
|
+
local: already.local.name,
|
|
230
|
+
});
|
|
62
231
|
return;
|
|
232
|
+
}
|
|
233
|
+
const from = entry.target && path.isAbsolute(context.filename)
|
|
234
|
+
? (() => {
|
|
235
|
+
const relative = path
|
|
236
|
+
.relative(path.dirname(context.filename), entry.target)
|
|
237
|
+
.split(path.sep)
|
|
238
|
+
.join('/')
|
|
239
|
+
.replace(/\.ts$/, '');
|
|
240
|
+
return relative.startsWith('.') ? relative : `./${relative}`;
|
|
241
|
+
})()
|
|
242
|
+
: entry.source;
|
|
63
243
|
const local = node.specifiers.find((s) => s.type === 'ImportDefaultSpecifier');
|
|
64
244
|
if (!local)
|
|
65
245
|
return;
|
|
66
246
|
const name = local.local.name;
|
|
67
|
-
const renamed = stylesTaken && name !== 'styles' ? name : 'styles';
|
|
247
|
+
const renamed = several || (stylesTaken && name !== 'styles') ? name : 'styles';
|
|
68
248
|
bindings.set(name, { entry, local: renamed });
|
|
69
249
|
const imported = renamed === 'styles'
|
|
70
|
-
? `import { styles } from '${
|
|
71
|
-
: `import { styles as ${renamed} } from '${
|
|
72
|
-
const
|
|
73
|
-
?
|
|
74
|
-
: `import '${CORE}'
|
|
250
|
+
? `import { styles } from '${from}';`
|
|
251
|
+
: `import { styles as ${renamed} } from '${from}';`;
|
|
252
|
+
const core = coreLocal
|
|
253
|
+
? `import * as ${coreLocal} from '${CORE}';`
|
|
254
|
+
: `import '${CORE}';`;
|
|
255
|
+
const replacement = hasCore ? imported : `${core}\n${imported}`;
|
|
75
256
|
if (!hasCore)
|
|
76
257
|
hasCore = true;
|
|
77
258
|
context.report({
|
|
78
259
|
node,
|
|
79
260
|
messageId: 'source',
|
|
80
|
-
data: { source:
|
|
261
|
+
data: { source: from },
|
|
81
262
|
fix: (fixer) => fixer.replaceText(node, replacement),
|
|
82
263
|
});
|
|
83
264
|
},
|
|
@@ -87,6 +268,14 @@ exports.adoptStyles = {
|
|
|
87
268
|
: undefined;
|
|
88
269
|
if (!binding)
|
|
89
270
|
return;
|
|
271
|
+
if (coreLocal && !stylingPosition(node) && !insideUse(node)) {
|
|
272
|
+
context.report({
|
|
273
|
+
node,
|
|
274
|
+
messageId: 'value',
|
|
275
|
+
fix: (fixer) => fixer.replaceText(node, `${coreLocal}.use(${context.sourceCode.getText(node)})`),
|
|
276
|
+
});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
90
279
|
const written = node.computed && node.property.type === 'Literal'
|
|
91
280
|
? String(node.property.value)
|
|
92
281
|
: !node.computed && node.property.type === 'Identifier'
|
|
@@ -105,6 +294,82 @@ exports.adoptStyles = {
|
|
|
105
294
|
fix: (fixer) => fixer.replaceText(node, `${local}.${key}`),
|
|
106
295
|
});
|
|
107
296
|
},
|
|
297
|
+
[`JSXAttribute[name.name='${styleProp}']`](node) {
|
|
298
|
+
if (node.value?.type !== 'JSXExpressionContainer')
|
|
299
|
+
return;
|
|
300
|
+
const style = node.parent.attributes.find((attribute) => attribute.type === 'JSXAttribute' &&
|
|
301
|
+
attribute.name?.name === 'style' &&
|
|
302
|
+
attribute.value?.type === 'JSXExpressionContainer' &&
|
|
303
|
+
attribute.value.expression.type === 'ObjectExpression');
|
|
304
|
+
if (!style)
|
|
305
|
+
return;
|
|
306
|
+
const properties = style.value.expression.properties;
|
|
307
|
+
const named = new Map();
|
|
308
|
+
for (const property of properties) {
|
|
309
|
+
if (property.type !== 'Property' || !property.computed)
|
|
310
|
+
continue;
|
|
311
|
+
const key = property.key.type === 'TSAsExpression'
|
|
312
|
+
? property.key.expression
|
|
313
|
+
: property.key;
|
|
314
|
+
if (key.type === 'Literal' && typeof key.value === 'string')
|
|
315
|
+
named.set(key.value, property);
|
|
316
|
+
}
|
|
317
|
+
if (named.size === 0)
|
|
318
|
+
return;
|
|
319
|
+
const consumed = new Set();
|
|
320
|
+
const call = (read) => {
|
|
321
|
+
const binding = read?.type === 'MemberExpression' &&
|
|
322
|
+
read.object.type === 'Identifier'
|
|
323
|
+
? bindings.get(read.object.name)
|
|
324
|
+
: undefined;
|
|
325
|
+
if (!binding)
|
|
326
|
+
return undefined;
|
|
327
|
+
const written = !read.computed && read.property.type === 'Identifier'
|
|
328
|
+
? read.property.name
|
|
329
|
+
: read.computed && read.property.type === 'Literal'
|
|
330
|
+
? String(read.property.value)
|
|
331
|
+
: undefined;
|
|
332
|
+
const key = written && (binding.entry.names[written] ?? written);
|
|
333
|
+
const variables = key ? binding.entry.functions?.[key] : undefined;
|
|
334
|
+
if (!variables || variables.some((v) => !named.has(v)))
|
|
335
|
+
return undefined;
|
|
336
|
+
for (const variable of variables)
|
|
337
|
+
consumed.add(named.get(variable));
|
|
338
|
+
return `${binding.local}.${key}(${variables
|
|
339
|
+
.map((variable) => context.sourceCode.getText(named.get(variable).value))
|
|
340
|
+
.join(', ')})`;
|
|
341
|
+
};
|
|
342
|
+
const expression = node.value.expression;
|
|
343
|
+
const elements = expression.type === 'ArrayExpression'
|
|
344
|
+
? expression.elements.filter(Boolean)
|
|
345
|
+
: [expression];
|
|
346
|
+
const texts = elements.map((element) => {
|
|
347
|
+
const called = call(styleRead(element));
|
|
348
|
+
if (!called)
|
|
349
|
+
return context.sourceCode.getText(element);
|
|
350
|
+
return element.type === 'LogicalExpression'
|
|
351
|
+
? `${context.sourceCode.getText(element.left)} ${element.operator} ${called}`
|
|
352
|
+
: called;
|
|
353
|
+
});
|
|
354
|
+
if (consumed.size === 0)
|
|
355
|
+
return;
|
|
356
|
+
const kept = properties.filter((property) => !consumed.has(property));
|
|
357
|
+
const replacement = expression.type === 'ArrayExpression'
|
|
358
|
+
? `[${texts.join(', ')}]`
|
|
359
|
+
: texts[0];
|
|
360
|
+
context.report({
|
|
361
|
+
node,
|
|
362
|
+
messageId: 'call',
|
|
363
|
+
fix: (fixer) => [
|
|
364
|
+
fixer.replaceText(expression, replacement),
|
|
365
|
+
kept.length === 0
|
|
366
|
+
? fixer.removeRange([style.range[0] - 1, style.range[1]])
|
|
367
|
+
: fixer.replaceText(style.value.expression, `{ ${kept
|
|
368
|
+
.map((property) => context.sourceCode.getText(property))
|
|
369
|
+
.join(', ')} }`),
|
|
370
|
+
],
|
|
371
|
+
});
|
|
372
|
+
},
|
|
108
373
|
JSXAttribute(node) {
|
|
109
374
|
if (node.name.type !== 'JSXIdentifier')
|
|
110
375
|
return;
|
|
@@ -114,6 +379,34 @@ exports.adoptStyles = {
|
|
|
114
379
|
if (!value || value.type !== 'JSXExpressionContainer')
|
|
115
380
|
return;
|
|
116
381
|
const expression = value.expression;
|
|
382
|
+
const joined = joinedElements(expression);
|
|
383
|
+
if (joined) {
|
|
384
|
+
if (!joined.some((element) => bindings.has(styleRead(element)?.object?.name)))
|
|
385
|
+
return;
|
|
386
|
+
const list = joined
|
|
387
|
+
.map((element) => context.sourceCode.getText(element))
|
|
388
|
+
.join(', ');
|
|
389
|
+
if (!intrinsic(node.parent) && coreLocal) {
|
|
390
|
+
context.report({
|
|
391
|
+
node: expression,
|
|
392
|
+
messageId: 'value',
|
|
393
|
+
fix: (fixer) => fixer.replaceText(expression, `${coreLocal}.use(${list})`),
|
|
394
|
+
});
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
context.report({
|
|
398
|
+
node: node.name,
|
|
399
|
+
messageId: 'prop',
|
|
400
|
+
data: { prop: styleProp },
|
|
401
|
+
fix: (fixer) => fixer.replaceText(node.name, styleProp),
|
|
402
|
+
});
|
|
403
|
+
context.report({
|
|
404
|
+
node: expression,
|
|
405
|
+
messageId: 'joined',
|
|
406
|
+
fix: (fixer) => fixer.replaceText(expression, `[${list}]`),
|
|
407
|
+
});
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
117
410
|
const root = expression.type === 'MemberExpression' &&
|
|
118
411
|
expression.object.type === 'Identifier'
|
|
119
412
|
? expression.object.name
|
|
@@ -9,9 +9,13 @@ export interface Converted {
|
|
|
9
9
|
code: string;
|
|
10
10
|
names: Record<string, string>;
|
|
11
11
|
composes: Record<string, string[]>;
|
|
12
|
+
functions: Record<string, string[]>;
|
|
12
13
|
reports: Report[];
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
interface Raw {
|
|
16
|
+
raw: string;
|
|
17
|
+
}
|
|
18
|
+
type Value = string | number | Raw;
|
|
15
19
|
export declare const toProperty: (name: string) => string;
|
|
16
20
|
export declare const toKey: (className: string) => string;
|
|
17
21
|
export declare const toValue: (raw: string) => Value;
|
|
@@ -41,6 +41,7 @@ const nest = (node, key) => {
|
|
|
41
41
|
node.nested.push([key, child]);
|
|
42
42
|
return child;
|
|
43
43
|
};
|
|
44
|
+
const isSpecificityBoost = (node) => node.type === 'pseudo' && /^:not\(#[\\_].*\)$/.test(node.toString());
|
|
44
45
|
const atRuleKeys = (rule) => {
|
|
45
46
|
const keys = [];
|
|
46
47
|
let parent = rule.parent;
|
|
@@ -57,7 +58,7 @@ const readSelector = (selector) => {
|
|
|
57
58
|
hint: 'Only a local class, or one local class under another, is converted.',
|
|
58
59
|
};
|
|
59
60
|
(0, postcss_selector_parser_1.default)((root) => {
|
|
60
|
-
const nodes = root.first.nodes.filter((n) => n.type !== 'string');
|
|
61
|
+
const nodes = root.first.nodes.filter((n) => n.type !== 'string' && !isSpecificityBoost(n));
|
|
61
62
|
const parts = [
|
|
62
63
|
{ pseudos: [] },
|
|
63
64
|
];
|
|
@@ -148,7 +149,16 @@ const serialise = (node, indent) => {
|
|
|
148
149
|
lines.push(`${indent}...${marker},`);
|
|
149
150
|
for (const [prop, value] of node.decls) {
|
|
150
151
|
const key = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(prop) ? prop : `'${prop}'`;
|
|
151
|
-
|
|
152
|
+
if (typeof value === 'object' && value.raw === prop) {
|
|
153
|
+
lines.push(`${indent}${prop},`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const written = typeof value === 'number'
|
|
157
|
+
? value
|
|
158
|
+
: typeof value === 'object'
|
|
159
|
+
? value.raw
|
|
160
|
+
: `'${value}'`;
|
|
161
|
+
lines.push(`${indent}${key}: ${written},`);
|
|
152
162
|
}
|
|
153
163
|
for (const [key, child] of node.nested) {
|
|
154
164
|
const written = key.startsWith('css.extended(') ? `[${key}]` : `'${key}'`;
|
|
@@ -224,12 +234,56 @@ function convertStylesheet(css) {
|
|
|
224
234
|
composes[target.key] = (composes[target.key] ?? []).concat(name.split(/\s+/).map(exports.toKey));
|
|
225
235
|
return;
|
|
226
236
|
}
|
|
227
|
-
|
|
237
|
+
const property = (0, exports.toProperty)(child.prop);
|
|
238
|
+
const held = node.decls.findIndex(([name]) => name === property);
|
|
239
|
+
if (held !== -1)
|
|
240
|
+
node.decls.splice(held, 1);
|
|
241
|
+
node.decls.push([property, (0, exports.toValue)(child.value)]);
|
|
228
242
|
});
|
|
229
243
|
}
|
|
230
244
|
});
|
|
245
|
+
const declared = new Set();
|
|
246
|
+
root.walkDecls((decl) => {
|
|
247
|
+
if (decl.prop.startsWith('--'))
|
|
248
|
+
declared.add(decl.prop);
|
|
249
|
+
});
|
|
250
|
+
const functions = {};
|
|
251
|
+
const VARIABLE = /^var\((--[A-Za-z0-9_-]+)\)$/;
|
|
252
|
+
const HASHED = /^x[0-9a-z]{7}$/;
|
|
253
|
+
const kebab = (name) => name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
254
|
+
const parameterise = (node, key, variables) => {
|
|
255
|
+
const owned = new RegExp(`^--[A-Za-z0-9_-]+-${kebab(key)}-([a-z0-9-]+)$`);
|
|
256
|
+
node.decls = node.decls.map(([prop, value]) => {
|
|
257
|
+
const match = typeof value === 'string' ? VARIABLE.exec(value.trim()) : null;
|
|
258
|
+
const variable = match?.[1];
|
|
259
|
+
if (!variable || declared.has(variable))
|
|
260
|
+
return [prop, value];
|
|
261
|
+
const parameter = owned.exec(variable)?.[1];
|
|
262
|
+
if (!parameter || HASHED.test(variable.slice(2).split('-')[0]))
|
|
263
|
+
return [prop, value];
|
|
264
|
+
if (!variables.includes(variable))
|
|
265
|
+
variables.push(variable);
|
|
266
|
+
return [prop, { raw: (0, exports.toKey)(parameter) }];
|
|
267
|
+
});
|
|
268
|
+
for (const [, child] of node.nested)
|
|
269
|
+
parameterise(child, key, variables);
|
|
270
|
+
};
|
|
271
|
+
for (const [key, node] of keys) {
|
|
272
|
+
const variables = [];
|
|
273
|
+
parameterise(node, key, variables);
|
|
274
|
+
if (variables.length > 0)
|
|
275
|
+
functions[key] = variables;
|
|
276
|
+
}
|
|
231
277
|
const body = [...keys.entries()]
|
|
232
|
-
.map(([key, node]) =>
|
|
278
|
+
.map(([key, node]) => {
|
|
279
|
+
const variables = functions[key];
|
|
280
|
+
if (!variables)
|
|
281
|
+
return ` ${key}: {\n${serialise(node, ' ')}\n },`;
|
|
282
|
+
const params = variables
|
|
283
|
+
.map((variable) => `${(0, exports.toKey)(variable.split('-').pop())}: string | number`)
|
|
284
|
+
.join(', ');
|
|
285
|
+
return ` ${key}: (${params}) => ({\n${serialise(node, ' ')}\n }),`;
|
|
286
|
+
})
|
|
233
287
|
.join('\n');
|
|
234
288
|
const code = `import * as css from '@plumeria/core';
|
|
235
289
|
|
|
@@ -237,5 +291,5 @@ export const styles = css.create({
|
|
|
237
291
|
${body}
|
|
238
292
|
});
|
|
239
293
|
`;
|
|
240
|
-
return { code, names, composes, reports };
|
|
294
|
+
return { code, names, composes, functions, reports };
|
|
241
295
|
}
|
|
@@ -14,9 +14,25 @@ export interface PlumeriaModule {
|
|
|
14
14
|
variables: string[];
|
|
15
15
|
}>;
|
|
16
16
|
aliases: Record<string, Record<string, string>>;
|
|
17
|
+
merges: Record<string, string>;
|
|
18
|
+
overrides: Record<string, Record<number, string>>;
|
|
17
19
|
definitionOnly: boolean;
|
|
18
20
|
reports: PlumeriaReport[];
|
|
19
21
|
}
|
|
22
|
+
export interface PlumeriaComposition {
|
|
23
|
+
parts: {
|
|
24
|
+
binding: string;
|
|
25
|
+
key: string;
|
|
26
|
+
}[];
|
|
27
|
+
slots: {
|
|
28
|
+
binding: string;
|
|
29
|
+
key: string;
|
|
30
|
+
}[][];
|
|
31
|
+
file: string;
|
|
32
|
+
line: number;
|
|
33
|
+
column: number;
|
|
34
|
+
}
|
|
35
|
+
export declare const compositionSignature: (classNames: string[]) => string;
|
|
20
36
|
export interface ThemeExtraction {
|
|
21
37
|
bindings: Record<string, Record<string, string>>;
|
|
22
38
|
globalCss: string;
|
|
@@ -42,4 +58,4 @@ export declare const __private: {
|
|
|
42
58
|
export declare function extractPlumeriaThemes(source: string): ThemeExtraction;
|
|
43
59
|
export declare function extractPlumeriaStatics(source: string, externalValues?: Record<string, unknown>): StaticExtraction;
|
|
44
60
|
export declare function extractPlumeriaAnimations(source: string, externalValues?: Record<string, unknown>): AnimationExtraction;
|
|
45
|
-
export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown
|
|
61
|
+
export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>, compositions?: PlumeriaComposition[]): PlumeriaModule | null;
|