@yahoo/uds-create-utils 1.1.1

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.
Files changed (60) hide show
  1. package/dist/array-utils/closestItem.d.ts +4 -0
  2. package/dist/array-utils/closestItem.js +9 -0
  3. package/dist/array-utils/removeItem.d.ts +4 -0
  4. package/dist/array-utils/removeItem.js +8 -0
  5. package/dist/arrayUtils.d.ts +4 -0
  6. package/dist/arrayUtils.js +9 -0
  7. package/dist/component-style-defaults.d.ts +5 -0
  8. package/dist/component-style-defaults.js +68 -0
  9. package/dist/config-snippet.d.ts +15 -0
  10. package/dist/config-snippet.js +261 -0
  11. package/dist/index.d.ts +25 -0
  12. package/dist/index.js +25 -0
  13. package/dist/logger.d.ts +20 -0
  14. package/dist/logger.js +79 -0
  15. package/dist/math-utils/clamp.d.ts +12 -0
  16. package/dist/math-utils/clamp.js +14 -0
  17. package/dist/motion-utils/interpolate.d.ts +10 -0
  18. package/dist/motion-utils/interpolate.js +25 -0
  19. package/dist/motion-utils/resolveMotionState.d.ts +34 -0
  20. package/dist/motion-utils/resolveMotionState.js +33 -0
  21. package/dist/object-utils/entries.d.ts +4 -0
  22. package/dist/object-utils/entries.js +6 -0
  23. package/dist/object-utils/flattenObj.d.ts +12 -0
  24. package/dist/object-utils/flattenObj.js +21 -0
  25. package/dist/object-utils/fromEntries.d.ts +4 -0
  26. package/dist/object-utils/fromEntries.js +6 -0
  27. package/dist/object-utils/keys.d.ts +4 -0
  28. package/dist/object-utils/keys.js +6 -0
  29. package/dist/object-utils/mapKeys.d.ts +5 -0
  30. package/dist/object-utils/mapKeys.js +10 -0
  31. package/dist/object-utils/mapValues.d.ts +5 -0
  32. package/dist/object-utils/mapValues.js +9 -0
  33. package/dist/string-utils/arrayToUnion.d.ts +4 -0
  34. package/dist/string-utils/arrayToUnion.js +6 -0
  35. package/dist/string-utils/capitalize.d.ts +4 -0
  36. package/dist/string-utils/capitalize.js +6 -0
  37. package/dist/string-utils/componentClassName.d.ts +58 -0
  38. package/dist/string-utils/componentClassName.js +70 -0
  39. package/dist/string-utils/createTemplate.d.ts +16 -0
  40. package/dist/string-utils/createTemplate.js +142 -0
  41. package/dist/string-utils/cssVar.d.ts +55 -0
  42. package/dist/string-utils/cssVar.js +63 -0
  43. package/dist/string-utils/cssVars.d.ts +81 -0
  44. package/dist/string-utils/cssVars.js +108 -0
  45. package/dist/string-utils/dedent.d.ts +47 -0
  46. package/dist/string-utils/dedent.js +68 -0
  47. package/dist/string-utils/indent.d.ts +4 -0
  48. package/dist/string-utils/indent.js +6 -0
  49. package/dist/string-utils/join.d.ts +5 -0
  50. package/dist/string-utils/join.js +6 -0
  51. package/dist/string-utils/kebabCase.d.ts +13 -0
  52. package/dist/string-utils/kebabCase.js +14 -0
  53. package/dist/string-utils/objectToInterface.d.ts +1 -0
  54. package/dist/string-utils/objectToInterface.js +6 -0
  55. package/dist/string-utils/split.d.ts +10 -0
  56. package/dist/string-utils/split.js +12 -0
  57. package/dist/string-utils/tsProperties.d.ts +29 -0
  58. package/dist/string-utils/tsProperties.js +60 -0
  59. package/dist/tsconfig.tsbuildinfo +1 -0
  60. package/package.json +40 -0
