@plumeria/utils 19.2.14 → 19.2.16
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/dist/parser.d.ts +1 -0
- package/dist/parser.js +44 -10
- package/dist/transform.js +134 -27
- package/dist/unresolvableValue.d.ts +15 -0
- package/dist/unresolvableValue.js +177 -0
- package/package.json +1 -1
package/dist/parser.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare function traverse(node: Module | JSXExpression, visitor: {
|
|
|
43
43
|
}) => void;
|
|
44
44
|
}): void;
|
|
45
45
|
export declare function objectExpressionToObject(node: ObjectExpression, staticTable: StaticTable, keyframesHashTable: KeyframesHashTable, viewTransitionHashTable: ViewTransitionHashTable, createThemeHashTable: CreateThemeHashTable, createThemeObjectTable: CreateThemeObjectTable, createHashTable: CreateHashTable, createStaticHashTable: CreateStaticHashTable, createStaticObjectTable: CreateStaticObjectTable, resolveVariable?: (name: string) => any): CSSObject;
|
|
46
|
+
export declare function collectLocalConstNodes(ast: Module): Map<string, any>;
|
|
46
47
|
export declare function collectLocalConsts(ast: Module): Record<string, any>;
|
|
47
48
|
export declare function resolveThemeSelector(node: Expression, staticTable: StaticTable, createStaticHashTable: CreateStaticHashTable, createStaticObjectTable: CreateStaticObjectTable): string;
|
|
48
49
|
export declare function resolveFileError(actualPath: string, importedName: string): {
|
package/dist/parser.js
CHANGED
|
@@ -39,6 +39,7 @@ exports.resolveOriginError = resolveOriginError;
|
|
|
39
39
|
exports.resolveComponentKey = resolveComponentKey;
|
|
40
40
|
exports.traverse = traverse;
|
|
41
41
|
exports.objectExpressionToObject = objectExpressionToObject;
|
|
42
|
+
exports.collectLocalConstNodes = collectLocalConstNodes;
|
|
42
43
|
exports.collectLocalConsts = collectLocalConsts;
|
|
43
44
|
exports.resolveThemeSelector = resolveThemeSelector;
|
|
44
45
|
exports.resolveFileError = resolveFileError;
|
|
@@ -101,6 +102,35 @@ const getRootIdentifier = (node) => {
|
|
|
101
102
|
return null;
|
|
102
103
|
};
|
|
103
104
|
exports.getRootIdentifier = getRootIdentifier;
|
|
105
|
+
const EXPRESSION_KINDS = {
|
|
106
|
+
NullLiteral: 'null',
|
|
107
|
+
ObjectExpression: 'an object literal',
|
|
108
|
+
ArrayExpression: 'an array literal',
|
|
109
|
+
CallExpression: 'a function call',
|
|
110
|
+
NewExpression: 'a new expression',
|
|
111
|
+
ConditionalExpression: 'a conditional expression',
|
|
112
|
+
ArrowFunctionExpression: 'a function',
|
|
113
|
+
FunctionExpression: 'a function',
|
|
114
|
+
TaggedTemplateExpression: 'a tagged template',
|
|
115
|
+
AwaitExpression: 'an await expression',
|
|
116
|
+
};
|
|
117
|
+
const expressionLabel = (node) => {
|
|
118
|
+
node = unwrapExpression(node);
|
|
119
|
+
if (exports.t.isIdentifier(node))
|
|
120
|
+
return node.value;
|
|
121
|
+
if (exports.t.isStringLiteral(node))
|
|
122
|
+
return `'${node.value}'`;
|
|
123
|
+
if (exports.t.isNumericLiteral(node))
|
|
124
|
+
return String(node.value);
|
|
125
|
+
if (exports.t.isMemberExpression(node)) {
|
|
126
|
+
const property = node.property;
|
|
127
|
+
if (property.type === 'Identifier')
|
|
128
|
+
return `${expressionLabel(node.object)}.${property.value}`;
|
|
129
|
+
if (property.type === 'Computed')
|
|
130
|
+
return `${expressionLabel(node.object)}[${expressionLabel(property.expression)}]`;
|
|
131
|
+
}
|
|
132
|
+
return EXPRESSION_KINDS[node.type] ?? 'an expression';
|
|
133
|
+
};
|
|
104
134
|
const getNamespaceMember = (node, rootName) => {
|
|
105
135
|
node = unwrapExpression(node);
|
|
106
136
|
if (exports.t.isCallExpression(node)) {
|
|
@@ -277,7 +307,7 @@ function objectExpressionToObject(node, staticTable, keyframesHashTable, viewTra
|
|
|
277
307
|
[root]: resolved,
|
|
278
308
|
}));
|
|
279
309
|
if (member === undefined)
|
|
280
|
-
throw new Error(`[plumeria] Unknown style member
|
|
310
|
+
throw new Error(`[plumeria] Unknown style member: ${expressionLabel(val)} does not exist.`);
|
|
281
311
|
obj[key] = member;
|
|
282
312
|
return;
|
|
283
313
|
}
|
|
@@ -343,8 +373,7 @@ function objectExpressionToObject(node, staticTable, keyframesHashTable, viewTra
|
|
|
343
373
|
});
|
|
344
374
|
return obj;
|
|
345
375
|
}
|
|
346
|
-
function
|
|
347
|
-
const localConsts = {};
|
|
376
|
+
function collectLocalConstNodes(ast) {
|
|
348
377
|
const decls = new Map();
|
|
349
378
|
for (const node of ast.body) {
|
|
350
379
|
let declarations = [];
|
|
@@ -363,6 +392,11 @@ function collectLocalConsts(ast) {
|
|
|
363
392
|
}
|
|
364
393
|
}
|
|
365
394
|
}
|
|
395
|
+
return decls;
|
|
396
|
+
}
|
|
397
|
+
function collectLocalConsts(ast) {
|
|
398
|
+
const localConsts = {};
|
|
399
|
+
const decls = collectLocalConstNodes(ast);
|
|
366
400
|
const visiting = new Set();
|
|
367
401
|
function resolveValue(name) {
|
|
368
402
|
if (localConsts[name] !== undefined)
|
|
@@ -679,7 +713,7 @@ function evaluateTemplateLiteral(node, staticTable, keyframesHashTable, viewTran
|
|
|
679
713
|
const expr = node.expressions[i];
|
|
680
714
|
const evaluatedExpr = evaluateExpression(expr, staticTable, keyframesHashTable, viewTransitionHashTable, createThemeHashTable, createThemeObjectTable, createStaticHashTable, createStaticObjectTable);
|
|
681
715
|
if (evaluatedExpr === null || typeof evaluatedExpr === 'object')
|
|
682
|
-
throw new Error(
|
|
716
|
+
throw new Error(`[plumeria] Template value cannot be resolved to a primitive: ${expressionLabel(expr)} inside \${} must resolve to a string or number at build time.`);
|
|
683
717
|
result += String(evaluatedExpr);
|
|
684
718
|
}
|
|
685
719
|
}
|
|
@@ -747,7 +781,7 @@ function evaluateBinaryExpression(node, staticTable, keyframesHashTable, viewTra
|
|
|
747
781
|
const rightType = right === null ? 'null' : typeof right;
|
|
748
782
|
throw new Error(`[plumeria] Binary operator ${node.operator} requires ${expected} operands; received ${leftType} and ${rightType}.`);
|
|
749
783
|
}
|
|
750
|
-
throw new Error(`[plumeria] Unsupported binary operator: ${node.operator}
|
|
784
|
+
throw new Error(`[plumeria] Unsupported binary operator: ${node.operator}. Use arithmetic, bitwise, &&, ||, or ?? operators, which can be resolved at build time.`);
|
|
751
785
|
}
|
|
752
786
|
function evaluateCallExpression(node, staticTable) {
|
|
753
787
|
const callee = node.callee;
|
|
@@ -821,7 +855,7 @@ function evaluateExpression(node, staticTable, keyframesHashTable, viewTransitio
|
|
|
821
855
|
return createStaticObjectTable[hash];
|
|
822
856
|
}
|
|
823
857
|
}
|
|
824
|
-
throw new Error(`[plumeria] Cannot resolve static value: ${node.value}
|
|
858
|
+
throw new Error(`[plumeria] Cannot resolve static value: ${node.value}. It must be a const holding a string or number, declared in this file or imported from a module plumeria can read.`);
|
|
825
859
|
}
|
|
826
860
|
if (exports.t.isMemberExpression(node)) {
|
|
827
861
|
const resolved = resolveStaticTableMemberExpression(node, staticTable);
|
|
@@ -836,7 +870,7 @@ function evaluateExpression(node, staticTable, keyframesHashTable, viewTransitio
|
|
|
836
870
|
if (resolvedStatic !== undefined) {
|
|
837
871
|
return resolvedStatic;
|
|
838
872
|
}
|
|
839
|
-
throw new Error(
|
|
873
|
+
throw new Error(`[plumeria] Cannot resolve static member expression: ${expressionLabel(node)}. Its object must be a const or a value created by css.createStatic or css.createTheme.`);
|
|
840
874
|
}
|
|
841
875
|
if (exports.t.isBinaryExpression(node)) {
|
|
842
876
|
return evaluateBinaryExpression(node, staticTable, keyframesHashTable, viewTransitionHashTable, createThemeHashTable, createThemeObjectTable, createStaticHashTable, createStaticObjectTable);
|
|
@@ -856,12 +890,12 @@ function evaluateExpression(node, staticTable, keyframesHashTable, viewTransitio
|
|
|
856
890
|
if (node.operator === '~')
|
|
857
891
|
return ~value;
|
|
858
892
|
}
|
|
859
|
-
throw new Error(`[plumeria] Unsupported unary operand for ${node.operator}
|
|
893
|
+
throw new Error(`[plumeria] Unsupported unary operand for ${node.operator}: ${expressionLabel(node.argument)} must resolve to a number at build time.`);
|
|
860
894
|
}
|
|
861
895
|
if (exports.t.isParenthesisExpression(node)) {
|
|
862
896
|
return evaluateExpression(node.expression, staticTable, keyframesHashTable, viewTransitionHashTable, createThemeHashTable, createThemeObjectTable, createStaticHashTable, createStaticObjectTable);
|
|
863
897
|
}
|
|
864
|
-
throw new Error(`[plumeria] Unsupported expression type: ${node.
|
|
898
|
+
throw new Error(`[plumeria] Unsupported expression type: ${expressionLabel(node)}. Style values must be literals, consts, or arithmetic and template expressions of them.`);
|
|
865
899
|
}
|
|
866
900
|
function resolveKeyframesTableMemberExpression(node, keyframesHashTable) {
|
|
867
901
|
if (exports.t.isIdentifier(node)) {
|
|
@@ -1681,7 +1715,7 @@ function runScan(scanCwd = process.cwd()) {
|
|
|
1681
1715
|
else if (method === 'createTheme') {
|
|
1682
1716
|
const selector = resolveThemeSelector(init.arguments[0].expression, scopedStaticTable, localCreateStaticHashTable, localCreateStaticObjectTable);
|
|
1683
1717
|
if (!selector)
|
|
1684
|
-
throw new Error('[plumeria] createTheme
|
|
1718
|
+
throw new Error('[plumeria] createTheme needs a non-empty selector it can read at build time. Pass a string literal such as ".dark", or a name this file declares as one.');
|
|
1685
1719
|
const hash = (0, createTheme_1.themeHashOf)(selector, obj);
|
|
1686
1720
|
localTables.createThemeObjectTable[hash] = obj;
|
|
1687
1721
|
localCreateThemeObjectTable[hash] = obj;
|
package/dist/transform.js
CHANGED
|
@@ -38,6 +38,42 @@ const core_1 = require("@swc/core");
|
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const zss_engine_1 = require("zss-engine");
|
|
40
40
|
const parser_1 = require("./parser");
|
|
41
|
+
const unresolvableValue_1 = require("./unresolvableValue");
|
|
42
|
+
const spreadValues = (tables, constNodes, sourceOf) => {
|
|
43
|
+
const spread = (prop) => {
|
|
44
|
+
let values;
|
|
45
|
+
try {
|
|
46
|
+
values = (0, parser_1.objectExpressionToObject)({ properties: [prop] }, ...tables);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const argument = prop.arguments;
|
|
52
|
+
const declared = argument?.type === 'Identifier'
|
|
53
|
+
? constNodes.get(argument.value)
|
|
54
|
+
: undefined;
|
|
55
|
+
const source = declared && parser_1.t.isObjectExpression(declared) ? declared : undefined;
|
|
56
|
+
return { values, source };
|
|
57
|
+
};
|
|
58
|
+
const resolvesValue = (value) => {
|
|
59
|
+
try {
|
|
60
|
+
const probe = (0, parser_1.objectExpressionToObject)({
|
|
61
|
+
properties: [
|
|
62
|
+
{
|
|
63
|
+
type: 'KeyValueProperty',
|
|
64
|
+
key: { type: 'Identifier', value: '__probe' },
|
|
65
|
+
value,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
}, ...tables);
|
|
69
|
+
return '__probe' in probe;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return { spread, resolvesValue, sourceOf };
|
|
76
|
+
};
|
|
41
77
|
const create_1 = require("./create");
|
|
42
78
|
const dynamicKey_1 = require("./dynamicKey");
|
|
43
79
|
const escalation_1 = require("./escalation");
|
|
@@ -92,9 +128,9 @@ const destructuredPropAliases = (fn) => {
|
|
|
92
128
|
}
|
|
93
129
|
return aliases;
|
|
94
130
|
};
|
|
95
|
-
const propDefaultMessage = (source) => `
|
|
131
|
+
const propDefaultMessage = (source) => `[plumeria] A style prop default must be a defined style: "${source}". ` +
|
|
96
132
|
`Apply a conditional or dynamic style where the element is styled.`;
|
|
97
|
-
const spreadStyleMessage = (source) => `
|
|
133
|
+
const spreadStyleMessage = (source) => `[plumeria] Spread elements in a style array are not supported: "...${source}". ` +
|
|
98
134
|
`List each style explicitly.`;
|
|
99
135
|
const resolveLocalStyleAlias = (aliases, expression) => {
|
|
100
136
|
let current = expression;
|
|
@@ -160,6 +196,11 @@ const transformSource = async (env) => {
|
|
|
160
196
|
const sourceBuffer = Buffer.from(source, 'utf-8');
|
|
161
197
|
const leadingBytes = Buffer.byteLength(source.slice(0, leadingLen), 'utf-8');
|
|
162
198
|
const baseByteOffset = ast.span.start - leadingBytes;
|
|
199
|
+
const getSource = (node) => {
|
|
200
|
+
const start = node.span.start - baseByteOffset;
|
|
201
|
+
const end = node.span.end - baseByteOffset;
|
|
202
|
+
return sourceBuffer.subarray(start, end).toString('utf-8');
|
|
203
|
+
};
|
|
163
204
|
const throwCompilationError = (message, node) => {
|
|
164
205
|
let suffix = '';
|
|
165
206
|
if (node) {
|
|
@@ -196,16 +237,16 @@ const transformSource = async (env) => {
|
|
|
196
237
|
if (!isPlumeriaStyle) {
|
|
197
238
|
const failure = (0, parser_1.resolveOriginError)(node, rootId, localImports);
|
|
198
239
|
if (failure) {
|
|
199
|
-
throwCompilationError(`
|
|
240
|
+
throwCompilationError(`[plumeria] ${failure.message} (${path.basename(failure.filePath)})`);
|
|
200
241
|
}
|
|
201
|
-
throwCompilationError(`
|
|
242
|
+
throwCompilationError(`[plumeria] Dynamic or unresolvable style object "${getSource(node)}" is not supported.`, node);
|
|
202
243
|
}
|
|
203
244
|
}
|
|
204
245
|
};
|
|
205
246
|
const scannedTables = (0, parser_1.scanOverlay)();
|
|
206
247
|
const ownFailure = (0, parser_1.resolveFileError)(baseId, '');
|
|
207
248
|
if (ownFailure)
|
|
208
|
-
throwCompilationError(`
|
|
249
|
+
throwCompilationError(`[plumeria] ${ownFailure.message}`);
|
|
209
250
|
for (const node of ast.body) {
|
|
210
251
|
if (node.type === 'ImportDeclaration') {
|
|
211
252
|
const sourcePath = node.source.value;
|
|
@@ -309,6 +350,7 @@ const transformSource = async (env) => {
|
|
|
309
350
|
return records;
|
|
310
351
|
};
|
|
311
352
|
const localConsts = (0, parser_1.collectLocalConsts)(ast);
|
|
353
|
+
const localConstNodes = (0, parser_1.collectLocalConstNodes)(ast);
|
|
312
354
|
const importMap = {};
|
|
313
355
|
const keyframesImportMap = {};
|
|
314
356
|
const viewTransitionImportMap = {};
|
|
@@ -568,20 +610,30 @@ const transformSource = async (env) => {
|
|
|
568
610
|
if (propName && init && parser_1.t.isCallExpression(init)) {
|
|
569
611
|
if (['create', 'createTheme', 'createStatic'].includes(propName) &&
|
|
570
612
|
!topLevelDeclarators.has(node)) {
|
|
571
|
-
throwCompilationError(`
|
|
613
|
+
throwCompilationError(`[plumeria] css.${propName} must be assigned to a top-level variable so its styles can be resolved across files. Move this declaration outside the function or block.`, node);
|
|
572
614
|
}
|
|
573
615
|
if (['create', 'createTheme', 'createStatic'].includes(propName)) {
|
|
574
616
|
const argumentIndex = propName === 'createTheme' ? 1 : 0;
|
|
575
617
|
const argument = init.arguments[argumentIndex];
|
|
576
618
|
const object = argument && literalObjectArgument(argument.expression);
|
|
577
619
|
if (!object)
|
|
578
|
-
throwCompilationError(`
|
|
620
|
+
throwCompilationError(`[plumeria] css.${propName} needs a style object it can read at build time. Pass an object literal or a top-level constant containing one.`, init);
|
|
579
621
|
argument.expression = object;
|
|
580
622
|
registeredStyleCalls.add(init.span.start);
|
|
581
623
|
}
|
|
582
624
|
if (propName === 'create' &&
|
|
583
625
|
parser_1.t.isObjectExpression(init.arguments[0].expression)) {
|
|
584
626
|
const obj = (0, parser_1.objectExpressionToObject)(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
627
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(init.arguments[0].expression, obj, spreadValues([
|
|
628
|
+
mergedStaticTable,
|
|
629
|
+
mergedKeyframesTable,
|
|
630
|
+
mergedViewTransitionTable,
|
|
631
|
+
mergedCreateThemeHashTable,
|
|
632
|
+
scannedTables.createThemeObjectTable,
|
|
633
|
+
mergedCreateTable,
|
|
634
|
+
mergedCreateStaticHashTable,
|
|
635
|
+
scannedTables.createStaticObjectTable,
|
|
636
|
+
], localConstNodes, getSource));
|
|
585
637
|
if (obj) {
|
|
586
638
|
const hashMap = {};
|
|
587
639
|
Object.entries(obj).forEach(([key, style]) => {
|
|
@@ -625,13 +677,23 @@ const transformSource = async (env) => {
|
|
|
625
677
|
const selectorExpr = init.arguments[0].expression;
|
|
626
678
|
const selector = (0, parser_1.resolveThemeSelector)(selectorExpr, mergedStaticTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
627
679
|
if (!selector) {
|
|
628
|
-
throwCompilationError(`
|
|
680
|
+
throwCompilationError(`[plumeria] createTheme needs a selector it can read at build time. ` +
|
|
629
681
|
`Pass a string literal such as ".dark", or a name this file declares as one.`, selectorExpr);
|
|
630
682
|
}
|
|
631
683
|
if (selector.startsWith('@') && !(0, zss_engine_1.isAtRule)(selector)) {
|
|
632
|
-
throwCompilationError(`
|
|
684
|
+
throwCompilationError(`[plumeria] Unsupported at-rule: "${selector}". createTheme only supports nesting at-rules such as @media, @container, @supports, @layer, and @scope.`, selectorExpr);
|
|
633
685
|
}
|
|
634
686
|
const obj = (0, parser_1.objectExpressionToObject)(init.arguments[1].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
687
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(init.arguments[1].expression, obj, spreadValues([
|
|
688
|
+
mergedStaticTable,
|
|
689
|
+
mergedKeyframesTable,
|
|
690
|
+
mergedViewTransitionTable,
|
|
691
|
+
mergedCreateThemeHashTable,
|
|
692
|
+
scannedTables.createThemeObjectTable,
|
|
693
|
+
mergedCreateTable,
|
|
694
|
+
mergedCreateStaticHashTable,
|
|
695
|
+
scannedTables.createStaticObjectTable,
|
|
696
|
+
], localConstNodes, getSource));
|
|
635
697
|
const hash = (0, createTheme_1.themeHashOf)(selector, obj);
|
|
636
698
|
if (parser_1.t.isIdentifier(node.id)) {
|
|
637
699
|
const uniqueKey = `${resourcePath}-${node.id.value}`;
|
|
@@ -670,6 +732,16 @@ const transformSource = async (env) => {
|
|
|
670
732
|
idSpans.add(node.id.span.start);
|
|
671
733
|
}
|
|
672
734
|
const obj = (0, parser_1.objectExpressionToObject)(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
735
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(init.arguments[0].expression, obj, spreadValues([
|
|
736
|
+
mergedStaticTable,
|
|
737
|
+
mergedKeyframesTable,
|
|
738
|
+
mergedViewTransitionTable,
|
|
739
|
+
mergedCreateThemeHashTable,
|
|
740
|
+
scannedTables.createThemeObjectTable,
|
|
741
|
+
mergedCreateTable,
|
|
742
|
+
mergedCreateStaticHashTable,
|
|
743
|
+
scannedTables.createStaticObjectTable,
|
|
744
|
+
], localConstNodes, getSource));
|
|
673
745
|
const hash = (0, zss_engine_1.genBase36Hash)(obj, 1, 8);
|
|
674
746
|
if (parser_1.t.isIdentifier(node.id)) {
|
|
675
747
|
const uniqueKey = `${resourcePath}-${node.id.value}`;
|
|
@@ -775,13 +847,23 @@ const transformSource = async (env) => {
|
|
|
775
847
|
if (propName) {
|
|
776
848
|
if (['create', 'createTheme', 'createStatic'].includes(propName) &&
|
|
777
849
|
!registeredStyleCalls.has(node.span.start)) {
|
|
778
|
-
throwCompilationError(`
|
|
850
|
+
throwCompilationError(`[plumeria] css.${propName} must be assigned to a named top-level variable. Destructuring, assignment statements, and default-exported calls cannot be compiled.`, node);
|
|
779
851
|
}
|
|
780
852
|
const args = node.arguments;
|
|
781
853
|
if (propName === 'keyframes' && args.length > 0) {
|
|
782
854
|
const expr = args[0].expression;
|
|
783
855
|
if (parser_1.t.isObjectExpression(expr)) {
|
|
784
856
|
const obj = (0, parser_1.objectExpressionToObject)(expr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
857
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(expr, obj, spreadValues([
|
|
858
|
+
mergedStaticTable,
|
|
859
|
+
mergedKeyframesTable,
|
|
860
|
+
mergedViewTransitionTable,
|
|
861
|
+
mergedCreateThemeHashTable,
|
|
862
|
+
scannedTables.createThemeObjectTable,
|
|
863
|
+
mergedCreateTable,
|
|
864
|
+
mergedCreateStaticHashTable,
|
|
865
|
+
scannedTables.createStaticObjectTable,
|
|
866
|
+
], localConstNodes, getSource));
|
|
785
867
|
const hash = (0, zss_engine_1.genBase36Hash)(obj, 1, 8);
|
|
786
868
|
scannedTables.keyframesObjectTable[hash] = obj;
|
|
787
869
|
replacements.push({
|
|
@@ -795,6 +877,16 @@ const transformSource = async (env) => {
|
|
|
795
877
|
args.length > 0 &&
|
|
796
878
|
parser_1.t.isObjectExpression(args[0].expression)) {
|
|
797
879
|
const obj = (0, parser_1.objectExpressionToObject)(args[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
880
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(args[0].expression, obj, spreadValues([
|
|
881
|
+
mergedStaticTable,
|
|
882
|
+
mergedKeyframesTable,
|
|
883
|
+
mergedViewTransitionTable,
|
|
884
|
+
mergedCreateThemeHashTable,
|
|
885
|
+
scannedTables.createThemeObjectTable,
|
|
886
|
+
mergedCreateTable,
|
|
887
|
+
mergedCreateStaticHashTable,
|
|
888
|
+
scannedTables.createStaticObjectTable,
|
|
889
|
+
], localConstNodes, getSource));
|
|
798
890
|
const hash = (0, zss_engine_1.genBase36Hash)(obj, 1, 8);
|
|
799
891
|
scannedTables.viewTransitionObjectTable[hash] = obj;
|
|
800
892
|
replacements.push({
|
|
@@ -809,13 +901,23 @@ const transformSource = async (env) => {
|
|
|
809
901
|
const selectorExpr = args[0].expression;
|
|
810
902
|
const selector = (0, parser_1.resolveThemeSelector)(selectorExpr, mergedStaticTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
811
903
|
if (!selector) {
|
|
812
|
-
throwCompilationError(`
|
|
904
|
+
throwCompilationError(`[plumeria] createTheme needs a selector it can read at build time. ` +
|
|
813
905
|
`Pass a string literal such as ".dark", or a name this file declares as one.`, selectorExpr);
|
|
814
906
|
}
|
|
815
907
|
if (selector.startsWith('@') && !(0, zss_engine_1.isAtRule)(selector)) {
|
|
816
|
-
throwCompilationError(`
|
|
908
|
+
throwCompilationError(`[plumeria] Unsupported at-rule: "${selector}". createTheme only supports nesting at-rules such as @media, @container, @supports, @layer, and @scope.`, selectorExpr);
|
|
817
909
|
}
|
|
818
910
|
const obj = (0, parser_1.objectExpressionToObject)(args[1].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
911
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(args[1].expression, obj, spreadValues([
|
|
912
|
+
mergedStaticTable,
|
|
913
|
+
mergedKeyframesTable,
|
|
914
|
+
mergedViewTransitionTable,
|
|
915
|
+
mergedCreateThemeHashTable,
|
|
916
|
+
scannedTables.createThemeObjectTable,
|
|
917
|
+
mergedCreateTable,
|
|
918
|
+
mergedCreateStaticHashTable,
|
|
919
|
+
scannedTables.createStaticObjectTable,
|
|
920
|
+
], localConstNodes, getSource));
|
|
819
921
|
const hash = (0, createTheme_1.themeHashOf)(selector, obj);
|
|
820
922
|
scannedTables.createThemeObjectTable[hash] = obj;
|
|
821
923
|
if (scannedTables.createThemeSelectorTable) {
|
|
@@ -826,6 +928,16 @@ const transformSource = async (env) => {
|
|
|
826
928
|
args.length > 0 &&
|
|
827
929
|
parser_1.t.isObjectExpression(args[0].expression)) {
|
|
828
930
|
const obj = (0, parser_1.objectExpressionToObject)(args[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable);
|
|
931
|
+
(0, unresolvableValue_1.assertNoDroppedValues)(args[0].expression, obj, spreadValues([
|
|
932
|
+
mergedStaticTable,
|
|
933
|
+
mergedKeyframesTable,
|
|
934
|
+
mergedViewTransitionTable,
|
|
935
|
+
mergedCreateThemeHashTable,
|
|
936
|
+
scannedTables.createThemeObjectTable,
|
|
937
|
+
mergedCreateTable,
|
|
938
|
+
mergedCreateStaticHashTable,
|
|
939
|
+
scannedTables.createStaticObjectTable,
|
|
940
|
+
], localConstNodes, getSource));
|
|
829
941
|
const hash = (0, zss_engine_1.genBase36Hash)(obj, 1, 8);
|
|
830
942
|
scannedTables.createStaticObjectTable[hash] = obj;
|
|
831
943
|
}
|
|
@@ -901,11 +1013,6 @@ const transformSource = async (env) => {
|
|
|
901
1013
|
}
|
|
902
1014
|
Object.values(n).forEach((val) => excludeSubtreeSpans(val));
|
|
903
1015
|
};
|
|
904
|
-
const getSource = (node) => {
|
|
905
|
-
const start = node.span.start - baseByteOffset;
|
|
906
|
-
const end = node.span.end - baseByteOffset;
|
|
907
|
-
return sourceBuffer.subarray(start, end).toString('utf-8');
|
|
908
|
-
};
|
|
909
1016
|
const isVisibleReference = (expr) => {
|
|
910
1017
|
if (parser_1.t.isIdentifier(expr))
|
|
911
1018
|
return referenceIdents.references.has(expr.span.start);
|
|
@@ -1041,7 +1148,7 @@ const transformSource = async (env) => {
|
|
|
1041
1148
|
const argExpr = callArgs[0]?.expression;
|
|
1042
1149
|
if (callArgs.length > 1 ||
|
|
1043
1150
|
(argExpr && argExpr.type !== 'ObjectExpression')) {
|
|
1044
|
-
throwCompilationError(`
|
|
1151
|
+
throwCompilationError(`[plumeria] ${getSource(expr)} takes one object argument, because ${callee.property.value} destructures its parameter.`, expr);
|
|
1045
1152
|
}
|
|
1046
1153
|
const given = new Map();
|
|
1047
1154
|
(argExpr?.properties ?? []).forEach((prop) => {
|
|
@@ -1061,7 +1168,7 @@ const transformSource = async (env) => {
|
|
|
1061
1168
|
if (!source) {
|
|
1062
1169
|
if (func.defaults?.[local])
|
|
1063
1170
|
return;
|
|
1064
|
-
return throwCompilationError(`
|
|
1171
|
+
return throwCompilationError(`[plumeria] ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
|
|
1065
1172
|
}
|
|
1066
1173
|
if (!forceRuntime &&
|
|
1067
1174
|
isStaticArgValue(source) &&
|
|
@@ -1280,7 +1387,7 @@ const transformSource = async (env) => {
|
|
|
1280
1387
|
const dynamic = resolveDynamicCall(node);
|
|
1281
1388
|
if (dynamic) {
|
|
1282
1389
|
if (!isStyleProp) {
|
|
1283
|
-
throwCompilationError(`
|
|
1390
|
+
throwCompilationError(`[plumeria] css.use(${getSource(node)}) does not support dynamic function keys.`, node);
|
|
1284
1391
|
}
|
|
1285
1392
|
branchStyle = dynamic.style;
|
|
1286
1393
|
dynamic.vars.forEach((v) => dynamicVars.push({
|
|
@@ -1409,7 +1516,7 @@ const transformSource = async (env) => {
|
|
|
1409
1516
|
}
|
|
1410
1517
|
const carriesVars = possibilities.some((entry) => entry.hasVars);
|
|
1411
1518
|
if (carriesVars && !isStyleProp) {
|
|
1412
|
-
throwCompilationError(`
|
|
1519
|
+
throwCompilationError(`[plumeria] "${varName}" carries a dynamic function key, and css.use() returns only a class name. ` +
|
|
1413
1520
|
`Apply it to ${styleProp} on the element instead.`, expr);
|
|
1414
1521
|
}
|
|
1415
1522
|
const gate = gates.length ? `(${gates.join(' && ')}) && ` : '';
|
|
@@ -2169,7 +2276,7 @@ const transformSource = async (env) => {
|
|
|
2169
2276
|
}
|
|
2170
2277
|
const { classParts, isOptimizable, baseStyle, dynamicVars, propVarSpreads, } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
|
|
2171
2278
|
if (!isOptimizable) {
|
|
2172
|
-
throwCompilationError(`
|
|
2279
|
+
throwCompilationError(`[plumeria] Dynamic or unresolvable style object "${getSource(expr)}" is not supported.`, expr);
|
|
2173
2280
|
}
|
|
2174
2281
|
const styleParts = [
|
|
2175
2282
|
...existingStyleParts,
|
|
@@ -2239,7 +2346,7 @@ const transformSource = async (env) => {
|
|
|
2239
2346
|
const propKey = callee.property.value;
|
|
2240
2347
|
const styleInfo = localCreateStyles[varName];
|
|
2241
2348
|
if (styleInfo?.functions?.[propKey]) {
|
|
2242
|
-
throwCompilationError(`
|
|
2349
|
+
throwCompilationError(`[plumeria] css.use(${getSource(expr)}) does not support dynamic function keys.`, expr);
|
|
2243
2350
|
}
|
|
2244
2351
|
}
|
|
2245
2352
|
const { classParts, isOptimizable } = buildClassParts(args);
|
|
@@ -2252,7 +2359,7 @@ const transformSource = async (env) => {
|
|
|
2252
2359
|
});
|
|
2253
2360
|
}
|
|
2254
2361
|
else {
|
|
2255
|
-
throwCompilationError(`
|
|
2362
|
+
throwCompilationError(`[plumeria] Dynamic or unresolvable style object "${getSource(node)}" is not supported.`, node);
|
|
2256
2363
|
}
|
|
2257
2364
|
},
|
|
2258
2365
|
});
|
|
@@ -2262,7 +2369,7 @@ const transformSource = async (env) => {
|
|
|
2262
2369
|
if (appliedStyleProps.has(`${name}:${propName}`)) {
|
|
2263
2370
|
continue;
|
|
2264
2371
|
}
|
|
2265
|
-
throwCompilationError(`
|
|
2372
|
+
throwCompilationError(`[plumeria] "${propName}" is a style received through a prop but is never applied ` +
|
|
2266
2373
|
`to ${styleProp} or css.use() here. Apply it on an element this component renders; ` +
|
|
2267
2374
|
`a style prop cannot be passed on to another component.`, node);
|
|
2268
2375
|
}
|
|
@@ -2272,7 +2379,7 @@ const transformSource = async (env) => {
|
|
|
2272
2379
|
if (!isDev || keys.length === 0) {
|
|
2273
2380
|
return JSON.stringify('');
|
|
2274
2381
|
}
|
|
2275
|
-
const head = JSON.stringify(`
|
|
2382
|
+
const head = JSON.stringify(`[plumeria] "${info.name}.`);
|
|
2276
2383
|
const tail = JSON.stringify(`" was read at runtime. The file that read it was not compiled ` +
|
|
2277
2384
|
`because it does not reference "@plumeria/core" — add ` +
|
|
2278
2385
|
`import '@plumeria/core'; to it (see the stack below). Defined in ` +
|
|
@@ -2303,7 +2410,7 @@ const transformSource = async (env) => {
|
|
|
2303
2410
|
const end = call.span.end - baseByteOffset;
|
|
2304
2411
|
const isResolved = replacements.some((r) => r.start <= start && r.end >= end);
|
|
2305
2412
|
if (!isResolved) {
|
|
2306
|
-
throwCompilationError(`
|
|
2413
|
+
throwCompilationError(`[plumeria] ${getSource(call)} is only supported in the ${styleProp} prop. ` +
|
|
2307
2414
|
`A dynamic style function resolves to a class name and a CSS variable on the element itself, ` +
|
|
2308
2415
|
`so it cannot be passed through another prop or read as a value.`, call);
|
|
2309
2416
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ObjectExpression, SpreadElement } from '@swc/core';
|
|
2
|
+
type SourceObject = ObjectExpression;
|
|
3
|
+
type StaticValues = Record<string, unknown>;
|
|
4
|
+
type SpreadInfo = {
|
|
5
|
+
values: StaticValues;
|
|
6
|
+
source?: SourceObject;
|
|
7
|
+
};
|
|
8
|
+
type ResolveSpread = (prop: SpreadElement) => SpreadInfo | null;
|
|
9
|
+
export type StyleContext = {
|
|
10
|
+
spread: ResolveSpread;
|
|
11
|
+
resolvesValue: (value: unknown) => boolean;
|
|
12
|
+
sourceOf?: (node: unknown) => string;
|
|
13
|
+
};
|
|
14
|
+
export declare const assertNoDroppedValues: (node: SourceObject, resolved: StaticValues | null | undefined, context?: StyleContext, ignore?: Set<string>) => void;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertNoDroppedValues = void 0;
|
|
4
|
+
const keyValue = (key) => {
|
|
5
|
+
if (!key || typeof key !== 'object' || !('value' in key))
|
|
6
|
+
return;
|
|
7
|
+
const value = key.value;
|
|
8
|
+
return typeof value === 'string' || typeof value === 'number'
|
|
9
|
+
? value
|
|
10
|
+
: undefined;
|
|
11
|
+
};
|
|
12
|
+
const unresolvableValue = (key, node, context) => new Error(`[plumeria] Cannot resolve the value of "${key}" at build time (${context?.sourceOf?.(node) ?? node.type}). ` +
|
|
13
|
+
`Values in css.create must be statically known; pass a runtime value through the ` +
|
|
14
|
+
`function form, css.create({ name: (value) => ({ ... }) }).`);
|
|
15
|
+
const overwrittenLater = (properties, index, key, context) => {
|
|
16
|
+
for (let i = index + 1; i < properties.length; i++) {
|
|
17
|
+
const later = properties[i];
|
|
18
|
+
if (later.type === 'SpreadElement') {
|
|
19
|
+
const provided = context?.spread(later);
|
|
20
|
+
if (!provided)
|
|
21
|
+
return true;
|
|
22
|
+
if (String(key) in provided.values)
|
|
23
|
+
return true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (later.type === 'KeyValueProperty' && keyValue(later.key) === key)
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
};
|
|
31
|
+
const RUNTIME_ONLY = new Set([
|
|
32
|
+
'CallExpression',
|
|
33
|
+
'NewExpression',
|
|
34
|
+
'AwaitExpression',
|
|
35
|
+
'TaggedTemplateExpression',
|
|
36
|
+
'YieldExpression',
|
|
37
|
+
]);
|
|
38
|
+
const readsAnyOf = (node, names) => {
|
|
39
|
+
if (!node || typeof node !== 'object')
|
|
40
|
+
return false;
|
|
41
|
+
const value = node;
|
|
42
|
+
if (value.type === 'Identifier' && typeof value.value === 'string')
|
|
43
|
+
return names.has(value.value);
|
|
44
|
+
for (const key of Object.keys(value)) {
|
|
45
|
+
if (key === 'span')
|
|
46
|
+
continue;
|
|
47
|
+
const child = value[key];
|
|
48
|
+
if (Array.isArray(child)) {
|
|
49
|
+
for (const item of child)
|
|
50
|
+
if (readsAnyOf(item, names))
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
else if (readsAnyOf(child, names))
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
};
|
|
58
|
+
const parameterNames = (func) => {
|
|
59
|
+
const names = new Set();
|
|
60
|
+
const add = (pattern) => {
|
|
61
|
+
if (!pattern || typeof pattern !== 'object')
|
|
62
|
+
return;
|
|
63
|
+
if (pattern.type === 'Identifier' && typeof pattern.value === 'string')
|
|
64
|
+
names.add(pattern.value);
|
|
65
|
+
for (const key of ['pat', 'left', 'argument', 'value', 'key'])
|
|
66
|
+
if (pattern[key])
|
|
67
|
+
add(pattern[key]);
|
|
68
|
+
for (const key of ['properties', 'elements'])
|
|
69
|
+
if (Array.isArray(pattern[key]))
|
|
70
|
+
pattern[key].forEach(add);
|
|
71
|
+
};
|
|
72
|
+
(func.params ?? []).forEach((param) => add(param?.pat ?? param));
|
|
73
|
+
return names;
|
|
74
|
+
};
|
|
75
|
+
const assertStyleFunction = (func, context) => {
|
|
76
|
+
const params = parameterNames(func);
|
|
77
|
+
const body = func.body;
|
|
78
|
+
const returned = body?.type === 'ObjectExpression'
|
|
79
|
+
? body
|
|
80
|
+
: body?.type === 'ParenthesisExpression' &&
|
|
81
|
+
body.expression?.type === 'ObjectExpression'
|
|
82
|
+
? body.expression
|
|
83
|
+
: null;
|
|
84
|
+
if (!returned)
|
|
85
|
+
return;
|
|
86
|
+
const walk = (object) => {
|
|
87
|
+
const properties = object.properties ?? [];
|
|
88
|
+
for (let index = 0; index < properties.length; index++) {
|
|
89
|
+
const prop = properties[index];
|
|
90
|
+
if (prop.type !== 'KeyValueProperty')
|
|
91
|
+
continue;
|
|
92
|
+
const key = keyValue(prop.key);
|
|
93
|
+
if ((typeof key === 'string' || typeof key === 'number') &&
|
|
94
|
+
overwrittenLater(properties, index, key, context))
|
|
95
|
+
continue;
|
|
96
|
+
const value = prop.value;
|
|
97
|
+
if (!value)
|
|
98
|
+
continue;
|
|
99
|
+
if (value.type === 'ObjectExpression') {
|
|
100
|
+
walk(value);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (RUNTIME_ONLY.has(value.type) && !readsAnyOf(value, params))
|
|
104
|
+
throw unresolvableValue(key ?? '', value, context);
|
|
105
|
+
if ((value.type === 'Identifier' || value.type === 'MemberExpression') &&
|
|
106
|
+
context &&
|
|
107
|
+
!readsAnyOf(value, params) &&
|
|
108
|
+
!context.resolvesValue(value))
|
|
109
|
+
throw unresolvableValue(key ?? '', value, context);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
walk(returned);
|
|
113
|
+
};
|
|
114
|
+
const keysSuppliedAfter = (properties, index, context) => {
|
|
115
|
+
const keys = new Set();
|
|
116
|
+
for (let i = index + 1; i < properties.length; i++) {
|
|
117
|
+
const later = properties[i];
|
|
118
|
+
if (later.type === 'SpreadElement') {
|
|
119
|
+
const provided = context?.spread(later);
|
|
120
|
+
if (!provided)
|
|
121
|
+
return null;
|
|
122
|
+
for (const name of Object.keys(provided.values))
|
|
123
|
+
keys.add(name);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (later.type !== 'KeyValueProperty')
|
|
127
|
+
continue;
|
|
128
|
+
const key = keyValue(later.key);
|
|
129
|
+
if (typeof key !== 'string' && typeof key !== 'number')
|
|
130
|
+
return null;
|
|
131
|
+
keys.add(String(key));
|
|
132
|
+
}
|
|
133
|
+
return keys;
|
|
134
|
+
};
|
|
135
|
+
const assertNoDroppedValues = (node, resolved, context, ignore) => {
|
|
136
|
+
if (!resolved)
|
|
137
|
+
return;
|
|
138
|
+
for (let index = 0; index < node.properties.length; index++) {
|
|
139
|
+
const prop = node.properties[index];
|
|
140
|
+
if (prop.type === 'SpreadElement') {
|
|
141
|
+
const provided = context?.spread(prop);
|
|
142
|
+
if (!provided?.source)
|
|
143
|
+
continue;
|
|
144
|
+
const replaced = keysSuppliedAfter(node.properties, index, context);
|
|
145
|
+
if (replaced)
|
|
146
|
+
(0, exports.assertNoDroppedValues)(provided.source, provided.values, context, replaced);
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (prop.type !== 'KeyValueProperty')
|
|
150
|
+
continue;
|
|
151
|
+
const key = keyValue(prop.key);
|
|
152
|
+
if (typeof key !== 'string' && typeof key !== 'number')
|
|
153
|
+
continue;
|
|
154
|
+
if (ignore?.has(String(key)))
|
|
155
|
+
continue;
|
|
156
|
+
if (overwrittenLater(node.properties, index, key, context))
|
|
157
|
+
continue;
|
|
158
|
+
const value = prop.value;
|
|
159
|
+
if (!value)
|
|
160
|
+
continue;
|
|
161
|
+
if (value.type === 'ArrowFunctionExpression' ||
|
|
162
|
+
value.type === 'FunctionExpression') {
|
|
163
|
+
assertStyleFunction(value, context);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (value.type === 'NullLiteral')
|
|
167
|
+
continue;
|
|
168
|
+
if (!(key in resolved))
|
|
169
|
+
throw unresolvableValue(key, value, context);
|
|
170
|
+
if (value.type === 'ObjectExpression') {
|
|
171
|
+
const nested = resolved[key];
|
|
172
|
+
if (nested && typeof nested === 'object')
|
|
173
|
+
(0, exports.assertNoDroppedValues)(value, nested, context);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
exports.assertNoDroppedValues = assertNoDroppedValues;
|