@bamboocss/parser 1.16.0 → 1.17.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 +133 -8
- package/dist/index.d.cts +28 -9
- package/dist/index.d.mts +28 -9
- package/dist/index.mjs +134 -9
- package/package.json +10 -10
package/dist/index.cjs
CHANGED
|
@@ -531,7 +531,109 @@ const writtenProps = (node) => {
|
|
|
531
531
|
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
532
532
|
* declaration and keeps the rest, which is why this is not reported there.
|
|
533
533
|
*/
|
|
534
|
-
|
|
534
|
+
/**
|
|
535
|
+
* A recipe config the build could not fully read, level by level.
|
|
536
|
+
*
|
|
537
|
+
* `findUnresolvedStyles` reads the *call's* literal, which for a recipe holds `base` and
|
|
538
|
+
* `variants` rather than declarations — so a loss inside `base` is nested out of its reach.
|
|
539
|
+
* This walks the written source against the resolved data instead, and reports three ways a
|
|
540
|
+
* level can lose something:
|
|
541
|
+
*
|
|
542
|
+
* - a key written in the source that never arrived in the data — an unresolvable *value*,
|
|
543
|
+
* which leaves no trace in the box tree because the pair is never recorded at all;
|
|
544
|
+
* - a spread or computed key that contributed no keys beyond those written beside it;
|
|
545
|
+
* - the config not being an object literal at all, so nothing can be compared.
|
|
546
|
+
*
|
|
547
|
+
* Every level is unwrapped first. `as const` and `satisfies` are idiomatic on a recipe
|
|
548
|
+
* config, and reading through them is what the extractor does — a diagnostic that stops at
|
|
549
|
+
* the cast reports nothing for the exact configs most likely to be written that way.
|
|
550
|
+
*/
|
|
551
|
+
const findUnresolvedInValue = (node, resolved, path, out) => {
|
|
552
|
+
const value = node ? (0, _bamboocss_extractor.unwrapExpression)(node) : void 0;
|
|
553
|
+
if (!value) return;
|
|
554
|
+
if (ts_morph.Node.isArrayLiteralExpression(value)) {
|
|
555
|
+
value.getElements().forEach((element, index) => {
|
|
556
|
+
const at = path ? `${path}.${index}` : String(index);
|
|
557
|
+
const element_ = resolved?.[index];
|
|
558
|
+
if (ts_morph.Node.isSpreadElement(element) && element_ == null) {
|
|
559
|
+
out.push({
|
|
560
|
+
prop: at || void 0,
|
|
561
|
+
reason: "unenumerable-keys"
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
findUnresolvedInValue(element, element_, at, out);
|
|
566
|
+
});
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (!ts_morph.Node.isObjectLiteralExpression(value)) return;
|
|
570
|
+
const properties = value.getProperties();
|
|
571
|
+
const written = [];
|
|
572
|
+
let uncertain = false;
|
|
573
|
+
for (const property of properties) {
|
|
574
|
+
if (ts_morph.Node.isSpreadAssignment(property)) {
|
|
575
|
+
const spread = (0, _bamboocss_extractor.unwrapExpression)(property.getExpression());
|
|
576
|
+
if (ts_morph.Node.isObjectLiteralExpression(spread)) {
|
|
577
|
+
for (const inner of spread.getProperties()) {
|
|
578
|
+
if (!ts_morph.Node.isPropertyAssignment(inner) && !ts_morph.Node.isShorthandPropertyAssignment(inner)) continue;
|
|
579
|
+
const innerName = inner.getName();
|
|
580
|
+
if (!innerName.startsWith("[")) written.push(innerName.replace(/^['"]|['"]$/g, ""));
|
|
581
|
+
}
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
uncertain = true;
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (!ts_morph.Node.isPropertyAssignment(property) && !ts_morph.Node.isShorthandPropertyAssignment(property)) continue;
|
|
588
|
+
const name = property.getName();
|
|
589
|
+
if (name.startsWith("[")) {
|
|
590
|
+
uncertain = true;
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
written.push(name.replace(/^['"]|['"]$/g, ""));
|
|
594
|
+
}
|
|
595
|
+
const resolvedKeys = Boolean(resolved) && typeof resolved === "object" ? Object.keys(resolved) : [];
|
|
596
|
+
for (const name of written) if (!resolvedKeys.includes(name)) out.push({
|
|
597
|
+
prop: path ? `${path}.${name}` : name,
|
|
598
|
+
reason: "missing-property"
|
|
599
|
+
});
|
|
600
|
+
if (uncertain && !resolvedKeys.some((key) => !written.includes(key))) out.push({
|
|
601
|
+
prop: path || void 0,
|
|
602
|
+
reason: "unenumerable-keys"
|
|
603
|
+
});
|
|
604
|
+
for (const property of properties) {
|
|
605
|
+
if (!ts_morph.Node.isPropertyAssignment(property)) continue;
|
|
606
|
+
const name = property.getName().replace(/^['"]|['"]$/g, "");
|
|
607
|
+
if (name.startsWith("[")) continue;
|
|
608
|
+
findUnresolvedInValue(property.getInitializer(), resolved?.[name], path ? `${path}.${name}` : name, out);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
/** A recipe config the build could not fully read — see `findUnresolvedInValue`. */
|
|
612
|
+
const findUnresolvedRecipeStyles = (item) => {
|
|
613
|
+
const node = item.box?.getNode();
|
|
614
|
+
const sourceFile = node?.getSourceFile();
|
|
615
|
+
if (!node || !sourceFile) return [];
|
|
616
|
+
let argument = node;
|
|
617
|
+
if (ts_morph.Node.isCallExpression(argument)) {
|
|
618
|
+
const args = argument.getArguments();
|
|
619
|
+
if (args.length !== 1) return [];
|
|
620
|
+
argument = args[0];
|
|
621
|
+
}
|
|
622
|
+
const losses = [];
|
|
623
|
+
const config = argument ? (0, _bamboocss_extractor.unwrapExpression)(argument) : void 0;
|
|
624
|
+
if (!config || !ts_morph.Node.isObjectLiteralExpression(config)) losses.push({ reason: "unresolvable-value" });
|
|
625
|
+
else findUnresolvedInValue(config, item.data[0], "", losses);
|
|
626
|
+
if (!losses.length) return [];
|
|
627
|
+
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
628
|
+
return losses.map((loss) => ({
|
|
629
|
+
column,
|
|
630
|
+
filePath: sourceFile.getFilePath(),
|
|
631
|
+
kind: "recipe",
|
|
632
|
+
line,
|
|
633
|
+
...loss
|
|
634
|
+
}));
|
|
635
|
+
};
|
|
636
|
+
const findUnresolvedStyles = (item, kind) => {
|
|
535
637
|
const boxNode = item.box;
|
|
536
638
|
if (!boxNode) return [];
|
|
537
639
|
const node = boxNode.getNode();
|
|
@@ -563,6 +665,7 @@ const findUnresolvedStyles = (item) => {
|
|
|
563
665
|
};
|
|
564
666
|
return losses.map((loss) => ({
|
|
565
667
|
...at,
|
|
668
|
+
kind,
|
|
566
669
|
...loss
|
|
567
670
|
}));
|
|
568
671
|
};
|
|
@@ -617,6 +720,7 @@ var ParserResult = class {
|
|
|
617
720
|
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
618
721
|
this.unresolved.push({
|
|
619
722
|
filePath: sourceFile.getFilePath(),
|
|
723
|
+
kind: "grouped",
|
|
620
724
|
line,
|
|
621
725
|
column,
|
|
622
726
|
reason
|
|
@@ -648,12 +752,10 @@ var ParserResult = class {
|
|
|
648
752
|
const encoder = this.encoder;
|
|
649
753
|
const grouped = this.context.config.cssMode === "grouped";
|
|
650
754
|
const data = result.data.some(Array.isArray) ? result.data.flatMap((obj) => Array.isArray(obj) ? obj : [obj]) : result.data;
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
data.forEach((obj) => encoder.processAtomic(obj));
|
|
656
|
-
}
|
|
755
|
+
const unresolved = findUnresolvedStyles(result, grouped ? "grouped" : "atomic").filter((entry) => grouped || entry.reason === "unenumerable-keys");
|
|
756
|
+
if (unresolved.length) {
|
|
757
|
+
this.unresolved.push(...unresolved);
|
|
758
|
+
if (grouped) data.forEach((obj) => encoder.processAtomic(obj));
|
|
657
759
|
}
|
|
658
760
|
if (!grouped || data.length <= 1) {
|
|
659
761
|
data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
|
|
@@ -707,14 +809,37 @@ var ParserResult = class {
|
|
|
707
809
|
}
|
|
708
810
|
setCva(result) {
|
|
709
811
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
812
|
+
this.reportUnresolvedRecipe(result);
|
|
710
813
|
const encoder = this.encoder;
|
|
711
814
|
result.data.forEach((data) => encoder.processAtomicRecipe(data));
|
|
712
815
|
}
|
|
713
816
|
setSva(result) {
|
|
714
817
|
this.sva.add(this.append(Object.assign({ type: "sva" }, result)));
|
|
818
|
+
this.reportUnresolvedRecipe(result);
|
|
715
819
|
const encoder = this.encoder;
|
|
716
820
|
result.data.forEach((data) => encoder.processAtomicSlotRecipe(data));
|
|
717
821
|
}
|
|
822
|
+
/**
|
|
823
|
+
* Record a recipe config the build could not fully read.
|
|
824
|
+
*
|
|
825
|
+
* Not gated on `cssMode`, unlike the `css()` check in `setCss`. That one exists because
|
|
826
|
+
* grouping names a whole call with one class; this one exists because a recipe is named
|
|
827
|
+
* from a *hash of its config*, which is true in every mode. A declaration the build cannot
|
|
828
|
+
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
829
|
+
* another, and the element renders with no styles at all.
|
|
830
|
+
*
|
|
831
|
+
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
832
|
+
* group and let the runtime's degraded naming land on them; nothing can rescue a diverged
|
|
833
|
+
* hash except an explicit `className`, which is what the message says to reach for.
|
|
834
|
+
*/
|
|
835
|
+
reportUnresolvedRecipe(result) {
|
|
836
|
+
if (result.data.every((data) => {
|
|
837
|
+
const className = data?.className;
|
|
838
|
+
return typeof className === "string" && className !== "";
|
|
839
|
+
})) return;
|
|
840
|
+
const unresolved = findUnresolvedRecipeStyles(result);
|
|
841
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
842
|
+
}
|
|
718
843
|
setToken(result) {
|
|
719
844
|
this.token.add(this.append(Object.assign({ type: "token" }, result)));
|
|
720
845
|
}
|
|
@@ -747,7 +872,7 @@ var ParserResult = class {
|
|
|
747
872
|
*/
|
|
748
873
|
groupIsExact(result) {
|
|
749
874
|
if (result.data.length !== 1) return false;
|
|
750
|
-
return findUnresolvedStyles(result).length === 0;
|
|
875
|
+
return findUnresolvedStyles(result, "grouped").length === 0;
|
|
751
876
|
}
|
|
752
877
|
setRecipe(recipeName, result) {
|
|
753
878
|
(0, _bamboocss_shared.getOrCreateSet)(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
package/dist/index.d.cts
CHANGED
|
@@ -135,6 +135,19 @@ declare class Generator extends Context {
|
|
|
135
135
|
//#endregion
|
|
136
136
|
//#region src/unresolved-styles.d.ts
|
|
137
137
|
interface UnresolvedStyle {
|
|
138
|
+
/**
|
|
139
|
+
* What the loss costs, which decides how it is explained.
|
|
140
|
+
*
|
|
141
|
+
* - `grouped` — a `css()` call under `cssMode: 'grouped'`. It degrades: the runtime falls
|
|
142
|
+
* back to naming each declaration, and the build emits atomic rules alongside the group
|
|
143
|
+
* so the ones it resolved still apply.
|
|
144
|
+
* - `atomic` — a `css()` call under `cssMode: 'atomic'`. The declarations the build saw
|
|
145
|
+
* still apply; the ones it did not have no rule behind them, so they are simply absent.
|
|
146
|
+
* - `recipe` — a `cva`/`sva` config. There is no degrading. A recipe's classes are named
|
|
147
|
+
* from a hash of its config, so a declaration the build cannot see gives the two sides
|
|
148
|
+
* different names and *every* rule misses.
|
|
149
|
+
*/
|
|
150
|
+
kind: 'grouped' | 'atomic' | 'recipe';
|
|
138
151
|
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
139
152
|
prop?: string;
|
|
140
153
|
filePath: string;
|
|
@@ -152,15 +165,7 @@ interface UnresolvedStyle {
|
|
|
152
165
|
*/
|
|
153
166
|
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys' | 'ambiguous-merge' | 'too-many-combinations';
|
|
154
167
|
}
|
|
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[];
|
|
168
|
+
declare const findUnresolvedStyles: (item: ResultItem, kind: "grouped" | "atomic") => UnresolvedStyle[];
|
|
164
169
|
//#endregion
|
|
165
170
|
//#region src/parser-result.d.ts
|
|
166
171
|
declare class ParserResult implements ParserResultInterface {
|
|
@@ -205,6 +210,20 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
205
210
|
private callArgumentCount;
|
|
206
211
|
setCva(result: ResultItem): void;
|
|
207
212
|
setSva(result: ResultItem): void;
|
|
213
|
+
/**
|
|
214
|
+
* Record a recipe config the build could not fully read.
|
|
215
|
+
*
|
|
216
|
+
* Not gated on `cssMode`, unlike the `css()` check in `setCss`. That one exists because
|
|
217
|
+
* grouping names a whole call with one class; this one exists because a recipe is named
|
|
218
|
+
* from a *hash of its config*, which is true in every mode. A declaration the build cannot
|
|
219
|
+
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
220
|
+
* another, and the element renders with no styles at all.
|
|
221
|
+
*
|
|
222
|
+
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
223
|
+
* group and let the runtime's degraded naming land on them; nothing can rescue a diverged
|
|
224
|
+
* hash except an explicit `className`, which is what the message says to reach for.
|
|
225
|
+
*/
|
|
226
|
+
private reportUnresolvedRecipe;
|
|
208
227
|
setToken(result: ResultItem): void;
|
|
209
228
|
setViewTransition(result: ResultItem): void;
|
|
210
229
|
setPattern(name: string, result: ResultItem): void;
|
package/dist/index.d.mts
CHANGED
|
@@ -135,6 +135,19 @@ declare class Generator extends Context {
|
|
|
135
135
|
//#endregion
|
|
136
136
|
//#region src/unresolved-styles.d.ts
|
|
137
137
|
interface UnresolvedStyle {
|
|
138
|
+
/**
|
|
139
|
+
* What the loss costs, which decides how it is explained.
|
|
140
|
+
*
|
|
141
|
+
* - `grouped` — a `css()` call under `cssMode: 'grouped'`. It degrades: the runtime falls
|
|
142
|
+
* back to naming each declaration, and the build emits atomic rules alongside the group
|
|
143
|
+
* so the ones it resolved still apply.
|
|
144
|
+
* - `atomic` — a `css()` call under `cssMode: 'atomic'`. The declarations the build saw
|
|
145
|
+
* still apply; the ones it did not have no rule behind them, so they are simply absent.
|
|
146
|
+
* - `recipe` — a `cva`/`sva` config. There is no degrading. A recipe's classes are named
|
|
147
|
+
* from a hash of its config, so a declaration the build cannot see gives the two sides
|
|
148
|
+
* different names and *every* rule misses.
|
|
149
|
+
*/
|
|
150
|
+
kind: 'grouped' | 'atomic' | 'recipe';
|
|
138
151
|
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
139
152
|
prop?: string;
|
|
140
153
|
filePath: string;
|
|
@@ -152,15 +165,7 @@ interface UnresolvedStyle {
|
|
|
152
165
|
*/
|
|
153
166
|
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys' | 'ambiguous-merge' | 'too-many-combinations';
|
|
154
167
|
}
|
|
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[];
|
|
168
|
+
declare const findUnresolvedStyles: (item: ResultItem, kind: "grouped" | "atomic") => UnresolvedStyle[];
|
|
164
169
|
//#endregion
|
|
165
170
|
//#region src/parser-result.d.ts
|
|
166
171
|
declare class ParserResult implements ParserResultInterface {
|
|
@@ -205,6 +210,20 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
205
210
|
private callArgumentCount;
|
|
206
211
|
setCva(result: ResultItem): void;
|
|
207
212
|
setSva(result: ResultItem): void;
|
|
213
|
+
/**
|
|
214
|
+
* Record a recipe config the build could not fully read.
|
|
215
|
+
*
|
|
216
|
+
* Not gated on `cssMode`, unlike the `css()` check in `setCss`. That one exists because
|
|
217
|
+
* grouping names a whole call with one class; this one exists because a recipe is named
|
|
218
|
+
* from a *hash of its config*, which is true in every mode. A declaration the build cannot
|
|
219
|
+
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
220
|
+
* another, and the element renders with no styles at all.
|
|
221
|
+
*
|
|
222
|
+
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
223
|
+
* group and let the runtime's degraded naming land on them; nothing can rescue a diverged
|
|
224
|
+
* hash except an explicit `className`, which is what the message says to reach for.
|
|
225
|
+
*/
|
|
226
|
+
private reportUnresolvedRecipe;
|
|
208
227
|
setToken(result: ResultItem): void;
|
|
209
228
|
setViewTransition(result: ResultItem): void;
|
|
210
229
|
setPattern(name: string, result: ResultItem): void;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Node, Project as Project$1, ScriptKind, ts } from "ts-morph";
|
|
2
|
-
import { box, clearBoxNodeCache, extract, unbox } from "@bamboocss/extractor";
|
|
2
|
+
import { box, clearBoxNodeCache, extract, unbox, unwrapExpression } from "@bamboocss/extractor";
|
|
3
3
|
import { BambooError, compact, getOrCreateSet, patternFns } from "@bamboocss/shared";
|
|
4
4
|
import { logger } from "@bamboocss/logger";
|
|
5
5
|
import { match } from "ts-pattern";
|
|
@@ -530,7 +530,109 @@ const writtenProps = (node) => {
|
|
|
530
530
|
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
531
531
|
* declaration and keeps the rest, which is why this is not reported there.
|
|
532
532
|
*/
|
|
533
|
-
|
|
533
|
+
/**
|
|
534
|
+
* A recipe config the build could not fully read, level by level.
|
|
535
|
+
*
|
|
536
|
+
* `findUnresolvedStyles` reads the *call's* literal, which for a recipe holds `base` and
|
|
537
|
+
* `variants` rather than declarations — so a loss inside `base` is nested out of its reach.
|
|
538
|
+
* This walks the written source against the resolved data instead, and reports three ways a
|
|
539
|
+
* level can lose something:
|
|
540
|
+
*
|
|
541
|
+
* - a key written in the source that never arrived in the data — an unresolvable *value*,
|
|
542
|
+
* which leaves no trace in the box tree because the pair is never recorded at all;
|
|
543
|
+
* - a spread or computed key that contributed no keys beyond those written beside it;
|
|
544
|
+
* - the config not being an object literal at all, so nothing can be compared.
|
|
545
|
+
*
|
|
546
|
+
* Every level is unwrapped first. `as const` and `satisfies` are idiomatic on a recipe
|
|
547
|
+
* config, and reading through them is what the extractor does — a diagnostic that stops at
|
|
548
|
+
* the cast reports nothing for the exact configs most likely to be written that way.
|
|
549
|
+
*/
|
|
550
|
+
const findUnresolvedInValue = (node, resolved, path, out) => {
|
|
551
|
+
const value = node ? unwrapExpression(node) : void 0;
|
|
552
|
+
if (!value) return;
|
|
553
|
+
if (Node.isArrayLiteralExpression(value)) {
|
|
554
|
+
value.getElements().forEach((element, index) => {
|
|
555
|
+
const at = path ? `${path}.${index}` : String(index);
|
|
556
|
+
const element_ = resolved?.[index];
|
|
557
|
+
if (Node.isSpreadElement(element) && element_ == null) {
|
|
558
|
+
out.push({
|
|
559
|
+
prop: at || void 0,
|
|
560
|
+
reason: "unenumerable-keys"
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
findUnresolvedInValue(element, element_, at, out);
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (!Node.isObjectLiteralExpression(value)) return;
|
|
569
|
+
const properties = value.getProperties();
|
|
570
|
+
const written = [];
|
|
571
|
+
let uncertain = false;
|
|
572
|
+
for (const property of properties) {
|
|
573
|
+
if (Node.isSpreadAssignment(property)) {
|
|
574
|
+
const spread = unwrapExpression(property.getExpression());
|
|
575
|
+
if (Node.isObjectLiteralExpression(spread)) {
|
|
576
|
+
for (const inner of spread.getProperties()) {
|
|
577
|
+
if (!Node.isPropertyAssignment(inner) && !Node.isShorthandPropertyAssignment(inner)) continue;
|
|
578
|
+
const innerName = inner.getName();
|
|
579
|
+
if (!innerName.startsWith("[")) written.push(innerName.replace(/^['"]|['"]$/g, ""));
|
|
580
|
+
}
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
uncertain = true;
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (!Node.isPropertyAssignment(property) && !Node.isShorthandPropertyAssignment(property)) continue;
|
|
587
|
+
const name = property.getName();
|
|
588
|
+
if (name.startsWith("[")) {
|
|
589
|
+
uncertain = true;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
written.push(name.replace(/^['"]|['"]$/g, ""));
|
|
593
|
+
}
|
|
594
|
+
const resolvedKeys = Boolean(resolved) && typeof resolved === "object" ? Object.keys(resolved) : [];
|
|
595
|
+
for (const name of written) if (!resolvedKeys.includes(name)) out.push({
|
|
596
|
+
prop: path ? `${path}.${name}` : name,
|
|
597
|
+
reason: "missing-property"
|
|
598
|
+
});
|
|
599
|
+
if (uncertain && !resolvedKeys.some((key) => !written.includes(key))) out.push({
|
|
600
|
+
prop: path || void 0,
|
|
601
|
+
reason: "unenumerable-keys"
|
|
602
|
+
});
|
|
603
|
+
for (const property of properties) {
|
|
604
|
+
if (!Node.isPropertyAssignment(property)) continue;
|
|
605
|
+
const name = property.getName().replace(/^['"]|['"]$/g, "");
|
|
606
|
+
if (name.startsWith("[")) continue;
|
|
607
|
+
findUnresolvedInValue(property.getInitializer(), resolved?.[name], path ? `${path}.${name}` : name, out);
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
/** A recipe config the build could not fully read — see `findUnresolvedInValue`. */
|
|
611
|
+
const findUnresolvedRecipeStyles = (item) => {
|
|
612
|
+
const node = item.box?.getNode();
|
|
613
|
+
const sourceFile = node?.getSourceFile();
|
|
614
|
+
if (!node || !sourceFile) return [];
|
|
615
|
+
let argument = node;
|
|
616
|
+
if (Node.isCallExpression(argument)) {
|
|
617
|
+
const args = argument.getArguments();
|
|
618
|
+
if (args.length !== 1) return [];
|
|
619
|
+
argument = args[0];
|
|
620
|
+
}
|
|
621
|
+
const losses = [];
|
|
622
|
+
const config = argument ? unwrapExpression(argument) : void 0;
|
|
623
|
+
if (!config || !Node.isObjectLiteralExpression(config)) losses.push({ reason: "unresolvable-value" });
|
|
624
|
+
else findUnresolvedInValue(config, item.data[0], "", losses);
|
|
625
|
+
if (!losses.length) return [];
|
|
626
|
+
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
627
|
+
return losses.map((loss) => ({
|
|
628
|
+
column,
|
|
629
|
+
filePath: sourceFile.getFilePath(),
|
|
630
|
+
kind: "recipe",
|
|
631
|
+
line,
|
|
632
|
+
...loss
|
|
633
|
+
}));
|
|
634
|
+
};
|
|
635
|
+
const findUnresolvedStyles = (item, kind) => {
|
|
534
636
|
const boxNode = item.box;
|
|
535
637
|
if (!boxNode) return [];
|
|
536
638
|
const node = boxNode.getNode();
|
|
@@ -562,6 +664,7 @@ const findUnresolvedStyles = (item) => {
|
|
|
562
664
|
};
|
|
563
665
|
return losses.map((loss) => ({
|
|
564
666
|
...at,
|
|
667
|
+
kind,
|
|
565
668
|
...loss
|
|
566
669
|
}));
|
|
567
670
|
};
|
|
@@ -616,6 +719,7 @@ var ParserResult = class {
|
|
|
616
719
|
const { line, column } = sourceFile.getLineAndColumnAtPos(node.getStart());
|
|
617
720
|
this.unresolved.push({
|
|
618
721
|
filePath: sourceFile.getFilePath(),
|
|
722
|
+
kind: "grouped",
|
|
619
723
|
line,
|
|
620
724
|
column,
|
|
621
725
|
reason
|
|
@@ -647,12 +751,10 @@ var ParserResult = class {
|
|
|
647
751
|
const encoder = this.encoder;
|
|
648
752
|
const grouped = this.context.config.cssMode === "grouped";
|
|
649
753
|
const data = result.data.some(Array.isArray) ? result.data.flatMap((obj) => Array.isArray(obj) ? obj : [obj]) : result.data;
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
data.forEach((obj) => encoder.processAtomic(obj));
|
|
655
|
-
}
|
|
754
|
+
const unresolved = findUnresolvedStyles(result, grouped ? "grouped" : "atomic").filter((entry) => grouped || entry.reason === "unenumerable-keys");
|
|
755
|
+
if (unresolved.length) {
|
|
756
|
+
this.unresolved.push(...unresolved);
|
|
757
|
+
if (grouped) data.forEach((obj) => encoder.processAtomic(obj));
|
|
656
758
|
}
|
|
657
759
|
if (!grouped || data.length <= 1) {
|
|
658
760
|
data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
|
|
@@ -706,14 +808,37 @@ var ParserResult = class {
|
|
|
706
808
|
}
|
|
707
809
|
setCva(result) {
|
|
708
810
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
811
|
+
this.reportUnresolvedRecipe(result);
|
|
709
812
|
const encoder = this.encoder;
|
|
710
813
|
result.data.forEach((data) => encoder.processAtomicRecipe(data));
|
|
711
814
|
}
|
|
712
815
|
setSva(result) {
|
|
713
816
|
this.sva.add(this.append(Object.assign({ type: "sva" }, result)));
|
|
817
|
+
this.reportUnresolvedRecipe(result);
|
|
714
818
|
const encoder = this.encoder;
|
|
715
819
|
result.data.forEach((data) => encoder.processAtomicSlotRecipe(data));
|
|
716
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Record a recipe config the build could not fully read.
|
|
823
|
+
*
|
|
824
|
+
* Not gated on `cssMode`, unlike the `css()` check in `setCss`. That one exists because
|
|
825
|
+
* grouping names a whole call with one class; this one exists because a recipe is named
|
|
826
|
+
* from a *hash of its config*, which is true in every mode. A declaration the build cannot
|
|
827
|
+
* see changes the hash, so the build emits rules under one name and the browser asks for
|
|
828
|
+
* another, and the element renders with no styles at all.
|
|
829
|
+
*
|
|
830
|
+
* There is no fallback to pair with it either. Grouped can emit atomic rules alongside the
|
|
831
|
+
* group and let the runtime's degraded naming land on them; nothing can rescue a diverged
|
|
832
|
+
* hash except an explicit `className`, which is what the message says to reach for.
|
|
833
|
+
*/
|
|
834
|
+
reportUnresolvedRecipe(result) {
|
|
835
|
+
if (result.data.every((data) => {
|
|
836
|
+
const className = data?.className;
|
|
837
|
+
return typeof className === "string" && className !== "";
|
|
838
|
+
})) return;
|
|
839
|
+
const unresolved = findUnresolvedRecipeStyles(result);
|
|
840
|
+
if (unresolved.length) this.unresolved.push(...unresolved);
|
|
841
|
+
}
|
|
717
842
|
setToken(result) {
|
|
718
843
|
this.token.add(this.append(Object.assign({ type: "token" }, result)));
|
|
719
844
|
}
|
|
@@ -746,7 +871,7 @@ var ParserResult = class {
|
|
|
746
871
|
*/
|
|
747
872
|
groupIsExact(result) {
|
|
748
873
|
if (result.data.length !== 1) return false;
|
|
749
|
-
return findUnresolvedStyles(result).length === 0;
|
|
874
|
+
return findUnresolvedStyles(result, "grouped").length === 0;
|
|
750
875
|
}
|
|
751
876
|
setRecipe(recipeName, result) {
|
|
752
877
|
getOrCreateSet(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/parser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "The static parser for bamboo css",
|
|
5
5
|
"homepage": "https://bamboocss.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,17 +34,17 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"ts-morph": "28.0.0",
|
|
36
36
|
"ts-pattern": "5.9.0",
|
|
37
|
-
"@bamboocss/config": "^1.
|
|
38
|
-
"@bamboocss/core": "^1.
|
|
39
|
-
"@bamboocss/extractor": "1.
|
|
40
|
-
"@bamboocss/logger": "1.
|
|
41
|
-
"@bamboocss/shared": "1.
|
|
42
|
-
"@bamboocss/types": "1.
|
|
37
|
+
"@bamboocss/config": "^1.17.0",
|
|
38
|
+
"@bamboocss/core": "^1.17.0",
|
|
39
|
+
"@bamboocss/extractor": "1.17.0",
|
|
40
|
+
"@bamboocss/logger": "1.17.0",
|
|
41
|
+
"@bamboocss/shared": "1.17.0",
|
|
42
|
+
"@bamboocss/types": "1.17.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@bamboocss/generator": "1.
|
|
46
|
-
"@bamboocss/plugin-svelte": "1.
|
|
47
|
-
"@bamboocss/plugin-vue": "1.
|
|
45
|
+
"@bamboocss/generator": "1.17.0",
|
|
46
|
+
"@bamboocss/plugin-svelte": "1.17.0",
|
|
47
|
+
"@bamboocss/plugin-vue": "1.17.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown src/index.ts --format=esm,cjs --dts",
|