@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.
- package/dist/array-utils/closestItem.d.ts +4 -0
- package/dist/array-utils/closestItem.js +9 -0
- package/dist/array-utils/removeItem.d.ts +4 -0
- package/dist/array-utils/removeItem.js +8 -0
- package/dist/arrayUtils.d.ts +4 -0
- package/dist/arrayUtils.js +9 -0
- package/dist/component-style-defaults.d.ts +5 -0
- package/dist/component-style-defaults.js +68 -0
- package/dist/config-snippet.d.ts +15 -0
- package/dist/config-snippet.js +261 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +25 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.js +79 -0
- package/dist/math-utils/clamp.d.ts +12 -0
- package/dist/math-utils/clamp.js +14 -0
- package/dist/motion-utils/interpolate.d.ts +10 -0
- package/dist/motion-utils/interpolate.js +25 -0
- package/dist/motion-utils/resolveMotionState.d.ts +34 -0
- package/dist/motion-utils/resolveMotionState.js +33 -0
- package/dist/object-utils/entries.d.ts +4 -0
- package/dist/object-utils/entries.js +6 -0
- package/dist/object-utils/flattenObj.d.ts +12 -0
- package/dist/object-utils/flattenObj.js +21 -0
- package/dist/object-utils/fromEntries.d.ts +4 -0
- package/dist/object-utils/fromEntries.js +6 -0
- package/dist/object-utils/keys.d.ts +4 -0
- package/dist/object-utils/keys.js +6 -0
- package/dist/object-utils/mapKeys.d.ts +5 -0
- package/dist/object-utils/mapKeys.js +10 -0
- package/dist/object-utils/mapValues.d.ts +5 -0
- package/dist/object-utils/mapValues.js +9 -0
- package/dist/string-utils/arrayToUnion.d.ts +4 -0
- package/dist/string-utils/arrayToUnion.js +6 -0
- package/dist/string-utils/capitalize.d.ts +4 -0
- package/dist/string-utils/capitalize.js +6 -0
- package/dist/string-utils/componentClassName.d.ts +58 -0
- package/dist/string-utils/componentClassName.js +70 -0
- package/dist/string-utils/createTemplate.d.ts +16 -0
- package/dist/string-utils/createTemplate.js +142 -0
- package/dist/string-utils/cssVar.d.ts +55 -0
- package/dist/string-utils/cssVar.js +63 -0
- package/dist/string-utils/cssVars.d.ts +81 -0
- package/dist/string-utils/cssVars.js +108 -0
- package/dist/string-utils/dedent.d.ts +47 -0
- package/dist/string-utils/dedent.js +68 -0
- package/dist/string-utils/indent.d.ts +4 -0
- package/dist/string-utils/indent.js +6 -0
- package/dist/string-utils/join.d.ts +5 -0
- package/dist/string-utils/join.js +6 -0
- package/dist/string-utils/kebabCase.d.ts +13 -0
- package/dist/string-utils/kebabCase.js +14 -0
- package/dist/string-utils/objectToInterface.d.ts +1 -0
- package/dist/string-utils/objectToInterface.js +6 -0
- package/dist/string-utils/split.d.ts +10 -0
- package/dist/string-utils/split.js +12 -0
- package/dist/string-utils/tsProperties.d.ts +29 -0
- package/dist/string-utils/tsProperties.js +60 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- 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,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,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,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 };
|