@bamboocss/parser 1.21.0 → 1.22.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/dist/index.cjs +10 -116
- package/dist/index.d.cts +23 -59
- package/dist/index.d.mts +23 -59
- package/dist/index.mjs +10 -116
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -523,14 +523,7 @@ const writtenProps = (node) => {
|
|
|
523
523
|
uncertain
|
|
524
524
|
};
|
|
525
525
|
};
|
|
526
|
-
/**
|
|
527
|
-
* Every property of a `css()` call that will not reach the stylesheet.
|
|
528
|
-
*
|
|
529
|
-
* Only meaningful under `cssMode: 'grouped'`, where one class names the whole call: a
|
|
530
|
-
* property the build cannot resolve does not merely go missing, it changes the class, and
|
|
531
|
-
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
532
|
-
* declaration and keeps the rest, which is why this is not reported there.
|
|
533
|
-
*/
|
|
526
|
+
/** Every property of a `css()` call that will not reach the stylesheet. */
|
|
534
527
|
/**
|
|
535
528
|
* A recipe config the build could not fully read, level by level.
|
|
536
529
|
*
|
|
@@ -675,12 +668,6 @@ const hasKeyOutside = (resolved, names) => {
|
|
|
675
668
|
};
|
|
676
669
|
//#endregion
|
|
677
670
|
//#region src/parser-result.ts
|
|
678
|
-
function cartesian(arrays) {
|
|
679
|
-
if (arrays.length === 0) return [[]];
|
|
680
|
-
const [first, ...rest] = arrays;
|
|
681
|
-
const restProduct = cartesian(rest);
|
|
682
|
-
return first.flatMap((item) => restProduct.map((combo) => [item, ...combo]));
|
|
683
|
-
}
|
|
684
671
|
var ParserResult = class {
|
|
685
672
|
context;
|
|
686
673
|
/** Ordered list of all ResultItem */
|
|
@@ -697,35 +684,14 @@ var ParserResult = class {
|
|
|
697
684
|
/**
|
|
698
685
|
* `css()` calls whose styles the build could not fully see.
|
|
699
686
|
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
702
|
-
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
703
|
-
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
687
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
688
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
704
689
|
*/
|
|
705
690
|
unresolved = [];
|
|
706
691
|
constructor(context, encoder) {
|
|
707
692
|
this.context = context;
|
|
708
693
|
this.encoder = encoder ?? context.encoder;
|
|
709
694
|
}
|
|
710
|
-
/**
|
|
711
|
-
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
712
|
-
*
|
|
713
|
-
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
714
|
-
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
715
|
-
*/
|
|
716
|
-
reportUnresolved(result, reason) {
|
|
717
|
-
const node = result.box?.getNode();
|
|
718
|
-
const sourceFile = node?.getSourceFile();
|
|
719
|
-
if (!node || !sourceFile) return;
|
|
720
|
-
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
721
|
-
this.unresolved.push({
|
|
722
|
-
filePath: sourceFile.getFilePath(),
|
|
723
|
-
kind: "grouped",
|
|
724
|
-
line,
|
|
725
|
-
column,
|
|
726
|
-
reason
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
695
|
append(result) {
|
|
730
696
|
this.all.push(result);
|
|
731
697
|
return result;
|
|
@@ -750,62 +716,10 @@ var ParserResult = class {
|
|
|
750
716
|
setCss(result) {
|
|
751
717
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
752
718
|
const encoder = this.encoder;
|
|
753
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
754
719
|
const data = result.data.some(Array.isArray) ? result.data.flatMap((obj) => Array.isArray(obj) ? obj : [obj]) : result.data;
|
|
755
|
-
const unresolved = findUnresolvedStyles(result,
|
|
756
|
-
if (unresolved.length)
|
|
757
|
-
|
|
758
|
-
if (grouped) data.forEach((obj) => encoder.processAtomic(obj));
|
|
759
|
-
}
|
|
760
|
-
if (!grouped || data.length <= 1) {
|
|
761
|
-
data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
|
|
762
|
-
return;
|
|
763
|
-
}
|
|
764
|
-
const keyCounts = /* @__PURE__ */ new Map();
|
|
765
|
-
for (const obj of data) for (const key of Object.keys(obj)) keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
|
|
766
|
-
if (!Array.from(keyCounts.values()).some((c) => c > 1)) {
|
|
767
|
-
encoder.processGroupedMerge(data);
|
|
768
|
-
return;
|
|
769
|
-
}
|
|
770
|
-
if (this.callArgumentCount(result) > 1) {
|
|
771
|
-
this.reportUnresolved(result, "ambiguous-merge");
|
|
772
|
-
data.forEach((obj) => encoder.processAtomic(obj));
|
|
773
|
-
}
|
|
774
|
-
const overlappingKeys = /* @__PURE__ */ new Set();
|
|
775
|
-
keyCounts.forEach((count, key) => {
|
|
776
|
-
if (count > 1) overlappingKeys.add(key);
|
|
777
|
-
});
|
|
778
|
-
const baseEntries = [];
|
|
779
|
-
const branchEntries = [];
|
|
780
|
-
for (const obj of data) if (Object.keys(obj).some((k) => overlappingKeys.has(k))) branchEntries.push(obj);
|
|
781
|
-
else baseEntries.push(obj);
|
|
782
|
-
const branchGroups = /* @__PURE__ */ new Map();
|
|
783
|
-
for (const entry of branchEntries) {
|
|
784
|
-
const keySet = Object.keys(entry).sort().join("\0");
|
|
785
|
-
const group = branchGroups.get(keySet) || [];
|
|
786
|
-
group.push(entry);
|
|
787
|
-
branchGroups.set(keySet, group);
|
|
788
|
-
}
|
|
789
|
-
const groupArrays = Array.from(branchGroups.values());
|
|
790
|
-
if (groupArrays.reduce((acc, g) => acc * g.length, 1) > 32) {
|
|
791
|
-
this.reportUnresolved(result, "too-many-combinations");
|
|
792
|
-
data.forEach((obj) => {
|
|
793
|
-
encoder.processGrouped(obj);
|
|
794
|
-
encoder.processAtomic(obj);
|
|
795
|
-
});
|
|
796
|
-
return;
|
|
797
|
-
}
|
|
798
|
-
for (const combo of cartesian(groupArrays)) encoder.processGroupedMerge([...baseEntries, ...combo]);
|
|
799
|
-
}
|
|
800
|
-
/**
|
|
801
|
-
* How many arguments the call this result came from was written with.
|
|
802
|
-
*
|
|
803
|
-
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
804
|
-
* since the question only separates operands from branches and neither has operands.
|
|
805
|
-
*/
|
|
806
|
-
callArgumentCount(result) {
|
|
807
|
-
const node = result.box?.getNode();
|
|
808
|
-
return node && ts_morph.Node.isCallExpression(node) ? node.getArguments().length : 1;
|
|
720
|
+
const unresolved = findUnresolvedStyles(result, "atomic").filter((entry) => entry.reason === "unenumerable-keys");
|
|
721
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
722
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
809
723
|
}
|
|
810
724
|
setCva(result) {
|
|
811
725
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -822,10 +736,9 @@ var ParserResult = class {
|
|
|
822
736
|
/**
|
|
823
737
|
* Record a recipe config the build could not fully read.
|
|
824
738
|
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
739
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
740
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
741
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
829
742
|
* another, and the element renders with no styles at all.
|
|
830
743
|
*
|
|
831
744
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -853,26 +766,7 @@ var ParserResult = class {
|
|
|
853
766
|
type: "pattern",
|
|
854
767
|
name
|
|
855
768
|
}, result)));
|
|
856
|
-
|
|
857
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
858
|
-
result.data.forEach((obj) => encoder.processPattern(name, obj, grouped));
|
|
859
|
-
if (grouped && !this.groupIsExact(result)) result.data.forEach((obj) => encoder.processPattern(name, obj, false));
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
863
|
-
*
|
|
864
|
-
* True only when the build saw the whole thing at once: one style object, with every
|
|
865
|
-
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
866
|
-
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
867
|
-
* an unresolved value means the merge would not have matched anyway.
|
|
868
|
-
*
|
|
869
|
-
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
870
|
-
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
871
|
-
* deliberately conservative.
|
|
872
|
-
*/
|
|
873
|
-
groupIsExact(result) {
|
|
874
|
-
if (result.data.length !== 1) return false;
|
|
875
|
-
return findUnresolvedStyles(result, "grouped").length === 0;
|
|
769
|
+
result.data.forEach((obj) => this.encoder.processPattern(name, obj));
|
|
876
770
|
}
|
|
877
771
|
setRecipe(recipeName, result) {
|
|
878
772
|
(0, _bamboocss_shared.getOrCreateSet)(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
package/dist/index.d.cts
CHANGED
|
@@ -38,7 +38,7 @@ declare class Generator extends Context {
|
|
|
38
38
|
*
|
|
39
39
|
* `keep` carries references this cannot see for itself; see `collectTokenReferences`.
|
|
40
40
|
*/
|
|
41
|
-
pruneTokens: (sheet: Stylesheet, keep?: Set<string
|
|
41
|
+
pruneTokens: (sheet: Stylesheet, keep?: Set<string>, tokensReachableFromJs?: boolean) => {
|
|
42
42
|
removed: number;
|
|
43
43
|
kept: number;
|
|
44
44
|
removedProperties?: undefined;
|
|
@@ -47,6 +47,18 @@ declare class Generator extends Context {
|
|
|
47
47
|
removedProperties: number;
|
|
48
48
|
kept: number;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Drop the parts of the reset that style elements the source never renders.
|
|
52
|
+
*
|
|
53
|
+
* Off unless asked for. Unlike the token and keyframe passes there is no way to prove this
|
|
54
|
+
* from the build: an element rendered by a dependency, by `dangerouslySetInnerHTML` or by
|
|
55
|
+
* markdown is invisible to a scan of your own source, and the failure is an element quietly
|
|
56
|
+
* losing its reset rather than anything that reports itself.
|
|
57
|
+
*/
|
|
58
|
+
prunePreflight: (sheet: Stylesheet, rendered: Set<string>) => {
|
|
59
|
+
removedRules: number;
|
|
60
|
+
removedParts: number;
|
|
61
|
+
} | undefined;
|
|
50
62
|
/**
|
|
51
63
|
* Drop `@keyframes` nothing can reach. Same completeness requirement as
|
|
52
64
|
* `pruneTokens`: the sheet has to hold the whole stylesheet, or every keyframe looks
|
|
@@ -98,20 +110,6 @@ declare class Generator extends Context {
|
|
|
98
110
|
*/
|
|
99
111
|
private getAlwaysKeptTokenVars;
|
|
100
112
|
getParserCss: (decoder: StyleDecoder) => string;
|
|
101
|
-
/**
|
|
102
|
-
* The grouped class names this build emitted a rule for.
|
|
103
|
-
*
|
|
104
|
-
* Derived from the encoder rather than the decoder, so it is available as soon as
|
|
105
|
-
* extraction finishes and before a stylesheet exists. Both sides go through
|
|
106
|
-
* `groupClassName`, which is the same function the browser runtime calls — a registry
|
|
107
|
-
* built any other way would be a third spelling of a name that already has two.
|
|
108
|
-
*
|
|
109
|
-
* Unescaped, unlike `StyleDecoder`'s class names: this is compared against what `css()`
|
|
110
|
-
* returns into a `class` attribute, not against a selector. A grouped class is an opaque
|
|
111
|
-
* hash, so the two only differ in principle, but the principle is the one that matters
|
|
112
|
-
* here — the registry is only useful if it holds exactly what the runtime will ask about.
|
|
113
|
-
*/
|
|
114
|
-
getGroupRegistry: () => string[];
|
|
115
113
|
getCss: (stylesheet?: Stylesheet) => string;
|
|
116
114
|
/**
|
|
117
115
|
* Get CSS for a specific layer from the stylesheet
|
|
@@ -143,16 +141,13 @@ interface UnresolvedStyle {
|
|
|
143
141
|
/**
|
|
144
142
|
* What the loss costs, which decides how it is explained.
|
|
145
143
|
*
|
|
146
|
-
* - `
|
|
147
|
-
*
|
|
148
|
-
* so the ones it resolved still apply.
|
|
149
|
-
* - `atomic` — a `css()` call under `cssMode: 'atomic'`. The declarations the build saw
|
|
150
|
-
* still apply; the ones it did not have no rule behind them, so they are simply absent.
|
|
144
|
+
* - `atomic` — a `css()` call. The declarations the build saw still apply; the ones it
|
|
145
|
+
* did not have no rule behind them, so they are simply absent.
|
|
151
146
|
* - `recipe` — a `cva`/`sva` config. There is no degrading. A recipe's classes are named
|
|
152
147
|
* from a hash of its config, so a declaration the build cannot see gives the two sides
|
|
153
148
|
* different names and *every* rule misses.
|
|
154
149
|
*/
|
|
155
|
-
kind: '
|
|
150
|
+
kind: 'atomic' | 'recipe';
|
|
156
151
|
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
157
152
|
prop?: string;
|
|
158
153
|
filePath: string;
|
|
@@ -164,13 +159,12 @@ interface UnresolvedStyle {
|
|
|
164
159
|
* - `unresolvable-value` — a value it could not evaluate.
|
|
165
160
|
* - `missing-property` — a key that never arrived in the box tree at all.
|
|
166
161
|
* - `unenumerable-keys` — a spread or computed key, so it cannot say what the call sets.
|
|
167
|
-
* - `ambiguous-merge` — two arguments setting one property, which it cannot tell from a
|
|
168
162
|
* pair of alternatives.
|
|
169
163
|
* - `too-many-combinations` — more ternary branches than it will enumerate.
|
|
170
164
|
*/
|
|
171
|
-
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys'
|
|
165
|
+
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys';
|
|
172
166
|
}
|
|
173
|
-
declare const findUnresolvedStyles: (item: ResultItem, kind: "
|
|
167
|
+
declare const findUnresolvedStyles: (item: ResultItem, kind: "atomic") => UnresolvedStyle[];
|
|
174
168
|
//#endregion
|
|
175
169
|
//#region src/parser-result.d.ts
|
|
176
170
|
declare class ParserResult implements ParserResultInterface {
|
|
@@ -189,39 +183,22 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
189
183
|
/**
|
|
190
184
|
* `css()` calls whose styles the build could not fully see.
|
|
191
185
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
195
|
-
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
186
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
187
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
196
188
|
*/
|
|
197
189
|
unresolved: UnresolvedStyle[];
|
|
198
190
|
constructor(context: ParserOptions, encoder?: ParserOptions['encoder']);
|
|
199
|
-
/**
|
|
200
|
-
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
201
|
-
*
|
|
202
|
-
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
203
|
-
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
204
|
-
*/
|
|
205
|
-
private reportUnresolved;
|
|
206
191
|
append(result: ResultItem): ResultItem;
|
|
207
192
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
208
193
|
setCss(result: ResultItem): void;
|
|
209
|
-
/**
|
|
210
|
-
* How many arguments the call this result came from was written with.
|
|
211
|
-
*
|
|
212
|
-
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
213
|
-
* since the question only separates operands from branches and neither has operands.
|
|
214
|
-
*/
|
|
215
|
-
private callArgumentCount;
|
|
216
194
|
setCva(result: ResultItem): void;
|
|
217
195
|
setSva(result: ResultItem): void;
|
|
218
196
|
/**
|
|
219
197
|
* Record a recipe config the build could not fully read.
|
|
220
198
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
199
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
200
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
201
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
225
202
|
* another, and the element renders with no styles at all.
|
|
226
203
|
*
|
|
227
204
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -232,19 +209,6 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
232
209
|
setToken(result: ResultItem): void;
|
|
233
210
|
setViewTransition(result: ResultItem): void;
|
|
234
211
|
setPattern(name: string, result: ResultItem): void;
|
|
235
|
-
/**
|
|
236
|
-
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
237
|
-
*
|
|
238
|
-
* True only when the build saw the whole thing at once: one style object, with every
|
|
239
|
-
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
240
|
-
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
241
|
-
* an unresolved value means the merge would not have matched anyway.
|
|
242
|
-
*
|
|
243
|
-
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
244
|
-
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
245
|
-
* deliberately conservative.
|
|
246
|
-
*/
|
|
247
|
-
private groupIsExact;
|
|
248
212
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
249
213
|
isEmpty(): boolean;
|
|
250
214
|
setFilePath(filePath: string): this;
|
package/dist/index.d.mts
CHANGED
|
@@ -38,7 +38,7 @@ declare class Generator extends Context {
|
|
|
38
38
|
*
|
|
39
39
|
* `keep` carries references this cannot see for itself; see `collectTokenReferences`.
|
|
40
40
|
*/
|
|
41
|
-
pruneTokens: (sheet: Stylesheet, keep?: Set<string
|
|
41
|
+
pruneTokens: (sheet: Stylesheet, keep?: Set<string>, tokensReachableFromJs?: boolean) => {
|
|
42
42
|
removed: number;
|
|
43
43
|
kept: number;
|
|
44
44
|
removedProperties?: undefined;
|
|
@@ -47,6 +47,18 @@ declare class Generator extends Context {
|
|
|
47
47
|
removedProperties: number;
|
|
48
48
|
kept: number;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Drop the parts of the reset that style elements the source never renders.
|
|
52
|
+
*
|
|
53
|
+
* Off unless asked for. Unlike the token and keyframe passes there is no way to prove this
|
|
54
|
+
* from the build: an element rendered by a dependency, by `dangerouslySetInnerHTML` or by
|
|
55
|
+
* markdown is invisible to a scan of your own source, and the failure is an element quietly
|
|
56
|
+
* losing its reset rather than anything that reports itself.
|
|
57
|
+
*/
|
|
58
|
+
prunePreflight: (sheet: Stylesheet, rendered: Set<string>) => {
|
|
59
|
+
removedRules: number;
|
|
60
|
+
removedParts: number;
|
|
61
|
+
} | undefined;
|
|
50
62
|
/**
|
|
51
63
|
* Drop `@keyframes` nothing can reach. Same completeness requirement as
|
|
52
64
|
* `pruneTokens`: the sheet has to hold the whole stylesheet, or every keyframe looks
|
|
@@ -98,20 +110,6 @@ declare class Generator extends Context {
|
|
|
98
110
|
*/
|
|
99
111
|
private getAlwaysKeptTokenVars;
|
|
100
112
|
getParserCss: (decoder: StyleDecoder) => string;
|
|
101
|
-
/**
|
|
102
|
-
* The grouped class names this build emitted a rule for.
|
|
103
|
-
*
|
|
104
|
-
* Derived from the encoder rather than the decoder, so it is available as soon as
|
|
105
|
-
* extraction finishes and before a stylesheet exists. Both sides go through
|
|
106
|
-
* `groupClassName`, which is the same function the browser runtime calls — a registry
|
|
107
|
-
* built any other way would be a third spelling of a name that already has two.
|
|
108
|
-
*
|
|
109
|
-
* Unescaped, unlike `StyleDecoder`'s class names: this is compared against what `css()`
|
|
110
|
-
* returns into a `class` attribute, not against a selector. A grouped class is an opaque
|
|
111
|
-
* hash, so the two only differ in principle, but the principle is the one that matters
|
|
112
|
-
* here — the registry is only useful if it holds exactly what the runtime will ask about.
|
|
113
|
-
*/
|
|
114
|
-
getGroupRegistry: () => string[];
|
|
115
113
|
getCss: (stylesheet?: Stylesheet) => string;
|
|
116
114
|
/**
|
|
117
115
|
* Get CSS for a specific layer from the stylesheet
|
|
@@ -143,16 +141,13 @@ interface UnresolvedStyle {
|
|
|
143
141
|
/**
|
|
144
142
|
* What the loss costs, which decides how it is explained.
|
|
145
143
|
*
|
|
146
|
-
* - `
|
|
147
|
-
*
|
|
148
|
-
* so the ones it resolved still apply.
|
|
149
|
-
* - `atomic` — a `css()` call under `cssMode: 'atomic'`. The declarations the build saw
|
|
150
|
-
* still apply; the ones it did not have no rule behind them, so they are simply absent.
|
|
144
|
+
* - `atomic` — a `css()` call. The declarations the build saw still apply; the ones it
|
|
145
|
+
* did not have no rule behind them, so they are simply absent.
|
|
151
146
|
* - `recipe` — a `cva`/`sva` config. There is no degrading. A recipe's classes are named
|
|
152
147
|
* from a hash of its config, so a declaration the build cannot see gives the two sides
|
|
153
148
|
* different names and *every* rule misses.
|
|
154
149
|
*/
|
|
155
|
-
kind: '
|
|
150
|
+
kind: 'atomic' | 'recipe';
|
|
156
151
|
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
157
152
|
prop?: string;
|
|
158
153
|
filePath: string;
|
|
@@ -164,13 +159,12 @@ interface UnresolvedStyle {
|
|
|
164
159
|
* - `unresolvable-value` — a value it could not evaluate.
|
|
165
160
|
* - `missing-property` — a key that never arrived in the box tree at all.
|
|
166
161
|
* - `unenumerable-keys` — a spread or computed key, so it cannot say what the call sets.
|
|
167
|
-
* - `ambiguous-merge` — two arguments setting one property, which it cannot tell from a
|
|
168
162
|
* pair of alternatives.
|
|
169
163
|
* - `too-many-combinations` — more ternary branches than it will enumerate.
|
|
170
164
|
*/
|
|
171
|
-
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys'
|
|
165
|
+
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys';
|
|
172
166
|
}
|
|
173
|
-
declare const findUnresolvedStyles: (item: ResultItem, kind: "
|
|
167
|
+
declare const findUnresolvedStyles: (item: ResultItem, kind: "atomic") => UnresolvedStyle[];
|
|
174
168
|
//#endregion
|
|
175
169
|
//#region src/parser-result.d.ts
|
|
176
170
|
declare class ParserResult implements ParserResultInterface {
|
|
@@ -189,39 +183,22 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
189
183
|
/**
|
|
190
184
|
* `css()` calls whose styles the build could not fully see.
|
|
191
185
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
195
|
-
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
186
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
187
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
196
188
|
*/
|
|
197
189
|
unresolved: UnresolvedStyle[];
|
|
198
190
|
constructor(context: ParserOptions, encoder?: ParserOptions['encoder']);
|
|
199
|
-
/**
|
|
200
|
-
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
201
|
-
*
|
|
202
|
-
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
203
|
-
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
204
|
-
*/
|
|
205
|
-
private reportUnresolved;
|
|
206
191
|
append(result: ResultItem): ResultItem;
|
|
207
192
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
208
193
|
setCss(result: ResultItem): void;
|
|
209
|
-
/**
|
|
210
|
-
* How many arguments the call this result came from was written with.
|
|
211
|
-
*
|
|
212
|
-
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
213
|
-
* since the question only separates operands from branches and neither has operands.
|
|
214
|
-
*/
|
|
215
|
-
private callArgumentCount;
|
|
216
194
|
setCva(result: ResultItem): void;
|
|
217
195
|
setSva(result: ResultItem): void;
|
|
218
196
|
/**
|
|
219
197
|
* Record a recipe config the build could not fully read.
|
|
220
198
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
199
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
200
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
201
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
225
202
|
* another, and the element renders with no styles at all.
|
|
226
203
|
*
|
|
227
204
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -232,19 +209,6 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
232
209
|
setToken(result: ResultItem): void;
|
|
233
210
|
setViewTransition(result: ResultItem): void;
|
|
234
211
|
setPattern(name: string, result: ResultItem): void;
|
|
235
|
-
/**
|
|
236
|
-
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
237
|
-
*
|
|
238
|
-
* True only when the build saw the whole thing at once: one style object, with every
|
|
239
|
-
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
240
|
-
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
241
|
-
* an unresolved value means the merge would not have matched anyway.
|
|
242
|
-
*
|
|
243
|
-
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
244
|
-
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
245
|
-
* deliberately conservative.
|
|
246
|
-
*/
|
|
247
|
-
private groupIsExact;
|
|
248
212
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
249
213
|
isEmpty(): boolean;
|
|
250
214
|
setFilePath(filePath: string): this;
|
package/dist/index.mjs
CHANGED
|
@@ -522,14 +522,7 @@ const writtenProps = (node) => {
|
|
|
522
522
|
uncertain
|
|
523
523
|
};
|
|
524
524
|
};
|
|
525
|
-
/**
|
|
526
|
-
* Every property of a `css()` call that will not reach the stylesheet.
|
|
527
|
-
*
|
|
528
|
-
* Only meaningful under `cssMode: 'grouped'`, where one class names the whole call: a
|
|
529
|
-
* property the build cannot resolve does not merely go missing, it changes the class, and
|
|
530
|
-
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
531
|
-
* declaration and keeps the rest, which is why this is not reported there.
|
|
532
|
-
*/
|
|
525
|
+
/** Every property of a `css()` call that will not reach the stylesheet. */
|
|
533
526
|
/**
|
|
534
527
|
* A recipe config the build could not fully read, level by level.
|
|
535
528
|
*
|
|
@@ -674,12 +667,6 @@ const hasKeyOutside = (resolved, names) => {
|
|
|
674
667
|
};
|
|
675
668
|
//#endregion
|
|
676
669
|
//#region src/parser-result.ts
|
|
677
|
-
function cartesian(arrays) {
|
|
678
|
-
if (arrays.length === 0) return [[]];
|
|
679
|
-
const [first, ...rest] = arrays;
|
|
680
|
-
const restProduct = cartesian(rest);
|
|
681
|
-
return first.flatMap((item) => restProduct.map((combo) => [item, ...combo]));
|
|
682
|
-
}
|
|
683
670
|
var ParserResult = class {
|
|
684
671
|
context;
|
|
685
672
|
/** Ordered list of all ResultItem */
|
|
@@ -696,35 +683,14 @@ var ParserResult = class {
|
|
|
696
683
|
/**
|
|
697
684
|
* `css()` calls whose styles the build could not fully see.
|
|
698
685
|
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
702
|
-
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
686
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
687
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
703
688
|
*/
|
|
704
689
|
unresolved = [];
|
|
705
690
|
constructor(context, encoder) {
|
|
706
691
|
this.context = context;
|
|
707
692
|
this.encoder = encoder ?? context.encoder;
|
|
708
693
|
}
|
|
709
|
-
/**
|
|
710
|
-
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
711
|
-
*
|
|
712
|
-
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
713
|
-
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
714
|
-
*/
|
|
715
|
-
reportUnresolved(result, reason) {
|
|
716
|
-
const node = result.box?.getNode();
|
|
717
|
-
const sourceFile = node?.getSourceFile();
|
|
718
|
-
if (!node || !sourceFile) return;
|
|
719
|
-
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
720
|
-
this.unresolved.push({
|
|
721
|
-
filePath: sourceFile.getFilePath(),
|
|
722
|
-
kind: "grouped",
|
|
723
|
-
line,
|
|
724
|
-
column,
|
|
725
|
-
reason
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
694
|
append(result) {
|
|
729
695
|
this.all.push(result);
|
|
730
696
|
return result;
|
|
@@ -749,62 +715,10 @@ var ParserResult = class {
|
|
|
749
715
|
setCss(result) {
|
|
750
716
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
751
717
|
const encoder = this.encoder;
|
|
752
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
753
718
|
const data = result.data.some(Array.isArray) ? result.data.flatMap((obj) => Array.isArray(obj) ? obj : [obj]) : result.data;
|
|
754
|
-
const unresolved = findUnresolvedStyles(result,
|
|
755
|
-
if (unresolved.length)
|
|
756
|
-
|
|
757
|
-
if (grouped) data.forEach((obj) => encoder.processAtomic(obj));
|
|
758
|
-
}
|
|
759
|
-
if (!grouped || data.length <= 1) {
|
|
760
|
-
data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
const keyCounts = /* @__PURE__ */ new Map();
|
|
764
|
-
for (const obj of data) for (const key of Object.keys(obj)) keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
|
|
765
|
-
if (!Array.from(keyCounts.values()).some((c) => c > 1)) {
|
|
766
|
-
encoder.processGroupedMerge(data);
|
|
767
|
-
return;
|
|
768
|
-
}
|
|
769
|
-
if (this.callArgumentCount(result) > 1) {
|
|
770
|
-
this.reportUnresolved(result, "ambiguous-merge");
|
|
771
|
-
data.forEach((obj) => encoder.processAtomic(obj));
|
|
772
|
-
}
|
|
773
|
-
const overlappingKeys = /* @__PURE__ */ new Set();
|
|
774
|
-
keyCounts.forEach((count, key) => {
|
|
775
|
-
if (count > 1) overlappingKeys.add(key);
|
|
776
|
-
});
|
|
777
|
-
const baseEntries = [];
|
|
778
|
-
const branchEntries = [];
|
|
779
|
-
for (const obj of data) if (Object.keys(obj).some((k) => overlappingKeys.has(k))) branchEntries.push(obj);
|
|
780
|
-
else baseEntries.push(obj);
|
|
781
|
-
const branchGroups = /* @__PURE__ */ new Map();
|
|
782
|
-
for (const entry of branchEntries) {
|
|
783
|
-
const keySet = Object.keys(entry).sort().join("\0");
|
|
784
|
-
const group = branchGroups.get(keySet) || [];
|
|
785
|
-
group.push(entry);
|
|
786
|
-
branchGroups.set(keySet, group);
|
|
787
|
-
}
|
|
788
|
-
const groupArrays = Array.from(branchGroups.values());
|
|
789
|
-
if (groupArrays.reduce((acc, g) => acc * g.length, 1) > 32) {
|
|
790
|
-
this.reportUnresolved(result, "too-many-combinations");
|
|
791
|
-
data.forEach((obj) => {
|
|
792
|
-
encoder.processGrouped(obj);
|
|
793
|
-
encoder.processAtomic(obj);
|
|
794
|
-
});
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
for (const combo of cartesian(groupArrays)) encoder.processGroupedMerge([...baseEntries, ...combo]);
|
|
798
|
-
}
|
|
799
|
-
/**
|
|
800
|
-
* How many arguments the call this result came from was written with.
|
|
801
|
-
*
|
|
802
|
-
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
803
|
-
* since the question only separates operands from branches and neither has operands.
|
|
804
|
-
*/
|
|
805
|
-
callArgumentCount(result) {
|
|
806
|
-
const node = result.box?.getNode();
|
|
807
|
-
return node && Node.isCallExpression(node) ? node.getArguments().length : 1;
|
|
719
|
+
const unresolved = findUnresolvedStyles(result, "atomic").filter((entry) => entry.reason === "unenumerable-keys");
|
|
720
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
721
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
808
722
|
}
|
|
809
723
|
setCva(result) {
|
|
810
724
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -821,10 +735,9 @@ var ParserResult = class {
|
|
|
821
735
|
/**
|
|
822
736
|
* Record a recipe config the build could not fully read.
|
|
823
737
|
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
738
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
739
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
740
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
828
741
|
* another, and the element renders with no styles at all.
|
|
829
742
|
*
|
|
830
743
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -852,26 +765,7 @@ var ParserResult = class {
|
|
|
852
765
|
type: "pattern",
|
|
853
766
|
name
|
|
854
767
|
}, result)));
|
|
855
|
-
|
|
856
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
857
|
-
result.data.forEach((obj) => encoder.processPattern(name, obj, grouped));
|
|
858
|
-
if (grouped && !this.groupIsExact(result)) result.data.forEach((obj) => encoder.processPattern(name, obj, false));
|
|
859
|
-
}
|
|
860
|
-
/**
|
|
861
|
-
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
862
|
-
*
|
|
863
|
-
* True only when the build saw the whole thing at once: one style object, with every
|
|
864
|
-
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
865
|
-
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
866
|
-
* an unresolved value means the merge would not have matched anyway.
|
|
867
|
-
*
|
|
868
|
-
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
869
|
-
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
870
|
-
* deliberately conservative.
|
|
871
|
-
*/
|
|
872
|
-
groupIsExact(result) {
|
|
873
|
-
if (result.data.length !== 1) return false;
|
|
874
|
-
return findUnresolvedStyles(result, "grouped").length === 0;
|
|
768
|
+
result.data.forEach((obj) => this.encoder.processPattern(name, obj));
|
|
875
769
|
}
|
|
876
770
|
setRecipe(recipeName, result) {
|
|
877
771
|
getOrCreateSet(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "The static parser for bamboo css",
|
|
5
5
|
"homepage": "https://bamboocss.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,17 +34,17 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ts-morph": "28.0.0",
|
|
36
36
|
"ts-pattern": "5.9.0",
|
|
37
|
-
"@bamboocss/config": "^1.
|
|
38
|
-
"@bamboocss/core": "^1.
|
|
39
|
-
"@bamboocss/extractor": "1.
|
|
40
|
-
"@bamboocss/logger": "1.
|
|
41
|
-
"@bamboocss/shared": "1.
|
|
42
|
-
"@bamboocss/types": "1.
|
|
37
|
+
"@bamboocss/config": "^1.22.0",
|
|
38
|
+
"@bamboocss/core": "^1.22.0",
|
|
39
|
+
"@bamboocss/extractor": "1.22.0",
|
|
40
|
+
"@bamboocss/logger": "1.22.0",
|
|
41
|
+
"@bamboocss/shared": "1.22.0",
|
|
42
|
+
"@bamboocss/types": "1.22.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@bamboocss/generator": "1.
|
|
46
|
-
"@bamboocss/plugin-svelte": "1.
|
|
47
|
-
"@bamboocss/plugin-vue": "1.
|
|
45
|
+
"@bamboocss/generator": "1.22.0",
|
|
46
|
+
"@bamboocss/plugin-svelte": "1.22.0",
|
|
47
|
+
"@bamboocss/plugin-vue": "1.22.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown src/index.ts --format=esm,cjs --dts",
|