@@ -0,0 +1,33 @@
1
+ //#region src/motion-utils/resolveMotionState.ts
2
+ function isInterpolateMarker(value) {
3
+ return typeof value === "object" && value !== null && value.__type === "interpolate";
4
+ }
5
+ /**
6
+ * Resolve a single `interpolate({ output, extrapolate })` marker against
7
+ * an integer-ish `index`. Output is a discrete lookup by index for
8
+ * `index < output.length`; beyond that, `extrapolate` chooses the
9
+ * extension rule (clamp / linear-extend / identity).
10
+ */
11
+ function resolveInterpolate(marker, index) {
12
+ const { output, extrapolate } = marker;
13
+ if (index < output.length) return output[index];
14
+ if (extrapolate === "clamp") return output[output.length - 1];
15
+ if (extrapolate === "identity") return index;
16
+ const last = output[output.length - 1];
17
+ return last + (last - (output[output.length - 2] ?? last)) * (index - (output.length - 1));
18
+ }
19
+ /**
20
+ * Resolve every InterpolateMarker in a state keyframe object against
21
+ * `index`, leaving non-marker values unchanged. Used by codegen-emitted
22
+ * `__rms(...)` call sites — see
23
+ * `packages/codegen/src/build/get-styles/motion-transform.ts` for the
24
+ * call-site generation.
25
+ */
26
+ function resolveMotionState(stateKeyframe, index) {
27
+ const resolved = {};
28
+ for (const [key, value] of Object.entries(stateKeyframe)) if (isInterpolateMarker(value)) resolved[key] = resolveInterpolate(value, index);
29
+ else resolved[key] = value;
30
+ return resolved;
31
+ }
32
+ //#endregion
33
+ export { resolveInterpolate, resolveMotionState };
@@ -0,0 +1,4 @@
1
+ //#region src/object-utils/entries.d.ts
2
+ declare function entries<T>(item: T): [Extract<keyof T, string> extends never ? string : Extract<keyof T, string>, T[keyof T]][];
3
+ //#endregion
4
+ export { entries };
@@ -0,0 +1,6 @@
1
+ //#region src/object-utils/entries.ts
2
+ function entries(item) {
3
+ return Object.entries(item);
4
+ }
5
+ //#endregion
6
+ export { entries };
@@ -0,0 +1,12 @@
1
+ //#region src/object-utils/flattenObj.d.ts
2
+ type Prev = [never, 0, 1, 2, 3, 4];
3
+ type FlattenDepth<T, D extends Prev[number] = Prev[number], Sep extends string = '-', P extends string = ''> = [D] extends [0] ? Record<P, T> : T extends Record<string, unknown> ? { [K in keyof T & string]: FlattenDepth<T[K], Prev[D], Sep, P extends '' ? K : `${P}${Sep}${K}`> }[keyof T & string] : Record<P, T>;
4
+ type Merge<U> = (U extends unknown ? (_x: U) => void : never) extends ((_x: infer I) => void) ? { [K in keyof I]: I[K] } : never;
5
+ type Flattened<T, D extends Prev[number] = Prev[number], Sep extends string = '-', P extends string = ''> = Merge<FlattenDepth<T, D, Sep, P>>;
6
+ declare function flattenObj<T, D extends Prev[number] = Prev[number], Sep extends string = '-', P extends string = ''>(obj: T, params?: {
7
+ prefix?: P;
8
+ separator?: Sep;
9
+ depth?: D;
10
+ }): Flattened<T, D, Sep, P>;
11
+ //#endregion
12
+ export { flattenObj };
@@ -0,0 +1,21 @@
1
+ //#region src/object-utils/flattenObj.ts
2
+ function flattenObj(obj, params = {}) {
3
+ const MAX_DEPTH = 5;
4
+ const { prefix = "", separator = "-", depth = MAX_DEPTH } = params;
5
+ const runDepth = depth;
6
+ const result = {};
7
+ function recurse(current, currentKey, remainingDepth) {
8
+ if (remainingDepth <= 0 || typeof current !== "object" || current === null || Array.isArray(current)) {
9
+ result[currentKey] = current;
10
+ return;
11
+ }
12
+ for (const key of Object.keys(current)) {
13
+ const nextKey = currentKey ? `${currentKey}${separator}${key}` : key;
14
+ recurse(current[key], nextKey, remainingDepth - 1);
15
+ }
16
+ }
17
+ recurse(obj, prefix, runDepth);
18
+ return result;
19
+ }
20
+ //#endregion
21
+ export { flattenObj };
@@ -0,0 +1,4 @@
1
+ //#region src/object-utils/fromEntries.d.ts
2
+ declare function fromEntries<const T extends ReadonlyArray<readonly [PropertyKey, unknown]>>(entries: T): { [K in T[number] as K[0]]: K[1] };
3
+ //#endregion
4
+ export { fromEntries };
@@ -0,0 +1,6 @@
1
+ //#region src/object-utils/fromEntries.ts
2
+ function fromEntries(entries) {
3
+ return Object.fromEntries(entries);
4
+ }
5
+ //#endregion
6
+ export { fromEntries };
@@ -0,0 +1,4 @@
1
+ //#region src/object-utils/keys.d.ts
2
+ declare function keys<T extends object>(obj: T): (keyof T)[];
3
+ //#endregion
4
+ export { keys };
@@ -0,0 +1,6 @@
1
+ //#region src/object-utils/keys.ts
2
+ function keys(obj) {
3
+ return Object.keys(obj);
4
+ }
5
+ //#endregion
6
+ export { keys };
@@ -0,0 +1,5 @@
1
+ //#region src/object-utils/mapKeys.d.ts
2
+ type AnyObject = Record<string, unknown>;
3
+ declare function mapKeys<T extends AnyObject, K extends (value: T[keyof T], key: keyof T, obj: T) => PropertyKey>(obj: T, callbackFn: K): { [key in ReturnType<K>]: T[keyof T] };
4
+ //#endregion
5
+ export { mapKeys };
@@ -0,0 +1,10 @@
1
+ //#region src/object-utils/mapKeys.ts
2
+ function mapKeys(obj, callbackFn) {
3
+ return Object.keys(obj).reduce((acc, key) => {
4
+ const newKey = callbackFn(obj[key], key, obj);
5
+ acc[newKey] = obj[key];
6
+ return acc;
7
+ }, {});
8
+ }
9
+ //#endregion
10
+ export { mapKeys };
@@ -0,0 +1,5 @@
1
+ //#region src/object-utils/mapValues.d.ts
2
+ type AnyObject = Record<string, unknown>;
3
+ declare function mapValues<T extends AnyObject, K extends (value: T[keyof T], key: Extract<keyof T, string>, i: number) => unknown>(obj: T, callbackFn: K): { [key in keyof T]: ReturnType<K> };
4
+ //#endregion
5
+ export { mapValues };
@@ -0,0 +1,9 @@
1
+ //#region src/object-utils/mapValues.ts
2
+ function mapValues(obj, callbackFn) {
3
+ return Object.keys(obj).reduce((acc, key, i) => {
4
+ acc[key] = callbackFn(obj[key], key, i);
5
+ return acc;
6
+ }, {});
7
+ }
8
+ //#endregion
9
+ export { mapValues };
@@ -0,0 +1,4 @@
1
+ //#region src/string-utils/arrayToUnion.d.ts
2
+ declare function arrayToUnion<T extends string[]>(array: T): `'${T[number]}'`;
3
+ //#endregion
4
+ export { arrayToUnion };
@@ -0,0 +1,6 @@
1
+ //#region src/string-utils/arrayToUnion.ts
2
+ function arrayToUnion(array) {
3
+ return array.map((item) => `'${item}'`).join(" | ");
4
+ }
5
+ //#endregion
6
+ export { arrayToUnion };
@@ -0,0 +1,4 @@
1
+ //#region src/string-utils/capitalize.d.ts
2
+ declare function capitalize(s: string): string;
3
+ //#endregion
4
+ export { capitalize };
@@ -0,0 +1,6 @@
1
+ //#region src/string-utils/capitalize.ts
2
+ function capitalize(s) {
3
+ return s.charAt(0).toUpperCase() + s.slice(1);
4
+ }
5
+ //#endregion
6
+ export { capitalize };
@@ -0,0 +1,58 @@
1
+ //#region src/string-utils/componentClassName.d.ts
2
+ /**
3
+ * The four class-name accessors for a single component, with the config
4
+ * prefix + component name bound. The single source of truth for "what does
5
+ * this component's CSS class look like" — the resolved `Component` exposes
6
+ * one (`component.derived.classNames`) and the codegen emitters read it
7
+ * instead of re-deriving names, so the emitted CSS selectors and the
8
+ * generated TSX `className` strings can never drift.
9
+ */
10
+ interface ComponentClassNames {
11
+ layer(layer: string): string;
12
+ variant(layer: string, axis: string, value: string): string;
13
+ defaultProp(layer: string, prop: string): string;
14
+ compound(layer: string, conditions: Record<string, string>): string;
15
+ }
16
+ /**
17
+ * Build the {@link ComponentClassNames} accessor set for a component, binding
18
+ * `prefix` + `name` so callers supply only the per-class arguments. The prefix
19
+ * is applied here (once), wrapping the unprefixed shape the helpers below
20
+ * define — so the helpers stay the single definition of the class-name *shape*
21
+ * and this factory is the single definition of *namespacing*.
22
+ */
23
+ declare function componentClassNames(prefix: string, name: string): ComponentClassNames;
24
+ /**
25
+ * Build the base class name for a component layer.
26
+ *
27
+ * @example componentLayerClass('Switch', 'root') // "switch-root"
28
+ */
29
+ declare function componentLayerClass(componentName: string, layer: string): string;
30
+ /**
31
+ * Build the variant class name for a component layer.
32
+ *
33
+ * @example componentVariantClass('Switch', 'root', 'size', 'lg') // "switch-root--size-lg"
34
+ */
35
+ declare function componentVariantClass(componentName: string, layer: string, axis: string, value: string): string;
36
+ /**
37
+ * Build the class name carrying a component's own `defaultProps` styling for a
38
+ * prop, on a layer. Emitted in `@layer base` (so it loses to an embedding
39
+ * component's anatomy in `@layer components`) and applied by the generated impl
40
+ * only when the prop is absent — so an embedded sub-component's default
41
+ * `variant`/`color` no longer overrides the parent's anatomy, while an explicit
42
+ * prop still wins via its atomic/variant class in a later layer.
43
+ *
44
+ * @example componentDefaultClass('Text', 'root', 'color') // "text-root-color-default"
45
+ */
46
+ declare function componentDefaultClass(componentName: string, layer: string, prop: string): string;
47
+ /**
48
+ * Build the compound-variant class name for a component layer.
49
+ *
50
+ * Conditions are sorted by key to guarantee a stable, deterministic name.
51
+ *
52
+ * @example
53
+ * componentCompoundClass('Switch', 'root', { size: 'lg', color: 'primary' })
54
+ * // "switch-root--color-primary_size-lg"
55
+ */
56
+ declare function componentCompoundClass(componentName: string, layer: string, conditions: Record<string, string>): string;
57
+ //#endregion
58
+ export { ComponentClassNames, componentClassNames, componentCompoundClass, componentDefaultClass, componentLayerClass, componentVariantClass };
@@ -0,0 +1,70 @@
1
+ import { kebabCase } from "./kebabCase.js";
2
+ //#region src/string-utils/componentClassName.ts
3
+ /**
4
+ * Prepend the configured prefix to a component class-name body, mirroring how
5
+ * CSS variables are namespaced (`--<prefix>-…`). An empty prefix yields the
6
+ * bare, unprefixed form — parallel to the CSS-variable path's empty-prefix
7
+ * handling — so `prefix: ''` opts every component class out of namespacing.
8
+ */
9
+ function withPrefix(prefix, body) {
10
+ return prefix ? `${prefix}-${body}` : body;
11
+ }
12
+ /**
13
+ * Build the {@link ComponentClassNames} accessor set for a component, binding
14
+ * `prefix` + `name` so callers supply only the per-class arguments. The prefix
15
+ * is applied here (once), wrapping the unprefixed shape the helpers below
16
+ * define — so the helpers stay the single definition of the class-name *shape*
17
+ * and this factory is the single definition of *namespacing*.
18
+ */
19
+ function componentClassNames(prefix, name) {
20
+ return {
21
+ layer: (layer) => withPrefix(prefix, componentLayerClass(name, layer)),
22
+ variant: (layer, axis, value) => withPrefix(prefix, componentVariantClass(name, layer, axis, value)),
23
+ defaultProp: (layer, prop) => withPrefix(prefix, componentDefaultClass(name, layer, prop)),
24
+ compound: (layer, conditions) => withPrefix(prefix, componentCompoundClass(name, layer, conditions))
25
+ };
26
+ }
27
+ /**
28
+ * Build the base class name for a component layer.
29
+ *
30
+ * @example componentLayerClass('Switch', 'root') // "switch-root"
31
+ */
32
+ function componentLayerClass(componentName, layer) {
33
+ return `${kebabCase(componentName)}-${layer}`;
34
+ }
35
+ /**
36
+ * Build the variant class name for a component layer.
37
+ *
38
+ * @example componentVariantClass('Switch', 'root', 'size', 'lg') // "switch-root--size-lg"
39
+ */
40
+ function componentVariantClass(componentName, layer, axis, value) {
41
+ return `${kebabCase(componentName)}-${layer}--${axis}-${value}`;
42
+ }
43
+ /**
44
+ * Build the class name carrying a component's own `defaultProps` styling for a
45
+ * prop, on a layer. Emitted in `@layer base` (so it loses to an embedding
46
+ * component's anatomy in `@layer components`) and applied by the generated impl
47
+ * only when the prop is absent — so an embedded sub-component's default
48
+ * `variant`/`color` no longer overrides the parent's anatomy, while an explicit
49
+ * prop still wins via its atomic/variant class in a later layer.
50
+ *
51
+ * @example componentDefaultClass('Text', 'root', 'color') // "text-root-color-default"
52
+ */
53
+ function componentDefaultClass(componentName, layer, prop) {
54
+ return `${kebabCase(componentName)}-${layer}-${prop}-default`;
55
+ }
56
+ /**
57
+ * Build the compound-variant class name for a component layer.
58
+ *
59
+ * Conditions are sorted by key to guarantee a stable, deterministic name.
60
+ *
61
+ * @example
62
+ * componentCompoundClass('Switch', 'root', { size: 'lg', color: 'primary' })
63
+ * // "switch-root--color-primary_size-lg"
64
+ */
65
+ function componentCompoundClass(componentName, layer, conditions) {
66
+ const suffix = Object.entries(conditions).sort(([a], [b]) => a.localeCompare(b)).map(([a, v]) => `${a}-${v}`).join("_");
67
+ return `${kebabCase(componentName)}-${layer}--${suffix}`;
68
+ }
69
+ //#endregion
70
+ export { componentClassNames, componentCompoundClass, componentDefaultClass, componentLayerClass, componentVariantClass };
@@ -0,0 +1,16 @@
1
+ //#region src/string-utils/createTemplate.d.ts
2
+ type CreateTemplateParams = {
3
+ sort?: boolean;
4
+ sortMapKeys?: boolean;
5
+ sortObjectKeys?: boolean;
6
+ sortSetValues?: boolean;
7
+ sortArrayValues?: boolean;
8
+ formatAsTypes?: boolean;
9
+ formatAsInterfaces?: boolean;
10
+ };
11
+ declare function createTemplate(params?: CreateTemplateParams): (strings: TemplateStringsArray, ...expr: unknown[]) => string;
12
+ declare const typeTemplate: (strings: TemplateStringsArray, ...expr: unknown[]) => string;
13
+ declare const interfaceTemplate: (strings: TemplateStringsArray, ...expr: unknown[]) => string;
14
+ declare const template: (strings: TemplateStringsArray, ...expr: unknown[]) => string;
15
+ //#endregion
16
+ export { createTemplate, interfaceTemplate, template, typeTemplate };
@@ -0,0 +1,142 @@
1
+ import { entries } from "../object-utils/entries.js";
2
+ import { arrayToUnion } from "./arrayToUnion.js";
3
+ import { objectToInterface } from "./objectToInterface.js";
4
+ //#region src/string-utils/createTemplate.ts
5
+ function isPrimitiveArray(items) {
6
+ return items.every((val) => typeof val === "string" || typeof val === "number" || typeof val === "boolean");
7
+ }
8
+ function sortByAlphabet(prev, next) {
9
+ const prevValue = Array.isArray(prev) ? prev[0] : prev;
10
+ const nextValue = Array.isArray(next) ? next[0] : next;
11
+ /** @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Collator/Collator */
12
+ return prevValue.localeCompare(nextValue, void 0, {
13
+ numeric: true,
14
+ sensitivity: "base"
15
+ });
16
+ }
17
+ function objectToString(obj, indent = 2) {
18
+ return `{\n${entries(obj).map(([key, value]) => {
19
+ const space = " ".repeat(indent);
20
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) return `${space}${key}: ${objectToString(value, indent + 2)}`;
21
+ if (Object.getOwnPropertyDescriptor(obj, key)?.get) return `${space}get ${key}() ${obj[key].toString()}`;
22
+ return `${space}${key}: ${JSON.stringify(value)}`;
23
+ }).join(",\n")}\n${" ".repeat(indent - 2)}}`;
24
+ }
25
+ function createTemplate(params) {
26
+ const { sort = false, sortMapKeys = sort, sortObjectKeys = sort, sortSetValues = sort, sortArrayValues = sort, formatAsTypes = false, formatAsInterfaces = false } = params ?? {
27
+ sort: false,
28
+ sortMapKeys: false,
29
+ sortObjectKeys: false,
30
+ sortSetValues: false,
31
+ sortArrayValues: false,
32
+ formatAsTypes: false,
33
+ formatAsInterfaces: false
34
+ };
35
+ function template(strings, ...expr) {
36
+ let str = "";
37
+ /**
38
+ * Tagged template function for formatting codegenerated typescript types.
39
+ * This util will automatically join array of items as a union.
40
+ *
41
+ * You will use similiar to how we use the `css` tagged template function
42
+ * from linaria.
43
+ *
44
+ * import { writePrettyFile } from './writePrettyFile';
45
+ * import { createTemplate } from 'uds/utils';
46
+ *
47
+ * const template = createTemplate({ formatAsTypes: true });
48
+ * const paletteNames = ['foreground', 'background'];
49
+ *
50
+ * const paletteNameContent = template`
51
+ * type PaletteName = ${paletteNames};
52
+ * `
53
+ *
54
+ * writePrettyFile('packages/common/types/PaletteName', paletteNameContent);
55
+ *
56
+ * OUTPUT
57
+ *
58
+ * type PaletteName = 'foreground' | 'background';
59
+ *
60
+ */
61
+ if (formatAsTypes) expr.forEach((item) => {
62
+ if (item && typeof item === "object") {
63
+ let stringResult;
64
+ if (item && Array.isArray(item)) stringResult = arrayToUnion(item);
65
+ if (item instanceof Set) stringResult = arrayToUnion(Array.from(item));
66
+ Object.defineProperty(item, "toString", { value() {
67
+ return stringResult;
68
+ } });
69
+ }
70
+ });
71
+ if (formatAsInterfaces) expr.forEach((item) => {
72
+ if (item && typeof item === "object") {
73
+ let stringResult;
74
+ if (item && typeof item === "object") stringResult = objectToInterface(item);
75
+ Object.defineProperty(item, "toString", { value() {
76
+ return stringResult;
77
+ } });
78
+ }
79
+ });
80
+ /**
81
+ * A template function to serialize objects, arrays, Sets, and Maps.
82
+ *
83
+ * Benefits:
84
+ * - Directly handle exports within the template.
85
+ * - Manage type consistency for better type safety.
86
+ * - Supports complex data structures like Map and Set.
87
+ * - Custom sorting for various collections.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * const template = createTemplate();
92
+ * const blueToken = {
93
+ * hue: 'blue',
94
+ * step: 50,
95
+ * get name() {
96
+ * return `${this.hue}-${this.step}`
97
+ * }
98
+ * }
99
+ * const output = template`
100
+ * export const blue = ${blueToken}
101
+ * `
102
+ *
103
+ * // output will contain:
104
+ * // "export const blue = { hue: 'blue', step: 50, name: 'hue-50' }"
105
+ * ```
106
+ */
107
+ expr.forEach((item) => {
108
+ let result;
109
+ if (item && typeof item === "object") {
110
+ if (!Array.isArray(item) && Object.values(item).some((v) => typeof v === "object" && Object.getOwnPropertyDescriptor(v, "get")?.get)) result = objectToString(item);
111
+ else if (Array.isArray(item) && isPrimitiveArray(item) && sortArrayValues) result = item.sort();
112
+ else if (item instanceof Map) {
113
+ const itemMap = item;
114
+ let itemEntries = Array.from(itemMap.entries());
115
+ if (sortMapKeys) itemEntries = itemEntries.sort(sortByAlphabet);
116
+ result = Object.fromEntries(itemEntries);
117
+ } else if (item instanceof Set) {
118
+ const itemSet = item;
119
+ let itemValues = Array.from(itemSet.values());
120
+ if (sortSetValues) itemValues = itemValues.sort(sortByAlphabet);
121
+ result = itemValues;
122
+ } else if (!(item instanceof Map) && !(item instanceof Set)) if (sortObjectKeys) {
123
+ const sortedByKeys = entries(item).sort(sortByAlphabet);
124
+ result = Object.fromEntries(sortedByKeys);
125
+ } else result = item;
126
+ if (result && typeof result !== "string" && !Object.getOwnPropertyDescriptor(item, "toString")) Object.defineProperty(item, "toString", { value() {
127
+ return JSON.stringify(result);
128
+ } });
129
+ }
130
+ });
131
+ strings.forEach((string, i) => {
132
+ str = `${str}${string}${expr[i] || ""}`;
133
+ });
134
+ return str;
135
+ }
136
+ return template;
137
+ }
138
+ const typeTemplate = createTemplate({ formatAsTypes: true });
139
+ const interfaceTemplate = createTemplate({ formatAsInterfaces: true });
140
+ const template = createTemplate();
141
+ //#endregion
142
+ export { createTemplate, interfaceTemplate, template, typeTemplate };
@@ -0,0 +1,55 @@
1
+ import { KebabCase } from "./kebabCase.js";
2
+
3
+ //#region src/string-utils/cssVar.d.ts
4
+ /**
5
+ * Sanitize a token name for use in CSS identifiers (variable names, class names).
6
+ * Dots become underscores, slashes become hyphens to avoid collisions (e.g. 1.5 vs 1/5).
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * safeTokenName('always/black') // "always-black"
11
+ * safeTokenName('font.size.lg') // "font_size_lg"
12
+ * safeTokenName('1.5') // "1_5"
13
+ * safeTokenName('1/5') // "1-5"
14
+ * ```
15
+ */
16
+ declare function safeTokenName(name: string): string;
17
+ type CssVarName<Property extends string, Alias extends string> = `--${KebabCase<Property>}-${KebabCase<Alias>}`;
18
+ type CssVar<Property extends string, Alias extends string> = `var(${CssVarName<Property, Alias>})`;
19
+ /**
20
+ * Generate a CSS variable name from property and alias
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * cssVarName('fontFamily', 'sans') // "--font-family-sans"
25
+ * cssVarName('textVariant', 'display1') // "--text-variant-display1"
26
+ * ```
27
+ */
28
+ declare function cssVarName<Property extends string, Alias extends string>(property: Property, alias: Alias): CssVarName<Property, Alias>;
29
+ /**
30
+ * Generate a CSS variable reference
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * cssVar('fontFamily', 'sans') // "var(--font-family-sans)"
35
+ * ```
36
+ */
37
+ declare function cssVar<Property extends string, Alias extends string>(property: Property, alias: Alias): CssVar<Property, Alias>;
38
+ /**
39
+ * Generate CSS variable declarations from an object
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * cssVarDeclarations({ color: 'red', fontSize: '16px' })
44
+ * // "--color: red;\n--font-size: 16px;"
45
+ * ```
46
+ *
47
+ * @example Keys already prefixed with -- are preserved
48
+ * ```ts
49
+ * cssVarDeclarations({ '--color-brand': 'blue', fontSize: '16px' })
50
+ * // "--color-brand: blue;\n--font-size: 16px;"
51
+ * ```
52
+ */
53
+ declare function cssVarDeclarations(vars: Record<string, string | number>): string;
54
+ //#endregion
55
+ export { CssVar, CssVarName, cssVar, cssVarDeclarations, cssVarName, safeTokenName };
@@ -0,0 +1,63 @@
1
+ import { entries } from "../object-utils/entries.js";
2
+ import { kebabCase } from "./kebabCase.js";
3
+ //#region src/string-utils/cssVar.ts
4
+ /**
5
+ * Sanitize a token name for use in CSS identifiers (variable names, class names).
6
+ * Dots become underscores, slashes become hyphens to avoid collisions (e.g. 1.5 vs 1/5).
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * safeTokenName('always/black') // "always-black"
11
+ * safeTokenName('font.size.lg') // "font_size_lg"
12
+ * safeTokenName('1.5') // "1_5"
13
+ * safeTokenName('1/5') // "1-5"
14
+ * ```
15
+ */
16
+ function safeTokenName(name) {
17
+ return name.replace(/\./g, "_").replace(/\//g, "-");
18
+ }
19
+ /**
20
+ * Generate a CSS variable name from property and alias
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * cssVarName('fontFamily', 'sans') // "--font-family-sans"
25
+ * cssVarName('textVariant', 'display1') // "--text-variant-display1"
26
+ * ```
27
+ */
28
+ function cssVarName(property, alias) {
29
+ return `--${kebabCase(property)}-${kebabCase(alias)}`;
30
+ }
31
+ /**
32
+ * Generate a CSS variable reference
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * cssVar('fontFamily', 'sans') // "var(--font-family-sans)"
37
+ * ```
38
+ */
39
+ function cssVar(property, alias) {
40
+ return `var(${cssVarName(property, alias)})`;
41
+ }
42
+ /**
43
+ * Generate CSS variable declarations from an object
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * cssVarDeclarations({ color: 'red', fontSize: '16px' })
48
+ * // "--color: red;\n--font-size: 16px;"
49
+ * ```
50
+ *
51
+ * @example Keys already prefixed with -- are preserved
52
+ * ```ts
53
+ * cssVarDeclarations({ '--color-brand': 'blue', fontSize: '16px' })
54
+ * // "--color-brand: blue;\n--font-size: 16px;"
55
+ * ```
56
+ */
57
+ function cssVarDeclarations(vars) {
58
+ return entries(vars).map(([key, value]) => {
59
+ return `${key.startsWith("--") ? key : `--${kebabCase(key)}`}: ${value};`;
60
+ }).join("\n");
61
+ }
62
+ //#endregion
63
+ export { cssVar, cssVarDeclarations, cssVarName, safeTokenName };
@@ -0,0 +1,81 @@
1
+ //#region src/string-utils/cssVars.d.ts
2
+ /**
3
+ * Tagged template function for CSS variable generation.
4
+ * Automatically converts objects to CSS variable declarations.
5
+ *
6
+ * Handles three types of inputs:
7
+ * 1. Flat objects → CSS variables
8
+ * 2. Nested objects → CSS blocks with selectors
9
+ * 3. Arrays → joined with double newlines
10
+ *
11
+ * @example Flat object (CSS variables)
12
+ * ```tsx
13
+ * const vars = { color: 'red', fontSize: '16px' };
14
+ *
15
+ * const output = cssVars`
16
+ * :root {
17
+ * ${vars}
18
+ * }
19
+ * `;
20
+ *
21
+ * // Output:
22
+ * // :root {
23
+ * // --color: red;
24
+ * // --font-size: 16px;
25
+ * // }
26
+ * ```
27
+ *
28
+ * @example Nested object (CSS blocks)
29
+ * ```tsx
30
+ * const modeBlocks = {
31
+ * dark: { colorPrimary: '#000', colorSecondary: '#333' },
32
+ * light: { colorPrimary: '#fff', colorSecondary: '#eee' }
33
+ * };
34
+ *
35
+ * const output = cssVars`${modeBlocks}`;
36
+ *
37
+ * // Output:
38
+ * // .dark {
39
+ * // --color-primary: #000;
40
+ * // --color-secondary: #333;
41
+ * // }
42
+ * //
43
+ * // .light {
44
+ * // --color-primary: #fff;
45
+ * // --color-secondary: #eee;
46
+ * // }
47
+ * ```
48
+ *
49
+ * @example Media queries (special @media handling)
50
+ * ```tsx
51
+ * const mediaQueries = {
52
+ * '@media (min-width: 768px)': { spacing: '2rem' },
53
+ * '@media (min-width: 1024px)': { spacing: '3rem' }
54
+ * };
55
+ *
56
+ * const output = cssVars`${mediaQueries}`;
57
+ *
58
+ * // Output:
59
+ * // @media (min-width: 768px) {
60
+ * // :root {
61
+ * // --spacing: 2rem;
62
+ * // }
63
+ * // }
64
+ * //
65
+ * // @media (min-width: 1024px) {
66
+ * // :root {
67
+ * // --spacing: 3rem;
68
+ * // }
69
+ * // }
70
+ * ```
71
+ *
72
+ * @example Array (joined blocks)
73
+ * ```tsx
74
+ * const blocks = ['.dark { --color: black; }', '.light { --color: white; }'];
75
+ * const output = cssVars`${blocks}`;
76
+ * // Output: joined blocks with double newlines
77
+ * ```
78
+ */
79
+ declare function cssVars(strings: TemplateStringsArray, ...expr: unknown[]): string;
80
+ //#endregion
81
+ export { cssVars };