@bamboocss/parser 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +220 -145
- package/dist/index.d.cts +85 -6
- package/dist/index.d.mts +85 -6
- package/dist/index.mjs +221 -147
- package/package.json +10 -10
package/dist/index.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,29 +133,94 @@ declare class Generator extends Context {
|
|
|
119
133
|
* Get CSS for a specific theme
|
|
120
134
|
*/
|
|
121
135
|
//#endregion
|
|
136
|
+
//#region src/unresolved-styles.d.ts
|
|
137
|
+
interface UnresolvedStyle {
|
|
138
|
+
/** The property the build could not resolve, or `undefined` when only the count differs. */
|
|
139
|
+
prop?: string;
|
|
140
|
+
filePath: string;
|
|
141
|
+
line: number;
|
|
142
|
+
column: number;
|
|
143
|
+
/**
|
|
144
|
+
* How the build lost the call:
|
|
145
|
+
*
|
|
146
|
+
* - `unresolvable-value` — a value it could not evaluate.
|
|
147
|
+
* - `missing-property` — a key that never arrived in the box tree at all.
|
|
148
|
+
* - `unenumerable-keys` — a spread or computed key, so it cannot say what the call sets.
|
|
149
|
+
* - `ambiguous-merge` — two arguments setting one property, which it cannot tell from a
|
|
150
|
+
* pair of alternatives.
|
|
151
|
+
* - `too-many-combinations` — more ternary branches than it will enumerate.
|
|
152
|
+
*/
|
|
153
|
+
reason: 'unresolvable-value' | 'missing-property' | 'unenumerable-keys' | 'ambiguous-merge' | 'too-many-combinations';
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Every property of a `css()` call that will not reach the stylesheet.
|
|
157
|
+
*
|
|
158
|
+
* Only meaningful under `cssMode: 'grouped'`, where one class names the whole call: a
|
|
159
|
+
* property the build cannot resolve does not merely go missing, it changes the class, and
|
|
160
|
+
* the element renders with no styles at all. Under `atomic` the same call loses one
|
|
161
|
+
* declaration and keeps the rest, which is why this is not reported there.
|
|
162
|
+
*/
|
|
163
|
+
declare const findUnresolvedStyles: (item: ResultItem) => UnresolvedStyle[];
|
|
164
|
+
//#endregion
|
|
122
165
|
//#region src/parser-result.d.ts
|
|
123
166
|
declare class ParserResult implements ParserResultInterface {
|
|
124
167
|
private context;
|
|
125
168
|
/** Ordered list of all ResultItem */
|
|
126
169
|
all: ResultItem[];
|
|
127
|
-
jsx: Set<ResultItem>;
|
|
128
170
|
css: Set<ResultItem>;
|
|
129
171
|
cva: Set<ResultItem>;
|
|
130
172
|
sva: Set<ResultItem>;
|
|
131
173
|
token: Set<ResultItem>;
|
|
174
|
+
viewTransition: Set<ResultItem>;
|
|
132
175
|
recipe: Map<string, Set<ResultItem>>;
|
|
133
176
|
pattern: Map<string, Set<ResultItem>>;
|
|
134
177
|
filePath: string | undefined;
|
|
135
178
|
encoder: ParserOptions['encoder'];
|
|
179
|
+
/**
|
|
180
|
+
* `css()` calls whose styles the build could not fully see.
|
|
181
|
+
*
|
|
182
|
+
* Only collected under `cssMode: 'grouped'`, where one class names the whole call, so a
|
|
183
|
+
* property the build cannot resolve changes the class rather than dropping a declaration
|
|
184
|
+
* from it — and the element renders with no styles at all. Under `atomic` the same call
|
|
185
|
+
* keeps everything the build did resolve, which is not worth interrupting a build over.
|
|
186
|
+
*/
|
|
187
|
+
unresolved: UnresolvedStyle[];
|
|
136
188
|
constructor(context: ParserOptions, encoder?: ParserOptions['encoder']);
|
|
189
|
+
/**
|
|
190
|
+
* Record a call whose styles the build could not fully see, at the call's own position.
|
|
191
|
+
*
|
|
192
|
+
* Used for losses the box tree cannot show — a ternary past the combination cap emits
|
|
193
|
+
* fragments rather than whole objects, and every individual box in it resolved fine.
|
|
194
|
+
*/
|
|
195
|
+
private reportUnresolved;
|
|
137
196
|
append(result: ResultItem): ResultItem;
|
|
138
197
|
set(name: 'cva' | 'css' | 'sva' | 'token', result: ResultItem): void;
|
|
139
198
|
setCss(result: ResultItem): void;
|
|
199
|
+
/**
|
|
200
|
+
* How many arguments the call this result came from was written with.
|
|
201
|
+
*
|
|
202
|
+
* Returns 1 for anything that is not a call — a JSX element, or a box that lost its node —
|
|
203
|
+
* since the question only separates operands from branches and neither has operands.
|
|
204
|
+
*/
|
|
205
|
+
private callArgumentCount;
|
|
140
206
|
setCva(result: ResultItem): void;
|
|
141
207
|
setSva(result: ResultItem): void;
|
|
142
208
|
setToken(result: ResultItem): void;
|
|
143
|
-
|
|
209
|
+
setViewTransition(result: ResultItem): void;
|
|
144
210
|
setPattern(name: string, result: ResultItem): void;
|
|
211
|
+
/**
|
|
212
|
+
* Whether the group encoded for this result is the one the runtime will ask for.
|
|
213
|
+
*
|
|
214
|
+
* True only when the build saw the whole thing at once: one style object, with every
|
|
215
|
+
* value in it resolved. Several objects means the runtime merges them into a call this
|
|
216
|
+
* never encoded — `setCss` reconstructs those combinations, and nothing else does — and
|
|
217
|
+
* an unresolved value means the merge would not have matched anyway.
|
|
218
|
+
*
|
|
219
|
+
* Answering "no" costs a call site its atomic rules, which is CSS that duplicates the
|
|
220
|
+
* group. Answering a wrong "yes" costs the element every style it has, so this is
|
|
221
|
+
* deliberately conservative.
|
|
222
|
+
*/
|
|
223
|
+
private groupIsExact;
|
|
145
224
|
setRecipe(recipeName: string, result: ResultItem): void;
|
|
146
225
|
isEmpty(): boolean;
|
|
147
226
|
setFilePath(filePath: string): this;
|
|
@@ -152,7 +231,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
152
231
|
cva: ResultItem[];
|
|
153
232
|
sva: ResultItem[];
|
|
154
233
|
token: ResultItem[];
|
|
155
|
-
|
|
234
|
+
viewTransition: ResultItem[];
|
|
156
235
|
recipe: {
|
|
157
236
|
[k: string]: ResultItem[];
|
|
158
237
|
};
|
|
@@ -163,7 +242,7 @@ declare class ParserResult implements ParserResultInterface {
|
|
|
163
242
|
}
|
|
164
243
|
//#endregion
|
|
165
244
|
//#region src/parser.d.ts
|
|
166
|
-
declare function createParser(context: ParserOptions): (sourceFile: SourceFile | undefined, encoder?: Generator["encoder"], options?: ParserResultConfigureOptions
|
|
245
|
+
declare function createParser(context: ParserOptions): (sourceFile: SourceFile | undefined, encoder?: Generator["encoder"], options?: ParserResultConfigureOptions) => ParserResult | undefined;
|
|
167
246
|
//#endregion
|
|
168
247
|
//#region src/project.d.ts
|
|
169
248
|
interface ProjectOptions extends ProjectOptions$1 {
|
|
@@ -248,4 +327,4 @@ declare class Project {
|
|
|
248
327
|
classify: (fileMap: Map<string, ParserResultInterface>) => import("@bamboocss/types").ClassifyReport;
|
|
249
328
|
}
|
|
250
329
|
//#endregion
|
|
251
|
-
export { ParserResult, Project, ProjectOptions };
|
|
330
|
+
export { ParserResult, Project, ProjectOptions, type UnresolvedStyle, findUnresolvedStyles };
|
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,19 +581,46 @@ 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();
|
|
483
587
|
token = /* @__PURE__ */ new Set();
|
|
588
|
+
viewTransition = /* @__PURE__ */ new Set();
|
|
484
589
|
recipe = /* @__PURE__ */ new Map();
|
|
485
590
|
pattern = /* @__PURE__ */ new Map();
|
|
486
591
|
filePath;
|
|
487
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 = [];
|
|
488
602
|
constructor(context, encoder) {
|
|
489
603
|
this.context = context;
|
|
490
604
|
this.encoder = encoder ?? context.encoder;
|
|
491
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
|
+
}
|
|
492
624
|
append(result) {
|
|
493
625
|
this.all.push(result);
|
|
494
626
|
return result;
|
|
@@ -514,24 +646,36 @@ var ParserResult = class {
|
|
|
514
646
|
this.css.add(this.append(Object.assign({ type: "css" }, result)));
|
|
515
647
|
const encoder = this.encoder;
|
|
516
648
|
const grouped = this.context.config.cssMode === "grouped";
|
|
517
|
-
|
|
518
|
-
|
|
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));
|
|
519
659
|
return;
|
|
520
660
|
}
|
|
521
661
|
const keyCounts = /* @__PURE__ */ new Map();
|
|
522
|
-
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);
|
|
523
663
|
if (!Array.from(keyCounts.values()).some((c) => c > 1)) {
|
|
524
|
-
encoder.
|
|
664
|
+
encoder.processGroupedMerge(data);
|
|
525
665
|
return;
|
|
526
666
|
}
|
|
667
|
+
if (this.callArgumentCount(result) > 1) {
|
|
668
|
+
this.reportUnresolved(result, "ambiguous-merge");
|
|
669
|
+
data.forEach((obj) => encoder.processAtomic(obj));
|
|
670
|
+
}
|
|
527
671
|
const overlappingKeys = /* @__PURE__ */ new Set();
|
|
528
672
|
keyCounts.forEach((count, key) => {
|
|
529
673
|
if (count > 1) overlappingKeys.add(key);
|
|
530
674
|
});
|
|
531
|
-
const
|
|
675
|
+
const baseEntries = [];
|
|
532
676
|
const branchEntries = [];
|
|
533
|
-
for (const obj of
|
|
534
|
-
else
|
|
677
|
+
for (const obj of data) if (Object.keys(obj).some((k) => overlappingKeys.has(k))) branchEntries.push(obj);
|
|
678
|
+
else baseEntries.push(obj);
|
|
535
679
|
const branchGroups = /* @__PURE__ */ new Map();
|
|
536
680
|
for (const entry of branchEntries) {
|
|
537
681
|
const keySet = Object.keys(entry).sort().join("\0");
|
|
@@ -541,10 +685,24 @@ var ParserResult = class {
|
|
|
541
685
|
}
|
|
542
686
|
const groupArrays = Array.from(branchGroups.values());
|
|
543
687
|
if (groupArrays.reduce((acc, g) => acc * g.length, 1) > 32) {
|
|
544
|
-
|
|
688
|
+
this.reportUnresolved(result, "too-many-combinations");
|
|
689
|
+
data.forEach((obj) => {
|
|
690
|
+
encoder.processGrouped(obj);
|
|
691
|
+
encoder.processAtomic(obj);
|
|
692
|
+
});
|
|
545
693
|
return;
|
|
546
694
|
}
|
|
547
|
-
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;
|
|
548
706
|
}
|
|
549
707
|
setCva(result) {
|
|
550
708
|
this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
|
|
@@ -559,11 +717,10 @@ var ParserResult = class {
|
|
|
559
717
|
setToken(result) {
|
|
560
718
|
this.token.add(this.append(Object.assign({ type: "token" }, result)));
|
|
561
719
|
}
|
|
562
|
-
|
|
563
|
-
this.
|
|
720
|
+
setViewTransition(result) {
|
|
721
|
+
this.viewTransition.add(this.append(Object.assign({ type: "viewTransition" }, result)));
|
|
564
722
|
const encoder = this.encoder;
|
|
565
|
-
|
|
566
|
-
result.data.forEach((obj) => encoder.processStyleProps(obj, grouped));
|
|
723
|
+
result.data.forEach((obj) => encoder.processViewTransition(obj));
|
|
567
724
|
}
|
|
568
725
|
setPattern(name, result) {
|
|
569
726
|
getOrCreateSet(this.pattern, name).add(this.append(Object.assign({
|
|
@@ -571,7 +728,25 @@ var ParserResult = class {
|
|
|
571
728
|
name
|
|
572
729
|
}, result)));
|
|
573
730
|
const encoder = this.encoder;
|
|
574
|
-
|
|
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;
|
|
575
750
|
}
|
|
576
751
|
setRecipe(recipeName, result) {
|
|
577
752
|
getOrCreateSet(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
|
|
@@ -600,7 +775,7 @@ var ParserResult = class {
|
|
|
600
775
|
result.cva.forEach((item) => this.cva.add(this.append(item)));
|
|
601
776
|
result.sva.forEach((item) => this.sva.add(this.append(item)));
|
|
602
777
|
result.token.forEach((item) => this.token.add(this.append(item)));
|
|
603
|
-
result.
|
|
778
|
+
result.viewTransition.forEach((item) => this.viewTransition.add(this.append(item)));
|
|
604
779
|
result.recipe.forEach((items, name) => {
|
|
605
780
|
const set = getOrCreateSet(this.recipe, name);
|
|
606
781
|
items.forEach((item) => set.add(this.append(item)));
|
|
@@ -609,6 +784,7 @@ var ParserResult = class {
|
|
|
609
784
|
const set = getOrCreateSet(this.pattern, name);
|
|
610
785
|
items.forEach((item) => set.add(this.append(item)));
|
|
611
786
|
});
|
|
787
|
+
if (result.unresolved.length) this.unresolved.push(...result.unresolved);
|
|
612
788
|
return this;
|
|
613
789
|
}
|
|
614
790
|
toArray() {
|
|
@@ -620,7 +796,7 @@ var ParserResult = class {
|
|
|
620
796
|
cva: Array.from(this.cva),
|
|
621
797
|
sva: Array.from(this.sva),
|
|
622
798
|
token: Array.from(this.token),
|
|
623
|
-
|
|
799
|
+
viewTransition: Array.from(this.viewTransition),
|
|
624
800
|
recipe: Object.fromEntries(Array.from(this.recipe.entries()).map(([key, value]) => [key, Array.from(value)])),
|
|
625
801
|
pattern: Object.fromEntries(Array.from(this.pattern.entries()).map(([key, value]) => [key, Array.from(value)]))
|
|
626
802
|
};
|
|
@@ -645,8 +821,7 @@ const defaultEnv = { preset: "ECMA" };
|
|
|
645
821
|
const fallbackImpl = (...values) => values.some((value) => value === void 0) ? void 0 : `fallback(${values.join(", ")})`;
|
|
646
822
|
const evaluateOptions = { environment: defaultEnv };
|
|
647
823
|
function createParser(context) {
|
|
648
|
-
const { jsx, imports, recipes
|
|
649
|
-
const syntax = config.syntax;
|
|
824
|
+
const { jsx, imports, recipes } = context;
|
|
650
825
|
return function parse(sourceFile, encoder, options) {
|
|
651
826
|
if (!sourceFile) return;
|
|
652
827
|
const importDeclarations = getImportDeclarations(context, sourceFile);
|
|
@@ -681,12 +856,8 @@ function createParser(context) {
|
|
|
681
856
|
functions: {
|
|
682
857
|
matchFn: (prop) => file.matchFn(prop.fnName),
|
|
683
858
|
matchProp: () => true,
|
|
684
|
-
matchArg: (
|
|
685
|
-
if (file.isJsxFactory(prop.fnName) && prop.index === 1 && Node.isIdentifier(prop.argNode)) return false;
|
|
686
|
-
return true;
|
|
687
|
-
}
|
|
859
|
+
matchArg: () => true
|
|
688
860
|
},
|
|
689
|
-
taggedTemplates: syntax === "template-literal" ? { matchTaggedTemplate: (tag) => file.matchFn(tag.fnName) } : void 0,
|
|
690
861
|
getEvaluateOptions: (node) => {
|
|
691
862
|
if (!Node.isCallExpression(node)) return evaluateOptions;
|
|
692
863
|
const propAccessExpr = node.getExpression();
|
|
@@ -720,14 +891,6 @@ function createParser(context) {
|
|
|
720
891
|
box: query.box.value[0] ?? box.fallback(query.box),
|
|
721
892
|
data: combineResult(unbox(query.box.value[0]))
|
|
722
893
|
});
|
|
723
|
-
else if (query.kind === "tagged-template") {
|
|
724
|
-
const obj = astish(query.box.value);
|
|
725
|
-
parserResult.set(name, {
|
|
726
|
-
name,
|
|
727
|
-
box: query.box ?? box.fallback(query.box),
|
|
728
|
-
data: [obj]
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
894
|
});
|
|
732
895
|
}).when(imports.matchers.tokens.match, (name) => {
|
|
733
896
|
result.queryList.forEach((query) => {
|
|
@@ -753,116 +916,28 @@ function createParser(context) {
|
|
|
753
916
|
data: combineResult(unbox(query.box.value[0]))
|
|
754
917
|
});
|
|
755
918
|
});
|
|
756
|
-
}).when(
|
|
757
|
-
result.queryList.forEach((query) => {
|
|
758
|
-
if (query.kind === "call-expression" && query.box.value[1]) {
|
|
759
|
-
const map = query.box.value[1];
|
|
760
|
-
const boxNode = box.isMap(map) ? map : box.fallback(query.box);
|
|
761
|
-
const combined = combineResult(unbox(boxNode));
|
|
762
|
-
const result = {
|
|
763
|
-
name,
|
|
764
|
-
box: boxNode,
|
|
765
|
-
data: options?.transform?.({
|
|
766
|
-
type: "jsx-factory",
|
|
767
|
-
data: combined
|
|
768
|
-
}) ?? combined
|
|
769
|
-
};
|
|
770
|
-
if (box.isRecipe(map)) parserResult.setCva(result);
|
|
771
|
-
else parserResult.set("css", result);
|
|
772
|
-
const recipeOptions = query.box.value[2];
|
|
773
|
-
if (box.isUnresolvable(map) && recipeOptions && box.isMap(recipeOptions) && recipeOptions.value.has("defaultProps")) {
|
|
774
|
-
const maybeIdentifier = map.getNode();
|
|
775
|
-
if (Node.isIdentifier(maybeIdentifier)) {
|
|
776
|
-
const name = maybeIdentifier.getText();
|
|
777
|
-
const recipeName = file.getName(name);
|
|
778
|
-
parserResult.setRecipe(recipeName, {
|
|
779
|
-
type: "jsx-recipe",
|
|
780
|
-
name: recipeName,
|
|
781
|
-
box: recipeOptions,
|
|
782
|
-
data: combineResult(unbox(recipeOptions.value.get("defaultProps")))
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
} else if (query.kind === "tagged-template") {
|
|
787
|
-
const obj = astish(query.box.value);
|
|
788
|
-
parserResult.set("css", {
|
|
789
|
-
name,
|
|
790
|
-
box: query.box ?? box.fallback(query.box),
|
|
791
|
-
data: [obj]
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
});
|
|
795
|
-
}).when(file.isJsxFactory, (name) => {
|
|
919
|
+
}).when(file.isViewTransitionFn, (name) => {
|
|
796
920
|
result.queryList.forEach((query) => {
|
|
797
|
-
if (query.kind === "call-expression") {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
name,
|
|
803
|
-
box: boxNode,
|
|
804
|
-
data: options?.transform?.({
|
|
805
|
-
type: "jsx-factory",
|
|
806
|
-
data: combined
|
|
807
|
-
}) ?? combined
|
|
808
|
-
};
|
|
809
|
-
if (box.isRecipe(map)) parserResult.setCva(result);
|
|
810
|
-
else parserResult.set("css", result);
|
|
811
|
-
} else if (query.kind === "tagged-template") {
|
|
812
|
-
const obj = astish(query.box.value);
|
|
813
|
-
parserResult.set("css", {
|
|
814
|
-
name,
|
|
815
|
-
box: query.box ?? box.fallback(query.box),
|
|
816
|
-
data: [obj]
|
|
817
|
-
});
|
|
818
|
-
}
|
|
921
|
+
if (query.kind === "call-expression") parserResult.setViewTransition({
|
|
922
|
+
name,
|
|
923
|
+
box: query.box.value[0] ?? box.fallback(query.box),
|
|
924
|
+
data: combineResult(unbox(query.box.value[0]))
|
|
925
|
+
});
|
|
819
926
|
});
|
|
820
927
|
}).otherwise(() => {});
|
|
821
928
|
else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
|
|
822
929
|
const data = combineResult(unbox(query.box));
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
data
|
|
830
|
-
});
|
|
831
|
-
break;
|
|
832
|
-
case jsx.isJsxTagPattern(name) || jsx.isJsxTagPattern(alias):
|
|
833
|
-
parserResult.setPattern(name, {
|
|
834
|
-
type: "jsx-pattern",
|
|
835
|
-
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,
|
|
836
936
|
box: query.box,
|
|
837
937
|
data
|
|
838
938
|
});
|
|
839
|
-
break;
|
|
840
|
-
case jsx.isJsxTagRecipe(name):
|
|
841
|
-
recipes.filter(name).map((recipe) => {
|
|
842
|
-
parserResult.setRecipe(recipe.baseName, {
|
|
843
|
-
type: "jsx-recipe",
|
|
844
|
-
name,
|
|
845
|
-
box: query.box,
|
|
846
|
-
data
|
|
847
|
-
});
|
|
848
|
-
});
|
|
849
|
-
break;
|
|
850
|
-
case jsx.isJsxTagRecipe(alias):
|
|
851
|
-
recipes.filter(alias).map((recipe) => {
|
|
852
|
-
parserResult.setRecipe(recipe.baseName, {
|
|
853
|
-
type: "jsx-recipe",
|
|
854
|
-
name: alias,
|
|
855
|
-
box: query.box,
|
|
856
|
-
data
|
|
857
|
-
});
|
|
858
|
-
});
|
|
859
|
-
break;
|
|
860
|
-
default: parserResult.setJsx({
|
|
861
|
-
type: "jsx",
|
|
862
|
-
name,
|
|
863
|
-
box: query.box,
|
|
864
|
-
data
|
|
865
939
|
});
|
|
940
|
+
break;
|
|
866
941
|
}
|
|
867
942
|
});
|
|
868
943
|
});
|
|
@@ -1073,7 +1148,6 @@ var Project = class {
|
|
|
1073
1148
|
}
|
|
1074
1149
|
}) ?? this.transformFile(filePath, original);
|
|
1075
1150
|
if (original !== transformed) sourceFile.replaceWithText(transformed);
|
|
1076
|
-
if (hooks["parser:preprocess"]) options.transform = hooks["parser:preprocess"];
|
|
1077
1151
|
const result = this.parser(sourceFile, encoder, options)?.setFilePath(filePath);
|
|
1078
1152
|
hooks["parser:after"]?.({
|
|
1079
1153
|
filePath,
|
|
@@ -1090,4 +1164,4 @@ var Project = class {
|
|
|
1090
1164
|
};
|
|
1091
1165
|
};
|
|
1092
1166
|
//#endregion
|
|
1093
|
-
export { ParserResult, Project };
|
|
1167
|
+
export { ParserResult, Project, findUnresolvedStyles };
|