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