@bamboocss/parser 1.21.0 → 1.23.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 +94 -157
- package/dist/index.d.cts +32 -57
- package/dist/index.d.mts +32 -57
- package/dist/index.mjs +94 -157
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,7 @@ function classifyProject(ctx, resultMap) {
|
|
|
65
65
|
const { item, kind, filepath, localMaps } = opts;
|
|
66
66
|
if (!item.box || _bamboocss_extractor.box.isUnresolvable(item.box)) return;
|
|
67
67
|
if (!item.data) return;
|
|
68
|
+
if (item.type === "cva-call") return;
|
|
68
69
|
const componentReportItem = {
|
|
69
70
|
componentIndex: String(componentIndex++),
|
|
70
71
|
componentName: item.name,
|
|
@@ -523,14 +524,7 @@ const writtenProps = (node) => {
|
|
|
523
524
|
uncertain
|
|
524
525
|
};
|
|
525
526
|
};
|
|
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
|
-
*/
|
|
527
|
+
/** Every property of a `css()` call that will not reach the stylesheet. */
|
|
534
528
|
/**
|
|
535
529
|
* A recipe config the build could not fully read, level by level.
|
|
536
530
|
*
|
|
@@ -675,18 +669,13 @@ const hasKeyOutside = (resolved, names) => {
|
|
|
675
669
|
};
|
|
676
670
|
//#endregion
|
|
677
671
|
//#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
672
|
var ParserResult = class {
|
|
685
673
|
context;
|
|
686
674
|
/** Ordered list of all ResultItem */
|
|
687
675
|
all = [];
|
|
688
676
|
css = /* @__PURE__ */ new Set();
|
|
689
677
|
cva = /* @__PURE__ */ new Set();
|
|
678
|
+
cvaCall = /* @__PURE__ */ new Set();
|
|
690
679
|
sva = /* @__PURE__ */ new Set();
|
|
691
680
|
token = /* @__PURE__ */ new Set();
|
|
692
681
|
viewTransition = /* @__PURE__ */ new Set();
|
|
@@ -697,35 +686,14 @@ var ParserResult = class {
|
|
|
697
686
|
/**
|
|
698
687
|
* `css()` calls whose styles the build could not fully see.
|
|
699
688
|
*
|
|
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.
|
|
689
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
690
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
704
691
|
*/
|
|
705
692
|
unresolved = [];
|
|
706
693
|
constructor(context, encoder) {
|
|
707
694
|
this.context = context;
|
|
708
695
|
this.encoder = encoder ?? context.encoder;
|
|
709
696
|
}
|
|
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
697
|
append(result) {
|
|
730
698
|
this.all.push(result);
|
|
731
699
|
return result;
|
|
@@ -750,62 +718,10 @@ var ParserResult = class {
|
|
|
750
718
|
setCss(result) {
|
|
751
719
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
752
720
|
const encoder = this.encoder;
|
|
753
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
754
721
|
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;
|
|
722
|
+
const unresolved = findUnresolvedStyles(result, "atomic").filter((entry) => entry.reason === "unenumerable-keys");
|
|
723
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
724
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
809
725
|
}
|
|
810
726
|
setCva(result) {
|
|
811
727
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -813,6 +729,20 @@ var ParserResult = class {
|
|
|
813
729
|
const encoder = this.encoder;
|
|
814
730
|
result.data.forEach((data) => encoder.processAtomicRecipe(data));
|
|
815
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* A call of a locally-bound inline recipe -- `const badge = cva(...)`, then `badge({...})`.
|
|
734
|
+
*
|
|
735
|
+
* Recorded, not encoded. The rules already exist: `setCva` emitted them from the config,
|
|
736
|
+
* and a recipe's classes are named semantically from that config rather than from this
|
|
737
|
+
* call. What this adds is *visibility* -- the call site becomes something the fold can see
|
|
738
|
+
* and report on, where before it was indistinguishable from code nobody had parsed.
|
|
739
|
+
*/
|
|
740
|
+
setCvaCall(name, result) {
|
|
741
|
+
this.cvaCall.add(this.append(Object.assign({
|
|
742
|
+
type: "cva-call",
|
|
743
|
+
name
|
|
744
|
+
}, result)));
|
|
745
|
+
}
|
|
816
746
|
setSva(result) {
|
|
817
747
|
this.sva.add(this.append(Object.assign({ type: "sva" }, result)));
|
|
818
748
|
this.reportUnresolvedRecipe(result);
|
|
@@ -822,10 +752,9 @@ var ParserResult = class {
|
|
|
822
752
|
/**
|
|
823
753
|
* Record a recipe config the build could not fully read.
|
|
824
754
|
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
755
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
756
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
757
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
829
758
|
* another, and the element renders with no styles at all.
|
|
830
759
|
*
|
|
831
760
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -853,26 +782,7 @@ var ParserResult = class {
|
|
|
853
782
|
type: "pattern",
|
|
854
783
|
name
|
|
855
784
|
}, 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;
|
|
785
|
+
result.data.forEach((obj) => this.encoder.processPattern(name, obj));
|
|
876
786
|
}
|
|
877
787
|
setRecipe(recipeName, result) {
|
|
878
788
|
(0, _bamboocss_shared.getOrCreateSet)(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
|
@@ -900,6 +810,7 @@ var ParserResult = class {
|
|
|
900
810
|
result.css.forEach((item) => this.css.add(this.append(item)));
|
|
901
811
|
result.cva.forEach((item) => this.cva.add(this.append(item)));
|
|
902
812
|
result.sva.forEach((item) => this.sva.add(this.append(item)));
|
|
813
|
+
result.cvaCall.forEach((item) => this.cvaCall.add(this.append(item)));
|
|
903
814
|
result.token.forEach((item) => this.token.add(this.append(item)));
|
|
904
815
|
result.viewTransition.forEach((item) => this.viewTransition.add(this.append(item)));
|
|
905
816
|
result.recipe.forEach((items, name) => {
|
|
@@ -921,6 +832,7 @@ var ParserResult = class {
|
|
|
921
832
|
css: Array.from(this.css),
|
|
922
833
|
cva: Array.from(this.cva),
|
|
923
834
|
sva: Array.from(this.sva),
|
|
835
|
+
cvaCall: Array.from(this.cvaCall),
|
|
924
836
|
token: Array.from(this.token),
|
|
925
837
|
viewTransition: Array.from(this.viewTransition),
|
|
926
838
|
recipe: Object.fromEntries(Array.from(this.recipe.entries()).map(([key, value]) => [key, Array.from(value)])),
|
|
@@ -956,6 +868,20 @@ function createParser(context) {
|
|
|
956
868
|
_bamboocss_logger.logger.debug("ast:import", !file.isEmpty() ? `Found import { ${file.toString()} } in ${filePath}` : `No import found in ${filePath}`);
|
|
957
869
|
const parserResult = new ParserResult(context, encoder);
|
|
958
870
|
if (file.isEmpty() && !jsx.isEnabled) return parserResult;
|
|
871
|
+
if (file.importsRecipeFactory()) for (const statement of sourceFile.compilerNode.statements) {
|
|
872
|
+
if (!ts_morph.ts.isVariableStatement(statement)) continue;
|
|
873
|
+
if (!(statement.declarationList.flags & ts_morph.ts.NodeFlags.Const)) continue;
|
|
874
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
875
|
+
const initializer = declaration.initializer;
|
|
876
|
+
if (!initializer || !ts_morph.ts.isCallExpression(initializer)) continue;
|
|
877
|
+
const callee = initializer.expression;
|
|
878
|
+
if (!ts_morph.ts.isIdentifier(callee) || !ts_morph.ts.isIdentifier(declaration.name)) continue;
|
|
879
|
+
const imported = file.getName(callee.text);
|
|
880
|
+
if (imported !== "cva" && imported !== "sva") continue;
|
|
881
|
+
if (!file.matchFn(callee.text)) continue;
|
|
882
|
+
file.addLocalRecipe(declaration.name.text);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
959
885
|
(0, _bamboocss_extractor.extract)({
|
|
960
886
|
ast: sourceFile,
|
|
961
887
|
tokens: context.tokens ? {
|
|
@@ -1005,53 +931,64 @@ function createParser(context) {
|
|
|
1005
931
|
kind: result.kind,
|
|
1006
932
|
alias
|
|
1007
933
|
} : { kind: result.kind });
|
|
1008
|
-
if (result.kind === "function")
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
934
|
+
if (result.kind === "function") {
|
|
935
|
+
if (file.isLocalRecipe(alias) && !file.match(alias)) {
|
|
936
|
+
result.queryList.forEach((query) => {
|
|
937
|
+
if (query.kind === "call-expression") parserResult.setCvaCall(alias, {
|
|
938
|
+
name: alias,
|
|
939
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
940
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
941
|
+
});
|
|
1014
942
|
});
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
(0, ts_pattern.match)(name).when(imports.matchers.css.match, (name) => {
|
|
946
|
+
result.queryList.forEach((query) => {
|
|
947
|
+
if (query.kind === "call-expression") if (query.box.value.length > 1) parserResult.set(name, {
|
|
948
|
+
name,
|
|
949
|
+
box: query.box,
|
|
950
|
+
data: query.box.value.reduce((acc, value) => [...acc, ...combineResult((0, _bamboocss_extractor.unbox)(value))], [])
|
|
951
|
+
});
|
|
952
|
+
else parserResult.set(name, {
|
|
953
|
+
name,
|
|
954
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
955
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
956
|
+
});
|
|
1019
957
|
});
|
|
1020
|
-
})
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
958
|
+
}).when(imports.matchers.tokens.match, (name) => {
|
|
959
|
+
result.queryList.forEach((query) => {
|
|
960
|
+
if (query.kind === "call-expression") parserResult.setToken({
|
|
961
|
+
name,
|
|
962
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
963
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
964
|
+
});
|
|
1027
965
|
});
|
|
1028
|
-
})
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
966
|
+
}).when(file.isValidPattern, (name) => {
|
|
967
|
+
result.queryList.forEach((query) => {
|
|
968
|
+
if (query.kind === "call-expression") parserResult.setPattern(name, {
|
|
969
|
+
name,
|
|
970
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
971
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
972
|
+
});
|
|
1035
973
|
});
|
|
1036
|
-
})
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
974
|
+
}).when(file.isValidRecipe, (name) => {
|
|
975
|
+
result.queryList.forEach((query) => {
|
|
976
|
+
if (query.kind === "call-expression") parserResult.setRecipe(name, {
|
|
977
|
+
name,
|
|
978
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
979
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
980
|
+
});
|
|
1043
981
|
});
|
|
1044
|
-
})
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
982
|
+
}).when(file.isViewTransitionFn, (name) => {
|
|
983
|
+
result.queryList.forEach((query) => {
|
|
984
|
+
if (query.kind === "call-expression") parserResult.setViewTransition({
|
|
985
|
+
name,
|
|
986
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
987
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
988
|
+
});
|
|
1051
989
|
});
|
|
1052
|
-
});
|
|
1053
|
-
}).
|
|
1054
|
-
else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
990
|
+
}).otherwise(() => {});
|
|
991
|
+
} else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
1055
992
|
const data = combineResult((0, _bamboocss_extractor.unbox)(query.box));
|
|
1056
993
|
for (const tag of [name, alias]) {
|
|
1057
994
|
if (!jsx.isJsxTagRecipe(tag)) continue;
|
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 {
|
|
@@ -179,6 +173,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
179
173
|
all: ResultItem[];
|
|
180
174
|
css: Set<ResultItem>;
|
|
181
175
|
cva: Set<ResultItem>;
|
|
176
|
+
cvaCall: Set<ResultItem>;
|
|
182
177
|
sva: Set<ResultItem>;
|
|
183
178
|
token: Set<ResultItem>;
|
|
184
179
|
viewTransition: Set<ResultItem>;
|
|
@@ -189,39 +184,31 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
189
184
|
/**
|
|
190
185
|
* `css()` calls whose styles the build could not fully see.
|
|
191
186
|
*
|
|
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.
|
|
187
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
188
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
196
189
|
*/
|
|
197
190
|
unresolved: UnresolvedStyle[];
|
|
198
191
|
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
192
|
append(result: ResultItem): ResultItem;
|
|
207
193
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
208
194
|
setCss(result: ResultItem): void;
|
|
195
|
+
setCva(result: ResultItem): void;
|
|
209
196
|
/**
|
|
210
|
-
*
|
|
197
|
+
* A call of a locally-bound inline recipe -- `const badge = cva(...)`, then `badge({...})`.
|
|
211
198
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
199
|
+
* Recorded, not encoded. The rules already exist: `setCva` emitted them from the config,
|
|
200
|
+
* and a recipe's classes are named semantically from that config rather than from this
|
|
201
|
+
* call. What this adds is *visibility* -- the call site becomes something the fold can see
|
|
202
|
+
* and report on, where before it was indistinguishable from code nobody had parsed.
|
|
214
203
|
*/
|
|
215
|
-
|
|
216
|
-
setCva(result: ResultItem): void;
|
|
204
|
+
setCvaCall(name: string, result: ResultItem): void;
|
|
217
205
|
setSva(result: ResultItem): void;
|
|
218
206
|
/**
|
|
219
207
|
* Record a recipe config the build could not fully read.
|
|
220
208
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
209
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
210
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
211
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
225
212
|
* another, and the element renders with no styles at all.
|
|
226
213
|
*
|
|
227
214
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -232,19 +219,6 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
232
219
|
setToken(result: ResultItem): void;
|
|
233
220
|
setViewTransition(result: ResultItem): void;
|
|
234
221
|
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
222
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
249
223
|
isEmpty(): boolean;
|
|
250
224
|
setFilePath(filePath: string): this;
|
|
@@ -254,6 +228,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
254
228
|
css: ResultItem[];
|
|
255
229
|
cva: ResultItem[];
|
|
256
230
|
sva: ResultItem[];
|
|
231
|
+
cvaCall: ResultItem[];
|
|
257
232
|
token: ResultItem[];
|
|
258
233
|
viewTransition: ResultItem[];
|
|
259
234
|
recipe: {
|
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 {
|
|
@@ -179,6 +173,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
179
173
|
all: ResultItem[];
|
|
180
174
|
css: Set<ResultItem>;
|
|
181
175
|
cva: Set<ResultItem>;
|
|
176
|
+
cvaCall: Set<ResultItem>;
|
|
182
177
|
sva: Set<ResultItem>;
|
|
183
178
|
token: Set<ResultItem>;
|
|
184
179
|
viewTransition: Set<ResultItem>;
|
|
@@ -189,39 +184,31 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
189
184
|
/**
|
|
190
185
|
* `css()` calls whose styles the build could not fully see.
|
|
191
186
|
*
|
|
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.
|
|
187
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
188
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
196
189
|
*/
|
|
197
190
|
unresolved: UnresolvedStyle[];
|
|
198
191
|
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
192
|
append(result: ResultItem): ResultItem;
|
|
207
193
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
208
194
|
setCss(result: ResultItem): void;
|
|
195
|
+
setCva(result: ResultItem): void;
|
|
209
196
|
/**
|
|
210
|
-
*
|
|
197
|
+
* A call of a locally-bound inline recipe -- `const badge = cva(...)`, then `badge({...})`.
|
|
211
198
|
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
199
|
+
* Recorded, not encoded. The rules already exist: `setCva` emitted them from the config,
|
|
200
|
+
* and a recipe's classes are named semantically from that config rather than from this
|
|
201
|
+
* call. What this adds is *visibility* -- the call site becomes something the fold can see
|
|
202
|
+
* and report on, where before it was indistinguishable from code nobody had parsed.
|
|
214
203
|
*/
|
|
215
|
-
|
|
216
|
-
setCva(result: ResultItem): void;
|
|
204
|
+
setCvaCall(name: string, result: ResultItem): void;
|
|
217
205
|
setSva(result: ResultItem): void;
|
|
218
206
|
/**
|
|
219
207
|
* Record a recipe config the build could not fully read.
|
|
220
208
|
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
209
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
210
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
211
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
225
212
|
* another, and the element renders with no styles at all.
|
|
226
213
|
*
|
|
227
214
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -232,19 +219,6 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
232
219
|
setToken(result: ResultItem): void;
|
|
233
220
|
setViewTransition(result: ResultItem): void;
|
|
234
221
|
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
222
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
249
223
|
isEmpty(): boolean;
|
|
250
224
|
setFilePath(filePath: string): this;
|
|
@@ -254,6 +228,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
254
228
|
css: ResultItem[];
|
|
255
229
|
cva: ResultItem[];
|
|
256
230
|
sva: ResultItem[];
|
|
231
|
+
cvaCall: ResultItem[];
|
|
257
232
|
token: ResultItem[];
|
|
258
233
|
viewTransition: ResultItem[];
|
|
259
234
|
recipe: {
|
package/dist/index.mjs
CHANGED
|
@@ -64,6 +64,7 @@ function classifyProject(ctx, resultMap) {
|
|
|
64
64
|
const { item, kind, filepath, localMaps } = opts;
|
|
65
65
|
if (!item.box || box.isUnresolvable(item.box)) return;
|
|
66
66
|
if (!item.data) return;
|
|
67
|
+
if (item.type === "cva-call") return;
|
|
67
68
|
const componentReportItem = {
|
|
68
69
|
componentIndex: String(componentIndex++),
|
|
69
70
|
componentName: item.name,
|
|
@@ -522,14 +523,7 @@ const writtenProps = (node) => {
|
|
|
522
523
|
uncertain
|
|
523
524
|
};
|
|
524
525
|
};
|
|
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
|
-
*/
|
|
526
|
+
/** Every property of a `css()` call that will not reach the stylesheet. */
|
|
533
527
|
/**
|
|
534
528
|
* A recipe config the build could not fully read, level by level.
|
|
535
529
|
*
|
|
@@ -674,18 +668,13 @@ const hasKeyOutside = (resolved, names) => {
|
|
|
674
668
|
};
|
|
675
669
|
//#endregion
|
|
676
670
|
//#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
671
|
var ParserResult = class {
|
|
684
672
|
context;
|
|
685
673
|
/** Ordered list of all ResultItem */
|
|
686
674
|
all = [];
|
|
687
675
|
css = /* @__PURE__ */ new Set();
|
|
688
676
|
cva = /* @__PURE__ */ new Set();
|
|
677
|
+
cvaCall = /* @__PURE__ */ new Set();
|
|
689
678
|
sva = /* @__PURE__ */ new Set();
|
|
690
679
|
token = /* @__PURE__ */ new Set();
|
|
691
680
|
viewTransition = /* @__PURE__ */ new Set();
|
|
@@ -696,35 +685,14 @@ var ParserResult = class {
|
|
|
696
685
|
/**
|
|
697
686
|
* `css()` calls whose styles the build could not fully see.
|
|
698
687
|
*
|
|
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.
|
|
688
|
+
* A property the build cannot resolve has no rule behind it, so the declaration is simply
|
|
689
|
+
* absent from the element — silently. Only the surprising half is collected; see `setCss`.
|
|
703
690
|
*/
|
|
704
691
|
unresolved = [];
|
|
705
692
|
constructor(context, encoder) {
|
|
706
693
|
this.context = context;
|
|
707
694
|
this.encoder = encoder ?? context.encoder;
|
|
708
695
|
}
|
|
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
696
|
append(result) {
|
|
729
697
|
this.all.push(result);
|
|
730
698
|
return result;
|
|
@@ -749,62 +717,10 @@ var ParserResult = class {
|
|
|
749
717
|
setCss(result) {
|
|
750
718
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
751
719
|
const encoder = this.encoder;
|
|
752
|
-
const grouped = this.context.config.cssMode === "grouped";
|
|
753
720
|
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;
|
|
721
|
+
const unresolved = findUnresolvedStyles(result, "atomic").filter((entry) => entry.reason === "unenumerable-keys");
|
|
722
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
723
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
808
724
|
}
|
|
809
725
|
setCva(result) {
|
|
810
726
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -812,6 +728,20 @@ var ParserResult = class {
|
|
|
812
728
|
const encoder = this.encoder;
|
|
813
729
|
result.data.forEach((data) => encoder.processAtomicRecipe(data));
|
|
814
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* A call of a locally-bound inline recipe -- `const badge = cva(...)`, then `badge({...})`.
|
|
733
|
+
*
|
|
734
|
+
* Recorded, not encoded. The rules already exist: `setCva` emitted them from the config,
|
|
735
|
+
* and a recipe's classes are named semantically from that config rather than from this
|
|
736
|
+
* call. What this adds is *visibility* -- the call site becomes something the fold can see
|
|
737
|
+
* and report on, where before it was indistinguishable from code nobody had parsed.
|
|
738
|
+
*/
|
|
739
|
+
setCvaCall(name, result) {
|
|
740
|
+
this.cvaCall.add(this.append(Object.assign({
|
|
741
|
+
type: "cva-call",
|
|
742
|
+
name
|
|
743
|
+
}, result)));
|
|
744
|
+
}
|
|
815
745
|
setSva(result) {
|
|
816
746
|
this.sva.add(this.append(Object.assign({ type: "sva" }, result)));
|
|
817
747
|
this.reportUnresolvedRecipe(result);
|
|
@@ -821,10 +751,9 @@ var ParserResult = class {
|
|
|
821
751
|
/**
|
|
822
752
|
* Record a recipe config the build could not fully read.
|
|
823
753
|
*
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
754
|
+
* Reported in full, unlike the `css()` check in `setCss`, which keeps only the surprising
|
|
755
|
+
* half. A recipe is named from a *hash of its config*: a declaration the build cannot see
|
|
756
|
+
* changes the hash, so the build emits rules under one name and the browser asks for
|
|
828
757
|
* another, and the element renders with no styles at all.
|
|
829
758
|
*
|
|
830
759
|
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
@@ -852,26 +781,7 @@ var ParserResult = class {
|
|
|
852
781
|
type: "pattern",
|
|
853
782
|
name
|
|
854
783
|
}, 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;
|
|
784
|
+
result.data.forEach((obj) => this.encoder.processPattern(name, obj));
|
|
875
785
|
}
|
|
876
786
|
setRecipe(recipeName, result) {
|
|
877
787
|
getOrCreateSet(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
|
@@ -899,6 +809,7 @@ var ParserResult = class {
|
|
|
899
809
|
result.css.forEach((item) => this.css.add(this.append(item)));
|
|
900
810
|
result.cva.forEach((item) => this.cva.add(this.append(item)));
|
|
901
811
|
result.sva.forEach((item) => this.sva.add(this.append(item)));
|
|
812
|
+
result.cvaCall.forEach((item) => this.cvaCall.add(this.append(item)));
|
|
902
813
|
result.token.forEach((item) => this.token.add(this.append(item)));
|
|
903
814
|
result.viewTransition.forEach((item) => this.viewTransition.add(this.append(item)));
|
|
904
815
|
result.recipe.forEach((items, name) => {
|
|
@@ -920,6 +831,7 @@ var ParserResult = class {
|
|
|
920
831
|
css: Array.from(this.css),
|
|
921
832
|
cva: Array.from(this.cva),
|
|
922
833
|
sva: Array.from(this.sva),
|
|
834
|
+
cvaCall: Array.from(this.cvaCall),
|
|
923
835
|
token: Array.from(this.token),
|
|
924
836
|
viewTransition: Array.from(this.viewTransition),
|
|
925
837
|
recipe: Object.fromEntries(Array.from(this.recipe.entries()).map(([key, value]) => [key, Array.from(value)])),
|
|
@@ -955,6 +867,20 @@ function createParser(context) {
|
|
|
955
867
|
logger.debug("ast:import", !file.isEmpty() ? `Found import { ${file.toString()} } in ${filePath}` : `No import found in ${filePath}`);
|
|
956
868
|
const parserResult = new ParserResult(context, encoder);
|
|
957
869
|
if (file.isEmpty() && !jsx.isEnabled) return parserResult;
|
|
870
|
+
if (file.importsRecipeFactory()) for (const statement of sourceFile.compilerNode.statements) {
|
|
871
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
872
|
+
if (!(statement.declarationList.flags & ts.NodeFlags.Const)) continue;
|
|
873
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
874
|
+
const initializer = declaration.initializer;
|
|
875
|
+
if (!initializer || !ts.isCallExpression(initializer)) continue;
|
|
876
|
+
const callee = initializer.expression;
|
|
877
|
+
if (!ts.isIdentifier(callee) || !ts.isIdentifier(declaration.name)) continue;
|
|
878
|
+
const imported = file.getName(callee.text);
|
|
879
|
+
if (imported !== "cva" && imported !== "sva") continue;
|
|
880
|
+
if (!file.matchFn(callee.text)) continue;
|
|
881
|
+
file.addLocalRecipe(declaration.name.text);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
958
884
|
extract({
|
|
959
885
|
ast: sourceFile,
|
|
960
886
|
tokens: context.tokens ? {
|
|
@@ -1004,53 +930,64 @@ function createParser(context) {
|
|
|
1004
930
|
kind: result.kind,
|
|
1005
931
|
alias
|
|
1006
932
|
} : { kind: result.kind });
|
|
1007
|
-
if (result.kind === "function")
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
933
|
+
if (result.kind === "function") {
|
|
934
|
+
if (file.isLocalRecipe(alias) && !file.match(alias)) {
|
|
935
|
+
result.queryList.forEach((query) => {
|
|
936
|
+
if (query.kind === "call-expression") parserResult.setCvaCall(alias, {
|
|
937
|
+
name: alias,
|
|
938
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
939
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
940
|
+
});
|
|
1013
941
|
});
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
match(name).when(imports.matchers.css.match, (name) => {
|
|
945
|
+
result.queryList.forEach((query) => {
|
|
946
|
+
if (query.kind === "call-expression") if (query.box.value.length > 1) parserResult.set(name, {
|
|
947
|
+
name,
|
|
948
|
+
box: query.box,
|
|
949
|
+
data: query.box.value.reduce((acc, value) => [...acc, ...combineResult(unbox(value))], [])
|
|
950
|
+
});
|
|
951
|
+
else parserResult.set(name, {
|
|
952
|
+
name,
|
|
953
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
954
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
955
|
+
});
|
|
1018
956
|
});
|
|
1019
|
-
})
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
957
|
+
}).when(imports.matchers.tokens.match, (name) => {
|
|
958
|
+
result.queryList.forEach((query) => {
|
|
959
|
+
if (query.kind === "call-expression") parserResult.setToken({
|
|
960
|
+
name,
|
|
961
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
962
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
963
|
+
});
|
|
1026
964
|
});
|
|
1027
|
-
})
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
965
|
+
}).when(file.isValidPattern, (name) => {
|
|
966
|
+
result.queryList.forEach((query) => {
|
|
967
|
+
if (query.kind === "call-expression") parserResult.setPattern(name, {
|
|
968
|
+
name,
|
|
969
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
970
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
971
|
+
});
|
|
1034
972
|
});
|
|
1035
|
-
})
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
973
|
+
}).when(file.isValidRecipe, (name) => {
|
|
974
|
+
result.queryList.forEach((query) => {
|
|
975
|
+
if (query.kind === "call-expression") parserResult.setRecipe(name, {
|
|
976
|
+
name,
|
|
977
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
978
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
979
|
+
});
|
|
1042
980
|
});
|
|
1043
|
-
})
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
981
|
+
}).when(file.isViewTransitionFn, (name) => {
|
|
982
|
+
result.queryList.forEach((query) => {
|
|
983
|
+
if (query.kind === "call-expression") parserResult.setViewTransition({
|
|
984
|
+
name,
|
|
985
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
986
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
987
|
+
});
|
|
1050
988
|
});
|
|
1051
|
-
});
|
|
1052
|
-
}).
|
|
1053
|
-
else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
989
|
+
}).otherwise(() => {});
|
|
990
|
+
} else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
1054
991
|
const data = combineResult(unbox(query.box));
|
|
1055
992
|
for (const tag of [name, alias]) {
|
|
1056
993
|
if (!jsx.isJsxTagRecipe(tag)) continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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.23.0",
|
|
38
|
+
"@bamboocss/core": "^1.23.0",
|
|
39
|
+
"@bamboocss/extractor": "1.23.0",
|
|
40
|
+
"@bamboocss/logger": "1.23.0",
|
|
41
|
+
"@bamboocss/shared": "1.23.0",
|
|
42
|
+
"@bamboocss/types": "1.23.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@bamboocss/generator": "1.
|
|
46
|
-
"@bamboocss/plugin-svelte": "1.
|
|
47
|
-
"@bamboocss/plugin-vue": "1.
|
|
45
|
+
"@bamboocss/generator": "1.23.0",
|
|
46
|
+
"@bamboocss/plugin-svelte": "1.23.0",
|
|
47
|
+
"@bamboocss/plugin-vue": "1.23.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown src/index.ts --format=esm,cjs --dts",
|