@bamboocss/parser 1.11.1 → 1.11.2

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 ADDED
@@ -0,0 +1,977 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let ts_morph = require("ts-morph");
3
+ let _bamboocss_extractor = require("@bamboocss/extractor");
4
+ let _bamboocss_shared = require("@bamboocss/shared");
5
+ let _bamboocss_logger = require("@bamboocss/logger");
6
+ let ts_pattern = require("ts-pattern");
7
+ let _bamboocss_config_ts_path = require("@bamboocss/config/ts-path");
8
+ //#region src/classify.ts
9
+ function addTo(map, key, value) {
10
+ const set = map.get(key) ?? /* @__PURE__ */ new Set();
11
+ set.add(value);
12
+ map.set(key, set);
13
+ }
14
+ function classifyProject(ctx, resultMap) {
15
+ const byId = /* @__PURE__ */ new Map();
16
+ const byComponentIndex = /* @__PURE__ */ new Map();
17
+ const byFilepath = /* @__PURE__ */ new Map();
18
+ const byComponentInFilepath = /* @__PURE__ */ new Map();
19
+ const globalMaps = createReportMaps();
20
+ const byFilePathMaps = /* @__PURE__ */ new Map();
21
+ const conditions = new Map(Object.entries(ctx.conditions.values));
22
+ const { groupByProp } = getPropertyGroupMap(ctx);
23
+ let id = 0;
24
+ let componentIndex = 0;
25
+ const isKnownUtility = (reportItem, componentReportItem) => {
26
+ const { propName, value, tokenType } = reportItem;
27
+ if (ctx.utility.config[propName]) {
28
+ if (ctx.tokens.getByName(`${tokenType}.${value}`)) return true;
29
+ if (ctx.tokens.getReferences(String(value)).length > 0) return true;
30
+ if (ctx.utility.resolveColorMix(String(value)).color) return true;
31
+ return false;
32
+ }
33
+ if (componentReportItem.reportItemType === "pattern") {
34
+ const patternProp = ctx.patterns.getConfig(componentReportItem.componentName.toLowerCase())?.properties?.[propName];
35
+ if (!patternProp) return false;
36
+ if (patternProp.type === "boolean" || patternProp.type === "number") return true;
37
+ if (patternProp.type === "property" && patternProp.value) return Boolean(ctx.utility.config[patternProp.value]);
38
+ if (patternProp.type === "enum" && patternProp.value) return Boolean(patternProp.value.includes(String(value)));
39
+ if (patternProp.type === "token") return Boolean(ctx.tokens.getByName(`${patternProp.value}.${value}`));
40
+ return false;
41
+ }
42
+ return false;
43
+ };
44
+ const processPattern = (opts) => {
45
+ const { boxNode, data, item, filepath, localMaps } = opts;
46
+ const name = item.componentName;
47
+ const pattern = ctx.patterns.details.find((p) => p.match.test(name) || p.baseName === name);
48
+ if (!pattern) return;
49
+ const cssObj = pattern.config.transform?.(data || {}, _bamboocss_shared.patternFns) ?? {};
50
+ const newItem = {
51
+ name: "css",
52
+ type: "css",
53
+ box: _bamboocss_extractor.box.objectToMap((0, _bamboocss_shared.compact)(cssObj), boxNode.getNode(), boxNode.getStack()),
54
+ data: [cssObj]
55
+ };
56
+ Object.assign(newItem, { debug: true });
57
+ return processResultItem({
58
+ item: newItem,
59
+ kind: "function",
60
+ localMaps,
61
+ filepath
62
+ });
63
+ };
64
+ const processResultItem = (opts) => {
65
+ const { item, kind, filepath, localMaps } = opts;
66
+ if (!item.box || _bamboocss_extractor.box.isUnresolvable(item.box)) return;
67
+ if (!item.data) return;
68
+ const componentReportItem = {
69
+ componentIndex: String(componentIndex++),
70
+ componentName: item.name,
71
+ reportItemType: item.type,
72
+ kind,
73
+ filepath,
74
+ value: item.data,
75
+ range: item.box.getRange(),
76
+ contains: [],
77
+ debug: Reflect.has(item, "debug")
78
+ };
79
+ if (item.type === "pattern" || item.type === "jsx-pattern") return processPattern({
80
+ boxNode: item.box,
81
+ data: item.data[0],
82
+ item: componentReportItem,
83
+ filepath,
84
+ localMaps
85
+ });
86
+ if (_bamboocss_extractor.box.isArray(item.box)) {
87
+ addTo(byComponentInFilepath, filepath, componentReportItem.componentIndex);
88
+ return componentReportItem;
89
+ }
90
+ if (_bamboocss_extractor.box.isMap(item.box)) {
91
+ addTo(byComponentInFilepath, filepath, componentReportItem.componentIndex);
92
+ processMap({
93
+ map: item.box,
94
+ current: [],
95
+ componentReportItem,
96
+ filepath,
97
+ localMaps
98
+ });
99
+ return componentReportItem;
100
+ }
101
+ if (item.type === "recipe") {
102
+ addTo(byComponentInFilepath, filepath, componentReportItem.componentIndex);
103
+ return componentReportItem;
104
+ }
105
+ };
106
+ const processResultItemFn = (opts) => {
107
+ const { item, filepath, localMaps, type } = opts;
108
+ const componentReportItem = processResultItem({
109
+ item,
110
+ kind: type,
111
+ filepath,
112
+ localMaps
113
+ });
114
+ if (!componentReportItem) return;
115
+ addTo(globalMaps.byComponentOfKind, type, componentReportItem.componentIndex);
116
+ addTo(localMaps.byComponentOfKind, type, componentReportItem.componentIndex);
117
+ byComponentIndex.set(componentReportItem.componentIndex, componentReportItem);
118
+ };
119
+ const processMap = (opts) => {
120
+ const { map, current, componentReportItem, filepath, localMaps, skipRange } = opts;
121
+ const { reportItemType: type, kind, componentName: name } = componentReportItem;
122
+ map.value.forEach((attrNode, attrName) => {
123
+ if (_bamboocss_extractor.box.isLiteral(attrNode) || _bamboocss_extractor.box.isEmptyInitializer(attrNode)) {
124
+ const value = _bamboocss_extractor.box.isLiteral(attrNode) ? attrNode.value : true;
125
+ const propReportItem = {
126
+ index: String(id++),
127
+ componentIndex: String(componentReportItem.componentIndex),
128
+ componentName: name,
129
+ tokenType: void 0,
130
+ propName: attrName,
131
+ reportItemKind: "utility",
132
+ reportItemType: type,
133
+ kind,
134
+ filepath,
135
+ path: current.concat(attrName),
136
+ value,
137
+ isKnownValue: false,
138
+ range: skipRange ? null : map.getRange()
139
+ };
140
+ componentReportItem.contains.push(propReportItem.index);
141
+ if (conditions.has(attrName)) {
142
+ addTo(globalMaps.byConditionName, attrName, propReportItem.index);
143
+ addTo(localMaps.byConditionName, attrName, propReportItem.index);
144
+ propReportItem.propName = current[0] ?? attrName;
145
+ propReportItem.isKnownValue = isKnownUtility(propReportItem, componentReportItem);
146
+ propReportItem.conditionName = attrName;
147
+ } else {
148
+ if (current.length && conditions.has(current[0])) {
149
+ propReportItem.conditionName = current[0];
150
+ addTo(globalMaps.byConditionName, current[0], propReportItem.index);
151
+ addTo(localMaps.byConditionName, current[0], propReportItem.index);
152
+ }
153
+ const propName = ctx.utility.resolveShorthand(attrName);
154
+ const tokenType = ctx.utility.getTokenType(propName);
155
+ if (tokenType) {
156
+ propReportItem.reportItemKind = "token";
157
+ propReportItem.tokenType = tokenType;
158
+ }
159
+ propReportItem.propName = propName;
160
+ propReportItem.isKnownValue = isKnownUtility(propReportItem, componentReportItem);
161
+ addTo(globalMaps.byPropertyName, propName, propReportItem.index);
162
+ addTo(localMaps.byPropertyName, propName, propReportItem.index);
163
+ if (tokenType) {
164
+ addTo(globalMaps.byTokenType, tokenType, propReportItem.index);
165
+ addTo(localMaps.byTokenType, tokenType, propReportItem.index);
166
+ }
167
+ if (propName.toLowerCase().includes("color") || groupByProp.get(propName) === "Color" || tokenType === "colors") {
168
+ addTo(globalMaps.colorsUsed, value, propReportItem.index);
169
+ addTo(localMaps.colorsUsed, value, propReportItem.index);
170
+ }
171
+ if (ctx.utility.shorthands.has(attrName)) {
172
+ addTo(globalMaps.byShorthand, attrName, propReportItem.index);
173
+ addTo(localMaps.byShorthand, attrName, propReportItem.index);
174
+ }
175
+ }
176
+ if (current.length) {
177
+ addTo(globalMaps.byPropertyPath, propReportItem.path.join("."), propReportItem.index);
178
+ addTo(localMaps.byPropertyPath, propReportItem.path.join("."), propReportItem.index);
179
+ }
180
+ addTo(globalMaps.byTokenName, String(value), propReportItem.index);
181
+ addTo(localMaps.byTokenName, String(value), propReportItem.index);
182
+ addTo(globalMaps.byType, type, propReportItem.index);
183
+ addTo(localMaps.byType, type, propReportItem.index);
184
+ addTo(globalMaps.byComponentName, name, propReportItem.index);
185
+ addTo(localMaps.byComponentName, name, propReportItem.index);
186
+ addTo(globalMaps.fromKind, kind, propReportItem.index);
187
+ addTo(localMaps.fromKind, kind, propReportItem.index);
188
+ addTo(byFilepath, filepath, propReportItem.index);
189
+ byId.set(propReportItem.index, propReportItem);
190
+ return;
191
+ }
192
+ if (_bamboocss_extractor.box.isMap(attrNode) && attrNode.value.size) return processMap({
193
+ map: attrNode,
194
+ current: current.concat(attrName),
195
+ componentReportItem,
196
+ filepath,
197
+ localMaps
198
+ });
199
+ });
200
+ };
201
+ resultMap.forEach((parserResult, filepath) => {
202
+ if (parserResult.isEmpty()) return;
203
+ const localMaps = createReportMaps();
204
+ const componentFn = (item) => {
205
+ processResultItemFn({
206
+ item,
207
+ filepath,
208
+ localMaps,
209
+ type: "component"
210
+ });
211
+ };
212
+ const functionFn = (item) => {
213
+ processResultItemFn({
214
+ item,
215
+ filepath,
216
+ localMaps,
217
+ type: "function"
218
+ });
219
+ };
220
+ parserResult.jsx.forEach(componentFn);
221
+ parserResult.css.forEach(functionFn);
222
+ parserResult.cva.forEach(functionFn);
223
+ parserResult.pattern.forEach((itemList) => {
224
+ itemList.forEach(functionFn);
225
+ });
226
+ parserResult.recipe.forEach((itemList) => {
227
+ itemList.forEach(functionFn);
228
+ });
229
+ byFilePathMaps.set(filepath, localMaps);
230
+ });
231
+ const filesWithMostComponent = Object.fromEntries(Array.from(byComponentInFilepath.entries()).map(([filepath, list]) => [filepath, list.size]).sort((a, b) => b[1] - a[1]).slice(0, 10));
232
+ Object.entries(ctx.recipes.config).forEach(([key, recipe]) => {
233
+ const localMaps = createReportMaps();
234
+ const functionFn = (styleObject) => {
235
+ if (!styleObject) return;
236
+ const componentReportItem = {
237
+ componentIndex: "0",
238
+ componentName: `recipes.${key}.base`,
239
+ reportItemType: "css",
240
+ kind: "function",
241
+ filepath: `theme/recipes/${key}`,
242
+ value: styleObject,
243
+ range: null,
244
+ contains: []
245
+ };
246
+ processMap({
247
+ map: _bamboocss_extractor.box.objectToMap(styleObject, null, []),
248
+ current: [],
249
+ filepath: `@config/theme/recipes/${key}`,
250
+ skipRange: true,
251
+ localMaps,
252
+ componentReportItem
253
+ });
254
+ };
255
+ const isSlotRecipe = (_v) => ctx.recipes.isSlotRecipe(key);
256
+ if (isSlotRecipe(recipe)) {
257
+ Object.values(recipe.base ?? {}).forEach(functionFn);
258
+ Object.values(recipe.variants ?? {}).forEach((variants) => {
259
+ Object.values(variants).forEach((v) => {
260
+ Object.values(v).forEach(functionFn);
261
+ });
262
+ });
263
+ recipe.compoundVariants?.forEach((v) => {
264
+ Object.values(v.css).forEach(functionFn);
265
+ });
266
+ } else {
267
+ functionFn(recipe.base);
268
+ Object.values(recipe.variants ?? {}).forEach((variants) => {
269
+ Object.values(variants).forEach(functionFn);
270
+ });
271
+ recipe.compoundVariants?.forEach((v) => functionFn(v.css));
272
+ }
273
+ });
274
+ Object.values(ctx.config.globalCss ?? {}).forEach((styleObject) => {
275
+ if (!styleObject) return;
276
+ processMap({
277
+ map: _bamboocss_extractor.box.objectToMap(styleObject, null, []),
278
+ current: [],
279
+ filepath: "@config/globalCss",
280
+ skipRange: true,
281
+ localMaps: createReportMaps(),
282
+ componentReportItem: {
283
+ componentIndex: "0",
284
+ componentName: "global",
285
+ reportItemType: "css",
286
+ kind: "function",
287
+ filepath: "global",
288
+ value: styleObject,
289
+ range: null,
290
+ contains: []
291
+ }
292
+ });
293
+ });
294
+ return {
295
+ propById: byId,
296
+ componentById: byComponentIndex,
297
+ details: {
298
+ counts: {
299
+ filesWithTokens: byFilepath.size,
300
+ propNameUsed: globalMaps.byPropertyName.size,
301
+ tokenUsed: globalMaps.byTokenName.size,
302
+ shorthandUsed: globalMaps.byShorthand.size,
303
+ propertyPathUsed: globalMaps.byPropertyPath.size,
304
+ typeUsed: globalMaps.byType.size,
305
+ componentNameUsed: globalMaps.byComponentName.size,
306
+ kindUsed: globalMaps.fromKind.size,
307
+ componentOfKindUsed: globalMaps.byComponentOfKind.size,
308
+ colorsUsed: globalMaps.colorsUsed.size
309
+ },
310
+ stats: {
311
+ filesWithMostComponent,
312
+ mostUseds: getXMostUseds(globalMaps, 10)
313
+ }
314
+ },
315
+ derived: {
316
+ byFilepath,
317
+ byComponentInFilepath,
318
+ globalMaps,
319
+ byFilePathMaps
320
+ }
321
+ };
322
+ }
323
+ const getXMostUseds = (globalMaps, pickCount) => {
324
+ return {
325
+ propNames: getMostUsedInMap(globalMaps.byPropertyName, pickCount),
326
+ tokens: getMostUsedInMap(globalMaps.byTokenName, pickCount),
327
+ shorthands: getMostUsedInMap(globalMaps.byShorthand, pickCount),
328
+ conditions: getMostUsedInMap(globalMaps.byConditionName, pickCount),
329
+ propertyPaths: getMostUsedInMap(globalMaps.byPropertyPath, pickCount),
330
+ categories: getMostUsedInMap(globalMaps.byTokenType, pickCount),
331
+ types: getMostUsedInMap(globalMaps.byType, pickCount),
332
+ componentNames: getMostUsedInMap(globalMaps.byComponentName, pickCount),
333
+ fromKinds: getMostUsedInMap(globalMaps.fromKind, pickCount),
334
+ componentOfKinds: getMostUsedInMap(globalMaps.byComponentOfKind, pickCount),
335
+ colors: getMostUsedInMap(globalMaps.colorsUsed, pickCount)
336
+ };
337
+ };
338
+ const getMostUsedInMap = (map, pickCount) => {
339
+ return Array.from(map.entries()).map(([key, list]) => [key, list.size]).sort((a, b) => b[1] - a[1]).slice(0, pickCount).map(([key, count]) => ({
340
+ key,
341
+ count
342
+ }));
343
+ };
344
+ const defaultGroupNames = [
345
+ "System",
346
+ "Container",
347
+ "Display",
348
+ "Visibility",
349
+ "Position",
350
+ "Transform",
351
+ "Flex Layout",
352
+ "Grid Layout",
353
+ "Layout",
354
+ "Border",
355
+ "Border Radius",
356
+ "Width",
357
+ "Height",
358
+ "Margin",
359
+ "Padding",
360
+ "Color",
361
+ "Typography",
362
+ "Background",
363
+ "Shadow",
364
+ "Table",
365
+ "List",
366
+ "Scroll",
367
+ "Interactivity",
368
+ "Transition",
369
+ "Effect",
370
+ "Other",
371
+ "Focus Ring"
372
+ ];
373
+ function getPropertyGroupMap(ctx) {
374
+ const groups = new Map(defaultGroupNames.map((name) => [name, /* @__PURE__ */ new Set()]));
375
+ const groupByProp = /* @__PURE__ */ new Map();
376
+ const systemGroup = groups.get("System");
377
+ systemGroup.add("base");
378
+ systemGroup.add("colorPalette");
379
+ const otherStyleProps = groups.get("Other");
380
+ Object.entries(ctx.utility.config).map(([key, value]) => {
381
+ const group = value?.group;
382
+ if (!group) {
383
+ otherStyleProps.add(key);
384
+ return;
385
+ }
386
+ if (!groups.has(group)) groups.set(group, /* @__PURE__ */ new Set());
387
+ const set = groups.get(group);
388
+ if (value.shorthand) if (Array.isArray(value.shorthand)) value.shorthand.forEach((shorthand) => {
389
+ set.add(shorthand);
390
+ groupByProp.set(shorthand, group);
391
+ });
392
+ else {
393
+ set.add(value.shorthand);
394
+ groupByProp.set(value.shorthand, group);
395
+ }
396
+ set.add(key);
397
+ groupByProp.set(key, group);
398
+ });
399
+ return {
400
+ groups,
401
+ groupByProp
402
+ };
403
+ }
404
+ function createReportMaps() {
405
+ return {
406
+ byComponentOfKind: /* @__PURE__ */ new Map(),
407
+ byPropertyName: /* @__PURE__ */ new Map(),
408
+ byTokenType: /* @__PURE__ */ new Map(),
409
+ byConditionName: /* @__PURE__ */ new Map(),
410
+ byShorthand: /* @__PURE__ */ new Map(),
411
+ byTokenName: /* @__PURE__ */ new Map(),
412
+ byPropertyPath: /* @__PURE__ */ new Map(),
413
+ fromKind: /* @__PURE__ */ new Map(),
414
+ byType: /* @__PURE__ */ new Map(),
415
+ byComponentName: /* @__PURE__ */ new Map(),
416
+ colorsUsed: /* @__PURE__ */ new Map()
417
+ };
418
+ }
419
+ //#endregion
420
+ //#region src/get-module-specifier-value.ts
421
+ const getModuleSpecifierValue = (node) => {
422
+ try {
423
+ return node.getModuleSpecifierValue();
424
+ } catch {
425
+ return;
426
+ }
427
+ };
428
+ //#endregion
429
+ //#region src/get-import-declarations.ts
430
+ function getImportDeclarations(context, sourceFile) {
431
+ const { imports, tsOptions } = context;
432
+ const importDeclarations = [];
433
+ sourceFile.getImportDeclarations().forEach((node) => {
434
+ const mod = getModuleSpecifierValue(node);
435
+ if (!mod) return;
436
+ node.getNamedImports().forEach((specifier) => {
437
+ const name = specifier.getNameNode().getText();
438
+ const result = {
439
+ name,
440
+ alias: specifier.getAliasNode()?.getText() || name,
441
+ mod,
442
+ kind: "named"
443
+ };
444
+ if (!imports.match(result, (mod) => {
445
+ if (!tsOptions?.pathMappings) return;
446
+ return (0, _bamboocss_config_ts_path.resolveTsPathPattern)(tsOptions.pathMappings, mod);
447
+ })) return;
448
+ importDeclarations.push(result);
449
+ });
450
+ const namespace = node.getNamespaceImport();
451
+ if (namespace) {
452
+ const name = namespace.getText();
453
+ const result = {
454
+ name,
455
+ alias: name,
456
+ mod,
457
+ kind: "namespace"
458
+ };
459
+ if (!imports.match(result, (mod) => {
460
+ if (!tsOptions?.pathMappings) return;
461
+ return (0, _bamboocss_config_ts_path.resolveTsPathPattern)(tsOptions.pathMappings, mod);
462
+ })) return;
463
+ importDeclarations.push(result);
464
+ }
465
+ });
466
+ return importDeclarations;
467
+ }
468
+ //#endregion
469
+ //#region src/parser-result.ts
470
+ function cartesian(arrays) {
471
+ if (arrays.length === 0) return [[]];
472
+ const [first, ...rest] = arrays;
473
+ const restProduct = cartesian(rest);
474
+ return first.flatMap((item) => restProduct.map((combo) => [item, ...combo]));
475
+ }
476
+ var ParserResult = class {
477
+ context;
478
+ /** Ordered list of all ResultItem */
479
+ all = [];
480
+ jsx = /* @__PURE__ */ new Set();
481
+ css = /* @__PURE__ */ new Set();
482
+ cva = /* @__PURE__ */ new Set();
483
+ sva = /* @__PURE__ */ new Set();
484
+ token = /* @__PURE__ */ new Set();
485
+ recipe = /* @__PURE__ */ new Map();
486
+ pattern = /* @__PURE__ */ new Map();
487
+ filePath;
488
+ encoder;
489
+ constructor(context, encoder) {
490
+ this.context = context;
491
+ this.encoder = encoder ?? context.encoder;
492
+ }
493
+ append(result) {
494
+ this.all.push(result);
495
+ return result;
496
+ }
497
+ set(name, result) {
498
+ switch (name) {
499
+ case "css":
500
+ this.setCss(result);
501
+ break;
502
+ case "cva":
503
+ this.setCva(result);
504
+ break;
505
+ case "sva":
506
+ this.setSva(result);
507
+ break;
508
+ case "token":
509
+ this.setToken(result);
510
+ break;
511
+ default: throw new _bamboocss_shared.BambooError("UNKNOWN_RESULT_TYPE", `Unknown parser result type: "${name}". Expected one of: css, cva, sva, token`);
512
+ }
513
+ }
514
+ setCss(result) {
515
+ this.css.add(this.append(Object.assign({ type: "css" }, result)));
516
+ const encoder = this.encoder;
517
+ const grouped = this.context.config.cssMode === "grouped";
518
+ if (!grouped || result.data.length <= 1) {
519
+ result.data.forEach((obj) => grouped ? encoder.processGrouped(obj) : encoder.processAtomic(obj));
520
+ return;
521
+ }
522
+ const keyCounts = /* @__PURE__ */ new Map();
523
+ for (const obj of result.data) for (const key of Object.keys(obj)) keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
524
+ if (!Array.from(keyCounts.values()).some((c) => c > 1)) {
525
+ encoder.processGrouped(Object.assign({}, ...result.data));
526
+ return;
527
+ }
528
+ const overlappingKeys = /* @__PURE__ */ new Set();
529
+ keyCounts.forEach((count, key) => {
530
+ if (count > 1) overlappingKeys.add(key);
531
+ });
532
+ const base = {};
533
+ const branchEntries = [];
534
+ for (const obj of result.data) if (Object.keys(obj).some((k) => overlappingKeys.has(k))) branchEntries.push(obj);
535
+ else Object.assign(base, obj);
536
+ const branchGroups = /* @__PURE__ */ new Map();
537
+ for (const entry of branchEntries) {
538
+ const keySet = Object.keys(entry).sort().join("\0");
539
+ const group = branchGroups.get(keySet) || [];
540
+ group.push(entry);
541
+ branchGroups.set(keySet, group);
542
+ }
543
+ const groupArrays = Array.from(branchGroups.values());
544
+ if (groupArrays.reduce((acc, g) => acc * g.length, 1) > 32) {
545
+ result.data.forEach((obj) => encoder.processGrouped(obj));
546
+ return;
547
+ }
548
+ for (const combo of cartesian(groupArrays)) encoder.processGrouped(Object.assign({}, base, ...combo));
549
+ }
550
+ setCva(result) {
551
+ this.cva.add(this.append(Object.assign({ type: "cva" }, result)));
552
+ const encoder = this.encoder;
553
+ result.data.forEach((data) => encoder.processAtomicRecipe(data));
554
+ }
555
+ setSva(result) {
556
+ this.sva.add(this.append(Object.assign({ type: "sva" }, result)));
557
+ const encoder = this.encoder;
558
+ result.data.forEach((data) => encoder.processAtomicSlotRecipe(data));
559
+ }
560
+ setToken(result) {
561
+ this.token.add(this.append(Object.assign({ type: "token" }, result)));
562
+ }
563
+ setJsx(result) {
564
+ this.jsx.add(this.append(Object.assign({ type: "jsx" }, result)));
565
+ const encoder = this.encoder;
566
+ const grouped = this.context.config.cssMode === "grouped";
567
+ result.data.forEach((obj) => encoder.processStyleProps(obj, grouped));
568
+ }
569
+ setPattern(name, result) {
570
+ (0, _bamboocss_shared.getOrCreateSet)(this.pattern, name).add(this.append(Object.assign({
571
+ type: "pattern",
572
+ name
573
+ }, result)));
574
+ const encoder = this.encoder;
575
+ result.data.forEach((obj) => encoder.processPattern(name, obj, result.type ?? "pattern", result.name));
576
+ }
577
+ setRecipe(recipeName, result) {
578
+ (0, _bamboocss_shared.getOrCreateSet)(this.recipe, recipeName).add(this.append(Object.assign({ type: "recipe" }, result)));
579
+ const encoder = this.encoder;
580
+ const recipes = this.context.recipes;
581
+ if (!recipes.getConfig(recipeName)) return;
582
+ const recipe = result;
583
+ if (result.type) recipe.data.forEach((data) => {
584
+ const [recipeProps, styleProps] = recipes.splitProps(recipeName, data);
585
+ encoder.processStyleProps(styleProps);
586
+ encoder.processRecipe(recipeName, recipeProps);
587
+ });
588
+ else recipe.data.forEach((data) => {
589
+ encoder.processRecipe(recipeName, data);
590
+ });
591
+ }
592
+ isEmpty() {
593
+ return this.all.length === 0;
594
+ }
595
+ setFilePath(filePath) {
596
+ this.filePath = filePath;
597
+ return this;
598
+ }
599
+ merge(result) {
600
+ result.css.forEach((item) => this.css.add(this.append(item)));
601
+ result.cva.forEach((item) => this.cva.add(this.append(item)));
602
+ result.sva.forEach((item) => this.sva.add(this.append(item)));
603
+ result.token.forEach((item) => this.token.add(this.append(item)));
604
+ result.jsx.forEach((item) => this.jsx.add(this.append(item)));
605
+ result.recipe.forEach((items, name) => {
606
+ const set = (0, _bamboocss_shared.getOrCreateSet)(this.recipe, name);
607
+ items.forEach((item) => set.add(this.append(item)));
608
+ });
609
+ result.pattern.forEach((items, name) => {
610
+ const set = (0, _bamboocss_shared.getOrCreateSet)(this.pattern, name);
611
+ items.forEach((item) => set.add(this.append(item)));
612
+ });
613
+ return this;
614
+ }
615
+ toArray() {
616
+ return this.all;
617
+ }
618
+ toJSON() {
619
+ return {
620
+ css: Array.from(this.css),
621
+ cva: Array.from(this.cva),
622
+ sva: Array.from(this.sva),
623
+ token: Array.from(this.token),
624
+ jsx: Array.from(this.jsx),
625
+ recipe: Object.fromEntries(Array.from(this.recipe.entries()).map(([key, value]) => [key, Array.from(value)])),
626
+ pattern: Object.fromEntries(Array.from(this.pattern.entries()).map(([key, value]) => [key, Array.from(value)]))
627
+ };
628
+ }
629
+ };
630
+ //#endregion
631
+ //#region src/parser.ts
632
+ const combineResult = (unboxed) => {
633
+ return [
634
+ ...unboxed.conditions,
635
+ unboxed.raw,
636
+ ...unboxed.spreadConditions
637
+ ];
638
+ };
639
+ const defaultEnv = { preset: "ECMA" };
640
+ const evaluateOptions = { environment: defaultEnv };
641
+ function createParser(context) {
642
+ const { jsx, imports, recipes, config } = context;
643
+ const syntax = config.syntax;
644
+ return function parse(sourceFile, encoder, options) {
645
+ if (!sourceFile) return;
646
+ const importDeclarations = getImportDeclarations(context, sourceFile);
647
+ const file = imports.file(importDeclarations);
648
+ const filePath = sourceFile.getFilePath();
649
+ _bamboocss_logger.logger.debug("ast:import", !file.isEmpty() ? `Found import { ${file.toString()} } in ${filePath}` : `No import found in ${filePath}`);
650
+ const parserResult = new ParserResult(context, encoder);
651
+ if (file.isEmpty() && !jsx.isEnabled) return parserResult;
652
+ (0, _bamboocss_extractor.extract)({
653
+ ast: sourceFile,
654
+ tokens: context.tokens ? {
655
+ view: context.tokens.view,
656
+ isTokenFn: (fnName) => file.isTokenAlias(fnName)
657
+ } : void 0,
658
+ components: jsx.isEnabled ? {
659
+ matchTag: (prop) => {
660
+ if (options?.matchTag) {
661
+ const isBambooComponent = file.isBambooComponent(prop.tagName);
662
+ const matchTagMode = options.matchTagMode ?? "extend";
663
+ const isCustomMatch = options.matchTag(prop.tagName, isBambooComponent);
664
+ if (matchTagMode === "override") return isCustomMatch;
665
+ return isBambooComponent || isCustomMatch;
666
+ }
667
+ return !!file.matchTag(prop.tagName);
668
+ },
669
+ matchProp: (prop) => {
670
+ const isBambooProp = file.matchTagProp(prop.tagName, prop.propName);
671
+ if (options?.matchTagProp) return isBambooProp && options.matchTagProp(prop.tagName, prop.propName);
672
+ return isBambooProp;
673
+ }
674
+ } : void 0,
675
+ functions: {
676
+ matchFn: (prop) => file.matchFn(prop.fnName),
677
+ matchProp: () => true,
678
+ matchArg: (prop) => {
679
+ if (file.isJsxFactory(prop.fnName) && prop.index === 1 && ts_morph.Node.isIdentifier(prop.argNode)) return false;
680
+ return true;
681
+ }
682
+ },
683
+ taggedTemplates: syntax === "template-literal" ? { matchTaggedTemplate: (tag) => file.matchFn(tag.fnName) } : void 0,
684
+ getEvaluateOptions: (node) => {
685
+ if (!ts_morph.Node.isCallExpression(node)) return evaluateOptions;
686
+ const propAccessExpr = node.getExpression();
687
+ if (!ts_morph.Node.isPropertyAccessExpression(propAccessExpr)) return evaluateOptions;
688
+ let name = propAccessExpr.getText();
689
+ if (!file.isRawFn(name)) return evaluateOptions;
690
+ name = name.replace(".raw", "");
691
+ return { environment: Object.assign({}, defaultEnv, { extra: { [name]: { raw: (v) => v } } }) };
692
+ },
693
+ flags: { skipTraverseFiles: true }
694
+ }).forEach((result, alias) => {
695
+ const name = file.getName(file.normalizeFnName(alias));
696
+ _bamboocss_logger.logger.debug(`ast:${name}`, name !== alias ? {
697
+ kind: result.kind,
698
+ alias
699
+ } : { kind: result.kind });
700
+ if (result.kind === "function") (0, ts_pattern.match)(name).when(imports.matchers.css.match, (name) => {
701
+ result.queryList.forEach((query) => {
702
+ if (query.kind === "call-expression") if (query.box.value.length > 1) parserResult.set(name, {
703
+ name,
704
+ box: query.box,
705
+ data: query.box.value.reduce((acc, value) => [...acc, ...combineResult((0, _bamboocss_extractor.unbox)(value))], [])
706
+ });
707
+ else parserResult.set(name, {
708
+ name,
709
+ box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
710
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
711
+ });
712
+ else if (query.kind === "tagged-template") {
713
+ const obj = (0, _bamboocss_shared.astish)(query.box.value);
714
+ parserResult.set(name, {
715
+ name,
716
+ box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
717
+ data: [obj]
718
+ });
719
+ }
720
+ });
721
+ }).when(imports.matchers.tokens.match, (name) => {
722
+ result.queryList.forEach((query) => {
723
+ if (query.kind === "call-expression") parserResult.setToken({
724
+ name,
725
+ box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
726
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
727
+ });
728
+ });
729
+ }).when(file.isValidPattern, (name) => {
730
+ result.queryList.forEach((query) => {
731
+ if (query.kind === "call-expression") parserResult.setPattern(name, {
732
+ name,
733
+ box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
734
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
735
+ });
736
+ });
737
+ }).when(file.isValidRecipe, (name) => {
738
+ result.queryList.forEach((query) => {
739
+ if (query.kind === "call-expression") parserResult.setRecipe(name, {
740
+ name,
741
+ box: query.box.value[0] ?? _bamboocss_extractor.box.fallback(query.box),
742
+ data: combineResult((0, _bamboocss_extractor.unbox)(query.box.value[0]))
743
+ });
744
+ });
745
+ }).when(jsx.isJsxFactory, () => {
746
+ result.queryList.forEach((query) => {
747
+ if (query.kind === "call-expression" && query.box.value[1]) {
748
+ const map = query.box.value[1];
749
+ const boxNode = _bamboocss_extractor.box.isMap(map) ? map : _bamboocss_extractor.box.fallback(query.box);
750
+ const combined = combineResult((0, _bamboocss_extractor.unbox)(boxNode));
751
+ const result = {
752
+ name,
753
+ box: boxNode,
754
+ data: options?.transform?.({
755
+ type: "jsx-factory",
756
+ data: combined
757
+ }) ?? combined
758
+ };
759
+ if (_bamboocss_extractor.box.isRecipe(map)) parserResult.setCva(result);
760
+ else parserResult.set("css", result);
761
+ const recipeOptions = query.box.value[2];
762
+ if (_bamboocss_extractor.box.isUnresolvable(map) && recipeOptions && _bamboocss_extractor.box.isMap(recipeOptions) && recipeOptions.value.has("defaultProps")) {
763
+ const maybeIdentifier = map.getNode();
764
+ if (ts_morph.Node.isIdentifier(maybeIdentifier)) {
765
+ const name = maybeIdentifier.getText();
766
+ const recipeName = file.getName(name);
767
+ parserResult.setRecipe(recipeName, {
768
+ type: "jsx-recipe",
769
+ name: recipeName,
770
+ box: recipeOptions,
771
+ data: combineResult((0, _bamboocss_extractor.unbox)(recipeOptions.value.get("defaultProps")))
772
+ });
773
+ }
774
+ }
775
+ } else if (query.kind === "tagged-template") {
776
+ const obj = (0, _bamboocss_shared.astish)(query.box.value);
777
+ parserResult.set("css", {
778
+ name,
779
+ box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
780
+ data: [obj]
781
+ });
782
+ }
783
+ });
784
+ }).when(file.isJsxFactory, (name) => {
785
+ result.queryList.forEach((query) => {
786
+ if (query.kind === "call-expression") {
787
+ const map = query.box.value[0];
788
+ const boxNode = _bamboocss_extractor.box.isMap(map) ? map : _bamboocss_extractor.box.fallback(query.box);
789
+ const combined = combineResult((0, _bamboocss_extractor.unbox)(boxNode));
790
+ const result = {
791
+ name,
792
+ box: boxNode,
793
+ data: options?.transform?.({
794
+ type: "jsx-factory",
795
+ data: combined
796
+ }) ?? combined
797
+ };
798
+ if (_bamboocss_extractor.box.isRecipe(map)) parserResult.setCva(result);
799
+ else parserResult.set("css", result);
800
+ } else if (query.kind === "tagged-template") {
801
+ const obj = (0, _bamboocss_shared.astish)(query.box.value);
802
+ parserResult.set("css", {
803
+ name,
804
+ box: query.box ?? _bamboocss_extractor.box.fallback(query.box),
805
+ data: [obj]
806
+ });
807
+ }
808
+ });
809
+ }).otherwise(() => {});
810
+ else if (jsx.isEnabled && result.kind === "component") result.queryList.forEach((query) => {
811
+ const data = combineResult((0, _bamboocss_extractor.unbox)(query.box));
812
+ switch (true) {
813
+ case file.isJsxFactory(name) || file.isJsxFactory(alias):
814
+ parserResult.setJsx({
815
+ type: "jsx-factory",
816
+ name,
817
+ box: query.box,
818
+ data
819
+ });
820
+ break;
821
+ case jsx.isJsxTagPattern(name) || jsx.isJsxTagPattern(alias):
822
+ parserResult.setPattern(name, {
823
+ type: "jsx-pattern",
824
+ name,
825
+ box: query.box,
826
+ data
827
+ });
828
+ break;
829
+ case jsx.isJsxTagRecipe(name):
830
+ recipes.filter(name).map((recipe) => {
831
+ parserResult.setRecipe(recipe.baseName, {
832
+ type: "jsx-recipe",
833
+ name,
834
+ box: query.box,
835
+ data
836
+ });
837
+ });
838
+ break;
839
+ case jsx.isJsxTagRecipe(alias):
840
+ recipes.filter(alias).map((recipe) => {
841
+ parserResult.setRecipe(recipe.baseName, {
842
+ type: "jsx-recipe",
843
+ name: alias,
844
+ box: query.box,
845
+ data
846
+ });
847
+ });
848
+ break;
849
+ default: parserResult.setJsx({
850
+ type: "jsx",
851
+ name,
852
+ box: query.box,
853
+ data
854
+ });
855
+ }
856
+ });
857
+ });
858
+ return parserResult;
859
+ };
860
+ }
861
+ //#endregion
862
+ //#region src/project.ts
863
+ const normalizeCompilerOptions = (raw) => {
864
+ if (!raw) return {};
865
+ const { options } = ts_morph.ts.convertCompilerOptionsFromJson(raw, process.cwd());
866
+ return options;
867
+ };
868
+ const createTsProject = (options) => new ts_morph.Project({
869
+ skipAddingFilesFromTsConfig: true,
870
+ skipFileDependencyResolution: true,
871
+ skipLoadingLibFiles: true,
872
+ ...options,
873
+ compilerOptions: {
874
+ allowJs: true,
875
+ strictNullChecks: false,
876
+ skipLibCheck: true,
877
+ ...normalizeCompilerOptions(options.compilerOptions)
878
+ }
879
+ });
880
+ var Project = class {
881
+ options;
882
+ project;
883
+ parser;
884
+ get parserOptions() {
885
+ return this.options.parserOptions;
886
+ }
887
+ constructor(options) {
888
+ this.options = options;
889
+ const { parserOptions } = options;
890
+ this.project = createTsProject(options);
891
+ this.parser = createParser(parserOptions);
892
+ this.createSourceFiles();
893
+ }
894
+ get files() {
895
+ return this.options.getFiles();
896
+ }
897
+ getSourceFile = (filePath) => {
898
+ return this.project.getSourceFile(filePath);
899
+ };
900
+ createSourceFile = (filePath) => {
901
+ const { readFile } = this.options;
902
+ return this.project.createSourceFile(filePath, readFile(filePath), {
903
+ overwrite: true,
904
+ scriptKind: ts_morph.ScriptKind.TSX
905
+ });
906
+ };
907
+ createSourceFiles = () => {
908
+ const files = this.getFiles();
909
+ for (const file of files) this.createSourceFile(file);
910
+ };
911
+ addSourceFile = (filePath, content) => {
912
+ return this.project.createSourceFile(filePath, content, {
913
+ overwrite: true,
914
+ scriptKind: ts_morph.ScriptKind.TSX
915
+ });
916
+ };
917
+ removeSourceFile = (filePath) => {
918
+ const sourceFile = this.project.getSourceFile(filePath);
919
+ if (sourceFile) return this.project.removeSourceFile(sourceFile);
920
+ return false;
921
+ };
922
+ reloadSourceFile = (filePath) => {
923
+ return this.getSourceFile(filePath)?.refreshFromFileSystemSync();
924
+ };
925
+ reloadSourceFiles = () => {
926
+ const files = this.getFiles();
927
+ for (const file of files) this.getSourceFile(file)?.refreshFromFileSystemSync() ?? this.project.addSourceFileAtPath(file);
928
+ };
929
+ get readFile() {
930
+ return this.options.readFile;
931
+ }
932
+ get getFiles() {
933
+ return this.options.getFiles;
934
+ }
935
+ parseJson = (filePath) => {
936
+ const { readFile, parserOptions } = this.options;
937
+ const content = readFile(filePath);
938
+ parserOptions.encoder.fromJSON(JSON.parse(content));
939
+ return new ParserResult(parserOptions).setFilePath(filePath);
940
+ };
941
+ parseSourceFile = (filePath, encoder) => {
942
+ const { hooks } = this.options;
943
+ if (filePath.endsWith(".json")) return this.parseJson(filePath);
944
+ const sourceFile = this.project.getSourceFile(filePath);
945
+ if (!sourceFile) return;
946
+ const original = sourceFile.getText();
947
+ const options = {};
948
+ const transformed = hooks["parser:before"]?.({
949
+ filePath,
950
+ content: original,
951
+ configure(opts) {
952
+ const { matchTag, matchTagMode, matchTagProp } = opts;
953
+ if (matchTag) options.matchTag = matchTag;
954
+ if (matchTagMode) options.matchTagMode = matchTagMode;
955
+ if (matchTagProp) options.matchTagProp = matchTagProp;
956
+ }
957
+ }) ?? this.transformFile(filePath, original);
958
+ if (original !== transformed) sourceFile.replaceWithText(transformed);
959
+ if (hooks["parser:preprocess"]) options.transform = hooks["parser:preprocess"];
960
+ const result = this.parser(sourceFile, encoder, options)?.setFilePath(filePath);
961
+ hooks["parser:after"]?.({
962
+ filePath,
963
+ result
964
+ });
965
+ return result;
966
+ };
967
+ transformFile = (_filePath, content) => {
968
+ return content;
969
+ };
970
+ classify = (fileMap) => {
971
+ const { parserOptions } = this.options;
972
+ return classifyProject(parserOptions, fileMap);
973
+ };
974
+ };
975
+ //#endregion
976
+ exports.ParserResult = ParserResult;
977
+ exports.Project = Project;