catom 2.0.4 → 2.2.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,90 @@
1
+ import { Properties } from 'csstype';
2
+ export { Properties as CSSProperties } from 'csstype';
3
+
4
+ /**
5
+ * CSS properties with optional pseudo selectors
6
+ */
7
+ interface CSSPropertiesWithPseudo extends Properties {
8
+ /**
9
+ * Pseudo selector styles
10
+ * @example
11
+ * ```ts
12
+ * { ':hover': { color: 'blue' } }
13
+ * ```
14
+ */
15
+ pseudo?: {
16
+ [selector: string]: Properties;
17
+ };
18
+ }
19
+ /**
20
+ * Input type for the css() function
21
+ * Supports standard CSS properties plus media and pseudo selectors
22
+ */
23
+ interface CSSInput extends Properties {
24
+ /**
25
+ * Media query styles - can also contain pseudo selectors
26
+ * @example
27
+ * ```ts
28
+ * css({
29
+ * color: 'red',
30
+ * media: {
31
+ * '(max-width: 768px)': {
32
+ * color: 'blue',
33
+ * pseudo: { ':hover': { color: 'darkblue' } }
34
+ * }
35
+ * }
36
+ * })
37
+ * ```
38
+ */
39
+ media?: {
40
+ [query: string]: CSSPropertiesWithPseudo;
41
+ };
42
+ /**
43
+ * Pseudo selector styles
44
+ * @example
45
+ * ```ts
46
+ * css({
47
+ * color: 'red',
48
+ * pseudo: {
49
+ * ':hover': { color: 'blue' },
50
+ * ':focus': { outline: '2px solid blue' }
51
+ * }
52
+ * })
53
+ * ```
54
+ */
55
+ pseudo?: {
56
+ [selector: string]: Properties;
57
+ };
58
+ }
59
+ /**
60
+ * Define atomic CSS styles that are extracted at compile time.
61
+ *
62
+ * This function is transformed by catom/vite plugin during build.
63
+ * At runtime, it only serves as a type-safe placeholder.
64
+ *
65
+ * @param styles - CSS properties object with optional media and pseudo selectors
66
+ * @returns A string of space-separated atomic class names (at build time)
67
+ *
68
+ * @example
69
+ * ```tsx
70
+ * import { css } from 'catom'
71
+ *
72
+ * const button = css({
73
+ * backgroundColor: 'blue',
74
+ * color: 'white',
75
+ * padding: '8px 16px',
76
+ * borderRadius: '4px',
77
+ * pseudo: {
78
+ * ':hover': { backgroundColor: 'darkblue' }
79
+ * }
80
+ * })
81
+ *
82
+ * // In your component:
83
+ * <button className={button}>Click me</button>
84
+ * ```
85
+ *
86
+ * @throws Error if called at runtime (indicates plugin misconfiguration)
87
+ */
88
+ declare function css(_styles: CSSInput): string;
89
+
90
+ export { type CSSInput, type CSSPropertiesWithPseudo, css };
@@ -0,0 +1,10 @@
1
+ // src/index.ts
2
+ function css(_styles) {
3
+ throw new Error(
4
+ '[catom] css() was called at runtime. This usually means the catom vite plugin is not configured correctly. Make sure to add the plugin to your vite.config.ts: import catom from "catom/vite"'
5
+ );
6
+ }
7
+
8
+ export { css };
9
+ //# sourceMappingURL=index.js.map
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"names":[],"mappings":";AAmFO,SAAS,IAAI,OAAA,EAA2B;AAC7C,EAAA,MAAM,IAAI,KAAA;AAAA,IACR;AAAA,GAGF;AACF","file":"index.js","sourcesContent":["import type { Properties } from 'csstype'\n\n/**\n * CSS properties with optional pseudo selectors\n */\nexport interface CSSPropertiesWithPseudo extends Properties {\n /**\n * Pseudo selector styles\n * @example\n * ```ts\n * { ':hover': { color: 'blue' } }\n * ```\n */\n pseudo?: { [selector: string]: Properties }\n}\n\n/**\n * Input type for the css() function\n * Supports standard CSS properties plus media and pseudo selectors\n */\nexport interface CSSInput extends Properties {\n /**\n * Media query styles - can also contain pseudo selectors\n * @example\n * ```ts\n * css({\n * color: 'red',\n * media: {\n * '(max-width: 768px)': { \n * color: 'blue',\n * pseudo: { ':hover': { color: 'darkblue' } }\n * }\n * }\n * })\n * ```\n */\n media?: { [query: string]: CSSPropertiesWithPseudo }\n\n /**\n * Pseudo selector styles\n * @example\n * ```ts\n * css({\n * color: 'red',\n * pseudo: {\n * ':hover': { color: 'blue' },\n * ':focus': { outline: '2px solid blue' }\n * }\n * })\n * ```\n */\n pseudo?: { [selector: string]: Properties }\n}\n\n/**\n * Define atomic CSS styles that are extracted at compile time.\n *\n * This function is transformed by catom/vite plugin during build.\n * At runtime, it only serves as a type-safe placeholder.\n *\n * @param styles - CSS properties object with optional media and pseudo selectors\n * @returns A string of space-separated atomic class names (at build time)\n *\n * @example\n * ```tsx\n * import { css } from 'catom'\n *\n * const button = css({\n * backgroundColor: 'blue',\n * color: 'white',\n * padding: '8px 16px',\n * borderRadius: '4px',\n * pseudo: {\n * ':hover': { backgroundColor: 'darkblue' }\n * }\n * })\n *\n * // In your component:\n * <button className={button}>Click me</button>\n * ```\n *\n * @throws Error if called at runtime (indicates plugin misconfiguration)\n */\nexport function css(_styles: CSSInput): string {\n throw new Error(\n '[catom] css() was called at runtime. ' +\n 'This usually means the catom vite plugin is not configured correctly. ' +\n 'Make sure to add the plugin to your vite.config.ts: import catom from \"catom/vite\"'\n )\n}\n\n// Re-export the type for convenience\nexport type { Properties as CSSProperties } from 'csstype'\n"]}
@@ -0,0 +1,81 @@
1
+ import { Plugin } from 'vite';
2
+ import { Properties } from 'csstype';
3
+
4
+ /**
5
+ * Input type for the css() function
6
+ */
7
+ interface CSSInput extends Properties {
8
+ media?: {
9
+ [query: string]: Properties;
10
+ };
11
+ pseudo?: {
12
+ [selector: string]: Properties;
13
+ };
14
+ }
15
+ /**
16
+ * A single atomic CSS rule
17
+ */
18
+ interface CSSRule {
19
+ /** Unique class name (hash) */
20
+ hash: string;
21
+ /** CSS property in kebab-case */
22
+ property: string;
23
+ /** CSS value */
24
+ value: string;
25
+ /** Optional media query */
26
+ media?: string;
27
+ /** Optional pseudo selector (e.g., ':hover') */
28
+ pseudo?: string;
29
+ }
30
+ /**
31
+ * Plugin options
32
+ */
33
+ interface CatomPluginOptions {
34
+ /**
35
+ * File patterns to include for transformation
36
+ * @default /\.[jt]sx?$/
37
+ */
38
+ include?: string | RegExp | (string | RegExp)[];
39
+ /**
40
+ * File patterns to exclude from transformation
41
+ * @default /node_modules/
42
+ */
43
+ exclude?: string | RegExp | (string | RegExp)[];
44
+ /**
45
+ * Name of the css function to transform
46
+ * @default 'css'
47
+ */
48
+ functionName?: string;
49
+ }
50
+
51
+ /**
52
+ * Vite plugin for zero-runtime CSS-in-JS with atomic CSS generation
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * // vite.config.ts
57
+ * import { defineConfig } from 'vite'
58
+ * import catom from 'catom/vite'
59
+ *
60
+ * export default defineConfig({
61
+ * plugins: [catom()]
62
+ * })
63
+ * ```
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * // In your app code
68
+ * import { css } from 'catom'
69
+ * import 'virtual:catom.css'
70
+ *
71
+ * const button = css({
72
+ * color: 'red',
73
+ * padding: '8px',
74
+ * pseudo: { ':hover': { color: 'blue' } }
75
+ * })
76
+ * // After transform: const button = "_a1b2c3 _d4e5f6 _g7h8i9"
77
+ * ```
78
+ */
79
+ declare function catomPlugin(options?: CatomPluginOptions): Plugin;
80
+
81
+ export { type CSSInput, type CSSRule, type CatomPluginOptions, catomPlugin, catomPlugin as default };
@@ -0,0 +1,416 @@
1
+ import { createFilter } from 'vite';
2
+ import { parseSync } from '@swc/core';
3
+
4
+ // src/vite/index.ts
5
+
6
+ // src/core/hash.ts
7
+ function murmur2(str) {
8
+ let h = 0;
9
+ let k;
10
+ let i = 0;
11
+ let len = str.length;
12
+ for (; len >= 4; ++i, len -= 4) {
13
+ k = str.charCodeAt(i) & 255 | (str.charCodeAt(++i) & 255) << 8 | (str.charCodeAt(++i) & 255) << 16 | (str.charCodeAt(++i) & 255) << 24;
14
+ k = /* Math.imul(k, m): */
15
+ (k & 65535) * 1540483477 + ((k >>> 16) * 59797 << 16);
16
+ k ^= /* k >>> r: */
17
+ k >>> 24;
18
+ h = /* Math.imul(k, m): */
19
+ (k & 65535) * 1540483477 + ((k >>> 16) * 59797 << 16) ^ /* Math.imul(h, m): */
20
+ (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
21
+ }
22
+ switch (len) {
23
+ case 3:
24
+ h ^= (str.charCodeAt(i + 2) & 255) << 16;
25
+ // falls through
26
+ case 2:
27
+ h ^= (str.charCodeAt(i + 1) & 255) << 8;
28
+ // falls through
29
+ case 1:
30
+ h ^= str.charCodeAt(i) & 255;
31
+ h = /* Math.imul(h, m): */
32
+ (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
33
+ }
34
+ h ^= h >>> 13;
35
+ h = /* Math.imul(h, m): */
36
+ (h & 65535) * 1540483477 + ((h >>> 16) * 59797 << 16);
37
+ return ((h ^ h >>> 15) >>> 0).toString(36);
38
+ }
39
+ var PREFIX_CHARS = new Set("0123456789-".split(""));
40
+ function makeCSSCompatible(hash) {
41
+ if (PREFIX_CHARS.has(hash[0])) {
42
+ return `_${hash}`;
43
+ }
44
+ return hash;
45
+ }
46
+ function generateHash(identity) {
47
+ return makeCSSCompatible(murmur2(identity));
48
+ }
49
+
50
+ // src/core/css-generator.ts
51
+ var KEBAB_CASE_REGEX = /([a-z0-9]|(?=[A-Z]))([A-Z])/g;
52
+ function toKebabCase(property) {
53
+ return property.replace(KEBAB_CASE_REGEX, "$1-$2").toLowerCase();
54
+ }
55
+ function createRuleIdentity(property, value, media, pseudo) {
56
+ const mediaPrefix = media ? `@${media.trim()}` : "";
57
+ const pseudoPrefix = pseudo ? pseudo.trim() : "";
58
+ const kebabProp = toKebabCase(property);
59
+ return `${mediaPrefix}${pseudoPrefix}${kebabProp}:${value};`;
60
+ }
61
+ function createCSSRule(property, value, media, pseudo) {
62
+ const stringValue = String(value).trim();
63
+ const kebabProperty = toKebabCase(property.trim());
64
+ const identity = createRuleIdentity(property, stringValue, media, pseudo);
65
+ const hash = generateHash(identity);
66
+ return {
67
+ hash,
68
+ property: kebabProperty,
69
+ value: stringValue,
70
+ media: media?.trim(),
71
+ pseudo: pseudo?.trim()
72
+ };
73
+ }
74
+ function deduplicateRules(rules) {
75
+ const seen = /* @__PURE__ */ new Map();
76
+ for (const rule of rules) {
77
+ const identity = createRuleIdentity(rule.property, rule.value, rule.media, rule.pseudo);
78
+ if (!seen.has(identity)) {
79
+ seen.set(identity, rule);
80
+ }
81
+ }
82
+ return Array.from(seen.values());
83
+ }
84
+ function groupRulesByDeclaration(rules) {
85
+ const groups = /* @__PURE__ */ new Map();
86
+ for (const rule of rules) {
87
+ const declaration = `${rule.property}:${rule.value};`;
88
+ const groupKey = `${rule.media || ""}|${rule.pseudo || ""}|${declaration}`;
89
+ if (!groups.has(groupKey)) {
90
+ groups.set(groupKey, {
91
+ declaration,
92
+ hashes: /* @__PURE__ */ new Set(),
93
+ media: rule.media,
94
+ pseudo: rule.pseudo
95
+ });
96
+ }
97
+ groups.get(groupKey).hashes.add(rule.hash);
98
+ }
99
+ return Array.from(groups.values());
100
+ }
101
+ function generateCSS(rules) {
102
+ const dedupedRules = deduplicateRules(rules);
103
+ const regularRules = [];
104
+ const pseudoRules = [];
105
+ const mediaRules = /* @__PURE__ */ new Map();
106
+ for (const rule of dedupedRules) {
107
+ if (rule.media) {
108
+ const existing = mediaRules.get(rule.media) || [];
109
+ existing.push(rule);
110
+ mediaRules.set(rule.media, existing);
111
+ } else if (rule.pseudo) {
112
+ pseudoRules.push(rule);
113
+ } else {
114
+ regularRules.push(rule);
115
+ }
116
+ }
117
+ const cssLines = [];
118
+ const regularGroups = groupRulesByDeclaration(regularRules);
119
+ for (const group of regularGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {
120
+ const selectors = Array.from(group.hashes).sort().map((h) => `.${h}`).join(",\n");
121
+ cssLines.push(`${selectors} { ${group.declaration} }`);
122
+ }
123
+ const pseudoGroups = groupRulesByDeclaration(pseudoRules);
124
+ for (const group of pseudoGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {
125
+ const selectors = Array.from(group.hashes).sort().map((h) => `.${h}${group.pseudo}`).join(",\n");
126
+ cssLines.push(`${selectors} { ${group.declaration} }`);
127
+ }
128
+ const sortedMediaQueries = Array.from(mediaRules.entries()).sort(
129
+ ([a], [b]) => a.localeCompare(b)
130
+ );
131
+ for (const [query, rules2] of sortedMediaQueries) {
132
+ const mediaGroups = groupRulesByDeclaration(rules2);
133
+ const mediaLines = [];
134
+ for (const group of mediaGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {
135
+ const suffix = group.pseudo || "";
136
+ const selectors = Array.from(group.hashes).sort().map((h) => `.${h}${suffix}`).join(",\n");
137
+ mediaLines.push(` ${selectors} { ${group.declaration} }`);
138
+ }
139
+ cssLines.push(`@media ${query} {
140
+ ${mediaLines.join("\n")}
141
+ }`);
142
+ }
143
+ return cssLines.join("\n");
144
+ }
145
+ function isLiteral(expr) {
146
+ return expr.type === "StringLiteral" || expr.type === "NumericLiteral";
147
+ }
148
+ function getLiteralValue(expr) {
149
+ if (expr.type === "StringLiteral") {
150
+ return expr.value;
151
+ }
152
+ return expr.value;
153
+ }
154
+ function getPropertyKey(prop) {
155
+ if (prop.key.type === "Identifier") {
156
+ return prop.key.value;
157
+ }
158
+ if (prop.key.type === "StringLiteral") {
159
+ return prop.key.value;
160
+ }
161
+ return null;
162
+ }
163
+ function processPropertiesObject(obj, rules, media, pseudo) {
164
+ for (const prop of obj.properties) {
165
+ if (prop.type === "SpreadElement") {
166
+ if (prop.argument.type === "ObjectExpression") {
167
+ processPropertiesObject(prop.argument, rules, media, pseudo);
168
+ } else {
169
+ throw new Error(
170
+ `[catom] Spread elements must be object literals. Dynamic spreads are not supported at compile time.`
171
+ );
172
+ }
173
+ continue;
174
+ }
175
+ if (prop.type !== "KeyValueProperty") {
176
+ continue;
177
+ }
178
+ const keyName = getPropertyKey(prop);
179
+ if (!keyName) {
180
+ throw new Error(`[catom] Could not determine property key. Only identifiers and string literals are supported.`);
181
+ }
182
+ const value = prop.value;
183
+ if (keyName === "media") {
184
+ if (value.type !== "ObjectExpression") {
185
+ throw new Error(`[catom] 'media' property must be an object literal.`);
186
+ }
187
+ for (const mediaProp of value.properties) {
188
+ if (mediaProp.type !== "KeyValueProperty") continue;
189
+ const mediaQuery = getPropertyKey(mediaProp);
190
+ if (!mediaQuery) continue;
191
+ if (mediaProp.value.type !== "ObjectExpression") {
192
+ throw new Error(`[catom] Media query '${mediaQuery}' must contain an object literal.`);
193
+ }
194
+ processPropertiesObject(mediaProp.value, rules, mediaQuery, pseudo);
195
+ }
196
+ continue;
197
+ }
198
+ if (keyName === "pseudo") {
199
+ if (value.type !== "ObjectExpression") {
200
+ throw new Error(`[catom] 'pseudo' property must be an object literal.`);
201
+ }
202
+ for (const pseudoProp of value.properties) {
203
+ if (pseudoProp.type !== "KeyValueProperty") continue;
204
+ const pseudoSelector = getPropertyKey(pseudoProp);
205
+ if (!pseudoSelector) continue;
206
+ if (pseudoProp.value.type !== "ObjectExpression") {
207
+ throw new Error(`[catom] Pseudo selector '${pseudoSelector}' must contain an object literal.`);
208
+ }
209
+ processPropertiesObject(pseudoProp.value, rules, media, pseudoSelector);
210
+ }
211
+ continue;
212
+ }
213
+ let actualValue = value;
214
+ if (actualValue.type === "TsAsExpression") {
215
+ actualValue = actualValue.expression;
216
+ }
217
+ if (!isLiteral(actualValue)) {
218
+ throw new Error(
219
+ `[catom] Property '${keyName}' has a non-literal value. Only string and number literals are supported at compile time. Got: ${actualValue.type}`
220
+ );
221
+ }
222
+ const rule = createCSSRule(keyName, getLiteralValue(actualValue), media, pseudo);
223
+ rules.push(rule);
224
+ }
225
+ }
226
+ function processCSSCall(callExpr) {
227
+ const rules = [];
228
+ if (callExpr.arguments.length === 0) {
229
+ return { rules: [], classNames: "" };
230
+ }
231
+ const arg = callExpr.arguments[0];
232
+ if (arg.expression.type !== "ObjectExpression") {
233
+ throw new Error(
234
+ `[catom] css() must be called with an object literal. Got: ${arg.expression.type}`
235
+ );
236
+ }
237
+ processPropertiesObject(arg.expression, rules);
238
+ const classNames = rules.map((r) => r.hash).join(" ");
239
+ return { rules, classNames };
240
+ }
241
+ var SKIP_KEYS = /* @__PURE__ */ new Set(["span", "ctxt", "type", "value", "raw", "cooked"]);
242
+ function isASTNode(value) {
243
+ return typeof value === "object" && value !== null && typeof value.type === "string";
244
+ }
245
+ function walkAndTransform(node, functionName, allRules, replacements, visited = /* @__PURE__ */ new WeakSet()) {
246
+ if (!node || typeof node !== "object") return;
247
+ if (visited.has(node)) return;
248
+ visited.add(node);
249
+ if (isCallExpression(node)) {
250
+ const callee = node.callee;
251
+ if (callee.type === "Identifier" && callee.value === functionName) {
252
+ try {
253
+ const { rules, classNames } = processCSSCall(node);
254
+ allRules.push(...rules);
255
+ replacements.set(node.span.start, classNames);
256
+ } catch (error) {
257
+ const loc = node.span;
258
+ const prefix = loc ? `[${loc.start}:${loc.end}]` : "";
259
+ throw new Error(`${prefix} ${error.message}`);
260
+ }
261
+ }
262
+ }
263
+ for (const key of Object.keys(node)) {
264
+ if (SKIP_KEYS.has(key)) continue;
265
+ const value = node[key];
266
+ if (Array.isArray(value)) {
267
+ for (const item of value) {
268
+ if (isASTNode(item)) {
269
+ walkAndTransform(item, functionName, allRules, replacements, visited);
270
+ }
271
+ }
272
+ } else if (isASTNode(value)) {
273
+ walkAndTransform(value, functionName, allRules, replacements, visited);
274
+ }
275
+ }
276
+ }
277
+ function isCallExpression(node) {
278
+ return typeof node === "object" && node !== null && node.type === "CallExpression";
279
+ }
280
+ function transformCode(code, id, functionName = "css") {
281
+ const isTypeScript = /\.tsx?$/.test(id);
282
+ const isJSX = /\.[jt]sx$/.test(id);
283
+ let ast;
284
+ try {
285
+ ast = parseSync(code, {
286
+ syntax: isTypeScript ? "typescript" : "ecmascript",
287
+ tsx: isJSX && isTypeScript,
288
+ jsx: isJSX && !isTypeScript,
289
+ comments: true
290
+ });
291
+ } catch {
292
+ return { code, cssRules: [], transformed: false };
293
+ }
294
+ const allRules = [];
295
+ const replacements = /* @__PURE__ */ new Map();
296
+ walkAndTransform(ast, functionName, allRules, replacements);
297
+ if (replacements.size === 0) {
298
+ return { code, cssRules: [], transformed: false };
299
+ }
300
+ let result = code;
301
+ const sortedReplacements = Array.from(replacements.entries()).sort(
302
+ ([a], [b]) => b - a
303
+ // Sort descending so we replace from end to start
304
+ );
305
+ for (const [start, classNames] of sortedReplacements) {
306
+ const searchStart = start - 1;
307
+ const cssCallMatch = findCSSCallBounds(result, searchStart, functionName);
308
+ if (cssCallMatch) {
309
+ const replacement = JSON.stringify(classNames);
310
+ result = result.slice(0, cssCallMatch.start) + replacement + result.slice(cssCallMatch.end);
311
+ }
312
+ }
313
+ return {
314
+ code: result,
315
+ cssRules: allRules,
316
+ transformed: true
317
+ };
318
+ }
319
+ function findCSSCallBounds(code, startPos, functionName) {
320
+ const searchWindow = 50;
321
+ const searchStart = Math.max(0, startPos - searchWindow);
322
+ const searchEnd = Math.min(code.length, startPos + searchWindow);
323
+ const searchRegion = code.slice(searchStart, searchEnd);
324
+ const funcPattern = new RegExp(`\\b${functionName}\\s*\\(`);
325
+ const match = funcPattern.exec(searchRegion);
326
+ if (!match) return null;
327
+ const callStart = searchStart + match.index;
328
+ const parenStart = callStart + match[0].length - 1;
329
+ let depth = 1;
330
+ let i = parenStart + 1;
331
+ while (i < code.length && depth > 0) {
332
+ const char = code[i];
333
+ if (char === "(") depth++;
334
+ else if (char === ")") depth--;
335
+ i++;
336
+ }
337
+ if (depth !== 0) return null;
338
+ return { start: callStart, end: i };
339
+ }
340
+
341
+ // src/vite/index.ts
342
+ var VIRTUAL_MODULE_ID = "virtual:catom.css";
343
+ var RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
344
+ function catomPlugin(options = {}) {
345
+ const {
346
+ include = /\.[jt]sx?$/,
347
+ exclude = /node_modules/,
348
+ functionName = "css"
349
+ } = options;
350
+ const moduleCSS = /* @__PURE__ */ new Map();
351
+ let filter;
352
+ return {
353
+ name: "vite-plugin-catom",
354
+ // Ensure we run before other transforms
355
+ enforce: "pre",
356
+ configResolved() {
357
+ filter = createFilter(include, exclude);
358
+ },
359
+ buildStart() {
360
+ moduleCSS.clear();
361
+ },
362
+ resolveId(id) {
363
+ if (id === VIRTUAL_MODULE_ID) {
364
+ return RESOLVED_VIRTUAL_MODULE_ID;
365
+ }
366
+ return null;
367
+ },
368
+ load(id) {
369
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
370
+ const allRules = [];
371
+ for (const state of moduleCSS.values()) {
372
+ allRules.push(...state.rules);
373
+ }
374
+ const css = generateCSS(allRules);
375
+ return css;
376
+ }
377
+ return null;
378
+ },
379
+ transform(code, id) {
380
+ if (!filter(id)) {
381
+ return null;
382
+ }
383
+ if (!code.includes(functionName + "(")) {
384
+ return null;
385
+ }
386
+ try {
387
+ const result = transformCode(code, id, functionName);
388
+ if (!result.transformed) {
389
+ return null;
390
+ }
391
+ moduleCSS.set(id, {
392
+ rules: result.cssRules,
393
+ timestamp: Date.now()
394
+ });
395
+ return {
396
+ code: result.code,
397
+ map: null
398
+ // TODO: Add source map support
399
+ };
400
+ } catch (error) {
401
+ const message = error instanceof Error ? error.message : String(error);
402
+ this.error(`[catom] Error transforming ${id}: ${message}`);
403
+ }
404
+ },
405
+ // Handle module removal (for dev server)
406
+ watchChange(id) {
407
+ if (moduleCSS.has(id)) {
408
+ moduleCSS.delete(id);
409
+ }
410
+ }
411
+ };
412
+ }
413
+
414
+ export { catomPlugin, catomPlugin as default };
415
+ //# sourceMappingURL=index.js.map
416
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/hash.ts","../../src/core/css-generator.ts","../../src/vite/transform.ts","../../src/vite/index.ts"],"names":["rules"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Murmur2 hash implementation (from emotion-js)\n * Fast, non-cryptographic hash function for generating unique class names\n * https://github.com/emotion-js/emotion/blob/master/packages/hash/src/index.js\n */\nexport function murmur2(str: string): string {\n // 'm' and 'r' are mixing constants generated offline.\n // They're not really 'magic', they just happen to work well.\n // const m = 0x5bd1e995;\n // const r = 24;\n\n // Initialize the hash\n let h = 0\n\n // Mix 4 bytes at a time into the hash\n let k: number\n let i = 0\n let len = str.length\n\n for (; len >= 4; ++i, len -= 4) {\n k =\n (str.charCodeAt(i) & 0xff) |\n ((str.charCodeAt(++i) & 0xff) << 8) |\n ((str.charCodeAt(++i) & 0xff) << 16) |\n ((str.charCodeAt(++i) & 0xff) << 24)\n\n k =\n /* Math.imul(k, m): */\n (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)\n k ^= /* k >>> r: */ k >>> 24\n\n h =\n /* Math.imul(k, m): */\n ((k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)) ^\n /* Math.imul(h, m): */\n ((h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16))\n }\n\n // Handle the last few bytes of the input array\n switch (len) {\n case 3:\n h ^= (str.charCodeAt(i + 2) & 0xff) << 16\n // falls through\n case 2:\n h ^= (str.charCodeAt(i + 1) & 0xff) << 8\n // falls through\n case 1:\n h ^= str.charCodeAt(i) & 0xff\n h =\n /* Math.imul(h, m): */\n (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16)\n }\n\n // Do a few final mixes of the hash to ensure the last few\n // bytes are well-incorporated.\n h ^= h >>> 13\n h =\n /* Math.imul(h, m): */\n (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16)\n\n return ((h ^ (h >>> 15)) >>> 0).toString(36)\n}\n\n/**\n * Characters that require underscore prefix for valid CSS class names\n */\nconst PREFIX_CHARS = new Set('0123456789-'.split(''))\n\n/**\n * Make a hash safe for use as a CSS class name\n * Prefixes with underscore if starts with digit or dash\n */\nexport function makeCSSCompatible(hash: string): string {\n if (PREFIX_CHARS.has(hash[0])) {\n return `_${hash}`\n }\n return hash\n}\n\n/**\n * Generate a CSS-safe hash from an identity string\n */\nexport function generateHash(identity: string): string {\n return makeCSSCompatible(murmur2(identity))\n}\n","import { generateHash } from './hash.js'\nimport type { CSSRule } from './types.js'\n\n/**\n * Regex to convert camelCase to kebab-case\n */\nconst KEBAB_CASE_REGEX = /([a-z0-9]|(?=[A-Z]))([A-Z])/g\n\n/**\n * Convert camelCase CSS property to kebab-case\n * e.g., \"backgroundColor\" -> \"background-color\"\n */\nexport function toKebabCase(property: string): string {\n return property.replace(KEBAB_CASE_REGEX, '$1-$2').toLowerCase()\n}\n\n/**\n * Create a unique identity string for a CSS rule\n * Used for hashing and deduplication\n * Includes both media and pseudo in identity for proper deduplication\n */\nexport function createRuleIdentity(\n property: string,\n value: string,\n media?: string,\n pseudo?: string\n): string {\n const mediaPrefix = media ? `@${media.trim()}` : ''\n const pseudoPrefix = pseudo ? pseudo.trim() : ''\n const kebabProp = toKebabCase(property)\n return `${mediaPrefix}${pseudoPrefix}${kebabProp}:${value};`\n}\n\n/**\n * Create a CSS rule from property and value\n */\nexport function createCSSRule(\n property: string,\n value: string | number,\n media?: string,\n pseudo?: string\n): CSSRule {\n const stringValue = String(value).trim()\n const kebabProperty = toKebabCase(property.trim())\n const identity = createRuleIdentity(property, stringValue, media, pseudo)\n const hash = generateHash(identity)\n\n return {\n hash,\n property: kebabProperty,\n value: stringValue,\n media: media?.trim(),\n pseudo: pseudo?.trim(),\n }\n}\n\n/**\n * Deduplicate CSS rules by their identity\n * Rules with identical property:value (and media/pseudo) share the same hash\n */\nexport function deduplicateRules(rules: CSSRule[]): CSSRule[] {\n const seen = new Map<string, CSSRule>()\n\n for (const rule of rules) {\n const identity = createRuleIdentity(rule.property, rule.value, rule.media, rule.pseudo)\n if (!seen.has(identity)) {\n seen.set(identity, rule)\n }\n }\n\n return Array.from(seen.values())\n}\n\n/**\n * Group rules by their CSS declaration for merging selectors\n * e.g., multiple classes with same \"color: red\" get merged\n */\ninterface GroupedRule {\n declaration: string // e.g., \"color: red;\"\n hashes: Set<string>\n media?: string\n pseudo?: string\n}\n\nfunction groupRulesByDeclaration(rules: CSSRule[]): GroupedRule[] {\n const groups = new Map<string, GroupedRule>()\n\n for (const rule of rules) {\n const declaration = `${rule.property}:${rule.value};`\n const groupKey = `${rule.media || ''}|${rule.pseudo || ''}|${declaration}`\n\n if (!groups.has(groupKey)) {\n groups.set(groupKey, {\n declaration,\n hashes: new Set(),\n media: rule.media,\n pseudo: rule.pseudo,\n })\n }\n groups.get(groupKey)!.hashes.add(rule.hash)\n }\n\n return Array.from(groups.values())\n}\n\n/**\n * Generate final CSS string from rules\n * Handles deduplication and groups media queries\n */\nexport function generateCSS(rules: CSSRule[]): string {\n const dedupedRules = deduplicateRules(rules)\n\n // Separate rules by type\n const regularRules: CSSRule[] = []\n const pseudoRules: CSSRule[] = []\n const mediaRules = new Map<string, CSSRule[]>()\n\n for (const rule of dedupedRules) {\n if (rule.media) {\n const existing = mediaRules.get(rule.media) || []\n existing.push(rule)\n mediaRules.set(rule.media, existing)\n } else if (rule.pseudo) {\n pseudoRules.push(rule)\n } else {\n regularRules.push(rule)\n }\n }\n\n const cssLines: string[] = []\n\n // Generate regular rules (grouped by declaration)\n const regularGroups = groupRulesByDeclaration(regularRules)\n for (const group of regularGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {\n const selectors = Array.from(group.hashes)\n .sort()\n .map((h) => `.${h}`)\n .join(',\\n')\n cssLines.push(`${selectors} { ${group.declaration} }`)\n }\n\n // Generate pseudo rules\n const pseudoGroups = groupRulesByDeclaration(pseudoRules)\n for (const group of pseudoGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {\n const selectors = Array.from(group.hashes)\n .sort()\n .map((h) => `.${h}${group.pseudo}`)\n .join(',\\n')\n cssLines.push(`${selectors} { ${group.declaration} }`)\n }\n\n // Generate media queries\n const sortedMediaQueries = Array.from(mediaRules.entries()).sort(([a], [b]) =>\n a.localeCompare(b)\n )\n\n for (const [query, rules] of sortedMediaQueries) {\n const mediaGroups = groupRulesByDeclaration(rules)\n const mediaLines: string[] = []\n\n for (const group of mediaGroups.sort((a, b) => a.declaration.localeCompare(b.declaration))) {\n const suffix = group.pseudo || ''\n const selectors = Array.from(group.hashes)\n .sort()\n .map((h) => `.${h}${suffix}`)\n .join(',\\n')\n mediaLines.push(` ${selectors} { ${group.declaration} }`)\n }\n\n cssLines.push(`@media ${query} {\\n${mediaLines.join('\\n')}\\n}`)\n }\n\n return cssLines.join('\\n')\n}\n","import { parseSync } from '@swc/core'\nimport type {\n CallExpression,\n Expression,\n KeyValueProperty,\n ObjectExpression,\n Program,\n StringLiteral,\n NumericLiteral,\n SpreadElement,\n Property,\n} from '@swc/core'\nimport { createCSSRule } from '../core/css-generator.js'\nimport type { CSSRule, TransformResult } from '../core/types.js'\n\n/**\n * Check if an expression is a string or numeric literal\n */\nfunction isLiteral(expr: Expression): expr is StringLiteral | NumericLiteral {\n return expr.type === 'StringLiteral' || expr.type === 'NumericLiteral'\n}\n\n/**\n * Get the value from a literal expression\n */\nfunction getLiteralValue(expr: StringLiteral | NumericLiteral): string | number {\n if (expr.type === 'StringLiteral') {\n return expr.value\n }\n return expr.value\n}\n\n/**\n * Extract the key name from a property\n */\nfunction getPropertyKey(prop: KeyValueProperty): string | null {\n if (prop.key.type === 'Identifier') {\n return prop.key.value\n }\n if (prop.key.type === 'StringLiteral') {\n return prop.key.value\n }\n return null\n}\n\n/**\n * Process a CSS properties object and extract rules\n */\nfunction processPropertiesObject(\n obj: ObjectExpression,\n rules: CSSRule[],\n media?: string,\n pseudo?: string\n): void {\n for (const prop of obj.properties) {\n if (prop.type === 'SpreadElement') {\n // Handle spread - try to process if it's an object literal\n if (prop.argument.type === 'ObjectExpression') {\n processPropertiesObject(prop.argument, rules, media, pseudo)\n } else {\n throw new Error(\n `[catom] Spread elements must be object literals. ` +\n `Dynamic spreads are not supported at compile time.`\n )\n }\n continue\n }\n\n if (prop.type !== 'KeyValueProperty') {\n continue\n }\n\n const keyName = getPropertyKey(prop)\n if (!keyName) {\n throw new Error(`[catom] Could not determine property key. Only identifiers and string literals are supported.`)\n }\n\n const value = prop.value\n\n // Handle media queries\n if (keyName === 'media') {\n if (value.type !== 'ObjectExpression') {\n throw new Error(`[catom] 'media' property must be an object literal.`)\n }\n for (const mediaProp of value.properties) {\n if (mediaProp.type !== 'KeyValueProperty') continue\n const mediaQuery = getPropertyKey(mediaProp)\n if (!mediaQuery) continue\n if (mediaProp.value.type !== 'ObjectExpression') {\n throw new Error(`[catom] Media query '${mediaQuery}' must contain an object literal.`)\n }\n processPropertiesObject(mediaProp.value, rules, mediaQuery, pseudo)\n }\n continue\n }\n\n // Handle pseudo selectors\n if (keyName === 'pseudo') {\n if (value.type !== 'ObjectExpression') {\n throw new Error(`[catom] 'pseudo' property must be an object literal.`)\n }\n for (const pseudoProp of value.properties) {\n if (pseudoProp.type !== 'KeyValueProperty') continue\n const pseudoSelector = getPropertyKey(pseudoProp)\n if (!pseudoSelector) continue\n if (pseudoProp.value.type !== 'ObjectExpression') {\n throw new Error(`[catom] Pseudo selector '${pseudoSelector}' must contain an object literal.`)\n }\n processPropertiesObject(pseudoProp.value, rules, media, pseudoSelector)\n }\n continue\n }\n\n // Handle regular CSS properties\n // Unwrap TSAsExpression if present (e.g., `value as const`)\n let actualValue = value\n if (actualValue.type === 'TsAsExpression') {\n actualValue = actualValue.expression\n }\n\n if (!isLiteral(actualValue)) {\n throw new Error(\n `[catom] Property '${keyName}' has a non-literal value. ` +\n `Only string and number literals are supported at compile time. ` +\n `Got: ${actualValue.type}`\n )\n }\n\n const rule = createCSSRule(keyName, getLiteralValue(actualValue), media, pseudo)\n rules.push(rule)\n }\n}\n\n/**\n * Process a css() call expression and return the extracted rules and replacement hashes\n */\nfunction processCSSCall(callExpr: CallExpression): { rules: CSSRule[]; classNames: string } {\n const rules: CSSRule[] = []\n\n if (callExpr.arguments.length === 0) {\n return { rules: [], classNames: '' }\n }\n\n const arg = callExpr.arguments[0]\n if (arg.expression.type !== 'ObjectExpression') {\n throw new Error(\n `[catom] css() must be called with an object literal. ` +\n `Got: ${arg.expression.type}`\n )\n }\n\n processPropertiesObject(arg.expression, rules)\n\n const classNames = rules.map((r) => r.hash).join(' ')\n return { rules, classNames }\n}\n\n// Keys to skip during AST traversal (not AST nodes)\nconst SKIP_KEYS = new Set(['span', 'ctxt', 'type', 'value', 'raw', 'cooked'])\n\n/**\n * Check if a value is an AST node (has a type property)\n */\nfunction isASTNode(value: unknown): value is { type: string } {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Record<string, unknown>).type === 'string'\n )\n}\n\n/**\n * Recursively walk the AST and find/transform css() calls\n */\nfunction walkAndTransform(\n node: unknown,\n functionName: string,\n allRules: CSSRule[],\n replacements: Map<number, string>,\n visited = new WeakSet<object>()\n): void {\n if (!node || typeof node !== 'object') return\n \n // Prevent cycles\n if (visited.has(node as object)) return\n visited.add(node as object)\n\n // Check if this is a css() call\n if (isCallExpression(node)) {\n const callee = node.callee\n if (\n callee.type === 'Identifier' &&\n callee.value === functionName\n ) {\n try {\n const { rules, classNames } = processCSSCall(node)\n allRules.push(...rules)\n // Store replacement: we'll replace by position\n replacements.set(node.span.start, classNames)\n } catch (error) {\n // Re-throw with location info if available\n const loc = node.span\n const prefix = loc ? `[${loc.start}:${loc.end}]` : ''\n throw new Error(`${prefix} ${(error as Error).message}`)\n }\n }\n }\n\n // Recurse into AST node properties\n for (const key of Object.keys(node as Record<string, unknown>)) {\n if (SKIP_KEYS.has(key)) continue\n \n const value = (node as Record<string, unknown>)[key]\n if (Array.isArray(value)) {\n for (const item of value) {\n if (isASTNode(item)) {\n walkAndTransform(item, functionName, allRules, replacements, visited)\n }\n }\n } else if (isASTNode(value)) {\n walkAndTransform(value, functionName, allRules, replacements, visited)\n }\n }\n}\n\nfunction isCallExpression(node: unknown): node is CallExpression {\n return (\n typeof node === 'object' &&\n node !== null &&\n (node as { type?: string }).type === 'CallExpression'\n )\n}\n\n/**\n * Transform source code by replacing css() calls with class name strings\n */\nexport function transformCode(\n code: string,\n id: string,\n functionName: string = 'css'\n): TransformResult {\n // Determine syntax from file extension\n const isTypeScript = /\\.tsx?$/.test(id)\n const isJSX = /\\.[jt]sx$/.test(id)\n\n let ast: Program\n try {\n ast = parseSync(code, {\n syntax: isTypeScript ? 'typescript' : 'ecmascript',\n tsx: isJSX && isTypeScript,\n jsx: isJSX && !isTypeScript,\n comments: true,\n })\n } catch {\n // If parsing fails, return unchanged\n return { code, cssRules: [], transformed: false }\n }\n\n const allRules: CSSRule[] = []\n const replacements = new Map<number, string>()\n\n // Walk the AST\n walkAndTransform(ast, functionName, allRules, replacements)\n\n if (replacements.size === 0) {\n return { code, cssRules: [], transformed: false }\n }\n\n // Apply replacements to the source code\n // We need to do this carefully to handle position shifts\n let result = code\n const sortedReplacements = Array.from(replacements.entries()).sort(\n ([a], [b]) => b - a // Sort descending so we replace from end to start\n )\n\n for (const [start, classNames] of sortedReplacements) {\n // Find the css(...) call in the original code starting at this position\n // We need to find the matching closing paren\n const searchStart = start - 1 // SWC positions seem to be 1-indexed\n const cssCallMatch = findCSSCallBounds(result, searchStart, functionName)\n if (cssCallMatch) {\n const replacement = JSON.stringify(classNames)\n result =\n result.slice(0, cssCallMatch.start) +\n replacement +\n result.slice(cssCallMatch.end)\n }\n }\n\n return {\n code: result,\n cssRules: allRules,\n transformed: true,\n }\n}\n\n/**\n * Find the bounds of a css() call in source code\n */\nfunction findCSSCallBounds(\n code: string,\n startPos: number,\n functionName: string\n): { start: number; end: number } | null {\n // Look for the function name near this position\n const searchWindow = 50\n const searchStart = Math.max(0, startPos - searchWindow)\n const searchEnd = Math.min(code.length, startPos + searchWindow)\n const searchRegion = code.slice(searchStart, searchEnd)\n\n // Find the function call\n const funcPattern = new RegExp(`\\\\b${functionName}\\\\s*\\\\(`)\n const match = funcPattern.exec(searchRegion)\n if (!match) return null\n\n const callStart = searchStart + match.index\n const parenStart = callStart + match[0].length - 1\n\n // Find matching closing paren\n let depth = 1\n let i = parenStart + 1\n while (i < code.length && depth > 0) {\n const char = code[i]\n if (char === '(') depth++\n else if (char === ')') depth--\n i++\n }\n\n if (depth !== 0) return null\n\n return { start: callStart, end: i }\n}\n","import type { Plugin } from 'vite'\nimport { createFilter } from 'vite'\n\nimport { generateCSS } from '../core/css-generator.js'\nimport { transformCode } from './transform.js'\nimport type { CSSRule, CatomPluginOptions, ModuleCSSState } from '../core/types.js'\n\nexport type { CatomPluginOptions, CSSRule, CSSInput } from '../core/types.js'\n\nconst VIRTUAL_MODULE_ID = 'virtual:catom.css'\nconst RESOLVED_VIRTUAL_MODULE_ID = '\\0' + VIRTUAL_MODULE_ID\n\n/**\n * Vite plugin for zero-runtime CSS-in-JS with atomic CSS generation\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import { defineConfig } from 'vite'\n * import catom from 'catom/vite'\n *\n * export default defineConfig({\n * plugins: [catom()]\n * })\n * ```\n *\n * @example\n * ```ts\n * // In your app code\n * import { css } from 'catom'\n * import 'virtual:catom.css'\n *\n * const button = css({\n * color: 'red',\n * padding: '8px',\n * pseudo: { ':hover': { color: 'blue' } }\n * })\n * // After transform: const button = \"_a1b2c3 _d4e5f6 _g7h8i9\"\n * ```\n */\nexport default function catomPlugin(options: CatomPluginOptions = {}): Plugin {\n const {\n include = /\\.[jt]sx?$/,\n exclude = /node_modules/,\n functionName = 'css',\n } = options\n\n // Per-build state: Map<moduleId, ModuleCSSState>\n // This gets reset on each build but persists during dev for the same module\n const moduleCSS = new Map<string, ModuleCSSState>()\n\n // Create filter for file matching\n let filter: (id: string) => boolean\n\n return {\n name: 'vite-plugin-catom',\n\n // Ensure we run before other transforms\n enforce: 'pre',\n\n configResolved() {\n filter = createFilter(include, exclude)\n },\n\n buildStart() {\n // Clear CSS state at the start of each build\n // This ensures clean builds and prevents stale CSS\n moduleCSS.clear()\n },\n\n resolveId(id) {\n if (id === VIRTUAL_MODULE_ID) {\n return RESOLVED_VIRTUAL_MODULE_ID\n }\n return null\n },\n\n load(id) {\n if (id === RESOLVED_VIRTUAL_MODULE_ID) {\n // Aggregate all CSS rules from all modules\n const allRules: CSSRule[] = []\n for (const state of moduleCSS.values()) {\n allRules.push(...state.rules)\n }\n\n // Generate deduplicated CSS\n const css = generateCSS(allRules)\n return css\n }\n return null\n },\n\n transform(code, id) {\n // Skip non-matching files\n if (!filter(id)) {\n return null\n }\n\n // Skip if no css() calls (quick check before parsing)\n if (!code.includes(functionName + '(')) {\n return null\n }\n\n try {\n const result = transformCode(code, id, functionName)\n\n if (!result.transformed) {\n return null\n }\n\n // Store CSS rules for this module\n moduleCSS.set(id, {\n rules: result.cssRules,\n timestamp: Date.now(),\n })\n\n return {\n code: result.code,\n map: null, // TODO: Add source map support\n }\n } catch (error) {\n // Include file path in error message\n const message = error instanceof Error ? error.message : String(error)\n this.error(`[catom] Error transforming ${id}: ${message}`)\n }\n },\n\n // Handle module removal (for dev server)\n watchChange(id) {\n if (moduleCSS.has(id)) {\n moduleCSS.delete(id)\n }\n },\n }\n}\n\n// Also export as named export for flexibility\nexport { catomPlugin }\n"]}
package/package.json CHANGED
@@ -1,23 +1,32 @@
1
1
  {
2
2
  "name": "catom",
3
- "version": "2.0.4",
4
- "description": "Zero-runtime atomic CSS-in-JS for Vite",
3
+ "version": "2.2.0",
4
+ "description": "Zero-runtime CSS-in-JS with atomic CSS generation",
5
5
  "type": "module",
6
- "main": "dist/index.js",
7
- "module": "dist/index.js",
8
- "types": "dist/index.d.ts",
6
+ "main": "dist/runtime/index.js",
7
+ "module": "dist/runtime/index.js",
8
+ "types": "dist/runtime/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js"
11
+ "types": "./dist/runtime/index.d.ts",
12
+ "import": "./dist/runtime/index.js"
13
+ },
14
+ "./vite": {
15
+ "types": "./dist/vite/index.d.ts",
16
+ "import": "./dist/vite/index.js"
17
+ },
18
+ "./virtual": {
19
+ "types": "./src/virtual.d.ts"
13
20
  }
14
21
  },
15
22
  "files": [
16
- "dist"
23
+ "dist",
24
+ "src/virtual.d.ts"
17
25
  ],
18
26
  "scripts": {
19
- "build": "tsc",
20
- "dev": "tsc --watch",
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "typecheck": "tsc --noEmit",
21
30
  "prepublishOnly": "npm run build"
22
31
  },
23
32
  "keywords": [
@@ -25,20 +34,31 @@
25
34
  "vite-plugin",
26
35
  "css-in-js",
27
36
  "atomic-css",
28
- "zero-runtime"
37
+ "zero-runtime",
38
+ "catom"
29
39
  ],
30
40
  "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/Hydrophobefireman/catom.git"
44
+ },
31
45
  "peerDependencies": {
32
- "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
46
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "vite": {
50
+ "optional": true
51
+ }
33
52
  },
