@mochi-css/config 3.0.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.
package/dist/index.js ADDED
@@ -0,0 +1,540 @@
1
+ //#region rolldown:runtime
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ let path = require("path");
25
+ path = __toESM(path);
26
+ let fs = require("fs");
27
+ fs = __toESM(fs);
28
+ let jiti = require("jiti");
29
+ jiti = __toESM(jiti);
30
+ let clsx = require("clsx");
31
+ clsx = __toESM(clsx);
32
+ let __swc_core = require("@swc/core");
33
+ __swc_core = __toESM(__swc_core);
34
+
35
+ //#region src/merge.ts
36
+ function mergeArrays(a, b) {
37
+ if (a !== void 0 && b !== void 0) return a.concat(b);
38
+ return a ?? b;
39
+ }
40
+ function mergeCallbacks(a, b) {
41
+ if (a !== void 0 && b !== void 0) return (...args) => {
42
+ a(...args);
43
+ b(...args);
44
+ };
45
+ return a ?? b;
46
+ }
47
+
48
+ //#endregion
49
+ //#region src/plugin/TransformationPipeline.ts
50
+ var TransformationPipeline = class {
51
+ transformations = [];
52
+ registerTransformation(transformation) {
53
+ this.transformations.push(transformation);
54
+ }
55
+ async transform(value, ...args) {
56
+ let ret = value;
57
+ for (const transformation of this.transformations) ret = await transformation(ret, ...args);
58
+ return ret;
59
+ }
60
+ getTransformations() {
61
+ return [...this.transformations];
62
+ }
63
+ };
64
+ function makePipeline(transformations) {
65
+ const ret = new TransformationPipeline();
66
+ for (const transformation of transformations) ret.registerTransformation(transformation);
67
+ return ret;
68
+ }
69
+ var FilteredTransformationPipeline = class {
70
+ transformations = [];
71
+ constructor(filter) {
72
+ this.filter = filter;
73
+ }
74
+ registerTransformation(transformation, data) {
75
+ this.transformations.push({
76
+ fn: transformation,
77
+ ctx: data
78
+ });
79
+ }
80
+ async transform(value, ...args) {
81
+ let ret = value;
82
+ for (const transformation of this.transformations) {
83
+ if (!this.filter(transformation.ctx, ...args)) continue;
84
+ ret = await transformation.fn(ret, ...args);
85
+ }
86
+ return ret;
87
+ }
88
+ getTransformations() {
89
+ return [...this.transformations];
90
+ }
91
+ };
92
+
93
+ //#endregion
94
+ //#region src/plugin/globEx.ts
95
+ function escapeRegexLiteral(s) {
96
+ return s.replace(/[.+^${}()|[\]\\]/g, "\\$&");
97
+ }
98
+ function globToRegexBody(pattern) {
99
+ let result = "";
100
+ let i = 0;
101
+ while (i < pattern.length) {
102
+ const char = pattern.charAt(i);
103
+ if (char === "*" && pattern.charAt(i + 1) === "*") {
104
+ result += ".*";
105
+ i += 2;
106
+ if (pattern.charAt(i) === "/") i++;
107
+ } else if (char === "*") {
108
+ result += "[^/]*";
109
+ i++;
110
+ } else if (char === "?") {
111
+ result += "[^/]";
112
+ i++;
113
+ } else if (char === "{") {
114
+ const end = pattern.indexOf("}", i);
115
+ if (end !== -1) {
116
+ const alts = pattern.slice(i + 1, end).split(",").map(escapeRegexLiteral);
117
+ result += `(${alts.join("|")})`;
118
+ i = end + 1;
119
+ } else {
120
+ result += "\\{";
121
+ i++;
122
+ }
123
+ } else {
124
+ result += /[.+^$|()[\]\\]/.test(char) ? `\\${char}` : char;
125
+ i++;
126
+ }
127
+ }
128
+ return result;
129
+ }
130
+ /**
131
+ * Converts a Turbopack loader filter glob pattern to a RegExp.
132
+ * - No `/` in pattern → matches filename only (anchored to last path segment)
133
+ * - Contains `/` → matches against the full project-relative path
134
+ */
135
+ function globExToRegex(pattern) {
136
+ const isPathPattern = pattern.includes("/");
137
+ const body = globToRegexBody(pattern);
138
+ return isPathPattern ? new RegExp(body) : /* @__PURE__ */ new RegExp(`(?:^|/)${body}$`);
139
+ }
140
+
141
+ //#endregion
142
+ //#region src/plugin/Context.ts
143
+ const fileFilterRegexCache = /* @__PURE__ */ new Map();
144
+ function getRegexForFilter(filter) {
145
+ let regex = fileFilterRegexCache.get(filter);
146
+ if (regex) return regex;
147
+ regex = globExToRegex(filter);
148
+ fileFilterRegexCache.set(filter, regex);
149
+ return regex;
150
+ }
151
+ const fileFilter = ({ filter }, { filePath }) => {
152
+ return filter === void 0 || getRegexForFilter(filter).test(filePath);
153
+ };
154
+ function makeFilePipeline() {
155
+ return new FilteredTransformationPipeline(fileFilter);
156
+ }
157
+ var AnalysisHookCollector = class {
158
+ hooks = [];
159
+ register(hook) {
160
+ this.hooks.push(hook);
161
+ }
162
+ getHooks() {
163
+ return [...this.hooks];
164
+ }
165
+ };
166
+ var FullContext = class {
167
+ sourceTransform = makeFilePipeline();
168
+ analysisTransform = new AnalysisHookCollector();
169
+ getAnalysisHooks() {
170
+ return this.analysisTransform.getHooks();
171
+ }
172
+ };
173
+
174
+ //#endregion
175
+ //#region src/config/index.ts
176
+ function defineConfig(config) {
177
+ return config;
178
+ }
179
+ async function resolveConfig(fileConfig, inlineConfig, defaults) {
180
+ const plugins = mergeArrays(fileConfig.plugins, inlineConfig?.plugins) ?? defaults?.plugins ?? [];
181
+ const resolved = {
182
+ roots: mergeArrays(fileConfig.roots, inlineConfig?.roots) ?? defaults?.roots ?? [],
183
+ extractors: mergeArrays(fileConfig.extractors, inlineConfig?.extractors) ?? defaults?.extractors ?? [],
184
+ splitCss: inlineConfig?.splitCss ?? fileConfig.splitCss ?? defaults?.splitCss ?? false,
185
+ onDiagnostic: mergeCallbacks(fileConfig.onDiagnostic, inlineConfig?.onDiagnostic),
186
+ plugins,
187
+ tmpDir: inlineConfig?.tmpDir ?? fileConfig.tmpDir ?? defaults?.tmpDir,
188
+ debug: inlineConfig?.debug ?? fileConfig.debug ?? defaults?.debug
189
+ };
190
+ return await makePipeline(plugins.map((plugin) => plugin.onConfigResolved).filter((c) => c !== void 0)).transform(resolved);
191
+ }
192
+ const configName = "mochi.config";
193
+ const CONFIG_FILE_NAMES = [
194
+ "mts",
195
+ "cts",
196
+ "ts",
197
+ "mjs",
198
+ "cjs",
199
+ "js"
200
+ ].map((ext) => `${configName}.${ext}`);
201
+ async function loadConfig(cwd) {
202
+ const dir = cwd ?? process.cwd();
203
+ const configFile = CONFIG_FILE_NAMES.map((name) => path.default.resolve(dir, name)).find((p) => fs.default.existsSync(p));
204
+ if (!configFile) return {};
205
+ const mod = await (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href).import(configFile);
206
+ const config = mod != null && typeof mod === "object" && "default" in mod ? mod.default : mod;
207
+ if (config == null || typeof config !== "object") return {};
208
+ return config;
209
+ }
210
+
211
+ //#endregion
212
+ //#region ../vanilla/dist/index.mjs
213
+ /**
214
+ * Hashing utilities for generating short, deterministic class names.
215
+ * Uses djb2 algorithm for fast string hashing.
216
+ * @module hash
217
+ */
218
+ /** Characters used for base-62 encoding (css-name safe variant of base-64) */
219
+ const hashBase = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
220
+ const base = 64;
221
+ /**
222
+ * Converts a number to a base-62 string representation.
223
+ * @param num - The number to convert
224
+ * @param maxLength - Optional maximum length of the output string
225
+ * @returns Base-62 encoded string representation of the number
226
+ */
227
+ function numberToBase62(num, maxLength) {
228
+ let out = "";
229
+ while (num > 0 && out.length < (maxLength ?? Infinity)) {
230
+ out = hashBase[num % base] + out;
231
+ num = Math.floor(num / base);
232
+ }
233
+ return out.length > 0 ? out : "0";
234
+ }
235
+ /**
236
+ * Generates a short hash string from input using the djb2 algorithm.
237
+ * Used to create unique, deterministic CSS class names from style content.
238
+ * @param input - The string to hash
239
+ * @param length - Maximum length of the hash output (default: 8)
240
+ * @returns A short, css-safe hash string
241
+ * @example
242
+ * shortHash("color: red;") // Returns something like "A1b2C3d4"
243
+ */
244
+ function shortHash(input, length = 8) {
245
+ let h = 5381;
246
+ for (let i = 0; i < input.length; i++) h = h * 33 ^ input.charCodeAt(i);
247
+ h >>>= 0;
248
+ return numberToBase62(h, length);
249
+ }
250
+ const MOCHI_CSS_TYPEOF = Symbol.for("mochi-css.MochiCSS");
251
+ /**
252
+ * Runtime representation of a CSS style definition with variant support.
253
+ * Holds generated class names and provides methods to compute the final
254
+ * className string based on selected variants.
255
+ *
256
+ * @template V - The variant definitions type mapping variant names to their options
257
+ *
258
+ * @example
259
+ * const styles = MochiCSS.from(new CSSObject({
260
+ * color: 'blue',
261
+ * variants: { size: { small: { fontSize: 12 }, large: { fontSize: 18 } } }
262
+ * }))
263
+ * styles.variant({ size: 'large' }) // Returns combined class names
264
+ */
265
+ var MochiCSS = class MochiCSS$1 {
266
+ $$typeof = MOCHI_CSS_TYPEOF;
267
+ /**
268
+ * Creates a new MochiCSS instance.
269
+ * @param classNames - Base class names to always include
270
+ * @param variantClassNames - Mapping of variant names to option class names
271
+ * @param defaultVariants - Default variant selections when not specified
272
+ */
273
+ constructor(classNames, variantClassNames, defaultVariants) {
274
+ this.classNames = classNames;
275
+ this.variantClassNames = variantClassNames;
276
+ this.defaultVariants = defaultVariants;
277
+ }
278
+ /**
279
+ * Computes the final className string based on variant selections.
280
+ * Compound variants are handled purely via CSS combined selectors,
281
+ * so no runtime matching is needed here.
282
+ * @param props - Variant selections
283
+ * @returns Combined className string for use in components
284
+ */
285
+ variant(props) {
286
+ const keys = new Set([...Object.keys(props), ...Object.keys(this.defaultVariants)].filter((k) => k in this.variantClassNames));
287
+ return (0, clsx.default)(this.classNames, ...keys.values().map((k) => {
288
+ const variantGroup = this.variantClassNames[k];
289
+ if (!variantGroup) return false;
290
+ const variantKey = ((k in props ? props[k] : void 0) ?? this.defaultVariants[k])?.toString();
291
+ if (variantKey == null) return false;
292
+ const selectedClassname = variantGroup[variantKey];
293
+ if (selectedClassname !== void 0) return selectedClassname;
294
+ const defaultKey = this.defaultVariants[k];
295
+ if (defaultKey == null) return false;
296
+ return variantGroup[defaultKey.toString()];
297
+ }));
298
+ }
299
+ /**
300
+ * Returns the CSS selector for this style (e.g. `.abc123`).
301
+ * Useful for targeting this component from another style.
302
+ */
303
+ get selector() {
304
+ return this.classNames.map((n) => `.${n}`).join();
305
+ }
306
+ toString() {
307
+ return this.selector;
308
+ }
309
+ /**
310
+ * Creates a MochiCSS instance from a CSSObject.
311
+ * Extracts class names from the compiled CSS blocks.
312
+ * @template V - The variant definitions type
313
+ * @param object - The compiled CSSObject to extract from
314
+ * @returns A new MochiCSS instance with the extracted class names
315
+ */
316
+ static from(object) {
317
+ return new MochiCSS$1([object.mainBlock.className], Object.fromEntries(Object.entries(object.variantBlocks).map(([key, variantOptions]) => {
318
+ return [key, Object.fromEntries(Object.entries(variantOptions).map(([optionKey, block]) => {
319
+ return [optionKey, block.className];
320
+ }))];
321
+ })), object.variantDefaults);
322
+ }
323
+ };
324
+ /**
325
+ * Creates a CSS style definition.
326
+ * The primary API for defining styles in Mochi-CSS.
327
+ *
328
+ * @template V - Tuple of variant definition types
329
+ * @param props - One or more style objects or existing MochiCSS instances to merge
330
+ * @returns A MochiCSS instance with all styles and variants combined
331
+ *
332
+ * @example
333
+ * // Simple usage
334
+ * const button = css({ padding: 8, borderRadius: 4 })
335
+ *
336
+ * @example
337
+ * // With variants
338
+ * const button = css({
339
+ * padding: 8,
340
+ * variants: {
341
+ * size: {
342
+ * small: { padding: 4 },
343
+ * large: { padding: 16 }
344
+ * }
345
+ * },
346
+ * defaultVariants: { size: 'small' }
347
+ * })
348
+ * button.variant({ size: 'large' }) // Get class names for large size
349
+ *
350
+ * @example
351
+ * // Merging multiple styles
352
+ * const combined = css(baseStyles, additionalStyles)
353
+ */
354
+ const emptyMochiCSS = new MochiCSS([], {}, {});
355
+ /**
356
+ * Wraps a condition in parentheses if not already wrapped.
357
+ */
358
+ function wrapParens(condition) {
359
+ const trimmed = condition.trim();
360
+ if (trimmed.startsWith("(") && trimmed.endsWith(")")) return trimmed;
361
+ return `(${trimmed})`;
362
+ }
363
+ function mediaFn(condition) {
364
+ return `@media ${wrapParens(condition)}`;
365
+ }
366
+ mediaFn.and = function(...conditions) {
367
+ return `@media ${conditions.map(wrapParens).join(" and ")}`;
368
+ };
369
+ mediaFn.or = function(...conditions) {
370
+ return `@media ${conditions.map(wrapParens).join(", ")}`;
371
+ };
372
+ Object.defineProperties(mediaFn, {
373
+ dark: {
374
+ get: () => "@media (prefers-color-scheme: dark)",
375
+ enumerable: true
376
+ },
377
+ light: {
378
+ get: () => "@media (prefers-color-scheme: light)",
379
+ enumerable: true
380
+ },
381
+ motion: {
382
+ get: () => "@media (prefers-reduced-motion: no-preference)",
383
+ enumerable: true
384
+ },
385
+ print: {
386
+ get: () => "@media print",
387
+ enumerable: true
388
+ }
389
+ });
390
+ function containerFn(condition) {
391
+ return `@container ${wrapParens(condition)}`;
392
+ }
393
+ containerFn.named = function(name, condition) {
394
+ return `@container ${name} ${wrapParens(condition)}`;
395
+ };
396
+ function supportsFn(condition) {
397
+ return `@supports ${wrapParens(condition)}`;
398
+ }
399
+ supportsFn.not = function(condition) {
400
+ return `@supports not ${wrapParens(condition)}`;
401
+ };
402
+ supportsFn.and = function(...conditions) {
403
+ return `@supports ${conditions.map(wrapParens).join(" and ")}`;
404
+ };
405
+ supportsFn.or = function(...conditions) {
406
+ return `@supports ${conditions.map(wrapParens).join(" or ")}`;
407
+ };
408
+
409
+ //#endregion
410
+ //#region src/styledIdTransform.ts
411
+ const STABLE_ID_RE = /^s-[0-9A-Za-z_-]+$/;
412
+ function collectStyledCalls(ast) {
413
+ const results = [];
414
+ function visitExpr(expr, varName) {
415
+ if (expr.type === "CallExpression") {
416
+ const callee = expr.callee;
417
+ if (callee.type === "Identifier" && callee.value === "styled" || callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.value === "styled") results.push({
418
+ call: expr,
419
+ varName
420
+ });
421
+ for (const arg of expr.arguments) visitExpr(arg.expression, null);
422
+ }
423
+ }
424
+ for (const item of ast.body) if (item.type === "VariableDeclaration") for (const d of item.declarations) {
425
+ const name = d.id.type === "Identifier" ? d.id.value : null;
426
+ if (d.init) visitExpr(d.init, name);
427
+ }
428
+ else if (item.type === "ExportDeclaration" && item.declaration.type === "VariableDeclaration") for (const d of item.declaration.declarations) {
429
+ const name = d.id.type === "Identifier" ? d.id.value : null;
430
+ if (d.init) visitExpr(d.init, name);
431
+ }
432
+ else if (item.type === "ExpressionStatement") visitExpr(item.expression, null);
433
+ else if (item.type === "ExportDefaultExpression") visitExpr(item.expression, "default");
434
+ return results;
435
+ }
436
+ /**
437
+ * Transforms source code to inject stable `s-` class IDs into every `styled()` call.
438
+ * Idempotent: skips calls that already have an `s-` string as the last argument.
439
+ */
440
+ function transformStyledIds(source, filePath) {
441
+ if (!source.includes("styled")) return source;
442
+ let ast;
443
+ try {
444
+ ast = __swc_core.parseSync(source, {
445
+ syntax: "typescript",
446
+ tsx: filePath.endsWith(".tsx"),
447
+ target: "es2022"
448
+ });
449
+ } catch {
450
+ return source;
451
+ }
452
+ const calls = collectStyledCalls(ast);
453
+ if (calls.length === 0) return source;
454
+ const toInject = calls.filter((entry) => {
455
+ const args = entry.call.arguments;
456
+ if (args.length === 0) return true;
457
+ const last = args[args.length - 1];
458
+ return !(last?.expression.type === "StringLiteral" && STABLE_ID_RE.test(last.expression.value));
459
+ });
460
+ if (toInject.length === 0) return source;
461
+ const sortedAsc = [...toInject].sort((a, b) => a.call.span.start - b.call.span.start);
462
+ let sourceGlobalBase = 0;
463
+ let searchFrom = 0;
464
+ const callsWithOffset = [];
465
+ for (const entry of sortedAsc) {
466
+ const idx = source.indexOf("styled(", searchFrom);
467
+ if (idx < 0) continue;
468
+ if (callsWithOffset.length === 0) {
469
+ const callee = entry.call.callee;
470
+ sourceGlobalBase = (callee.type === "Identifier" ? callee.span.start : callee.property.span.start) - idx;
471
+ }
472
+ callsWithOffset.push({
473
+ entry,
474
+ offset: entry.call.span.end - sourceGlobalBase - 1
475
+ });
476
+ searchFrom = idx + 1;
477
+ }
478
+ callsWithOffset.sort((a, b) => b.offset - a.offset);
479
+ let result = source;
480
+ callsWithOffset.forEach(({ entry, offset }, i) => {
481
+ const sortIdx = callsWithOffset.length - 1 - i;
482
+ const varName = entry.varName ?? String(sortIdx);
483
+ const id = "s-" + shortHash(filePath + ":" + varName);
484
+ if (offset < 0 || offset >= result.length || result[offset] !== ")") return;
485
+ result = result.slice(0, offset) + `, '${id}'` + result.slice(offset);
486
+ });
487
+ return result;
488
+ }
489
+
490
+ //#endregion
491
+ //#region src/styledIdPlugin.ts
492
+ const DUMMY_SPAN = {
493
+ start: 0,
494
+ end: 0,
495
+ ctxt: 0
496
+ };
497
+ function hasStableId(call) {
498
+ const args = call.arguments;
499
+ if (args.length === 0) return false;
500
+ const last = args[args.length - 1];
501
+ return last?.expression.type === "StringLiteral" && STABLE_ID_RE.test(last.expression.value);
502
+ }
503
+ /**
504
+ * Returns a MochiPlugin that injects stable `s-` class IDs into every `styled()` call.
505
+ * - Registers a `sourceTransform` for runtime source injection (Vite/Next `transform` hook).
506
+ * - Registers an `analysisTransform` for CSS extraction via direct AST mutation.
507
+ */
508
+ function styledIdPlugin() {
509
+ return {
510
+ name: "mochi-styled-ids",
511
+ onLoad(context) {
512
+ context.sourceTransform.registerTransformation((source, { filePath }) => transformStyledIds(source, filePath), { filter: "*.{ts,tsx,js,jsx}" });
513
+ context.analysisTransform.register((index) => {
514
+ for (const [filePath, fileInfo] of index.files) collectStyledCalls(fileInfo.ast).filter(({ call }) => !hasStableId(call)).forEach(({ call, varName }, i) => {
515
+ const id = "s-" + shortHash(filePath + ":" + (varName ?? String(i)));
516
+ call.arguments.push({
517
+ spread: void 0,
518
+ expression: {
519
+ type: "StringLiteral",
520
+ span: DUMMY_SPAN,
521
+ value: id,
522
+ raw: `'${id}'`
523
+ }
524
+ });
525
+ });
526
+ });
527
+ }
528
+ };
529
+ }
530
+
531
+ //#endregion
532
+ exports.FullContext = FullContext;
533
+ exports.TransformationPipeline = TransformationPipeline;
534
+ exports.defineConfig = defineConfig;
535
+ exports.loadConfig = loadConfig;
536
+ exports.mergeArrays = mergeArrays;
537
+ exports.mergeCallbacks = mergeCallbacks;
538
+ exports.resolveConfig = resolveConfig;
539
+ exports.styledIdPlugin = styledIdPlugin;
540
+ //# sourceMappingURL=index.js.map