@kithinji/arcane 1.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/build.js ADDED
@@ -0,0 +1,46 @@
1
+ import * as esbuild from "esbuild";
2
+ import { execSync } from "child_process";
3
+
4
+ async function build() {
5
+ await esbuild.build({
6
+ bundle: true,
7
+ sourcemap: true,
8
+ minify: false,
9
+ entryPoints: ["src/index.ts"],
10
+ platform: "node",
11
+ format: "esm",
12
+ outfile: "dist/node/index.mjs",
13
+ packages: "external",
14
+ conditions: ["node"],
15
+ });
16
+
17
+ await esbuild.build({
18
+ bundle: true,
19
+ sourcemap: true,
20
+ minify: false,
21
+ entryPoints: ["src/index.ts"],
22
+ platform: "node",
23
+ format: "cjs",
24
+ outfile: "dist/node/index.cjs",
25
+ packages: "external",
26
+ conditions: ["node"],
27
+ });
28
+
29
+ await esbuild.build({
30
+ bundle: true,
31
+ sourcemap: true,
32
+ minify: false,
33
+ entryPoints: ["src/index.ts"],
34
+ platform: "browser",
35
+ format: "esm",
36
+ outfile: "dist/browser/index.mjs",
37
+ external: [],
38
+ conditions: ["browser", "module", "default"],
39
+ });
40
+
41
+ // execSync("npx tsc --emitDeclarationOnly --declaration --outDir dist/types", {
42
+ // stdio: "inherit",
43
+ // });
44
+ }
45
+
46
+ build();
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@kithinji/arcane",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1",
7
+ "build": "node build.js"
8
+ },
9
+ "exports": {
10
+ ".": {
11
+ "browser": {
12
+ "import": "./dist/browser/index.mjs"
13
+ },
14
+ "node": {
15
+ "import": "./dist/node/index.js",
16
+ "require": "./dist/node/index.cjs"
17
+ },
18
+ "types": "./dist/types/index.d.ts"
19
+ }
20
+ },
21
+ "keywords": [],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "type": "module",
25
+ "devDependencies": {
26
+ "@types/node": "^25.0.3",
27
+ "esbuild": "^0.27.2",
28
+ "typescript": "^5.9.3"
29
+ },
30
+ "dependencies": {}
31
+ }
File without changes
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./style";
@@ -0,0 +1 @@
1
+ export * from "./style";
@@ -0,0 +1,523 @@
1
+ import { MacroContext } from "@kithinji/pod";
2
+ import ts from "typescript";
3
+ import { MapNamespaces } from "./style_types";
4
+
5
+ const UNITLESS_PROPS = new Set([
6
+ "z-index",
7
+ "opacity",
8
+ "flex-grow",
9
+ "flex-shrink",
10
+ "flex",
11
+ "order",
12
+ "font-weight",
13
+ "line-height",
14
+ "zoom",
15
+ "column-count",
16
+ "animation-iteration-count",
17
+ "grid-column",
18
+ "grid-row",
19
+ "grid-column-start",
20
+ "grid-column-end",
21
+ "grid-row-start",
22
+ "grid-row-end",
23
+ "tab-size",
24
+ "counter-increment",
25
+ "counter-reset",
26
+ "orphans",
27
+ "widows",
28
+ ]);
29
+
30
+ const DEV_MODE = process.env.NODE_ENV === "development";
31
+
32
+ function simpleHash(str: string): string {
33
+ let hash = 0;
34
+ for (let i = 0; i < str.length; i++) {
35
+ const char = str.charCodeAt(i);
36
+ hash = (hash << 5) - hash + char;
37
+ hash = hash & hash;
38
+ }
39
+
40
+ return Math.abs(hash).toString(36).padStart(8, "0").slice(0, 8);
41
+ }
42
+
43
+ function hashProperty(
44
+ prop: string,
45
+ value: string | number,
46
+ cssRules: Map<string, string>
47
+ ): string {
48
+ const input = `${prop}:${value}`;
49
+ let hash = simpleHash(input);
50
+ let className: string;
51
+
52
+ if (DEV_MODE) {
53
+ // Add readable hint in development
54
+ const hint = prop
55
+ .replace(/[^a-z]/gi, "")
56
+ .slice(0, 3)
57
+ .toLowerCase();
58
+ className = `a-${hint}-${hash}`;
59
+ } else {
60
+ className = `a-${hash}`;
61
+ }
62
+
63
+ let attempt = 0;
64
+
65
+ // Handle hash collisions
66
+ while (cssRules.has(className)) {
67
+ const existing = cssRules.get(className)!;
68
+ const kebabProp = toKebabCase(prop);
69
+ const normalizedValue = normalizeValue(prop, value);
70
+ const expectedRule = `.${className} { ${kebabProp}: ${normalizedValue}; }`;
71
+
72
+ // If the existing rule matches, it's the same style (deduplication)
73
+ if (existing.includes(`${kebabProp}: ${normalizedValue}`)) {
74
+ break;
75
+ }
76
+
77
+ // Collision detected, rehash
78
+ hash = simpleHash(`${input}-${++attempt}`);
79
+ className = DEV_MODE ? `a-${prop.slice(0, 3)}-${hash}` : `a-${hash}`;
80
+
81
+ if (attempt > 100) {
82
+ throw new Error(
83
+ `Hash collision limit exceeded for property ${prop}:${value}`
84
+ );
85
+ }
86
+ }
87
+
88
+ return className;
89
+ }
90
+
91
+ function toKebabCase(str: string): string {
92
+ // Handle vendor prefixes (webkit, moz, ms)
93
+ if (str.match(/^(webkit|moz|ms)[A-Z]/)) {
94
+ return "-" + str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
95
+ }
96
+
97
+ // Handle CSS custom properties
98
+ if (str.startsWith("--")) {
99
+ return str;
100
+ }
101
+
102
+ return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
103
+ }
104
+
105
+ function normalizeValue(prop: string, value: string | number): string {
106
+ if (typeof value === "number") {
107
+ const kebabProp = toKebabCase(prop);
108
+
109
+ if (UNITLESS_PROPS.has(kebabProp)) {
110
+ return String(value);
111
+ }
112
+
113
+ return `${value}px`;
114
+ }
115
+
116
+ return String(value);
117
+ }
118
+
119
+ function isPseudoOrMediaKey(key: string): boolean {
120
+ return key.startsWith(":") || key.startsWith("@") || key.startsWith("&");
121
+ }
122
+
123
+ function validateStyleValue(value: any, path: string): void {
124
+ if (value === null) {
125
+ throw new Error(
126
+ `Invalid style value at ${path}: null is not allowed. Use undefined or omit the property.`
127
+ );
128
+ }
129
+
130
+ if (typeof value === "function") {
131
+ throw new Error(
132
+ `Invalid style value at ${path}: functions must be resolved at build time.`
133
+ );
134
+ }
135
+ }
136
+
137
+ function processStyleValue(
138
+ prop: string,
139
+ value: any,
140
+ cssRules: Map<string, string>,
141
+ path: string = prop
142
+ ): string {
143
+ validateStyleValue(value, path);
144
+
145
+ if (prop.startsWith("--")) {
146
+ const className = hashProperty(prop, value, cssRules);
147
+ if (!cssRules.has(className)) {
148
+ const cssRule = `.${className} { ${prop}: ${value}; }`;
149
+ cssRules.set(className, cssRule);
150
+ }
151
+ return className;
152
+ }
153
+
154
+ const kebabProp = toKebabCase(prop);
155
+
156
+ // Handle nested objects for pseudo-classes, media queries, etc.
157
+ if (typeof value === "object" && !Array.isArray(value)) {
158
+ const classes: string[] = [];
159
+
160
+ for (const [nestedKey, nestedValue] of Object.entries(value)) {
161
+ validateStyleValue(nestedValue, `${path}.${nestedKey}`);
162
+
163
+ if (nestedKey === "default") {
164
+ const normalizedValue = normalizeValue(
165
+ prop,
166
+ nestedValue as string | number
167
+ );
168
+ const className = hashProperty(prop, normalizedValue, cssRules);
169
+
170
+ if (!cssRules.has(className)) {
171
+ const cssRule = `.${className} { ${kebabProp}: ${normalizedValue}; }`;
172
+ cssRules.set(className, cssRule);
173
+ }
174
+
175
+ classes.push(className);
176
+ } else if (isPseudoOrMediaKey(nestedKey)) {
177
+ const normalizedValue = normalizeValue(
178
+ prop,
179
+ nestedValue as string | number
180
+ );
181
+ const className = hashProperty(
182
+ `${prop}${nestedKey}`,
183
+ normalizedValue,
184
+ cssRules
185
+ );
186
+
187
+ if (!cssRules.has(className)) {
188
+ let cssRule: string;
189
+
190
+ if (nestedKey.startsWith("@")) {
191
+ // Media query
192
+ cssRule = `${nestedKey} { .${className} { ${kebabProp}: ${normalizedValue}; } }`;
193
+ } else if (nestedKey.startsWith("&")) {
194
+ // Nesting selector (e.g., &:hover, & > div)
195
+ const selector = nestedKey.slice(1);
196
+ cssRule = `.${className}${selector} { ${kebabProp}: ${normalizedValue}; }`;
197
+ } else {
198
+ // Pseudo-class/element (e.g., :hover, ::before)
199
+ cssRule = `.${className}${nestedKey} { ${kebabProp}: ${normalizedValue}; }`;
200
+ }
201
+
202
+ cssRules.set(className, cssRule);
203
+ }
204
+
205
+ classes.push(className);
206
+ } else {
207
+ throw new Error(
208
+ `Invalid nested key "${nestedKey}" at ${path}. Expected "default", a pseudo-class (":hover"), media query ("@media"), or nesting selector ("&").`
209
+ );
210
+ }
211
+ }
212
+
213
+ return classes.join(" ");
214
+ }
215
+
216
+ const normalizedValue = normalizeValue(prop, value);
217
+ const className = hashProperty(prop, normalizedValue, cssRules);
218
+
219
+ if (!cssRules.has(className)) {
220
+ const cssRule = `.${className} { ${kebabProp}: ${normalizedValue}; }`;
221
+ cssRules.set(className, cssRule);
222
+ }
223
+
224
+ return className;
225
+ }
226
+
227
+ function processStyleObject(
228
+ styleObj: Record<string, any>,
229
+ cssRules: Map<string, string>
230
+ ): Record<string, string> {
231
+ const result: Record<string, string> = {};
232
+
233
+ for (const [namespace, styles] of Object.entries(styleObj)) {
234
+ if (typeof styles !== "object" || Array.isArray(styles)) {
235
+ throw new Error(
236
+ `Invalid style namespace "${namespace}": expected an object, got ${typeof styles}`
237
+ );
238
+ }
239
+
240
+ const classes: string[] = [];
241
+
242
+ for (const [prop, value] of Object.entries(styles)) {
243
+ if (value === undefined) continue;
244
+
245
+ const className = processStyleValue(
246
+ prop,
247
+ value,
248
+ cssRules,
249
+ `${namespace}.${prop}`
250
+ );
251
+
252
+ if (className) {
253
+ classes.push(className);
254
+ }
255
+ }
256
+
257
+ result[namespace] = classes.filter(Boolean).join(" ");
258
+ }
259
+
260
+ return result;
261
+ }
262
+
263
+ export function style$<const T extends Record<string, any>>(
264
+ style: T,
265
+ context?: MacroContext
266
+ ): MapNamespaces<T> {
267
+ if (!context) {
268
+ throw new Error(
269
+ "style$ macro requires MacroContext. Ensure you're using this as a build-time macro."
270
+ );
271
+ }
272
+
273
+ const rStyle = style as unknown as ts.Node;
274
+ const value = context.resolveNodeValue(rStyle);
275
+
276
+ if (value == undefined) {
277
+ throw new Error(
278
+ `Could not resolve style object at build time. ` +
279
+ `Ensure all values are statically analyzable (no runtime expressions, dynamic imports should be inlined).`
280
+ );
281
+ }
282
+
283
+ if (typeof value !== "object" || Array.isArray(value)) {
284
+ throw new Error(
285
+ `style$ expects an object with style namespaces, got ${typeof value}`
286
+ );
287
+ }
288
+
289
+ const cssRules = new Map<string, string>();
290
+ const classNameMap = processStyleObject(value, cssRules);
291
+
292
+ context.store.set("style_rules", Array.from(cssRules.values()));
293
+
294
+ const properties = Object.entries(classNameMap).map(([key, className]) =>
295
+ context.factory.createPropertyAssignment(
296
+ context.factory.createStringLiteral(key),
297
+ context.factory.createStringLiteral(className)
298
+ )
299
+ );
300
+
301
+ return context.factory.createObjectLiteralExpression(properties, true) as any;
302
+ }
303
+
304
+ type Fragment =
305
+ | { kind: "static"; value: string }
306
+ | { kind: "dynamic"; expr: ts.Expression; stringSafe: boolean };
307
+
308
+ export function apply$(...c: any[]) {
309
+ if (c.length < 1) {
310
+ throw new Error("apply$ requires at least one argument plus MacroContext");
311
+ }
312
+
313
+ const context = c.pop() as MacroContext;
314
+
315
+ if (!context || !context.factory) {
316
+ throw new Error(
317
+ "apply$ macro requires MacroContext as the last argument. Ensure you're using this as a build-time macro."
318
+ );
319
+ }
320
+
321
+ const args = c as ts.Expression[];
322
+
323
+ if (args.length === 0) {
324
+ return context.factory.createObjectLiteralExpression(
325
+ [
326
+ context.factory.createPropertyAssignment(
327
+ context.factory.createIdentifier("className"),
328
+ context.factory.createStringLiteral("")
329
+ ),
330
+ ],
331
+ false
332
+ );
333
+ }
334
+
335
+ const f = context.factory;
336
+
337
+ function isTrue(e: ts.Expression): boolean {
338
+ return e.kind === ts.SyntaxKind.TrueKeyword;
339
+ }
340
+
341
+ function isFalse(e: ts.Expression): boolean {
342
+ return e.kind === ts.SyntaxKind.FalseKeyword;
343
+ }
344
+
345
+ function isEmptyString(e: ts.Expression): boolean {
346
+ return ts.isStringLiteral(e) && e.text === "";
347
+ }
348
+
349
+ function tryResolveStatic(expr: ts.Expression): string | null {
350
+ // Try to resolve string literals directly
351
+ if (ts.isStringLiteral(expr)) {
352
+ return expr.text;
353
+ }
354
+
355
+ // Try to resolve property access chains (e.g., classes.button)
356
+ if (!ts.isPropertyAccessExpression(expr)) return null;
357
+
358
+ const chain: string[] = [];
359
+ let cur: ts.Expression = expr;
360
+
361
+ while (ts.isPropertyAccessExpression(cur)) {
362
+ chain.unshift(cur.name.text);
363
+ cur = cur.expression;
364
+ }
365
+
366
+ if (!ts.isIdentifier(cur)) return null;
367
+ chain.unshift(cur.text);
368
+
369
+ const root = context.resolveIdentifier(f.createIdentifier(chain[0]));
370
+ let value = context.resolveNodeValue(root);
371
+
372
+ if (value == null) return null;
373
+
374
+ for (let i = 1; i < chain.length; i++) {
375
+ if (typeof value !== "object" || !(chain[i] in value)) {
376
+ return null;
377
+ }
378
+ value = value[chain[i]];
379
+ }
380
+
381
+ return typeof value === "string" ? value : null;
382
+ }
383
+
384
+ function build(expr: ts.Expression): Fragment {
385
+ // Handle empty strings
386
+ if (isEmptyString(expr)) {
387
+ return { kind: "static", value: "" };
388
+ }
389
+
390
+ // Try static resolution first
391
+ const s = tryResolveStatic(expr);
392
+ if (s != null) {
393
+ return { kind: "static", value: s };
394
+ }
395
+
396
+ // Handle: condition && "class"
397
+ if (
398
+ ts.isBinaryExpression(expr) &&
399
+ expr.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken
400
+ ) {
401
+ if (isTrue(expr.left)) return build(expr.right);
402
+ if (isFalse(expr.left)) return { kind: "static", value: "" };
403
+
404
+ const rs = tryResolveStatic(expr.right);
405
+ if (rs != null) {
406
+ // Optimize to: ["", "class"][+condition]
407
+ return {
408
+ kind: "dynamic",
409
+ stringSafe: true,
410
+ expr: f.createElementAccessExpression(
411
+ f.createArrayLiteralExpression([
412
+ f.createStringLiteral(""),
413
+ f.createStringLiteral(rs),
414
+ ]),
415
+ f.createPrefixUnaryExpression(ts.SyntaxKind.PlusToken, expr.left)
416
+ ),
417
+ };
418
+ }
419
+
420
+ return { kind: "dynamic", expr, stringSafe: false };
421
+ }
422
+
423
+ // Handle: condition ? "a" : "b"
424
+ if (ts.isConditionalExpression(expr)) {
425
+ if (isTrue(expr.condition)) return build(expr.whenTrue);
426
+ if (isFalse(expr.condition)) return build(expr.whenFalse);
427
+
428
+ const t = tryResolveStatic(expr.whenTrue);
429
+ const fv = tryResolveStatic(expr.whenFalse);
430
+
431
+ if (t != null && fv != null) {
432
+ // Optimize to: ["b", "a"][+condition]
433
+ return {
434
+ kind: "dynamic",
435
+ stringSafe: true,
436
+ expr: f.createElementAccessExpression(
437
+ f.createArrayLiteralExpression([
438
+ f.createStringLiteral(fv),
439
+ f.createStringLiteral(t),
440
+ ]),
441
+ f.createPrefixUnaryExpression(
442
+ ts.SyntaxKind.PlusToken,
443
+ expr.condition
444
+ )
445
+ ),
446
+ };
447
+ }
448
+
449
+ return { kind: "dynamic", expr, stringSafe: false };
450
+ }
451
+
452
+ return { kind: "dynamic", expr, stringSafe: false };
453
+ }
454
+
455
+ // Build and merge fragments
456
+ const frags: Fragment[] = [];
457
+
458
+ for (const arg of args) {
459
+ const frag = build(arg);
460
+
461
+ // Skip empty static values
462
+ if (frag.kind === "static" && frag.value === "") {
463
+ continue;
464
+ }
465
+
466
+ const last = frags[frags.length - 1];
467
+
468
+ // Merge consecutive static fragments
469
+ if (frag.kind === "static" && last?.kind === "static") {
470
+ last.value += " " + frag.value;
471
+ } else {
472
+ frags.push(frag);
473
+ }
474
+ }
475
+
476
+ // Generate final className expression
477
+ let classExpr: ts.Expression;
478
+
479
+ if (frags.length === 0) {
480
+ // All classes were empty
481
+ classExpr = f.createStringLiteral("");
482
+ } else if (frags.every((f) => f.kind === "static")) {
483
+ // All static - compile to single string
484
+ classExpr = f.createStringLiteral(
485
+ frags
486
+ .map((f) => f.value)
487
+ .join(" ")
488
+ .trim()
489
+ );
490
+ } else {
491
+ // Mixed static/dynamic - generate array.join(" ")
492
+ classExpr = f.createCallExpression(
493
+ f.createPropertyAccessExpression(
494
+ f.createArrayLiteralExpression(
495
+ frags.map((frag) => {
496
+ if (frag.kind === "static") {
497
+ return f.createStringLiteral(frag.value);
498
+ }
499
+ if (frag.stringSafe) {
500
+ return frag.expr;
501
+ }
502
+ // Wrap unsafe expressions in conditional: expr ? expr : ""
503
+ return f.createConditionalExpression(
504
+ frag.expr,
505
+ undefined,
506
+ frag.expr,
507
+ undefined,
508
+ f.createStringLiteral("")
509
+ );
510
+ })
511
+ ),
512
+ "join"
513
+ ),
514
+ undefined,
515
+ [f.createStringLiteral(" ")]
516
+ );
517
+ }
518
+
519
+ return f.createObjectLiteralExpression(
520
+ [f.createPropertyAssignment(f.createIdentifier("className"), classExpr)],
521
+ false
522
+ );
523
+ }
@@ -0,0 +1,102 @@
1
+ import { CSSProperties } from "./types";
2
+ import { CSSType } from "./var_types";
3
+
4
+ declare const StyleClassNameTag: unique symbol;
5
+ export type StyleClassNameFor<K, V> = string & {
6
+ _opaque: typeof StyleClassNameTag;
7
+ _key: K;
8
+ _value: V;
9
+ };
10
+
11
+ declare const StyleVarTag: unique symbol;
12
+ declare class _StyleVar<out Val> {
13
+ private _opaque: typeof StyleVarTag;
14
+ private _value: Val;
15
+ }
16
+ export type StyleVar<Val> = _StyleVar<Val> & string;
17
+
18
+ type PseudoClassStr = `:${string}`;
19
+ type AtRuleStr = `@${string}`;
20
+
21
+ type CondStr = PseudoClassStr | AtRuleStr;
22
+
23
+ type CSSPropertiesWithExtras = Partial<
24
+ Readonly<
25
+ CSSProperties & {
26
+ "::after": CSSProperties;
27
+ "::backdrop": CSSProperties;
28
+ "::before": CSSProperties;
29
+ "::cue": CSSProperties;
30
+ "::cue-region": CSSProperties;
31
+ "::first-letter": CSSProperties;
32
+ "::first-line": CSSProperties;
33
+ "::file-selector-button": CSSProperties;
34
+ "::grammar-error": CSSProperties;
35
+ "::marker": CSSProperties;
36
+ // This is a pattern and not a static key so it cannot be typed correctly.
37
+ // [key: `::part(${string})` | `::slotted(${string})`]: CSSProperties;
38
+ "::placeholder": CSSProperties;
39
+ "::selection": CSSProperties;
40
+ // This is a pattern and not a static key so it cannot be typed correctly.
41
+ // '::slotted()': CSSProperties;
42
+ "::spelling-error": CSSProperties;
43
+ "::target-text": CSSProperties;
44
+ "::-webkit-scrollbar"?: CSSProperties;
45
+ "::-webkit-scrollbar-button"?: CSSProperties;
46
+ "::-webkit-scrollbar-thumb"?: CSSProperties;
47
+ "::-webkit-scrollbar-track"?: CSSProperties;
48
+ "::-webkit-scrollbar-track-piece"?: CSSProperties;
49
+ "::-webkit-scrollbar-corner"?: CSSProperties;
50
+ "::-webkit-resizer"?: CSSProperties;
51
+ // webkit styles used for Search in Safari
52
+ "::-webkit-search-decoration"?: CSSProperties;
53
+ "::-webkit-search-cancel-button"?: CSSProperties;
54
+ "::-webkit-search-results-button"?: CSSProperties;
55
+ "::-webkit-search-results-decoration"?: CSSProperties;
56
+ // For input ranges in Chromium
57
+ "::-webkit-slider-thumb"?: CSSProperties;
58
+ "::-webkit-slider-runnable-track"?: CSSProperties;
59
+ // For input ranges in Firefox
60
+ "::-moz-range-thumb"?: CSSProperties;
61
+ "::-moz-range-track"?: CSSProperties;
62
+ "::-moz-range-progress"?: CSSProperties;
63
+ }
64
+ >
65
+ >;
66
+
67
+ type NotUndefined = {} | null;
68
+ type UserAuthoredStyles =
69
+ | CSSPropertiesWithExtras
70
+ | { [key: string]: NotUndefined };
71
+
72
+ type ComplexStyleValueType<T> = T extends StyleVar<infer U>
73
+ ? U extends CSSType<infer V>
74
+ ? V
75
+ : U
76
+ : T extends string | number | null | symbol
77
+ ? T
78
+ : T extends ReadonlyArray<infer U>
79
+ ? ComplexStyleValueType<U>
80
+ : T extends Readonly<{ default: infer A; [cond: CondStr]: infer B }>
81
+ ? ComplexStyleValueType<A> | ComplexStyleValueType<B>
82
+ : T;
83
+
84
+ declare const StyleInlineStylesTag: unique symbol;
85
+
86
+ export type InlineStyles = {
87
+ _opaque: typeof StyleInlineStylesTag;
88
+ };
89
+
90
+ export type MapNamespace<CSS> = Readonly<{
91
+ [Key in keyof CSS]: StyleClassNameFor<Key, ComplexStyleValueType<CSS[Key]>>;
92
+ }>;
93
+
94
+ export type MapNamespaces<
95
+ S extends {
96
+ [key: string]: UserAuthoredStyles | ((...args: any) => UserAuthoredStyles);
97
+ }
98
+ > = Readonly<{
99
+ [Key in keyof S]: S[Key] extends (...args: infer Args) => infer Obj
100
+ ? (...args: Args) => Readonly<[MapNamespace<Obj>, InlineStyles]>
101
+ : MapNamespace<S[Key]>;
102
+ }>;