@mintlify/common 1.0.1258 → 1.0.1260

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.
@@ -2,93 +2,906 @@ import { walk } from 'estree-walker';
2
2
  import { visit } from 'unist-util-visit';
3
3
  import { segment } from './segment.js';
4
4
  export const MINTLIFY_TAILWIND_PREFIX = 'mint-';
5
+ const CLASS_HELPERS = new Set(['cn', 'clsx', 'classNames', 'classnames', 'twMerge', 'twJoin']);
6
+ const NON_MUTATING_METHODS = new Set([
7
+ 'at',
8
+ 'charAt',
9
+ 'concat',
10
+ 'endsWith',
11
+ 'includes',
12
+ 'indexOf',
13
+ 'join',
14
+ 'lastIndexOf',
15
+ 'padEnd',
16
+ 'padStart',
17
+ 'repeat',
18
+ 'slice',
19
+ 'split',
20
+ 'startsWith',
21
+ 'toLowerCase',
22
+ 'toString',
23
+ 'toUpperCase',
24
+ 'trim',
25
+ 'trimEnd',
26
+ 'trimStart',
27
+ ]);
28
+ const IMMUTABLE_GLOBALS = new Set(['undefined', 'NaN', 'Infinity']);
29
+ const MAX_INLINE_DEPTH = 32;
30
+ const MAX_NODES_PER_INLINE = 1000;
31
+ const MAX_INLINED_NODES_PER_PAGE = 20000;
5
32
  export const rehypeDynamicTailwindCss = (tailwindSelectors) => (tree) => {
6
33
  const normalizedTailwindSelectors = tailwindSelectors === null || tailwindSelectors === void 0 ? void 0 : tailwindSelectors.map(normalizeSelector);
34
+ if (!(normalizedTailwindSelectors === null || normalizedTailwindSelectors === void 0 ? void 0 : normalizedTailwindSelectors.length))
35
+ return;
36
+ const moduleScope = new Map();
37
+ const moduleScopes = [moduleScope];
38
+ const scopeByNode = new Map();
39
+ const aliases = [];
40
+ visit(tree, (node) => {
41
+ if (node.type === 'mdxjsEsm' && node.data && node.data.estree) {
42
+ collectDeclarations(node.data.estree.body, moduleScope, moduleScopes);
43
+ collectVarNames(node.data.estree).forEach((name) => moduleScope.set(name, createBinding(moduleScopes)));
44
+ }
45
+ });
46
+ const estrees = [];
47
+ visit(tree, (node) => {
48
+ var _a;
49
+ if (node.type === 'mdxjsEsm' ||
50
+ node.type === 'mdxFlowExpression' ||
51
+ node.type === 'mdxTextExpression') {
52
+ if ((_a = node.data) === null || _a === void 0 ? void 0 : _a.estree)
53
+ estrees.push(node.data.estree);
54
+ }
55
+ else if (node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') {
56
+ node.attributes.forEach((attr) => {
57
+ var _a, _b;
58
+ const estree = attr.type === 'mdxJsxExpressionAttribute'
59
+ ? (_a = attr.data) === null || _a === void 0 ? void 0 : _a.estree
60
+ : attr.value && typeof attr.value !== 'string'
61
+ ? (_b = attr.value.data) === null || _b === void 0 ? void 0 : _b.estree
62
+ : undefined;
63
+ if (estree)
64
+ estrees.push(estree);
65
+ });
66
+ }
67
+ });
68
+ estrees.forEach((estree) => analyzeProgram(estree, moduleScopes, scopeByNode, aliases));
69
+ propagateMutations(aliases);
70
+ const context = {
71
+ tailwindSelectors: normalizedTailwindSelectors,
72
+ changes: { count: 0 },
73
+ pageBudget: { remaining: MAX_INLINED_NODES_PER_PAGE },
74
+ inlineDepth: 0,
75
+ };
7
76
  visit(tree, (node) => {
77
+ var _a;
8
78
  if ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
9
79
  node.name &&
10
80
  typeof node.name === 'string' &&
11
81
  node.attributes.length > 0) {
12
- const className = node.attributes.find((attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'className');
13
- if (className && typeof className.value === 'string') {
14
- className.value = transformClassNames(className.value, normalizedTailwindSelectors);
82
+ const className = node.attributes.find((attr) => attr.type === 'mdxJsxAttribute' && (attr.name === 'className' || attr.name === 'class'));
83
+ const value = className === null || className === void 0 ? void 0 : className.value;
84
+ if (className && typeof value === 'string') {
85
+ className.value = transformClassNames(value, normalizedTailwindSelectors);
86
+ }
87
+ else if (value && typeof value !== 'string' && ((_a = value.data) === null || _a === void 0 ? void 0 : _a.estree)) {
88
+ // example: <div className={isActive ? "bg-red-500" : "bg-blue-500"}>
89
+ const [statement] = value.data.estree.body;
90
+ if (statement && statement.type === 'ExpressionStatement') {
91
+ statement.expression = transformExpressionClassNames(statement.expression, moduleScopes, context);
92
+ }
15
93
  }
16
94
  }
17
95
  if (node.type === 'mdxjsEsm' && node.data && node.data.estree) {
18
- transformMdxJsEsmNode(node.data.estree, normalizedTailwindSelectors);
96
+ transformMdxJsEsmNode(node.data.estree, moduleScopes, scopeByNode, context);
19
97
  }
20
98
  });
21
99
  };
22
- const transformMdxJsEsmNode = (estreeNode, tailwindSelectors) => {
23
- walk(estreeNode, {
100
+ const createBinding = (scopes, init) => ({
101
+ init,
102
+ scopes,
103
+ reassigned: false,
104
+ mutated: false,
105
+ aliasOf: [],
106
+ });
107
+ const collectDeclarations = (statements, scope, scopes) => {
108
+ for (const statement of statements) {
109
+ if (statement.type === 'ImportDeclaration') {
110
+ statement.specifiers.forEach((specifier) => scope.set(specifier.local.name, createBinding(scopes)));
111
+ continue;
112
+ }
113
+ const declaration = statement.type === 'ExportNamedDeclaration' || statement.type === 'ExportDefaultDeclaration'
114
+ ? statement.declaration
115
+ : statement;
116
+ if (!declaration)
117
+ continue;
118
+ if ((declaration.type === 'FunctionDeclaration' || declaration.type === 'ClassDeclaration') &&
119
+ declaration.id) {
120
+ scope.set(declaration.id.name, createBinding(scopes));
121
+ }
122
+ if (declaration.type !== 'VariableDeclaration' || declaration.kind === 'var')
123
+ continue;
124
+ for (const declarator of declaration.declarations) {
125
+ if (declaration.kind === 'const' && declarator.id.type === 'Identifier' && declarator.init) {
126
+ scope.set(declarator.id.name, createBinding(scopes, declarator.init));
127
+ }
128
+ else {
129
+ getPatternNames(declarator.id).forEach((name) => scope.set(name, createBinding(scopes)));
130
+ }
131
+ }
132
+ }
133
+ };
134
+ const collectVarNames = (root) => {
135
+ const names = [];
136
+ walk(root, {
24
137
  enter(node) {
25
- if (node.type === 'JSXAttribute' &&
26
- node.name.type === 'JSXIdentifier' &&
27
- (node.name.name === 'className' || node.name.name === 'class')) {
28
- if (node.value && node.value.type === 'JSXExpressionContainer') {
29
- const expression = node.value.expression;
30
- if (expression.type === 'TemplateLiteral') {
31
- // example: className={`bg-green-500`}
32
- expression.quasis.forEach((quasi) => {
33
- if (quasi.value.cooked) {
34
- const originalValue = quasi.value.cooked;
35
- const transformedValue = transformClassNames(originalValue, tailwindSelectors);
36
- if (originalValue !== transformedValue) {
37
- quasi.value.cooked = transformedValue;
38
- quasi.value.raw = transformedValue;
39
- }
40
- }
41
- });
42
- // example: className={`${isActive ? "bg-red-500" : "bg-blue-500"}`}
43
- expression.expressions.forEach((expr) => {
44
- transformExpressionClassNames(expr, tailwindSelectors);
45
- });
138
+ if (node !== root && isFunctionNode(node)) {
139
+ this.skip();
140
+ return;
141
+ }
142
+ if (node.type === 'VariableDeclaration' && node.kind === 'var') {
143
+ node.declarations.forEach((declarator) => names.push(...getPatternNames(declarator.id)));
144
+ }
145
+ },
146
+ });
147
+ return names;
148
+ };
149
+ const getPatternNames = (pattern) => getPatternIdentifiers(pattern).map((identifier) => identifier.name);
150
+ const getPatternIdentifiers = (pattern) => {
151
+ switch (pattern.type) {
152
+ case 'Identifier':
153
+ return [pattern];
154
+ case 'AssignmentPattern':
155
+ return getPatternIdentifiers(pattern.left);
156
+ case 'RestElement':
157
+ return getPatternIdentifiers(pattern.argument);
158
+ case 'ArrayPattern':
159
+ return pattern.elements.flatMap((element) => (element ? getPatternIdentifiers(element) : []));
160
+ case 'ObjectPattern':
161
+ return pattern.properties.flatMap((property) => getPatternIdentifiers(property.type === 'Property' ? property.value : property));
162
+ default:
163
+ return [];
164
+ }
165
+ };
166
+ const resolveBinding = (name, scopes) => {
167
+ var _a;
168
+ for (let i = scopes.length - 1; i >= 0; i--) {
169
+ const binding = (_a = scopes[i]) === null || _a === void 0 ? void 0 : _a.get(name);
170
+ if (binding)
171
+ return binding;
172
+ }
173
+ return undefined;
174
+ };
175
+ const isFunctionNode = (node) => node.type === 'FunctionDeclaration' ||
176
+ node.type === 'FunctionExpression' ||
177
+ node.type === 'ArrowFunctionExpression';
178
+ const isClassAttribute = (node) => node.name.type === 'JSXIdentifier' &&
179
+ (node.name.name === 'className' || node.name.name === 'class');
180
+ const createScope = (node, scopes) => {
181
+ var _a;
182
+ const scope = new Map();
183
+ const chain = [...scopes, scope];
184
+ const declare = (name) => scope.set(name, createBinding(chain));
185
+ if (isFunctionNode(node)) {
186
+ if (node.type === 'FunctionExpression' && node.id)
187
+ declare(node.id.name);
188
+ node.params.flatMap(getPatternNames).forEach(declare);
189
+ collectVarNames(node.body).forEach(declare);
190
+ return scope;
191
+ }
192
+ switch (node.type) {
193
+ case 'BlockStatement':
194
+ case 'StaticBlock':
195
+ collectDeclarations(node.body, scope, chain);
196
+ return scope;
197
+ case 'SwitchStatement':
198
+ collectDeclarations(node.cases.flatMap((switchCase) => switchCase.consequent), scope, chain);
199
+ return scope;
200
+ case 'ForStatement':
201
+ if (((_a = node.init) === null || _a === void 0 ? void 0 : _a.type) === 'VariableDeclaration' && node.init.kind !== 'var') {
202
+ node.init.declarations.forEach((declarator) => getPatternNames(declarator.id).forEach(declare));
203
+ }
204
+ return scope;
205
+ case 'ForInStatement':
206
+ case 'ForOfStatement':
207
+ if (node.left.type === 'VariableDeclaration' && node.left.kind !== 'var') {
208
+ node.left.declarations.forEach((declarator) => getPatternNames(declarator.id).forEach(declare));
209
+ }
210
+ return scope;
211
+ case 'CatchClause':
212
+ if (node.param)
213
+ getPatternNames(node.param).forEach(declare);
214
+ return scope;
215
+ case 'ClassExpression':
216
+ if (node.id)
217
+ declare(node.id.name);
218
+ return scope;
219
+ default:
220
+ return undefined;
221
+ }
222
+ };
223
+ const propagateMutations = (aliases) => {
224
+ var _a;
225
+ const links = new Map();
226
+ const link = (from, to) => {
227
+ const targets = links.get(from);
228
+ if (targets)
229
+ targets.push(to);
230
+ else
231
+ links.set(from, [to]);
232
+ };
233
+ aliases.forEach((alias) => alias.aliasOf.forEach((source) => {
234
+ link(alias, source);
235
+ link(source, alias);
236
+ }));
237
+ const pending = [...links.keys()].filter((binding) => binding.mutated);
238
+ for (let current = pending.pop(); current; current = pending.pop()) {
239
+ for (const next of (_a = links.get(current)) !== null && _a !== void 0 ? _a : []) {
240
+ if (next.mutated)
241
+ continue;
242
+ next.mutated = true;
243
+ pending.push(next);
244
+ }
245
+ }
246
+ };
247
+ const analyzeProgram = (program, baseScopes, scopeByNode, aliases) => {
248
+ const scopes = [...baseScopes];
249
+ const ancestors = [];
250
+ const declaredIdentifiers = new Set();
251
+ const writtenIdentifiers = new Set();
252
+ const declare = (pattern) => pattern && getPatternIdentifiers(pattern).forEach((id) => declaredIdentifiers.add(id));
253
+ const write = (pattern) => getPatternIdentifiers(pattern).forEach((id) => writtenIdentifiers.add(id));
254
+ walk(program, {
255
+ enter(node, parent, key) {
256
+ const scope = createScope(node, scopes);
257
+ if (scope) {
258
+ scopes.push(scope);
259
+ scopeByNode.set(node, scope);
260
+ }
261
+ if (isFunctionNode(node)) {
262
+ if (node.type !== 'ArrowFunctionExpression')
263
+ declare(node.id);
264
+ node.params.forEach(declare);
265
+ }
266
+ else if (node.type === 'VariableDeclarator') {
267
+ declare(node.id);
268
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) === 'VariableDeclaration' && parent.kind === 'var') {
269
+ getPatternNames(node.id).forEach((name) => {
270
+ const binding = resolveBinding(name, scopes);
271
+ if (binding)
272
+ binding.reassigned = true;
273
+ });
274
+ }
275
+ }
276
+ else if (node.type === 'CatchClause') {
277
+ declare(node.param);
278
+ }
279
+ else if ((node.type === 'ClassDeclaration' || node.type === 'ClassExpression') && node.id) {
280
+ declare(node.id);
281
+ }
282
+ else if (node.type === 'ImportDeclaration') {
283
+ node.specifiers.forEach((specifier) => declare(specifier.local));
284
+ }
285
+ else if (node.type === 'AssignmentExpression') {
286
+ write(node.left);
287
+ }
288
+ else if (node.type === 'UpdateExpression' && node.argument.type === 'Identifier') {
289
+ writtenIdentifiers.add(node.argument);
290
+ }
291
+ else if ((node.type === 'ForInStatement' || node.type === 'ForOfStatement') &&
292
+ node.left.type !== 'VariableDeclaration') {
293
+ write(node.left);
294
+ }
295
+ if (node.type === 'Identifier' && !declaredIdentifiers.has(node)) {
296
+ const binding = resolveBinding(node.name, scopes);
297
+ if (writtenIdentifiers.has(node)) {
298
+ if (binding)
299
+ binding.reassigned = true;
300
+ }
301
+ else if (parent && isReferencePosition(parent, key)) {
302
+ const usage = classifyReference(node, ancestors, scopes);
303
+ if (usage === 'mutates') {
304
+ if (binding)
305
+ binding.mutated = true;
306
+ }
307
+ else if (usage !== 'reads' && binding) {
308
+ usage.aliasOf.push(binding);
309
+ aliases.push(usage);
46
310
  }
47
311
  }
312
+ }
313
+ ancestors.push(node);
314
+ },
315
+ leave(node) {
316
+ ancestors.pop();
317
+ if (scopeByNode.has(node) && scopes[scopes.length - 1] === scopeByNode.get(node)) {
318
+ scopes.pop();
319
+ }
320
+ },
321
+ });
322
+ };
323
+ const isReferencePosition = (parent, key) => {
324
+ switch (parent.type) {
325
+ case 'MemberExpression':
326
+ return key !== 'property' || parent.computed;
327
+ case 'Property':
328
+ case 'MethodDefinition':
329
+ case 'PropertyDefinition':
330
+ return key !== 'key' || parent.computed;
331
+ case 'LabeledStatement':
332
+ case 'BreakStatement':
333
+ case 'ContinueStatement':
334
+ case 'MetaProperty':
335
+ return false;
336
+ case 'ExportSpecifier':
337
+ return key === 'local';
338
+ case 'ImportSpecifier':
339
+ return false;
340
+ default:
341
+ return true;
342
+ }
343
+ };
344
+ const classifyReference = (node, ancestors, scopes) => {
345
+ var _a;
346
+ let child = node;
347
+ for (let i = ancestors.length - 1; i >= 0; i--) {
348
+ const parent = ancestors[i];
349
+ if (!parent)
350
+ break;
351
+ switch (parent.type) {
352
+ case 'MemberExpression':
353
+ if (parent.object !== child)
354
+ return 'reads';
355
+ child = parent;
356
+ continue;
357
+ case 'ChainExpression':
358
+ case 'AwaitExpression':
359
+ case 'LogicalExpression':
360
+ child = parent;
361
+ continue;
362
+ case 'ConditionalExpression':
363
+ if (parent.test === child)
364
+ return 'reads';
365
+ child = parent;
366
+ continue;
367
+ case 'SequenceExpression':
368
+ if (parent.expressions[parent.expressions.length - 1] !== child)
369
+ return 'reads';
370
+ child = parent;
371
+ continue;
372
+ case 'CallExpression':
373
+ case 'NewExpression':
374
+ if (parent.callee === child) {
375
+ if (child.type !== 'MemberExpression')
376
+ return 'reads';
377
+ return !child.computed &&
378
+ child.property.type === 'Identifier' &&
379
+ NON_MUTATING_METHODS.has(child.property.name)
380
+ ? 'reads'
381
+ : 'mutates';
382
+ }
383
+ return parent.type === 'CallExpression' &&
384
+ parent.callee.type === 'Identifier' &&
385
+ CLASS_HELPERS.has(parent.callee.name)
386
+ ? 'reads'
387
+ : 'mutates';
388
+ case 'SpreadElement':
389
+ case 'ArrayExpression':
390
+ case 'ObjectExpression':
391
+ child = parent;
392
+ continue;
393
+ case 'Property':
394
+ if (parent.value !== child)
395
+ return 'reads';
396
+ child = parent;
397
+ continue;
398
+ case 'TemplateLiteral':
399
+ case 'BinaryExpression':
400
+ case 'TaggedTemplateExpression':
401
+ case 'IfStatement':
402
+ case 'WhileStatement':
403
+ case 'DoWhileStatement':
404
+ case 'SwitchStatement':
405
+ case 'SwitchCase':
406
+ return 'reads';
407
+ case 'UnaryExpression':
408
+ return parent.operator === 'delete' ? 'mutates' : 'reads';
409
+ case 'ExpressionStatement':
410
+ case 'JSXExpressionContainer':
411
+ case 'JSXSpreadAttribute':
412
+ return 'reads';
413
+ case 'VariableDeclarator': {
414
+ const declaration = ancestors[i - 1];
415
+ if (parent.init === child &&
416
+ parent.id.type === 'Identifier' &&
417
+ (declaration === null || declaration === void 0 ? void 0 : declaration.type) === 'VariableDeclaration' &&
418
+ declaration.kind === 'const') {
419
+ return (_a = resolveBinding(parent.id.name, scopes)) !== null && _a !== void 0 ? _a : 'mutates';
420
+ }
421
+ return 'mutates';
422
+ }
423
+ default:
424
+ return 'mutates';
425
+ }
426
+ }
427
+ return 'reads';
428
+ };
429
+ const transformMdxJsEsmNode = (estreeNode, moduleScopes, scopeByNode, context) => {
430
+ const scopes = [...moduleScopes];
431
+ walk(estreeNode, {
432
+ enter(node) {
433
+ const scope = scopeByNode.get(node);
434
+ if (scope)
435
+ scopes.push(scope);
436
+ if (node.type === 'JSXAttribute' && isClassAttribute(node)) {
48
437
  // example: className="bg-green-500"
49
- if (node.value && node.value.type === 'Literal' && typeof node.value.value === 'string') {
50
- const originalValue = node.value.value;
51
- const transformedValue = transformClassNames(originalValue, tailwindSelectors);
52
- if (originalValue !== transformedValue) {
53
- node.value.value = transformedValue;
54
- if (typeof node.value.raw === 'string') {
55
- node.value.raw = JSON.stringify(transformedValue);
56
- }
57
- }
438
+ if (node.value && node.value.type === 'Literal') {
439
+ transformLiteral(node.value, context);
440
+ }
441
+ if (node.value &&
442
+ node.value.type === 'JSXExpressionContainer' &&
443
+ node.value.expression.type !== 'JSXEmptyExpression') {
444
+ node.value.expression = transformExpressionClassNames(node.value.expression, scopes, context);
58
445
  }
59
446
  }
60
447
  },
448
+ leave(node) {
449
+ if (scopeByNode.has(node))
450
+ scopes.pop();
451
+ },
61
452
  });
62
453
  };
63
- const transformExpressionClassNames = (expr, tailwindSelectors) => {
64
- if (!expr)
65
- return;
66
- if (expr.type === 'Literal' && typeof expr.value === 'string') {
67
- const originalValue = expr.value;
68
- const transformedValue = transformClassNames(originalValue, tailwindSelectors);
69
- if (originalValue !== transformedValue) {
70
- expr.value = transformedValue;
71
- if (typeof expr.raw === 'string') {
72
- expr.raw = JSON.stringify(transformedValue);
454
+ const transformExpressionClassNames = (expr, scopes, context, inlining = new Set(), structural = false) => {
455
+ const transform = (child) => transformExpressionClassNames(child, scopes, context, inlining);
456
+ const transformStructural = (child) => transformExpressionClassNames(child, scopes, context, inlining, true);
457
+ const transformElement = (element) => {
458
+ if (element.type !== 'SpreadElement')
459
+ return transform(element);
460
+ element.argument = transformStructural(element.argument);
461
+ return element;
462
+ };
463
+ switch (expr.type) {
464
+ case 'Literal':
465
+ transformLiteral(expr, context);
466
+ return expr;
467
+ case 'TemplateLiteral':
468
+ // example: className={`bg-green-500`}
469
+ expr.quasis.forEach((quasi) => {
470
+ if (quasi.value.cooked) {
471
+ const transformedValue = transformClassNames(quasi.value.cooked, context.tailwindSelectors);
472
+ if (quasi.value.cooked !== transformedValue) {
473
+ quasi.value.cooked = transformedValue;
474
+ quasi.value.raw = transformedValue;
475
+ context.changes.count++;
476
+ }
477
+ }
478
+ });
479
+ // example: className={`${isActive ? "bg-red-500" : "bg-blue-500"}`}
480
+ expr.expressions = expr.expressions.map(transform);
481
+ return expr;
482
+ case 'ConditionalExpression':
483
+ // example: ${isActive ? "bg-red-500" : "bg-blue-500"}
484
+ expr.consequent = (structural ? transformStructural : transform)(expr.consequent);
485
+ expr.alternate = (structural ? transformStructural : transform)(expr.alternate);
486
+ return expr;
487
+ case 'LogicalExpression':
488
+ // example: ${isActive && "bg-red-500"}
489
+ expr.left = (structural ? transformStructural : transform)(expr.left);
490
+ expr.right = (structural ? transformStructural : transform)(expr.right);
491
+ return expr;
492
+ case 'BinaryExpression':
493
+ if (expr.operator === '+') {
494
+ // example: ${"bg-red-500" + "bg-blue-500"}
495
+ expr.left = transform(expr.left);
496
+ expr.right = transform(expr.right);
497
+ }
498
+ return expr;
499
+ case 'ArrayExpression':
500
+ // example: ["bg-red-500", "bg-blue-500"]
501
+ expr.elements = expr.elements.map((element) => element && transformElement(element));
502
+ return expr;
503
+ case 'CallExpression':
504
+ if (!isClassCall(expr))
505
+ return expr;
506
+ // example: ["bg-red-500", "bg-blue-500"].join(" ")
507
+ if (expr.callee.type === 'MemberExpression' && expr.callee.object.type !== 'Super') {
508
+ expr.callee.object = transformStructural(expr.callee.object);
509
+ expr.arguments = expr.arguments.map(transformElement);
73
510
  }
511
+ else {
512
+ expr.arguments = expr.arguments.map((argument) => argument.type === 'SpreadElement'
513
+ ? transformElement(argument)
514
+ : transformStructural(argument));
515
+ }
516
+ return expr;
517
+ case 'ObjectExpression':
518
+ // example: cn({ "bg-blue-500": isActive })
519
+ transformObjectKeys(expr, transform, transformStructural, context);
520
+ return expr;
521
+ case 'MemberExpression':
522
+ // example: const styles = { active: "bg-red-500" }; className={styles.active}
523
+ return transformMemberExpression(expr, scopes, transform, context);
524
+ case 'Identifier': {
525
+ // example: const classes = "bg-red-500"; className={classes}
526
+ const binding = resolveBinding(expr.name, scopes);
527
+ if (!binding || inlining.has(binding))
528
+ return expr;
529
+ const inlined = cloneForUse(expr, scopes, scopes, createCloneState(context), structural);
530
+ if (!inlined || inlined.type === 'Identifier')
531
+ return expr;
532
+ const nextInlining = new Set([...inlining, binding]);
533
+ return applyIfChanged(expr, inlined, (child) => transformExpressionClassNames(child, scopes, context, nextInlining), context);
74
534
  }
535
+ default:
536
+ return expr;
75
537
  }
76
- else if (expr.type === 'ConditionalExpression') {
77
- // example: ${isActive ? "bg-red-500" : "bg-blue-500"}
78
- transformExpressionClassNames(expr.consequent, tailwindSelectors);
79
- transformExpressionClassNames(expr.alternate, tailwindSelectors);
538
+ };
539
+ const createCloneState = (context) => ({
540
+ inlining: new Set(),
541
+ local: { remaining: MAX_NODES_PER_INLINE },
542
+ page: context.pageBudget,
543
+ });
544
+ const applyIfChanged = (original, replacement, transform, context) => {
545
+ if (context.inlineDepth >= MAX_INLINE_DEPTH)
546
+ return original;
547
+ context.inlineDepth++;
548
+ const before = context.changes.count;
549
+ const transformed = transform(replacement);
550
+ context.inlineDepth--;
551
+ return context.changes.count === before ? original : transformed;
552
+ };
553
+ const transformMemberExpression = (expr, scopes, transform, context) => {
554
+ if (getStaticKey(expr) !== undefined) {
555
+ const resolved = resolveStaticValue(expr, scopes);
556
+ const inlined = resolved && cloneForUse(resolved.expr, resolved.scopes, scopes, createCloneState(context));
557
+ return inlined ? applyIfChanged(expr, inlined, transform, context) : expr;
80
558
  }
81
- else if (expr.type === 'LogicalExpression') {
82
- // example: ${isActive && "bg-red-500"}
83
- transformExpressionClassNames(expr.left, tailwindSelectors);
84
- transformExpressionClassNames(expr.right, tailwindSelectors);
559
+ const object = resolveObject(expr.object, scopes, context);
560
+ if (!object)
561
+ return expr;
562
+ const before = context.changes.count;
563
+ object.properties.forEach((property) => {
564
+ const value = property.type === 'Property' ? asExpression(property.value) : undefined;
565
+ if (property.type === 'Property' && value)
566
+ property.value = transform(value);
567
+ });
568
+ if (context.changes.count !== before)
569
+ expr.object = object;
570
+ return expr;
571
+ };
572
+ const transformObjectKeys = (expr, transform, transformStructural, context) => {
573
+ expr.properties.forEach((property) => {
574
+ if (property.type === 'SpreadElement') {
575
+ property.argument = transformStructural(property.argument);
576
+ return;
577
+ }
578
+ if (property.computed) {
579
+ if (property.key.type !== 'PrivateIdentifier')
580
+ property.key = transform(property.key);
581
+ return;
582
+ }
583
+ const keyName = getPropertyKeyName(property);
584
+ if (keyName === undefined)
585
+ return;
586
+ const transformedValue = transformClassNames(keyName, context.tailwindSelectors);
587
+ if (keyName !== transformedValue) {
588
+ property.key = toLiteral(transformedValue);
589
+ property.shorthand = false;
590
+ context.changes.count++;
591
+ }
592
+ });
593
+ };
594
+ const resolveObject = (node, scopes, context) => {
595
+ if (node.type === 'ObjectExpression')
596
+ return node;
597
+ const resolved = resolveStaticValue(node, scopes);
598
+ if ((resolved === null || resolved === void 0 ? void 0 : resolved.expr.type) !== 'ObjectExpression')
599
+ return undefined;
600
+ const cloned = cloneForUse(resolved.expr, resolved.scopes, scopes, createCloneState(context));
601
+ return (cloned === null || cloned === void 0 ? void 0 : cloned.type) === 'ObjectExpression' ? cloned : undefined;
602
+ };
603
+ const resolveStaticValue = (node, scopes, resolving = new Set(), depth = 0) => {
604
+ if (node.type === 'Super' || depth >= MAX_INLINE_DEPTH)
605
+ return undefined;
606
+ if (node.type === 'Identifier') {
607
+ const binding = resolveBinding(node.name, scopes);
608
+ if (!(binding === null || binding === void 0 ? void 0 : binding.init))
609
+ return { expr: node, scopes };
610
+ if (resolving.has(binding) || binding.mutated)
611
+ return undefined;
612
+ return resolveStaticValue(binding.init, binding.scopes, new Set([...resolving, binding]), depth + 1);
613
+ }
614
+ if (node.type === 'MemberExpression') {
615
+ const key = getStaticKey(node);
616
+ if (key === undefined)
617
+ return { expr: node, scopes };
618
+ const object = resolveStaticValue(node.object, scopes, resolving, depth + 1);
619
+ if ((object === null || object === void 0 ? void 0 : object.expr.type) !== 'ObjectExpression')
620
+ return undefined;
621
+ const value = findPropertyValue(object.expr, key);
622
+ return value && resolveStaticValue(value, object.scopes, resolving, depth + 1);
85
623
  }
86
- else if (expr.type === 'BinaryExpression' && expr.operator === '+') {
87
- // example: ${"bg-red-500" + "bg-blue-500"}
88
- transformExpressionClassNames(expr.left, tailwindSelectors);
89
- transformExpressionClassNames(expr.right, tailwindSelectors);
624
+ return { expr: node, scopes };
625
+ };
626
+ const createsObjectCache = new WeakMap();
627
+ const isStringCache = new WeakMap();
628
+ const createsObject = (expr, scopes, visiting = new Set(), depth = 0) => {
629
+ const cached = createsObjectCache.get(expr);
630
+ if (cached !== undefined)
631
+ return cached;
632
+ if (depth >= MAX_INLINE_DEPTH)
633
+ return true;
634
+ const result = computeCreatesObject(expr, scopes, visiting, depth);
635
+ createsObjectCache.set(expr, result);
636
+ return result;
637
+ };
638
+ const computeCreatesObject = (expr, scopes, visiting, depth) => {
639
+ const check = (child) => createsObject(child, scopes, visiting, depth);
640
+ switch (expr.type) {
641
+ case 'Literal':
642
+ return 'regex' in expr;
643
+ case 'TemplateLiteral':
644
+ case 'BinaryExpression':
645
+ case 'UnaryExpression':
646
+ return false;
647
+ case 'ConditionalExpression':
648
+ return check(expr.consequent) || check(expr.alternate);
649
+ case 'LogicalExpression':
650
+ return check(expr.left) || check(expr.right);
651
+ case 'CallExpression':
652
+ if (!isClassCall(expr) || expr.callee.type === 'Super')
653
+ return true;
654
+ if (expr.callee.type === 'Identifier')
655
+ return false;
656
+ if (expr.callee.type !== 'MemberExpression' || expr.callee.object.type === 'Super')
657
+ return true;
658
+ return (expr.callee.property.type === 'Identifier' &&
659
+ expr.callee.property.name === 'concat' &&
660
+ !isString(expr.callee.object, scopes, visiting, depth));
661
+ case 'Identifier': {
662
+ const binding = resolveBinding(expr.name, scopes);
663
+ if (!(binding === null || binding === void 0 ? void 0 : binding.init) || visiting.has(binding))
664
+ return false;
665
+ return createsObject(binding.init, binding.scopes, new Set([...visiting, binding]), depth + 1);
666
+ }
667
+ case 'MemberExpression': {
668
+ const resolved = resolveStaticValue(expr, scopes);
669
+ if (resolved && resolved.expr !== expr) {
670
+ return createsObject(resolved.expr, resolved.scopes, visiting, depth + 1);
671
+ }
672
+ return (expr.object.type !== 'Super' && createsObject(expr.object, scopes, visiting, depth + 1));
673
+ }
674
+ default:
675
+ return true;
676
+ }
677
+ };
678
+ const isString = (expr, scopes, visiting, depth) => {
679
+ const cached = isStringCache.get(expr);
680
+ if (cached !== undefined)
681
+ return cached;
682
+ if (depth >= MAX_INLINE_DEPTH)
683
+ return false;
684
+ const result = computeIsString(expr, scopes, visiting, depth);
685
+ isStringCache.set(expr, result);
686
+ return result;
687
+ };
688
+ const computeIsString = (expr, scopes, visiting, depth) => {
689
+ switch (expr.type) {
690
+ case 'Literal':
691
+ return typeof expr.value === 'string';
692
+ case 'TemplateLiteral':
693
+ return true;
694
+ case 'BinaryExpression':
695
+ return (expr.operator === '+' &&
696
+ (isString(expr.left, scopes, visiting, depth) ||
697
+ isString(expr.right, scopes, visiting, depth)));
698
+ case 'CallExpression':
699
+ return isClassCall(expr) && !createsObject(expr, scopes, visiting, depth);
700
+ case 'Identifier': {
701
+ const binding = resolveBinding(expr.name, scopes);
702
+ if (!(binding === null || binding === void 0 ? void 0 : binding.init) || visiting.has(binding))
703
+ return false;
704
+ return isString(binding.init, binding.scopes, new Set([...visiting, binding]), depth + 1);
705
+ }
706
+ default:
707
+ return false;
708
+ }
709
+ };
710
+ const getRootIdentifier = (expr) => {
711
+ if (expr.type === 'Identifier')
712
+ return expr;
713
+ if (expr.type === 'MemberExpression')
714
+ return getRootIdentifier(expr.object);
715
+ return undefined;
716
+ };
717
+ const cloneForUse = (expr, declarationScopes, useScopes, state, structural = false) => {
718
+ var _a;
719
+ state.local.remaining--;
720
+ state.page.remaining--;
721
+ if (state.local.remaining < 0 || state.page.remaining < 0)
722
+ return undefined;
723
+ const clone = (child) => cloneForUse(child, declarationScopes, useScopes, state);
724
+ const cloneStructural = (child) => cloneForUse(child, declarationScopes, useScopes, state, true);
725
+ const cloneElement = (element) => {
726
+ if (element.type !== 'SpreadElement')
727
+ return clone(element);
728
+ const argument = cloneStructural(element.argument);
729
+ return argument && Object.assign(Object.assign({}, element), { argument });
730
+ };
731
+ const cloneAll = (items, cloneItem) => {
732
+ const cloned = [];
733
+ for (const item of items) {
734
+ const result = cloneItem(item);
735
+ if (result === undefined)
736
+ return undefined;
737
+ cloned.push(result);
738
+ }
739
+ return cloned;
740
+ };
741
+ switch (expr.type) {
742
+ case 'Literal':
743
+ return structuredClone(expr);
744
+ case 'TemplateLiteral': {
745
+ const expressions = cloneAll(expr.expressions, clone);
746
+ return expressions && Object.assign(Object.assign({}, expr), { quasis: structuredClone(expr.quasis), expressions });
747
+ }
748
+ case 'ArrayExpression': {
749
+ const elements = cloneAll(expr.elements, (element) => element === null ? null : cloneElement(element));
750
+ return elements && Object.assign(Object.assign({}, expr), { elements });
751
+ }
752
+ case 'BinaryExpression': {
753
+ const left = clone(expr.left);
754
+ const right = clone(expr.right);
755
+ return left && right && Object.assign(Object.assign({}, expr), { left, right });
756
+ }
757
+ case 'LogicalExpression': {
758
+ const left = (structural ? cloneStructural : clone)(expr.left);
759
+ const right = (structural ? cloneStructural : clone)(expr.right);
760
+ return left && right && Object.assign(Object.assign({}, expr), { left, right });
761
+ }
762
+ case 'ConditionalExpression': {
763
+ const test = clone(expr.test);
764
+ const consequent = (structural ? cloneStructural : clone)(expr.consequent);
765
+ const alternate = (structural ? cloneStructural : clone)(expr.alternate);
766
+ return test && consequent && alternate && Object.assign(Object.assign({}, expr), { test, consequent, alternate });
767
+ }
768
+ case 'UnaryExpression': {
769
+ if (expr.operator === 'delete')
770
+ return undefined;
771
+ const argument = clone(expr.argument);
772
+ return argument && Object.assign(Object.assign({}, expr), { argument });
773
+ }
774
+ case 'ObjectExpression': {
775
+ const properties = cloneAll(expr.properties, (property) => {
776
+ if (property.type === 'SpreadElement') {
777
+ const argument = cloneStructural(property.argument);
778
+ return argument && Object.assign(Object.assign({}, property), { argument });
779
+ }
780
+ if (property.kind !== 'init' || property.method)
781
+ return undefined;
782
+ const value = asExpression(property.value);
783
+ const clonedValue = value && clone(value);
784
+ if (!clonedValue)
785
+ return undefined;
786
+ if (!property.computed) {
787
+ return Object.assign(Object.assign({}, property), { key: structuredClone(property.key), value: clonedValue, shorthand: false });
788
+ }
789
+ if (property.key.type === 'PrivateIdentifier')
790
+ return undefined;
791
+ const key = clone(property.key);
792
+ return key && Object.assign(Object.assign({}, property), { key, value: clonedValue, shorthand: false });
793
+ });
794
+ return properties && Object.assign(Object.assign({}, expr), { properties });
795
+ }
796
+ case 'MemberExpression': {
797
+ if (expr.object.type === 'Super' || expr.property.type === 'PrivateIdentifier') {
798
+ return undefined;
799
+ }
800
+ const root = getRootIdentifier(expr.object);
801
+ if (root && ((_a = resolveBinding(root.name, declarationScopes)) === null || _a === void 0 ? void 0 : _a.mutated))
802
+ return undefined;
803
+ const object = cloneStructural(expr.object);
804
+ const property = expr.computed ? clone(expr.property) : structuredClone(expr.property);
805
+ return object && property && Object.assign(Object.assign({}, expr), { object, property });
806
+ }
807
+ case 'CallExpression': {
808
+ if (!isClassCall(expr) || expr.callee.type === 'Super')
809
+ return undefined;
810
+ const callee = clone(expr.callee);
811
+ const args = cloneAll(expr.arguments, (argument) => expr.callee.type === 'Identifier' && argument.type !== 'SpreadElement'
812
+ ? cloneStructural(argument)
813
+ : cloneElement(argument));
814
+ return callee && args && Object.assign(Object.assign({}, expr), { callee, arguments: args });
815
+ }
816
+ case 'Identifier': {
817
+ const binding = resolveBinding(expr.name, declarationScopes);
818
+ if ((binding === null || binding === void 0 ? void 0 : binding.init) &&
819
+ !state.inlining.has(binding) &&
820
+ state.inlining.size < MAX_INLINE_DEPTH &&
821
+ (!createsObject(binding.init, binding.scopes) || (structural && !binding.mutated))) {
822
+ state.inlining.add(binding);
823
+ const inlined = cloneForUse(binding.init, binding.scopes, useScopes, state, structural);
824
+ state.inlining.delete(binding);
825
+ if (inlined)
826
+ return inlined;
827
+ }
828
+ const useBinding = resolveBinding(expr.name, useScopes);
829
+ const isStable = binding
830
+ ? !binding.reassigned && !binding.mutated && binding === useBinding
831
+ : !useBinding && IMMUTABLE_GLOBALS.has(expr.name);
832
+ return isStable ? structuredClone(expr) : undefined;
833
+ }
834
+ default:
835
+ return undefined;
836
+ }
837
+ };
838
+ const isClassCall = (expr) => {
839
+ const { callee } = expr;
840
+ if (callee.type === 'Identifier')
841
+ return CLASS_HELPERS.has(callee.name);
842
+ return (callee.type === 'MemberExpression' &&
843
+ !callee.computed &&
844
+ callee.property.type === 'Identifier' &&
845
+ (callee.property.name === 'join' || callee.property.name === 'concat'));
846
+ };
847
+ const getStaticKey = (expr) => {
848
+ if (!expr.computed && expr.property.type === 'Identifier')
849
+ return expr.property.name;
850
+ if (expr.property.type === 'Literal' && typeof expr.property.value === 'string') {
851
+ return expr.property.value;
852
+ }
853
+ return undefined;
854
+ };
855
+ const getPropertyKeyName = (property) => {
856
+ if (!property.computed && property.key.type === 'Identifier')
857
+ return property.key.name;
858
+ if (property.key.type === 'Literal' && typeof property.key.value === 'string') {
859
+ return property.key.value;
860
+ }
861
+ return undefined;
862
+ };
863
+ const findPropertyValue = (object, key) => {
864
+ for (let i = object.properties.length - 1; i >= 0; i--) {
865
+ const property = object.properties[i];
866
+ if (!property || property.type === 'SpreadElement')
867
+ return undefined;
868
+ const name = getPropertyKeyName(property);
869
+ if (name === undefined) {
870
+ if (property.computed)
871
+ return undefined;
872
+ continue;
873
+ }
874
+ if (name !== key)
875
+ continue;
876
+ if (property.kind !== 'init' || property.method)
877
+ return undefined;
878
+ return asExpression(property.value);
879
+ }
880
+ return undefined;
881
+ };
882
+ const asExpression = (node) => node.type === 'ObjectPattern' ||
883
+ node.type === 'ArrayPattern' ||
884
+ node.type === 'RestElement' ||
885
+ node.type === 'AssignmentPattern'
886
+ ? undefined
887
+ : node;
888
+ const transformLiteral = (expr, context) => {
889
+ if (typeof expr.value !== 'string')
890
+ return;
891
+ const transformedValue = transformClassNames(expr.value, context.tailwindSelectors);
892
+ if (expr.value !== transformedValue) {
893
+ expr.value = transformedValue;
894
+ if (typeof expr.raw === 'string') {
895
+ expr.raw = JSON.stringify(transformedValue);
896
+ }
897
+ context.changes.count++;
90
898
  }
91
899
  };
900
+ const toLiteral = (value) => ({
901
+ type: 'Literal',
902
+ value,
903
+ raw: JSON.stringify(value),
904
+ });
92
905
  const transformClassNames = (value, tailwindSelectors) => {
93
906
  var _a, _b, _c, _d;
94
907
  if (!tailwindSelectors || !tailwindSelectors.length)