@csszyx/unplugin 0.1.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.
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/css-mangler.ts
31
+ var css_mangler_exports = {};
32
+ __export(css_mangler_exports, {
33
+ createPostCSSPlugin: () => createPostCSSPlugin,
34
+ escapeCSSClassName: () => escapeCSSClassName,
35
+ mangleCSS: () => mangleCSS,
36
+ mangleCSSSync: () => mangleCSSSync,
37
+ unescapeTailwindClass: () => unescapeTailwindClass
38
+ });
39
+ module.exports = __toCommonJS(css_mangler_exports);
40
+ var import_postcss = __toESM(require("postcss"), 1);
41
+ var import_postcss_selector_parser = __toESM(require("postcss-selector-parser"), 1);
42
+ function unescapeTailwindClass(escapedName) {
43
+ let result = "";
44
+ let i = 0;
45
+ while (i < escapedName.length) {
46
+ if (escapedName[i] === "\\") {
47
+ i++;
48
+ if (i >= escapedName.length) {
49
+ break;
50
+ }
51
+ const char = escapedName[i];
52
+ if (/[0-9a-fA-F]/.test(char)) {
53
+ let hexStr = "";
54
+ while (i < escapedName.length && /[0-9a-fA-F]/.test(escapedName[i]) && hexStr.length < 6) {
55
+ hexStr += escapedName[i];
56
+ i++;
57
+ }
58
+ if (i < escapedName.length && escapedName[i] === " ") {
59
+ i++;
60
+ }
61
+ const codePoint = parseInt(hexStr, 16);
62
+ if (codePoint > 0) {
63
+ result += String.fromCodePoint(codePoint);
64
+ }
65
+ continue;
66
+ }
67
+ result += char;
68
+ i++;
69
+ } else {
70
+ result += escapedName[i];
71
+ i++;
72
+ }
73
+ }
74
+ return result;
75
+ }
76
+ function escapeCSSClassName(className) {
77
+ let result = "";
78
+ for (let i = 0; i < className.length; i++) {
79
+ const char = className[i];
80
+ const code = char.charCodeAt(0);
81
+ if (i === 0) {
82
+ if (/[0-9]/.test(char)) {
83
+ result += "\\3" + char + " ";
84
+ continue;
85
+ }
86
+ if (char === "-" && i + 1 < className.length) {
87
+ const next = className[i + 1];
88
+ if (/[0-9]/.test(next) || next === "-") {
89
+ result += "\\-";
90
+ continue;
91
+ }
92
+ }
93
+ }
94
+ if (/[!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/.test(char)) {
95
+ result += "\\" + char;
96
+ } else if (code >= 128) {
97
+ result += char;
98
+ } else {
99
+ result += char;
100
+ }
101
+ }
102
+ return result;
103
+ }
104
+ function createSelectorProcessor(mangleMap, mangledClasses, unmangledClasses) {
105
+ return (0, import_postcss_selector_parser.default)((selectors) => {
106
+ selectors.walkClasses((classNode) => {
107
+ const originalValue = classNode.value;
108
+ const unescapedValue = unescapeTailwindClass(originalValue);
109
+ let mangledValue, matchedKey;
110
+ if (mangleMap[unescapedValue]) {
111
+ mangledValue = mangleMap[unescapedValue];
112
+ matchedKey = unescapedValue;
113
+ } else if (mangleMap[originalValue]) {
114
+ mangledValue = mangleMap[originalValue];
115
+ matchedKey = originalValue;
116
+ }
117
+ if (mangledValue && matchedKey) {
118
+ classNode.value = mangledValue;
119
+ mangledClasses.add(matchedKey);
120
+ } else {
121
+ unmangledClasses.add(originalValue);
122
+ }
123
+ });
124
+ });
125
+ }
126
+ async function mangleCSS(css, mangleMap, options = {}) {
127
+ const mangledClasses = /* @__PURE__ */ new Set();
128
+ const unmangledClasses = /* @__PURE__ */ new Set();
129
+ let transformedCount = 0;
130
+ const selectorProcessor = createSelectorProcessor(
131
+ mangleMap,
132
+ mangledClasses,
133
+ unmangledClasses
134
+ );
135
+ const csszyxManglerPlugin = {
136
+ postcssPlugin: "csszyx-css-mangler",
137
+ Rule(rule) {
138
+ try {
139
+ const originalSelector = rule.selector;
140
+ const newSelector = selectorProcessor.processSync(originalSelector);
141
+ if (newSelector !== originalSelector) {
142
+ rule.selector = newSelector;
143
+ transformedCount++;
144
+ }
145
+ } catch (error) {
146
+ if (options.debug) {
147
+ console.warn(`[csszyx] Failed to process selector: ${rule.selector}`, error);
148
+ }
149
+ }
150
+ }
151
+ };
152
+ const result = await (0, import_postcss.default)([csszyxManglerPlugin]).process(css, {
153
+ from: options.from
154
+ });
155
+ if (options.debug) {
156
+ console.log(`[csszyx] CSS Mangler: ${transformedCount} selectors transformed`);
157
+ console.log(`[csszyx] Mangled classes: ${mangledClasses.size}`);
158
+ console.log(`[csszyx] Unmangled classes: ${unmangledClasses.size}`);
159
+ }
160
+ return {
161
+ css: result.css,
162
+ transformedCount,
163
+ mangledClasses: Array.from(mangledClasses),
164
+ unmangledClasses: Array.from(unmangledClasses)
165
+ };
166
+ }
167
+ function mangleCSSSync(css, mangleMap, options = {}) {
168
+ const mangledClasses = /* @__PURE__ */ new Set();
169
+ const unmangledClasses = /* @__PURE__ */ new Set();
170
+ let transformedCount = 0;
171
+ const selectorProcessor = createSelectorProcessor(
172
+ mangleMap,
173
+ mangledClasses,
174
+ unmangledClasses
175
+ );
176
+ const root = import_postcss.default.parse(css, { from: options.from });
177
+ root.walkRules((rule) => {
178
+ try {
179
+ const originalSelector = rule.selector;
180
+ const newSelector = selectorProcessor.processSync(originalSelector);
181
+ if (newSelector !== originalSelector) {
182
+ rule.selector = newSelector;
183
+ transformedCount++;
184
+ }
185
+ } catch (error) {
186
+ if (options.debug) {
187
+ console.warn(`[csszyx] Failed to process selector: ${rule.selector}`, error);
188
+ }
189
+ }
190
+ });
191
+ if (options.debug) {
192
+ console.log(`[csszyx] CSS Mangler: ${transformedCount} selectors transformed`);
193
+ console.log(`[csszyx] Mangled classes: ${mangledClasses.size}`);
194
+ console.log(`[csszyx] Unmangled classes: ${unmangledClasses.size}`);
195
+ }
196
+ return {
197
+ css: root.toString(),
198
+ transformedCount,
199
+ mangledClasses: Array.from(mangledClasses),
200
+ unmangledClasses: Array.from(unmangledClasses)
201
+ };
202
+ }
203
+ function createPostCSSPlugin(mangleMap, options = {}) {
204
+ const mangledClasses = /* @__PURE__ */ new Set();
205
+ const unmangledClasses = /* @__PURE__ */ new Set();
206
+ const selectorProcessor = createSelectorProcessor(
207
+ mangleMap,
208
+ mangledClasses,
209
+ unmangledClasses
210
+ );
211
+ return {
212
+ postcssPlugin: "csszyx-css-mangler",
213
+ Rule(rule) {
214
+ try {
215
+ const originalSelector = rule.selector;
216
+ const newSelector = selectorProcessor.processSync(originalSelector);
217
+ if (newSelector !== originalSelector) {
218
+ rule.selector = newSelector;
219
+ }
220
+ } catch (error) {
221
+ if (options.debug) {
222
+ console.warn(`[csszyx] Failed to process selector: ${rule.selector}`, error);
223
+ }
224
+ }
225
+ },
226
+ OnceExit() {
227
+ if (options.debug) {
228
+ console.log(`[csszyx] Mangled ${mangledClasses.size} unique classes`);
229
+ }
230
+ }
231
+ };
232
+ }
233
+ // Annotate the CommonJS export names for ESM import in node:
234
+ 0 && (module.exports = {
235
+ createPostCSSPlugin,
236
+ escapeCSSClassName,
237
+ mangleCSS,
238
+ mangleCSSSync,
239
+ unescapeTailwindClass
240
+ });
@@ -0,0 +1,123 @@
1
+ import postcss from 'postcss';
2
+
3
+ /**
4
+ * CSS Mangler Module - Zero-Risk CSS Selector Transformation.
5
+ *
6
+ * Uses PostCSS AST to safely transform CSS class selectors using the mangle map.
7
+ * This module guarantees:
8
+ * - Only class selectors are touched (never IDs, attributes, CSS variables)
9
+ * - Exact class matching (no partial replacements)
10
+ * - Proper handling of Tailwind's escaped characters
11
+ * - Zero CSS syntax errors after transformation
12
+ *
13
+ * @module @csszyx/unplugin/css-mangler
14
+ */
15
+
16
+ /**
17
+ * Mangle map type: original class name -> mangled ID.
18
+ */
19
+ type MangleMap = Record<string, string>;
20
+ /**
21
+ * CSS Mangler options.
22
+ */
23
+ interface CSSManglerOptions {
24
+ /**
25
+ * Enable debug logging.
26
+ */
27
+ debug?: boolean;
28
+ /**
29
+ * Source file path for better error messages.
30
+ */
31
+ from?: string;
32
+ }
33
+ /**
34
+ * CSS Mangler result.
35
+ */
36
+ interface CSSManglerResult {
37
+ /**
38
+ * The transformed CSS.
39
+ */
40
+ css: string;
41
+ /**
42
+ * Number of selectors transformed.
43
+ */
44
+ transformedCount: number;
45
+ /**
46
+ * List of classes that were mangled.
47
+ */
48
+ mangledClasses: string[];
49
+ /**
50
+ * List of classes not found in the mangle map.
51
+ */
52
+ unmangledClasses: string[];
53
+ }
54
+ /**
55
+ * Unescape a Tailwind CSS class name.
56
+ *
57
+ * Tailwind uses CSS escape sequences for special characters:
58
+ * - `\.` for literal `.` (e.g., `p-0\.5` -> `p-0.5`)
59
+ * - `\/` for literal `/` (e.g., `w-1\/2` -> `w-1/2`)
60
+ * - `\:` for literal `:` (e.g., `hover\:bg-red-500` in CSS -> `hover:bg-red-500`)
61
+ * - `\!` for literal `!` (important modifier)
62
+ * - `\[` and `\]` for arbitrary values
63
+ * - `\#` for hex colors
64
+ * - `\@` for at-rules in class names
65
+ * - `\32` (hex for '2') for numeric prefixes like `2xl:`
66
+ * - Unicode escapes like `\31 ` (space-terminated)
67
+ *
68
+ * @param {string} escapedName - The escaped class name from CSS selector
69
+ * @returns {string} The unescaped class name
70
+ */
71
+ declare function unescapeTailwindClass(escapedName: string): string;
72
+ /**
73
+ * Escape a class name for use in CSS selector.
74
+ *
75
+ * This is the inverse of unescapeTailwindClass.
76
+ *
77
+ * @param {string} className - The unescaped class name
78
+ * @returns {string} The escaped class name for CSS
79
+ */
80
+ declare function escapeCSSClassName(className: string): string;
81
+ /**
82
+ * Mangle CSS selectors using the provided mangle map.
83
+ *
84
+ * This function uses PostCSS AST to safely transform only class selectors,
85
+ * ensuring zero risk of breaking CSS syntax or mangling unintended content.
86
+ *
87
+ * @param {string} css - The CSS content to transform
88
+ * @param {MangleMap} mangleMap - The mangle map (original -> mangled)
89
+ * @param {CSSManglerOptions} options - Options
90
+ * @returns {Promise<CSSManglerResult>} The transformation result
91
+ */
92
+ declare function mangleCSS(css: string, mangleMap: MangleMap, options?: CSSManglerOptions): Promise<CSSManglerResult>;
93
+ /**
94
+ * Synchronous version of mangleCSS.
95
+ *
96
+ * @param {string} css - The CSS content to transform
97
+ * @param {MangleMap} mangleMap - The mangle map
98
+ * @param {CSSManglerOptions} options - Options
99
+ * @returns {CSSManglerResult} The transformation result
100
+ */
101
+ declare function mangleCSSSync(css: string, mangleMap: MangleMap, options?: CSSManglerOptions): CSSManglerResult;
102
+ /**
103
+ * Create a PostCSS plugin for CSS mangling.
104
+ *
105
+ * This can be used directly in a PostCSS pipeline.
106
+ *
107
+ * @param {MangleMap} mangleMap - The mangle map
108
+ * @param {CSSManglerOptions} options - Options
109
+ * @returns {postcss.Plugin} PostCSS plugin
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import postcss from 'postcss';
114
+ * import { createPostCSSPlugin } from '@csszyx/unplugin/css-mangler';
115
+ *
116
+ * const result = await postcss([
117
+ * createPostCSSPlugin(mangleMap, { debug: true })
118
+ * ]).process(css);
119
+ * ```
120
+ */
121
+ declare function createPostCSSPlugin(mangleMap: MangleMap, options?: CSSManglerOptions): postcss.Plugin;
122
+
123
+ export { type CSSManglerOptions, type CSSManglerResult, type MangleMap, createPostCSSPlugin, escapeCSSClassName, mangleCSS, mangleCSSSync, unescapeTailwindClass };
@@ -0,0 +1,123 @@
1
+ import postcss from 'postcss';
2
+
3
+ /**
4
+ * CSS Mangler Module - Zero-Risk CSS Selector Transformation.
5
+ *
6
+ * Uses PostCSS AST to safely transform CSS class selectors using the mangle map.
7
+ * This module guarantees:
8
+ * - Only class selectors are touched (never IDs, attributes, CSS variables)
9
+ * - Exact class matching (no partial replacements)
10
+ * - Proper handling of Tailwind's escaped characters
11
+ * - Zero CSS syntax errors after transformation
12
+ *
13
+ * @module @csszyx/unplugin/css-mangler
14
+ */
15
+
16
+ /**
17
+ * Mangle map type: original class name -> mangled ID.
18
+ */
19
+ type MangleMap = Record<string, string>;
20
+ /**
21
+ * CSS Mangler options.
22
+ */
23
+ interface CSSManglerOptions {
24
+ /**
25
+ * Enable debug logging.
26
+ */
27
+ debug?: boolean;
28
+ /**
29
+ * Source file path for better error messages.
30
+ */
31
+ from?: string;
32
+ }
33
+ /**
34
+ * CSS Mangler result.
35
+ */
36
+ interface CSSManglerResult {
37
+ /**
38
+ * The transformed CSS.
39
+ */
40
+ css: string;
41
+ /**
42
+ * Number of selectors transformed.
43
+ */
44
+ transformedCount: number;
45
+ /**
46
+ * List of classes that were mangled.
47
+ */
48
+ mangledClasses: string[];
49
+ /**
50
+ * List of classes not found in the mangle map.
51
+ */
52
+ unmangledClasses: string[];
53
+ }
54
+ /**
55
+ * Unescape a Tailwind CSS class name.
56
+ *
57
+ * Tailwind uses CSS escape sequences for special characters:
58
+ * - `\.` for literal `.` (e.g., `p-0\.5` -> `p-0.5`)
59
+ * - `\/` for literal `/` (e.g., `w-1\/2` -> `w-1/2`)
60
+ * - `\:` for literal `:` (e.g., `hover\:bg-red-500` in CSS -> `hover:bg-red-500`)
61
+ * - `\!` for literal `!` (important modifier)
62
+ * - `\[` and `\]` for arbitrary values
63
+ * - `\#` for hex colors
64
+ * - `\@` for at-rules in class names
65
+ * - `\32` (hex for '2') for numeric prefixes like `2xl:`
66
+ * - Unicode escapes like `\31 ` (space-terminated)
67
+ *
68
+ * @param {string} escapedName - The escaped class name from CSS selector
69
+ * @returns {string} The unescaped class name
70
+ */
71
+ declare function unescapeTailwindClass(escapedName: string): string;
72
+ /**
73
+ * Escape a class name for use in CSS selector.
74
+ *
75
+ * This is the inverse of unescapeTailwindClass.
76
+ *
77
+ * @param {string} className - The unescaped class name
78
+ * @returns {string} The escaped class name for CSS
79
+ */
80
+ declare function escapeCSSClassName(className: string): string;
81
+ /**
82
+ * Mangle CSS selectors using the provided mangle map.
83
+ *
84
+ * This function uses PostCSS AST to safely transform only class selectors,
85
+ * ensuring zero risk of breaking CSS syntax or mangling unintended content.
86
+ *
87
+ * @param {string} css - The CSS content to transform
88
+ * @param {MangleMap} mangleMap - The mangle map (original -> mangled)
89
+ * @param {CSSManglerOptions} options - Options
90
+ * @returns {Promise<CSSManglerResult>} The transformation result
91
+ */
92
+ declare function mangleCSS(css: string, mangleMap: MangleMap, options?: CSSManglerOptions): Promise<CSSManglerResult>;
93
+ /**
94
+ * Synchronous version of mangleCSS.
95
+ *
96
+ * @param {string} css - The CSS content to transform
97
+ * @param {MangleMap} mangleMap - The mangle map
98
+ * @param {CSSManglerOptions} options - Options
99
+ * @returns {CSSManglerResult} The transformation result
100
+ */
101
+ declare function mangleCSSSync(css: string, mangleMap: MangleMap, options?: CSSManglerOptions): CSSManglerResult;
102
+ /**
103
+ * Create a PostCSS plugin for CSS mangling.
104
+ *
105
+ * This can be used directly in a PostCSS pipeline.
106
+ *
107
+ * @param {MangleMap} mangleMap - The mangle map
108
+ * @param {CSSManglerOptions} options - Options
109
+ * @returns {postcss.Plugin} PostCSS plugin
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import postcss from 'postcss';
114
+ * import { createPostCSSPlugin } from '@csszyx/unplugin/css-mangler';
115
+ *
116
+ * const result = await postcss([
117
+ * createPostCSSPlugin(mangleMap, { debug: true })
118
+ * ]).process(css);
119
+ * ```
120
+ */
121
+ declare function createPostCSSPlugin(mangleMap: MangleMap, options?: CSSManglerOptions): postcss.Plugin;
122
+
123
+ export { type CSSManglerOptions, type CSSManglerResult, type MangleMap, createPostCSSPlugin, escapeCSSClassName, mangleCSS, mangleCSSSync, unescapeTailwindClass };
@@ -0,0 +1,14 @@
1
+ import {
2
+ createPostCSSPlugin,
3
+ escapeCSSClassName,
4
+ mangleCSS,
5
+ mangleCSSSync,
6
+ unescapeTailwindClass
7
+ } from "./chunk-4M7CPGP7.js";
8
+ export {
9
+ createPostCSSPlugin,
10
+ escapeCSSClassName,
11
+ mangleCSS,
12
+ mangleCSSSync,
13
+ unescapeTailwindClass
14
+ };