@mintlify/common 1.0.1113 → 1.0.1115

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.
@@ -43,6 +43,7 @@ export const COMPONENTS_WITHOUT_ID = new Set([
43
43
  'FileTree',
44
44
  'Variation',
45
45
  'Visibility',
46
+ 'MDX',
46
47
  'Badge',
47
48
  'Color',
48
49
  'ZoomImage',
@@ -1,22 +1,21 @@
1
1
  import { walk } from 'estree-walker';
2
2
  import { visit } from 'unist-util-visit';
3
+ import { segment } from './segment.js';
3
4
  export const MINTLIFY_TAILWIND_PREFIX = 'mint-';
4
5
  export const rehypeDynamicTailwindCss = (tailwindSelectors) => (tree) => {
6
+ const normalizedTailwindSelectors = tailwindSelectors === null || tailwindSelectors === void 0 ? void 0 : tailwindSelectors.map(normalizeSelector);
5
7
  visit(tree, (node) => {
6
8
  if ((node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement') &&
7
9
  node.name &&
8
10
  typeof node.name === 'string' &&
9
- // check for valid html tags
10
- (/^[a-z]/.test(node.name) || node.name === 'Heading') &&
11
- !node.name.includes('.') &&
12
11
  node.attributes.length > 0) {
13
12
  const className = node.attributes.find((attr) => attr.type === 'mdxJsxAttribute' && attr.name === 'className');
14
13
  if (className && typeof className.value === 'string') {
15
- className.value = transformClassNames(className.value, tailwindSelectors);
14
+ className.value = transformClassNames(className.value, normalizedTailwindSelectors);
16
15
  }
17
16
  }
18
17
  if (node.type === 'mdxjsEsm' && node.data && node.data.estree) {
19
- transformMdxJsEsmNode(node.data.estree, tailwindSelectors);
18
+ transformMdxJsEsmNode(node.data.estree, normalizedTailwindSelectors);
20
19
  }
21
20
  });
22
21
  };
@@ -93,31 +92,31 @@ const transformExpressionClassNames = (expr, tailwindSelectors) => {
93
92
  const transformClassNames = (value, tailwindSelectors) => {
94
93
  if (!tailwindSelectors || !tailwindSelectors.length)
95
94
  return value;
96
- return value
97
- .split(/\s+/)
98
- .map((className) => {
99
- if (!className)
100
- return '';
101
- if (className.startsWith('[') || className.startsWith(MINTLIFY_TAILWIND_PREFIX)) {
102
- return className;
103
- }
104
- const parts = className.split(':');
105
- let baseClass = parts.pop();
106
- const hasImportantModifier = baseClass.startsWith('!');
107
- if (hasImportantModifier) {
108
- baseClass = baseClass.slice(1);
109
- }
110
- const variants = parts;
111
- if (tailwindSelectors.some((selector) => {
112
- // remove the leading dot and any escaping backslashes from the selector
113
- // for example, .bg-orange-400\\/50 -> bg-orange-400/50
114
- const normalizedSelector = selector.replace(/^\.|\\+/g, '');
115
- return normalizedSelector.includes(baseClass);
116
- })) {
117
- const transformedClass = `${hasImportantModifier ? '!' : ''}${MINTLIFY_TAILWIND_PREFIX}${baseClass}`;
118
- return [...variants, transformedClass].join(':');
119
- }
120
- return className;
121
- })
95
+ return splitClassNames(value)
96
+ .map((className) => transformClassName(className, tailwindSelectors))
122
97
  .join(' ');
123
98
  };
99
+ const transformClassName = (className, tailwindSelectors) => {
100
+ const classSegments = splitClassSegments(className);
101
+ const baseClassSegment = classSegments[classSegments.length - 1];
102
+ if (!baseClassSegment)
103
+ return className;
104
+ const hasImportantModifier = baseClassSegment.startsWith('!');
105
+ const baseClass = hasImportantModifier ? baseClassSegment.slice(1) : baseClassSegment;
106
+ if (!baseClass || baseClass.startsWith(MINTLIFY_TAILWIND_PREFIX)) {
107
+ return className;
108
+ }
109
+ if (!isTailwindClass(className, tailwindSelectors)) {
110
+ return className;
111
+ }
112
+ classSegments[classSegments.length - 1] =
113
+ `${hasImportantModifier ? '!' : ''}${MINTLIFY_TAILWIND_PREFIX}${baseClass}`;
114
+ return classSegments.join(':');
115
+ };
116
+ const isTailwindClass = (className, tailwindSelectors) => {
117
+ const normalizedClassName = normalizeSelector(className);
118
+ return tailwindSelectors.some((selector) => selector.includes(`.${normalizedClassName}`));
119
+ };
120
+ const splitClassNames = (value) => value.split(/\s+/).filter(Boolean);
121
+ const splitClassSegments = (value) => segment(value, ':').filter(Boolean);
122
+ const normalizeSelector = (selector) => selector.replace(/\\+/g, '');
@@ -0,0 +1,13 @@
1
+ /**
2
+ * This splits a string on a top-level character.
3
+ *
4
+ * Regex doesn't support recursion (at least not the JS-flavored version),
5
+ * so we have to use a tiny state machine to keep track of paren placement.
6
+ *
7
+ * Expected behavior using commas:
8
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
9
+ * ┬ ┬ ┬ ┬
10
+ * x x x ╰──────── Split because top-level
11
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
12
+ */
13
+ export declare function segment(input: string, separator: string): string[];
@@ -0,0 +1,94 @@
1
+ // Vendored from Tailwind CSS v4 (MIT), unchanged apart from formatting:
2
+ // https://github.com/tailwindlabs/tailwindcss/blob/main/packages/tailwindcss/src/utils/segment.ts
3
+ const BACKSLASH = 0x5c;
4
+ const OPEN_CURLY = 0x7b;
5
+ const CLOSE_CURLY = 0x7d;
6
+ const OPEN_PAREN = 0x28;
7
+ const CLOSE_PAREN = 0x29;
8
+ const OPEN_BRACKET = 0x5b;
9
+ const CLOSE_BRACKET = 0x5d;
10
+ const DOUBLE_QUOTE = 0x22;
11
+ const SINGLE_QUOTE = 0x27;
12
+ // This is a shared buffer that is used to keep track of the current nesting level
13
+ // of parens, brackets, and braces. It is used to determine if a character is at
14
+ // the top-level of a string. This is a performance optimization to avoid memory
15
+ // allocations on every call to `segment`.
16
+ const closingBracketStack = new Uint8Array(256);
17
+ /**
18
+ * This splits a string on a top-level character.
19
+ *
20
+ * Regex doesn't support recursion (at least not the JS-flavored version),
21
+ * so we have to use a tiny state machine to keep track of paren placement.
22
+ *
23
+ * Expected behavior using commas:
24
+ * var(--a, 0 0 1px rgb(0, 0, 0)), 0 0 1px rgb(0, 0, 0)
25
+ * ┬ ┬ ┬ ┬
26
+ * x x x ╰──────── Split because top-level
27
+ * ╰──────────────┴──┴───────────── Ignored b/c inside >= 1 levels of parens
28
+ */
29
+ export function segment(input, separator) {
30
+ // SAFETY: We can use an index into a shared buffer because this function is
31
+ // synchronous, non-recursive, and runs in a single-threaded environment.
32
+ let stackPos = 0;
33
+ const parts = [];
34
+ let lastPos = 0;
35
+ const len = input.length;
36
+ const separatorCode = separator.charCodeAt(0);
37
+ for (let idx = 0; idx < len; idx++) {
38
+ const char = input.charCodeAt(idx);
39
+ if (stackPos === 0 && char === separatorCode) {
40
+ parts.push(input.slice(lastPos, idx));
41
+ lastPos = idx + 1;
42
+ continue;
43
+ }
44
+ switch (char) {
45
+ case BACKSLASH:
46
+ // The next character is escaped, so we skip it.
47
+ idx += 1;
48
+ break;
49
+ // Strings should be handled as-is until the end of the string. No need to
50
+ // worry about balancing parens, brackets, or curlies inside a string.
51
+ case SINGLE_QUOTE:
52
+ case DOUBLE_QUOTE:
53
+ // Ensure we don't go out of bounds.
54
+ while (++idx < len) {
55
+ const nextChar = input.charCodeAt(idx);
56
+ // The next character is escaped, so we skip it.
57
+ if (nextChar === BACKSLASH) {
58
+ idx += 1;
59
+ continue;
60
+ }
61
+ if (nextChar === char) {
62
+ break;
63
+ }
64
+ }
65
+ break;
66
+ case OPEN_PAREN:
67
+ closingBracketStack[stackPos] = CLOSE_PAREN;
68
+ stackPos++;
69
+ break;
70
+ case OPEN_BRACKET:
71
+ closingBracketStack[stackPos] = CLOSE_BRACKET;
72
+ stackPos++;
73
+ break;
74
+ case OPEN_CURLY:
75
+ closingBracketStack[stackPos] = CLOSE_CURLY;
76
+ stackPos++;
77
+ break;
78
+ case CLOSE_BRACKET:
79
+ case CLOSE_CURLY:
80
+ case CLOSE_PAREN:
81
+ if (stackPos > 0 && char === closingBracketStack[stackPos - 1]) {
82
+ // SAFETY: The buffer does not need to be mutated because the stack is
83
+ // only ever read from or written to its current position. Its current
84
+ // position is only ever incremented after writing to it. Meaning that
85
+ // the buffer can be dirty for the next use and still be correct since
86
+ // reading/writing always starts at position `0`.
87
+ stackPos--;
88
+ }
89
+ break;
90
+ }
91
+ }
92
+ parts.push(input.slice(lastPos));
93
+ return parts;
94
+ }