@bamboocss/parser 1.14.0 → 1.16.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 +220 -145
- package/dist/index.d.cts +85 -6
- package/dist/index.d.mts +85 -6
- package/dist/index.mjs +221 -147
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -44,7 +44,7 @@ function classifyProject(ctx, resultMap) {
|
|
|
44
44
|
const processPattern = (opts) => {
|
|
45
45
|
const { boxNode, data, item, filepath, localMaps } = opts;
|
|
46
46
|
const name = item.componentName;
|
|
47
|
-
const pattern = ctx.patterns.details.find((p) => p.
|
|
47
|
+
const pattern = ctx.patterns.details.find((p) => p.baseName === name);
|
|
48
48
|
if (!pattern) return;
|
|
49
49
|
const cssObj = pattern.config.transform?.(data || {}, _bamboocss_shared.patternFns) ?? {};
|
|
50
50
|
const newItem = {
|
|
@@ -76,7 +76,7 @@ function classifyProject(ctx, resultMap) {
|
|
|
76
76
|
contains: [],
|
|
77
77
|
debug: Reflect.has(item, "debug")
|
|
78
78
|
};
|
|
79
|
-
if (item.type === "pattern"
|
|
79
|
+
if (item.type === "pattern") return processPattern({
|
|
80
80
|
boxNode: item.box,
|
|
81
81
|
data: item.data[0],
|
|
82
82
|
item: componentReportItem,
|
|
@@ -201,14 +201,6 @@ function classifyProject(ctx, resultMap) {
|
|
|
201
201
|
resultMap.forEach((parserResult, filepath) => {
|
|
202
202
|
if (parserResult.isEmpty()) return;
|
|
203
203
|
const localMaps = createReportMaps();
|
|
204
|
-
const componentFn = (item) => {
|
|
205
|
-
processResultItemFn({
|
|
206
|
-
item,
|
|
207
|
-
filepath,
|
|
208
|
-
localMaps,
|
|
209
|
-
type: "component"
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
204
|
const functionFn = (item) => {
|
|
213
205
|
processResultItemFn({
|
|
214
206
|
item,
|
|
@@ -217,7 +209,6 @@ function classifyProject(ctx, resultMap) {
|
|
|
217
209
|
type: "function"
|
|
218
210
|
});
|
|
219
211
|
};
|
|
220
|
-
parserResult.jsx.forEach(componentFn);
|
|
221
212
|
parserResult.css.forEach(functionFn);
|
|
222
213
|
parserResult.cva.forEach(functionFn);
|
|
223
214
|
parserResult.pattern.forEach((itemList) => {
|
|
@@ -466,6 +457,120 @@ function getImportDeclarations(context, sourceFile) {
|
|
|
466
457
|
return importDeclarations;
|
|
467
458
|
}
|
|
468
459
|
//#endregion
|
|
460
|
+
//#region src/unresolved-styles.ts
|
|
461
|
+
/**
|
|
462
|
+
* Whether every box under this one carries a value the build can actually see.
|
|
463
|
+
*
|
|
464
|
+
* Mirrors `isStaticBox` in `@bamboocss/vite`, which asks the same question to decide
|
|
465
|
+
* whether a call is safe to fold. Duplicated rather than shared for now because the fold's
|
|
466
|
+
* copy also answers questions about ternaries that a diagnostic does not care about;
|
|
467
|
+
* unifying them is worth doing when the fold's detection moves out of the vite package.
|
|
468
|
+
*/
|
|
469
|
+
const findUnresolvable = (node, path, out, seen = /* @__PURE__ */ new Set()) => {
|
|
470
|
+
if (!node || seen.has(node)) return;
|
|
471
|
+
seen.add(node);
|
|
472
|
+
if (_bamboocss_extractor.box.isUnresolvable(node)) {
|
|
473
|
+
out.push(path.join("."));
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (_bamboocss_extractor.box.isConditional(node)) return;
|
|
477
|
+
if (_bamboocss_extractor.box.isLiteral(node) && node.value === void 0) {
|
|
478
|
+
if (ts_morph.Node.isTemplateExpression(node.getNode())) out.push(path.join("."));
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (_bamboocss_extractor.box.isMap(node)) {
|
|
482
|
+
for (const [key, child] of node.value) findUnresolvable(child, [...path, key], out, seen);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (_bamboocss_extractor.box.isArray(node)) node.value.forEach((child, index) => findUnresolvable(child, [...path, String(index)], out, seen));
|
|
486
|
+
};
|
|
487
|
+
/**
|
|
488
|
+
* Property names written at the top level of the call's own object literal.
|
|
489
|
+
*
|
|
490
|
+
* A key whose value the extractor could not evaluate at all is not boxed as
|
|
491
|
+
* `unresolvable` — `maybeBoxNode` returns nothing and the pair is never recorded
|
|
492
|
+
* (`get-object-literal-expression-prop-pairs.ts` has no fallback), so the property
|
|
493
|
+
* disappears with no trace in the box tree. Reading the source back is the only way to
|
|
494
|
+
* notice, and it is why `css({ color: getColor() })` needs this and not just the walk above.
|
|
495
|
+
*
|
|
496
|
+
* Returns `undefined` only when the argument is not a single object literal at all, so the
|
|
497
|
+
* caller reports nothing rather than something wrong.
|
|
498
|
+
*/
|
|
499
|
+
const writtenProps = (node) => {
|
|
500
|
+
let literal = node;
|
|
501
|
+
if (literal && ts_morph.Node.isCallExpression(literal)) {
|
|
502
|
+
const args = literal.getArguments();
|
|
503
|
+
if (args.length !== 1) return void 0;
|
|
504
|
+
literal = args[0];
|
|
505
|
+
}
|
|
506
|
+
if (!literal || !ts_morph.Node.isObjectLiteralExpression(literal)) return void 0;
|
|
507
|
+
const names = [];
|
|
508
|
+
let uncertain = false;
|
|
509
|
+
for (const property of literal.getProperties()) {
|
|
510
|
+
if (ts_morph.Node.isPropertyAssignment(property) || ts_morph.Node.isShorthandPropertyAssignment(property)) {
|
|
511
|
+
const name = property.getName();
|
|
512
|
+
if (name.startsWith("[")) {
|
|
513
|
+
uncertain = true;
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
names.push(name.replace(/^['"]|['"]$/g, ""));
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
uncertain = true;
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
names,
|
|
523
|
+
uncertain
|
|
524
|
+
};
|
|
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
|
+
*/
|
|
534
|
+
const findUnresolvedStyles = (item) => {
|
|
535
|
+
const boxNode = item.box;
|
|
536
|
+
if (!boxNode) return [];
|
|
537
|
+
const node = boxNode.getNode();
|
|
538
|
+
const sourceFile = node?.getSourceFile();
|
|
539
|
+
if (!node || !sourceFile) return [];
|
|
540
|
+
const found = [];
|
|
541
|
+
findUnresolvable(boxNode, [], found);
|
|
542
|
+
const losses = found.map((prop) => ({
|
|
543
|
+
prop: prop || void 0,
|
|
544
|
+
reason: "unresolvable-value"
|
|
545
|
+
}));
|
|
546
|
+
const written = writtenProps(node);
|
|
547
|
+
if (written) {
|
|
548
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
549
|
+
for (const entry of item.data) if (entry && typeof entry === "object") for (const key of Object.keys(entry)) resolved.add(key);
|
|
550
|
+
if (_bamboocss_extractor.box.isMap(boxNode)) for (const key of boxNode.value.keys()) resolved.add(key);
|
|
551
|
+
for (const prop of written.names) if (!resolved.has(prop)) losses.push({
|
|
552
|
+
prop,
|
|
553
|
+
reason: "missing-property"
|
|
554
|
+
});
|
|
555
|
+
if (written.uncertain && !hasKeyOutside(resolved, written.names)) losses.push({ reason: "unenumerable-keys" });
|
|
556
|
+
}
|
|
557
|
+
if (!losses.length) return [];
|
|
558
|
+
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
559
|
+
const at = {
|
|
560
|
+
filePath: sourceFile.getFilePath(),
|
|
561
|
+
line,
|
|
562
|
+
column
|
|
563
|
+
};
|
|
564
|
+
return losses.map((loss) => ({
|
|
565
|
+
...at,
|
|
566
|
+
...loss
|
|
567
|
+
}));
|
|
568
|
+
};
|
|
569
|
+
const hasKeyOutside = (resolved, names) => {
|
|
570
|
+
for (const key of resolved) if (!names.includes(key)) return true;
|
|
571
|
+
return false;
|
|
572
|
+
};
|
|
573
|
+
//#endregion
|
|
469
574
|
//#region src/parser-result.ts
|
|
470
575
|
function cartesian(arrays) {
|
|
471
576
|
if (arrays.length === 0) return [[]];
|
|
@@ -477,19 +582,46 @@ var ParserResult = class {
|
|
|
477
582
|
context;
|
|
478
583
|
/** Ordered list of all ResultItem */
|
|
479
584
|
all = [];
|
|
480
|
-
jsx = /* @__PURE__ */ new Set();
|
|
481
585
|
css = /* @__PURE__ */ new Set();
|
|
482
586
|
cva = /* @__PURE__ */ new Set();
|
|
483
587
|
sva = /* @__PURE__ */ new Set();
|
|
484
588
|
token = /* @__PURE__ */ new Set();
|
|
589
|
+
viewTransition = /* @__PURE__ */ new Set();
|
|
485
590
|
recipe = /* @__PURE__ */ new Map();
|
|
486
591
|
pattern = /* @__PURE__ */ new Map();
|
|
487
592
|
filePath;
|
|
488
593
|
encoder;
|
|
594
|
+
/**
|
|
595
|
+
* `css()` calls whose styles the build could not fully see.
|
|
596
|
+
*
|
|
597
|
+
* Only collected under `cssMode: 'grouped'`, where one class names the whole call, so a
|
|
598
|
+
* property the build cannot resolve changes the class rather than dropping a declaration
|
|
599
|
+
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
600
|
+
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
601
|
+
*/
|
|
602
|
+
unresolved = [];
|
|
489
603
|
constructor(context, encoder) {
|
|
490
604
|
this.context = context;
|
|
491
605
|
this.encoder = encoder ?? context.encoder;
|
|
492
606
|
}
|
|
607
|
+
/**
|
|
608
|
+
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
609
|
+
*
|
|
610
|
+
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
611
|
+
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
612
|
+
*/
|
|
613
|
+
reportUnresolved(result, reason) {
|
|
614
|
+
const node = result.box?.getNode();
|
|
615
|
+
const sourceFile = node?.getSourceFile();
|
|
616
|
+
if (!node || !sourceFile) return;
|
|
617
|
+
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
618
|
+
this.unresolved.push({
|
|
619
|
+
filePath: sourceFile.getFilePath(),
|
|
620
|
+
line,
|
|
621
|
+
column,
|
|
622
|
+
reason
|
|
623
|
+
});
|
|
624
|
+
}
|
|
493
625
|
append(result) {
|
|
494
626
|
this.all.push(result);
|
|
495
627
|
return result;
|
|
@@ -515,24 +647,36 @@ var ParserResult = class {
|
|
|
515
647
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
516
648
|
const encoder = this.encoder;
|
|
517
649
|
const grouped = this.context.config.cssMode === "grouped";
|
|
518
|
-
|
|
519
|
-
|
|
650
|
+
const data = result.data.some(Array.isArray) ? result.data.flatMap((obj) => Array.isArray(obj) ? obj : [obj]) : result.data;
|
|
651
|
+
if (grouped) {
|
|
652
|
+
const unresolved = findUnresolvedStyles(result);
|
|
653
|
+
if (unresolved.length) {
|
|
654
|
+
this.unresolved.push(...unresolved);
|
|
655
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (!grouped || data.length <= 1) {
|
|
659
|
+
data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
|
|
520
660
|
return;
|
|
521
661
|
}
|
|
522
662
|
const keyCounts = /* @__PURE__ */ new Map();
|
|
523
|
-
for (const obj of
|
|
663
|
+
for (const obj of data) for (const key of Object.keys(obj)) keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
|
|
524
664
|
if (!Array.from(keyCounts.values()).some((c) => c > 1)) {
|
|
525
|
-
encoder.
|
|
665
|
+
encoder.processGroupedMerge(data);
|
|
526
666
|
return;
|
|
527
667
|
}
|
|
668
|
+
if (this.callArgumentCount(result) > 1) {
|
|
669
|
+
this.reportUnresolved(result, "ambiguous-merge");
|
|
670
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
671
|
+
}
|
|
528
672
|
const overlappingKeys = /* @__PURE__ */ new Set();
|
|
529
673
|
keyCounts.forEach((count, key) => {
|
|
530
674
|
if (count > 1) overlappingKeys.add(key);
|
|
531
675
|
});
|
|
532
|
-
const
|
|
676
|
+
const baseEntries = [];
|
|
533
677
|
const branchEntries = [];
|
|
534
|
-
for (const obj of
|
|
535
|
-
else
|
|
678
|
+
for (const obj of data) if (Object.keys(obj).some((k) => overlappingKeys.has(k))) branchEntries.push(obj);
|
|
679
|
+
else baseEntries.push(obj);
|
|
536
680
|
const branchGroups = /* @__PURE__ */ new Map();
|
|
537
681
|
for (const entry of branchEntries) {
|
|
538
682
|
const keySet = Object.keys(entry).sort().join("\0");
|
|
@@ -542,10 +686,24 @@ var ParserResult = class {
|
|
|
542
686
|
}
|
|
543
687
|
const groupArrays = Array.from(branchGroups.values());
|
|
544
688
|
if (groupArrays.reduce((acc, g) => acc * g.length, 1) > 32) {
|
|
545
|
-
|
|
689
|
+
this.reportUnresolved(result, "too-many-combinations");
|
|
690
|
+
data.forEach((obj) => {
|
|
691
|
+
encoder.processGrouped(obj);
|
|
692
|
+
encoder.processAtomic(obj);
|
|
693
|
+
});
|
|
546
694
|
return;
|
|
547
695
|
}
|
|
548
|
-
for (const combo of cartesian(groupArrays)) encoder.
|
|
696
|
+
for (const combo of cartesian(groupArrays)) encoder.processGroupedMerge([...baseEntries, ...combo]);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* How many arguments the call this result came from was written with.
|
|
700
|
+
*
|
|
701
|
+
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
702
|
+
* since the question only separates operands from branches and neither has operands.
|
|
703
|
+
*/
|
|
704
|
+
callArgumentCount(result) {
|
|
705
|
+
const node = result.box?.getNode();
|
|
706
|
+
return node && ts_morph.Node.isCallExpression(node) ? node.getArguments().length : 1;
|
|
549
707
|
}
|
|
550
708
|
setCva(result) {
|
|
551
709
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -560,11 +718,10 @@ var ParserResult = class {
|
|
|
560
718
|
setToken(result) {
|
|
561
719
|
this.token.add(this.append(Object.assign({ type: "token" }, result)));
|
|
562
720
|
}
|
|
563
|
-
|
|
564
|
-
this.
|
|
721
|
+
setViewTransition(result) {
|
|
722
|
+
this.viewTransition.add(this.append(Object.assign({ type: "viewTransition" }, result)));
|
|
565
723
|
const encoder = this.encoder;
|
|
566
|
-
|
|
567
|
-
result.data.forEach((obj) => encoder.processStyleProps(obj, grouped));
|
|
724
|
+
result.data.forEach((obj) => encoder.processViewTransition(obj));
|
|
568
725
|
}
|
|
569
726
|
setPattern(name, result) {
|
|
570
727
|
(0, _bamboocss_shared.getOrCreateSet)(this.pattern, name).add(this.append(Object.assign({
|
|
@@ -572,7 +729,25 @@ var ParserResult = class {
|
|
|
572
729
|
name
|
|
573
730
|
}, result)));
|
|
574
731
|
const encoder = this.encoder;
|
|
575
|
-
|
|
732
|
+
const grouped = this.context.config.cssMode === "grouped";
|
|
733
|
+
result.data.forEach((obj) => encoder.processPattern(name, obj, grouped));
|
|
734
|
+
if (grouped && !this.groupIsExact(result)) result.data.forEach((obj) => encoder.processPattern(name, obj, false));
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
738
|
+
*
|
|
739
|
+
* True only when the build saw the whole thing at once: one style object, with every
|
|
740
|
+
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
741
|
+
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
742
|
+
* an unresolved value means the merge would not have matched anyway.
|
|
743
|
+
*
|
|
744
|
+
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
745
|
+
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
746
|
+
* deliberately conservative.
|
|
747
|
+
*/
|
|
748
|
+
groupIsExact(result) {
|
|
749
|
+
if (result.data.length !== 1) return false;
|
|
750
|
+
return findUnresolvedStyles(result).length === 0;
|
|
576
751
|
}
|
|
577
752
|
setRecipe(recipeName, result) {
|
|
578
753
|
(0, _bamboocss_shared.getOrCreateSet)(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
|
@@ -601,7 +776,7 @@ var ParserResult = class {
|
|
|
601
776
|
result.cva.forEach((item) => this.cva.add(this.append(item)));
|
|
602
777
|
result.sva.forEach((item) => this.sva.add(this.append(item)));
|
|
603
778
|
result.token.forEach((item) => this.token.add(this.append(item)));
|
|
604
|
-
result.
|
|
779
|
+
result.viewTransition.forEach((item) => this.viewTransition.add(this.append(item)));
|
|
605
780
|
result.recipe.forEach((items, name) => {
|
|
606
781
|
const set = (0, _bamboocss_shared.getOrCreateSet)(this.recipe, name);
|
|
607
782
|
items.forEach((item) => set.add(this.append(item)));
|
|
@@ -610,6 +785,7 @@ var ParserResult = class {
|
|
|
610
785
|
const set = (0, _bamboocss_shared.getOrCreateSet)(this.pattern, name);
|
|
611
786
|
items.forEach((item) => set.add(this.append(item)));
|
|
612
787
|
});
|
|
788
|
+
if (result.unresolved.length) this.unresolved.push(...result.unresolved);
|
|
613
789
|
return this;
|
|
614
790
|
}
|
|
615
791
|
toArray() {
|
|
@@ -621,7 +797,7 @@ var ParserResult = class {
|
|
|
621
797
|
cva: Array.from(this.cva),
|
|
622
798
|
sva: Array.from(this.sva),
|
|
623
799
|
token: Array.from(this.token),
|
|
624
|
-
|
|
800
|
+
viewTransition: Array.from(this.viewTransition),
|
|
625
801
|
recipe: Object.fromEntries(Array.from(this.recipe.entries()).map(([key, value]) => [key, Array.from(value)])),
|
|
626
802
|
pattern: Object.fromEntries(Array.from(this.pattern.entries()).map(([key, value]) => [key, Array.from(value)]))
|
|
627
803
|
};
|
|
@@ -646,8 +822,7 @@ const defaultEnv = { preset: "ECMA" };
|
|
|
646
822
|
const fallbackImpl = (...values) => values.some((value) => value === void 0) ? void 0 : `fallback(${values.join(", ")})`;
|
|
647
823
|
const evaluateOptions = { environment: defaultEnv };
|
|
648
824
|
function createParser(context) {
|
|
649
|
-
const { jsx, imports, recipes
|
|
650
|
-
const syntax = config.syntax;
|
|
825
|
+
const { jsx, imports, recipes } = context;
|
|
651
826
|
return function parse(sourceFile, encoder, options) {
|
|
652
827
|
if (!sourceFile) return;
|
|
653
828
|
const importDeclarations = getImportDeclarations(context, sourceFile);
|
|
@@ -682,12 +857,8 @@ function createParser(context) {
|
|
|
682
857
|
functions: {
|
|
683
858
|
matchFn: (prop) => file.matchFn(prop.fnName),
|
|
684
859
|
matchProp: () => true,
|
|
685
|
-
matchArg: (
|
|
686
|
-
if (file.isJsxFactory(prop.fnName) && prop.index === 1 && ts_morph.Node.isIdentifier(prop.argNode)) return false;
|
|
687
|
-
return true;
|
|
688
|
-
}
|
|
860
|
+
matchArg: () => true
|
|
689
861
|
},
|
|
690
|
-
taggedTemplates: syntax === "template-literal" ? { matchTaggedTemplate: (tag) => file.matchFn(tag.fnName) } : void 0,
|
|
691
862
|
getEvaluateOptions: (node) => {
|
|
692
863
|
if (!ts_morph.Node.isCallExpression(node)) return evaluateOptions;
|
|
693
864
|
const propAccessExpr = node.getExpression();
|
|
@@ -721,14 +892,6 @@ function createParser(context) {
|
|
|
721
892
|
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
722
893
|
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
723
894
|
});
|
|
724
|
-
else if (query.kind === "tagged-template") {
|
|
725
|
-
const obj = (0, _bamboocss_shared.astish)(query.box.value);
|
|
726
|
-
parserResult.set(name, {
|
|
727
|
-
name,
|
|
728
|
-
box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
|
|
729
|
-
data: [obj]
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
895
|
});
|
|
733
896
|
}).when(imports.matchers.tokens.match, (name) => {
|
|
734
897
|
result.queryList.forEach((query) => {
|
|
@@ -754,116 +917,28 @@ function createParser(context) {
|
|
|
754
917
|
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
755
918
|
});
|
|
756
919
|
});
|
|
757
|
-
}).when(
|
|
758
|
-
result.queryList.forEach((query) => {
|
|
759
|
-
if (query.kind === "call-expression" && query.box.value[1]) {
|
|
760
|
-
const map = query.box.value[1];
|
|
761
|
-
const boxNode = _bamboocss_extractor.box.isMap(map) ? map : _bamboocss_extractor.box.fallback(query.box);
|
|
762
|
-
const combined = combineResult((0, _bamboocss_extractor.unbox)(boxNode));
|
|
763
|
-
const result = {
|
|
764
|
-
name,
|
|
765
|
-
box: boxNode,
|
|
766
|
-
data: options?.transform?.({
|
|
767
|
-
type: "jsx-factory",
|
|
768
|
-
data: combined
|
|
769
|
-
}) ?? combined
|
|
770
|
-
};
|
|
771
|
-
if (_bamboocss_extractor.box.isRecipe(map)) parserResult.setCva(result);
|
|
772
|
-
else parserResult.set("css", result);
|
|
773
|
-
const recipeOptions = query.box.value[2];
|
|
774
|
-
if (_bamboocss_extractor.box.isUnresolvable(map) && recipeOptions && _bamboocss_extractor.box.isMap(recipeOptions) && recipeOptions.value.has("defaultProps")) {
|
|
775
|
-
const maybeIdentifier = map.getNode();
|
|
776
|
-
if (ts_morph.Node.isIdentifier(maybeIdentifier)) {
|
|
777
|
-
const name = maybeIdentifier.getText();
|
|
778
|
-
const recipeName = file.getName(name);
|
|
779
|
-
parserResult.setRecipe(recipeName, {
|
|
780
|
-
type: "jsx-recipe",
|
|
781
|
-
name: recipeName,
|
|
782
|
-
box: recipeOptions,
|
|
783
|
-
data: combineResult((0, _bamboocss_extractor.unbox)(recipeOptions.value.get("defaultProps")))
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
} else if (query.kind === "tagged-template") {
|
|
788
|
-
const obj = (0, _bamboocss_shared.astish)(query.box.value);
|
|
789
|
-
parserResult.set("css", {
|
|
790
|
-
name,
|
|
791
|
-
box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
|
|
792
|
-
data: [obj]
|
|
793
|
-
});
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
}).when(file.isJsxFactory, (name) => {
|
|
920
|
+
}).when(file.isViewTransitionFn, (name) => {
|
|
797
921
|
result.queryList.forEach((query) => {
|
|
798
|
-
if (query.kind === "call-expression") {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
name,
|
|
804
|
-
box: boxNode,
|
|
805
|
-
data: options?.transform?.({
|
|
806
|
-
type: "jsx-factory",
|
|
807
|
-
data: combined
|
|
808
|
-
}) ?? combined
|
|
809
|
-
};
|
|
810
|
-
if (_bamboocss_extractor.box.isRecipe(map)) parserResult.setCva(result);
|
|
811
|
-
else parserResult.set("css", result);
|
|
812
|
-
} else if (query.kind === "tagged-template") {
|
|
813
|
-
const obj = (0, _bamboocss_shared.astish)(query.box.value);
|
|
814
|
-
parserResult.set("css", {
|
|
815
|
-
name,
|
|
816
|
-
box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
|
|
817
|
-
data: [obj]
|
|
818
|
-
});
|
|
819
|
-
}
|
|
922
|
+
if (query.kind === "call-expression") parserResult.setViewTransition({
|
|
923
|
+
name,
|
|
924
|
+
box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
|
|
925
|
+
data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
|
|
926
|
+
});
|
|
820
927
|
});
|
|
821
928
|
}).otherwise(() => {});
|
|
822
929
|
else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
823
930
|
const data = combineResult((0, _bamboocss_extractor.unbox)(query.box));
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
data
|
|
831
|
-
});
|
|
832
|
-
break;
|
|
833
|
-
case jsx.isJsxTagPattern(name) || jsx.isJsxTagPattern(alias):
|
|
834
|
-
parserResult.setPattern(name, {
|
|
835
|
-
type: "jsx-pattern",
|
|
836
|
-
name,
|
|
931
|
+
for (const tag of [name, alias]) {
|
|
932
|
+
if (!jsx.isJsxTagRecipe(tag)) continue;
|
|
933
|
+
recipes.filter(tag).forEach((recipe) => {
|
|
934
|
+
parserResult.setRecipe(recipe.baseName, {
|
|
935
|
+
type: "jsx-recipe",
|
|
936
|
+
name: tag,
|
|
837
937
|
box: query.box,
|
|
838
938
|
data
|
|
839
939
|
});
|
|
840
|
-
break;
|
|
841
|
-
case jsx.isJsxTagRecipe(name):
|
|
842
|
-
recipes.filter(name).map((recipe) => {
|
|
843
|
-
parserResult.setRecipe(recipe.baseName, {
|
|
844
|
-
type: "jsx-recipe",
|
|
845
|
-
name,
|
|
846
|
-
box: query.box,
|
|
847
|
-
data
|
|
848
|
-
});
|
|
849
|
-
});
|
|
850
|
-
break;
|
|
851
|
-
case jsx.isJsxTagRecipe(alias):
|
|
852
|
-
recipes.filter(alias).map((recipe) => {
|
|
853
|
-
parserResult.setRecipe(recipe.baseName, {
|
|
854
|
-
type: "jsx-recipe",
|
|
855
|
-
name: alias,
|
|
856
|
-
box: query.box,
|
|
857
|
-
data
|
|
858
|
-
});
|
|
859
|
-
});
|
|
860
|
-
break;
|
|
861
|
-
default: parserResult.setJsx({
|
|
862
|
-
type: "jsx",
|
|
863
|
-
name,
|
|
864
|
-
box: query.box,
|
|
865
|
-
data
|
|
866
940
|
});
|
|
941
|
+
break;
|
|
867
942
|
}
|
|
868
943
|
});
|
|
869
944
|
});
|
|
@@ -1074,7 +1149,6 @@ var Project = class {
|
|
|
1074
1149
|
}
|
|
1075
1150
|
}) ?? this.transformFile(filePath, original);
|
|
1076
1151
|
if (original !== transformed) sourceFile.replaceWithText(transformed);
|
|
1077
|
-
if (hooks["parser:preprocess"]) options.transform = hooks["parser:preprocess"];
|
|
1078
1152
|
const result = this.parser(sourceFile, encoder, options)?.setFilePath(filePath);
|
|
1079
1153
|
hooks["parser:after"]?.({
|
|
1080
1154
|
filePath,
|
|
@@ -1093,3 +1167,4 @@ var Project = class {
|
|
|
1093
1167
|
//#endregion
|
|
1094
1168
|
exports.ParserResult = ParserResult;
|
|
1095
1169
|
exports.Project = Project;
|
|
1170
|
+
exports.findUnresolvedStyles = findUnresolvedStyles;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, ParserOptions, StyleDecoder, Stylesheet } from "@bamboocss/core";
|
|
2
|
-
import { ArtifactId, BambooHooks, ConfigTsOptions, CssArtifactType,
|
|
2
|
+
import { ArtifactId, BambooHooks, ConfigTsOptions, CssArtifactType, LoadConfigResult, ParserResultConfigureOptions, ParserResultInterface, ResultItem, Runtime, SpecFile, SpecType, SpecTypeMap } from "@bamboocss/types";
|
|
3
3
|
import { FileSystemRefreshResult, Project as Project$1, ProjectOptions as ProjectOptions$1, SourceFile } from "ts-morph";
|
|
4
4
|
|
|
5
5
|
//#region ../generator/dist/index.d.cts
|
|
@@ -93,6 +93,20 @@ declare class Generator extends Context {
|
|
|
93
93
|
*/
|
|
94
94
|
private getAlwaysKeptTokenVars;
|
|
95
95
|
getParserCss: (decoder: StyleDecoder) => string;
|
|
96
|
+
/**
|
|
97
|
+
* The grouped class names this build emitted a rule for.
|
|
98
|
+
*
|
|
99
|
+
* Derived from the encoder rather than the decoder, so it is available as soon as
|
|
100
|
+
* extraction finishes and before a stylesheet exists. Both sides go through
|
|
101
|
+
* `groupClassName`, which is the same function the browser runtime calls — a registry
|
|
102
|
+
* built any other way would be a third spelling of a name that already has two.
|
|
103
|
+
*
|
|
104
|
+
* Unescaped, unlike `StyleDecoder`'s class names: this is compared against what `css()`
|
|
105
|
+
* returns into a `class` attribute, not against a selector. A grouped class is an opaque
|
|
106
|
+
* hash, so the two only differ in principle, but the principle is the one that matters
|
|
107
|
+
* here — the registry is only useful if it holds exactly what the runtime will ask about.
|
|
108
|
+
*/
|
|
109
|
+
getGroupRegistry: () => string[];
|
|
96
110
|
getCss: (stylesheet?: Stylesheet) => string;
|
|
97
111
|
/**
|
|
98
112
|
* Get CSS for a specific layer from the stylesheet
|
|
@@ -119,29 +133,94 @@ declare class Generator extends Context {
|
|
|
119
133
|
* Get CSS for a specific theme
|
|
120
134
|
*/
|
|
121
135
|
//#endregion
|
|
136
|
+
//#region src/unresolved-styles.d.ts
|
|
137
|
+
interface UnresolvedStyle {
|
|
138
|
+
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
139
|
+
prop?: string;
|
|
140
|
+
filePath: string;
|
|
141
|
+
line: number;
|
|
142
|
+
column: number;
|
|
143
|
+
/**
|
|
144
|
+
* How the build lost the call:
|
|
145
|
+
*
|
|
146
|
+
* - `unresolvable-value` — a value it could not evaluate.
|
|
147
|
+
* - `missing-property` — a key that never arrived in the box tree at all.
|
|
148
|
+
* - `unenumerable-keys` — a spread or computed key, so it cannot say what the call sets.
|
|
149
|
+
* - `ambiguous-merge` — two arguments setting one property, which it cannot tell from a
|
|
150
|
+
* pair of alternatives.
|
|
151
|
+
* - `too-many-combinations` — more ternary branches than it will enumerate.
|
|
152
|
+
*/
|
|
153
|
+
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys' | 'ambiguous-merge' | 'too-many-combinations';
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Every property of a `css()` call that will not reach the stylesheet.
|
|
157
|
+
*
|
|
158
|
+
* Only meaningful under `cssMode: 'grouped'`, where one class names the whole call: a
|
|
159
|
+
* property the build cannot resolve does not merely go missing, it changes the class, and
|
|
160
|
+
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
161
|
+
* declaration and keeps the rest, which is why this is not reported there.
|
|
162
|
+
*/
|
|
163
|
+
declare const findUnresolvedStyles: (item: ResultItem) => UnresolvedStyle[];
|
|
164
|
+
//#endregion
|
|
122
165
|
//#region src/parser-result.d.ts
|
|
123
166
|
declare class ParserResult implements ParserResultInterface {
|
|
124
167
|
private context;
|
|
125
168
|
/** Ordered list of all ResultItem */
|
|
126
169
|
all: ResultItem[];
|
|
127
|
-
jsx: Set<ResultItem>;
|
|
128
170
|
css: Set<ResultItem>;
|
|
129
171
|
cva: Set<ResultItem>;
|
|
130
172
|
sva: Set<ResultItem>;
|
|
131
173
|
token: Set<ResultItem>;
|
|
174
|
+
viewTransition: Set<ResultItem>;
|
|
132
175
|
recipe: Map<string, Set<ResultItem>>;
|
|
133
176
|
pattern: Map<string, Set<ResultItem>>;
|
|
134
177
|
filePath: string | undefined;
|
|
135
178
|
encoder: ParserOptions['encoder'];
|
|
179
|
+
/**
|
|
180
|
+
* `css()` calls whose styles the build could not fully see.
|
|
181
|
+
*
|
|
182
|
+
* Only collected under `cssMode: 'grouped'`, where one class names the whole call, so a
|
|
183
|
+
* property the build cannot resolve changes the class rather than dropping a declaration
|
|
184
|
+
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
185
|
+
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
186
|
+
*/
|
|
187
|
+
unresolved: UnresolvedStyle[];
|
|
136
188
|
constructor(context: ParserOptions, encoder?: ParserOptions['encoder']);
|
|
189
|
+
/**
|
|
190
|
+
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
191
|
+
*
|
|
192
|
+
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
193
|
+
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
194
|
+
*/
|
|
195
|
+
private reportUnresolved;
|
|
137
196
|
append(result: ResultItem): ResultItem;
|
|
138
197
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
139
198
|
setCss(result: ResultItem): void;
|
|
199
|
+
/**
|
|
200
|
+
* How many arguments the call this result came from was written with.
|
|
201
|
+
*
|
|
202
|
+
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
203
|
+
* since the question only separates operands from branches and neither has operands.
|
|
204
|
+
*/
|
|
205
|
+
private callArgumentCount;
|
|
140
206
|
setCva(result: ResultItem): void;
|
|
141
207
|
setSva(result: ResultItem): void;
|
|
142
208
|
setToken(result: ResultItem): void;
|
|
143
|
-
|
|
209
|
+
setViewTransition(result: ResultItem): void;
|
|
144
210
|
setPattern(name: string, result: ResultItem): void;
|
|
211
|
+
/**
|
|
212
|
+
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
213
|
+
*
|
|
214
|
+
* True only when the build saw the whole thing at once: one style object, with every
|
|
215
|
+
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
216
|
+
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
217
|
+
* an unresolved value means the merge would not have matched anyway.
|
|
218
|
+
*
|
|
219
|
+
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
220
|
+
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
221
|
+
* deliberately conservative.
|
|
222
|
+
*/
|
|
223
|
+
private groupIsExact;
|
|
145
224
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
146
225
|
isEmpty(): boolean;
|
|
147
226
|
setFilePath(filePath: string): this;
|
|
@@ -152,7 +231,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
152
231
|
cva: ResultItem[];
|
|
153
232
|
sva: ResultItem[];
|
|
154
233
|
token: ResultItem[];
|
|
155
|
-
|
|
234
|
+
viewTransition: ResultItem[];
|
|
156
235
|
recipe: {
|
|
157
236
|
[k: string]: ResultItem[];
|
|
158
237
|
};
|
|
@@ -163,7 +242,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
163
242
|
}
|
|
164
243
|
//#endregion
|
|
165
244
|
//#region src/parser.d.ts
|
|
166
|
-
declare function createParser(context: ParserOptions): (sourceFile: SourceFile | undefined, encoder?: Generator["encoder"], options?: ParserResultConfigureOptions
|
|
245
|
+
declare function createParser(context: ParserOptions): (sourceFile: SourceFile | undefined, encoder?: Generator["encoder"], options?: ParserResultConfigureOptions) => ParserResult | undefined;
|
|
167
246
|
//#endregion
|
|
168
247
|
//#region src/project.d.ts
|
|
169
248
|
interface ProjectOptions extends ProjectOptions$1 {
|
|
@@ -248,4 +327,4 @@ declare class Project {
|
|
|
248
327
|
classify: (fileMap: Map<string, ParserResultInterface>) => import("@bamboocss/types").ClassifyReport;
|
|
249
328
|
}
|
|
250
329
|
//#endregion
|
|
251
|
-
export { ParserResult, Project, ProjectOptions };
|
|
330
|
+
export { ParserResult, Project, ProjectOptions, type UnresolvedStyle, findUnresolvedStyles };
|