34
53
  "dependencies": {
35
- "unplugin-ast": "^0.15.4",
36
- "csstype": "^3.1.1"
54
+ "@swc/core": "^1.15.11",
55
+ "csstype": "^3.2.3"
37
56
  },
38
57
  "devDependencies": {
39
- "@babel/types": "^7.26.0",
40
- "@types/node": "^20.0.0",
41
- "typescript": "^5.0.0",
42
- "vite": "^5.0.0"
58
+ "@types/node": "^20.11.0",
59
+ "tsup": "^8.0.0",
60
+ "tsx": "^4.21.0",
61
+ "typescript": "^5.3.0",
62
+ "vite": "^7.0.0"
43
63
  }
44
64
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Virtual module type declaration for catom CSS
3
+ * Add this to your tsconfig.json "include" or reference it in a .d.ts file
4
+ */
5
+ declare module 'virtual:catom.css' {
6
+ // This is a CSS module with no exports
7
+ const content: string
8
+ export default content
9
+ }
package/dist/css.d.ts DELETED
@@ -1,39 +0,0 @@
1
- export interface CSSRule {
2
- className: string;
3
- rule: string;
4
- }
5
- /**
6
- * Generate a CSS rule from a property-value pair.
7
- * This is a pure function - same input always gives same output.
8
- *
9
- * @param property - CSS property name (camelCase or kebab-case)
10
- * @param value - CSS value
11
- * @param context - Optional context like media query or pseudo selector
12
- * @returns CSSRule with className and the full CSS rule string
13
- */
14
- export declare function generateRule(property: string, value: string | number, context?: {
15
- media?: string;
16
- pseudo?: string;
17
- }): CSSRule;
18
- export interface StyleObject {
19
- [key: string]: string | number | StyleObject | undefined;
20
- media?: Record<string, Record<string, string | number>>;
21
- pseudo?: Record<string, Record<string, string | number>>;
22
- }
23
- export interface ProcessResult {
24
- classNames: string[];
25
- rules: string[];
26
- }
27
- /**
28
- * Process a style object and return class names + CSS rules.
29
- * Pure function - no side effects, no global state.
30
- */
31
- export declare function processStyleObject(obj: StyleObject, context?: {
32
- media?: string;
33
- pseudo?: string;
34
- }): ProcessResult;
35
- /**
36
- * Deduplicate and sort CSS rules for consistent output.
37
- */
38
- export declare function dedupeRules(rules: string[]): string;
39
- //# sourceMappingURL=css.d.ts.map
package/dist/css.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CA2BT;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,aAAa,CAiCf;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnD"}
package/dist/css.js DELETED
@@ -1,87 +0,0 @@
1
- import { murmur2 } from "./hash.js";
2
- const KEBAB_CASE_RE = /([a-z0-9]|(?=[A-Z]))([A-Z])/g;
3
- const NUMERIC_START = /^[0-9-]/;
4
- function toKebabCase(prop) {
5
- return prop.replace(KEBAB_CASE_RE, "$1-$2").toLowerCase();
6
- }
7
- function sanitizeClassName(hash) {
8
- // CSS class names can't start with a digit or hyphen
9
- return NUMERIC_START.test(hash) ? `_${hash}` : hash;
10
- }
11
- /**
12
- * Generate a CSS rule from a property-value pair.
13
- * This is a pure function - same input always gives same output.
14
- *
15
- * @param property - CSS property name (camelCase or kebab-case)
16
- * @param value - CSS value
17
- * @param context - Optional context like media query or pseudo selector
18
- * @returns CSSRule with className and the full CSS rule string
19
- */
20
- export function generateRule(property, value, context) {
21
- const cssProp = toKebabCase(property.trim());
22
- const cssValue = String(value).trim();
23
- // The identity includes context so same property in different contexts gets different class
24
- const identity = [
25
- context?.media || "",
26
- context?.pseudo || "",
27
- cssProp,
28
- cssValue,
29
- ].join("|");
30
- const className = sanitizeClassName(murmur2(identity));
31
- const declaration = `${cssProp}:${cssValue}`;
32
- let rule;
33
- const selector = context?.pseudo
34
- ? `.${className}${context.pseudo}`
35
- : `.${className}`;
36
- if (context?.media) {
37
- rule = `@media ${context.media}{${selector}{${declaration}}}`;
38
- }
39
- else {
40
- rule = `${selector}{${declaration}}`;
41
- }
42
- return { className, rule };
43
- }
44
- /**
45
- * Process a style object and return class names + CSS rules.
46
- * Pure function - no side effects, no global state.
47
- */
48
- export function processStyleObject(obj, context) {
49
- const classNames = [];
50
- const rules = [];
51
- for (const key of Object.keys(obj)) {
52
- const value = obj[key];
53
- if (value === undefined)
54
- continue;
55
- if (key === "media" && typeof value === "object") {
56
- // Handle media queries: media: { "(max-width: 768px)": { color: "red" } }
57
- for (const query of Object.keys(value)) {
58
- const mediaStyles = value[query];
59
- const result = processStyleObject(mediaStyles, { media: query });
60
- classNames.push(...result.classNames);
61
- rules.push(...result.rules);
62
- }
63
- }
64
- else if (key === "pseudo" && typeof value === "object") {
65
- // Handle pseudo selectors: pseudo: { ":hover": { color: "blue" } }
66
- for (const selector of Object.keys(value)) {
67
- const pseudoStyles = value[selector];
68
- const result = processStyleObject(pseudoStyles, { pseudo: selector });
69
- classNames.push(...result.classNames);
70
- rules.push(...result.rules);
71
- }
72
- }
73
- else if (typeof value === "string" || typeof value === "number") {
74
- // Regular CSS property
75
- const { className, rule } = generateRule(key, value, context);
76
- classNames.push(className);
77
- rules.push(rule);
78
- }
79
- }
80
- return { classNames, rules };
81
- }
82
- /**
83
- * Deduplicate and sort CSS rules for consistent output.
84
- */
85
- export function dedupeRules(rules) {
86
- return [...new Set(rules)].sort().join("\n");
87
- }
package/dist/hash.d.ts DELETED
@@ -1,7 +0,0 @@
1
- /**
2
- * Murmur2 hash - deterministic hash function.
3
- * Same input always produces the same output.
4
- * Adapted from emotion-js.
5
- */
6
- export declare function murmur2(str: string): string;
7
- //# sourceMappingURL=hash.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqC3C"}
package/dist/hash.js DELETED
@@ -1,37 +0,0 @@
1
- /**
2
- * Murmur2 hash - deterministic hash function.
3
- * Same input always produces the same output.
4
- * Adapted from emotion-js.
5
- */
6
- export function murmur2(str) {
7
- let h = 0;
8
- let k;
9
- let i = 0;
10
- let len = str.length;
11
- for (; len >= 4; ++i, len -= 4) {
12
- k =
13
- (str.charCodeAt(i) & 0xff) |
14
- ((str.charCodeAt(++i) & 0xff) << 8) |
15
- ((str.charCodeAt(++i) & 0xff) << 16) |
16
- ((str.charCodeAt(++i) & 0xff) << 24);
17
- k = (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16);
18
- k ^= k >>> 24;
19
- h =
20
- ((k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)) ^
21
- ((h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16));
22
- }
23
- switch (len) {
24
- case 3:
25
- h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
26
- // fallthrough
27
- case 2:
28
- h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
29
- // fallthrough
30
- case 1:
31
- h ^= str.charCodeAt(i) & 0xff;
32
- h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
33
- }
34
- h ^= h >>> 13;
35
- h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
36
- return ((h ^ (h >>> 15)) >>> 0).toString(36);
37
- }
package/dist/index.d.ts DELETED
@@ -1,38 +0,0 @@
1
- import type { Properties } from "csstype";
2
- export { default } from "./plugin.js";
3
- export type { CatomOptions } from "./plugin.js";
4
- export { processStyleObject, generateRule, dedupeRules } from "./css.js";
5
- export type { CSSRule, StyleObject, ProcessResult } from "./css.js";
6
- /**
7
- * Define atomic CSS styles. Transformed at build time to class names.
8
- *
9
- * @example
10
- * ```ts
11
- * import { css } from "catom-vite";
12
- *
13
- * const className = css({
14
- * display: "flex",
15
- * alignItems: "center",
16
- * padding: "1rem",
17
- * media: {
18
- * "(max-width: 768px)": {
19
- * flexDirection: "column"
20
- * }
21
- * },
22
- * pseudo: {
23
- * ":hover": {
24
- * backgroundColor: "#f0f0f0"
25
- * }
26
- * }
27
- * });
28
- * ```
29
- */
30
- export declare function css(_styles: Properties & {
31
- media?: {
32
- [query: string]: Properties;
33
- };
34
- pseudo?: {
35
- [selector: string]: Properties;
36
- };
37
- }): string;
38
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACzE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,GAAG,CACjB,OAAO,EAAE,UAAU,GAAG;IACpB,KAAK,CAAC,EAAE;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IACxC,MAAM,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAC7C,GACA,MAAM,CAKR"}
package/dist/index.js DELETED
@@ -1,31 +0,0 @@
1
- export { default } from "./plugin.js";
2
- // Re-export utilities for advanced usage
3
- export { processStyleObject, generateRule, dedupeRules } from "./css.js";
4
- /**
5
- * Define atomic CSS styles. Transformed at build time to class names.
6
- *
7
- * @example
8
- * ```ts
9
- * import { css } from "catom-vite";
10
- *
11
- * const className = css({
12
- * display: "flex",
13
- * alignItems: "center",
14
- * padding: "1rem",
15
- * media: {
16
- * "(max-width: 768px)": {
17
- * flexDirection: "column"
18
- * }
19
- * },
20
- * pseudo: {
21
- * ":hover": {
22
- * backgroundColor: "#f0f0f0"
23
- * }
24
- * }
25
- * });
26
- * ```
27
- */
28
- export function css(_styles) {
29
- throw new Error("catom css() was called at runtime. " +
30
- "Ensure the catom-vite plugin is configured in vite.config.ts");
31
- }
package/dist/plugin.d.ts DELETED
@@ -1,11 +0,0 @@
1
- import type { Plugin } from "vite";
2
- export interface CatomOptions {
3
- /** Function name to transform. Default: "css" */
4
- functionName?: string;
5
- /** File patterns to include. Default: /\.[jt]sx?$/ */
6
- include?: RegExp | RegExp[];
7
- /** File patterns to exclude */
8
- exclude?: RegExp | RegExp[];
9
- }
10
- export default function catom(options?: CatomOptions): Plugin[];
11
- //# sourceMappingURL=plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC7B;AAKD,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,MAAM,EAAE,CAoFlE"}
package/dist/plugin.js DELETED
@@ -1,75 +0,0 @@
1
- import AST from "unplugin-ast/vite";
2
- import { createCatomTransformer } from "./transformer.js";
3
- import { dedupeRules } from "./css.js";
4
- const VIRTUAL_ID = "virtual:catom.css";
5
- const RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
6
- export default function catom(options = {}) {
7
- const { functionName = "css", include = /\.[jt]sx?$/, exclude, } = options;
8
- // Collect CSS rules from all files.
9
- // Using a Set for automatic deduplication (deterministic hashing means same rule = same string)
10
- const collectedRules = new Set();
11
- // The AST plugin handles transformation
12
- const astPlugin = AST({
13
- include: Array.isArray(include) ? include : [include],
14
- exclude: exclude ? (Array.isArray(exclude) ? exclude : [exclude]) : undefined,
15
- enforce: "pre",
16
- transformer: [
17
- createCatomTransformer(functionName, (rules) => {
18
- for (const rule of rules) {
19
- collectedRules.add(rule);
20
- }
21
- }),
22
- ],
23
- });
24
- // The CSS plugin handles virtual module and CSS emission
25
- const cssPlugin = {
26
- name: "catom:css",
27
- enforce: "post",
28
- buildStart() {
29
- // Clear collected rules at the start of each build
30
- collectedRules.clear();
31
- },
32
- resolveId(id) {
33
- if (id === VIRTUAL_ID) {
34
- return RESOLVED_VIRTUAL_ID;
35
- }
36
- },
37
- load(id) {
38
- if (id === RESOLVED_VIRTUAL_ID) {
39
- // In dev, return empty - we inject via transformIndexHtml
40
- // In build, this will be populated by generateBundle
41
- return "";
42
- }
43
- },
44
- // For dev: inject CSS into HTML
45
- transformIndexHtml: {
46
- order: "post",
47
- handler() {
48
- if (collectedRules.size === 0)
49
- return [];
50
- const css = dedupeRules([...collectedRules]);
51
- return [
52
- {
53
- tag: "style",
54
- attrs: { "data-catom": "" },
55
- children: css,
56
- injectTo: "head",
57
- },
58
- ];
59
- },
60
- },
61
- // For build: emit CSS file
62
- generateBundle() {
63
- if (collectedRules.size === 0)
64
- return;
65
- const css = dedupeRules([...collectedRules]);
66
- this.emitFile({
67
- type: "asset",
68
- fileName: "catom.css",
69
- source: css,
70
- });
71
- },
72
- };
73
- // Return both plugins - AST first (pre), then CSS (post)
74
- return [astPlugin, cssPlugin];
75
- }
@@ -1,14 +0,0 @@
1
- import type { CallExpression } from "@babel/types";
2
- import type { Transformer } from "unplugin-ast";
3
- export interface TransformResult {
4
- classNames: string;
5
- rules: string[];
6
- }
7
- /**
8
- * Creates an unplugin-ast transformer that finds css() calls
9
- * and replaces them with class name strings.
10
- *
11
- * Returns the collected CSS rules separately for aggregation.
12
- */
13
- export declare function createCatomTransformer(functionName: string, onRules: (rules: string[]) => void): Transformer<CallExpression>;
14
- //# sourceMappingURL=transformer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../src/transformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA0B,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoDhD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GACjC,WAAW,CAAC,cAAc,CAAC,CA+B7B"}
@@ -1,84 +0,0 @@
1
- import { processStyleObject } from "./css.js";
2
- /**
3
- * Extract a JS value from a Babel AST node.
4
- * Only handles static, compile-time constant values.
5
- */
6
- function extractValue(node) {
7
- switch (node.type) {
8
- case "StringLiteral":
9
- return node.value;
10
- case "NumericLiteral":
11
- return node.value;
12
- case "ObjectExpression":
13
- return extractObject(node);
14
- case "TSAsExpression":
15
- return extractValue(node.expression);
16
- case "Identifier":
17
- if (node.name === "undefined")
18
- return undefined;
19
- throw new Error(`Cannot evaluate identifier "${node.name}" at compile time`);
20
- default:
21
- throw new Error(`Cannot evaluate ${node.type} at compile time`);
22
- }
23
- }
24
- function extractObject(node) {
25
- const result = {};
26
- for (const prop of node.properties) {
27
- if (prop.type === "SpreadElement") {
28
- // Handle spread of object literals
29
- if (prop.argument.type === "ObjectExpression") {
30
- Object.assign(result, extractObject(prop.argument));
31
- }
32
- else {
33
- throw new Error("Spread elements must be object literals at compile time");
34
- }
35
- }
36
- else if (prop.type === "ObjectProperty") {
37
- let key;
38
- if (prop.key.type === "Identifier") {
39
- key = prop.key.name;
40
- }
41
- else if (prop.key.type === "StringLiteral") {
42
- key = prop.key.value;
43
- }
44
- else {
45
- throw new Error(`Unsupported key type: ${prop.key.type}`);
46
- }
47
- result[key] = extractValue(prop.value);
48
- }
49
- }
50
- return result;
51
- }
52
- /**
53
- * Creates an unplugin-ast transformer that finds css() calls
54
- * and replaces them with class name strings.
55
- *
56
- * Returns the collected CSS rules separately for aggregation.
57
- */
58
- export function createCatomTransformer(functionName, onRules) {
59
- return {
60
- onNode(node) {
61
- return (node.type === "CallExpression" &&
62
- node.callee.type === "Identifier" &&
63
- node.callee.name === functionName);
64
- },
65
- transform(node) {
66
- const arg = node.arguments[0];
67
- if (!arg || arg.type !== "ObjectExpression") {
68
- throw new Error(`${functionName}() requires an object literal argument`);
69
- }
70
- try {
71
- const styleObj = extractObject(arg);
72
- const { classNames, rules } = processStyleObject(styleObj);
73
- // Report rules to the plugin
74
- onRules(rules);
75
- // Return as a string - unplugin-ast will handle it
76
- return JSON.stringify(classNames.join(" "));
77
- }
78
- catch (err) {
79
- const message = err instanceof Error ? err.message : String(err);
80
- throw new Error(`Failed to process ${functionName}(): ${message}`);
81
- }
82
- },
83
- };
84
- }