@crab-dev/css 0.1.17 → 0.1.18
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/README.md +21 -0
- package/index.cjs +45 -0
- package/index.d.ts +19 -0
- package/index.mjs +44 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,27 @@ const style = assignVars({
|
|
|
78
78
|
})
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
## Immutable design tokens
|
|
82
|
+
|
|
83
|
+
Use `defineTokens` for nested token structures that are interpolated from another module. Wake
|
|
84
|
+
recognizes only a direct top-level `const` initialized through this imported helper; the argument
|
|
85
|
+
must contain statically evaluable plain objects, arrays, and primitive values.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { css, defineTokens } from '@crab-dev/css'
|
|
89
|
+
|
|
90
|
+
export const tokens = defineTokens({
|
|
91
|
+
color: { accent: 'rebeccapurple' },
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
export const button = css`
|
|
95
|
+
color: ${tokens.color.accent};
|
|
96
|
+
`
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The return type is deeply readonly and the runtime value is deeply frozen. Ordinary mutable
|
|
100
|
+
objects do not acquire this cross-module compiler guarantee.
|
|
101
|
+
|
|
81
102
|
## Compile-time contract
|
|
82
103
|
|
|
83
104
|
`css`, `keyframes`, and `globalStyle` must be used as direct tagged templates
|
package/index.cjs
CHANGED
|
@@ -27,6 +27,50 @@ function globalStyle() {
|
|
|
27
27
|
return compilerOnly('globalStyle')
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function defineTokens(value) {
|
|
31
|
+
if (value === null || typeof value !== 'object') {
|
|
32
|
+
throw new TypeError('@crab-dev/css: defineTokens expects a plain object or array.')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pending = [value]
|
|
36
|
+
const seen = new WeakSet()
|
|
37
|
+
while (pending.length > 0) {
|
|
38
|
+
const current = pending.pop()
|
|
39
|
+
if (seen.has(current)) continue
|
|
40
|
+
seen.add(current)
|
|
41
|
+
|
|
42
|
+
const prototype = Object.getPrototypeOf(current)
|
|
43
|
+
const plainObject = prototype === null || Object.getPrototypeOf(prototype) === null
|
|
44
|
+
if (!Array.isArray(current) && !plainObject) {
|
|
45
|
+
throw new TypeError('@crab-dev/css: defineTokens accepts only plain objects and arrays.')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const descriptors = Object.getOwnPropertyDescriptors(current)
|
|
49
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
50
|
+
if (typeof key === 'symbol') {
|
|
51
|
+
throw new TypeError('@crab-dev/css: defineTokens does not accept symbol keys.')
|
|
52
|
+
}
|
|
53
|
+
const descriptor = descriptors[key]
|
|
54
|
+
if ('get' in descriptor || 'set' in descriptor) {
|
|
55
|
+
throw new TypeError('@crab-dev/css: defineTokens does not accept accessors.')
|
|
56
|
+
}
|
|
57
|
+
const item = descriptor.value
|
|
58
|
+
if (item === null) {
|
|
59
|
+
continue
|
|
60
|
+
} else if (typeof item === 'object') {
|
|
61
|
+
pending.push(item)
|
|
62
|
+
} else if (
|
|
63
|
+
!['string', 'number', 'boolean', 'undefined'].includes(typeof item) ||
|
|
64
|
+
(typeof item === 'number' && !Number.isFinite(item))
|
|
65
|
+
) {
|
|
66
|
+
throw new TypeError('@crab-dev/css: defineTokens contains an unsupported value.')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Object.freeze(current)
|
|
70
|
+
}
|
|
71
|
+
return value
|
|
72
|
+
}
|
|
73
|
+
|
|
30
74
|
function cx(...values) {
|
|
31
75
|
const classes = []
|
|
32
76
|
const pending = values.slice().reverse()
|
|
@@ -146,5 +190,6 @@ exports.assignVars = assignVars
|
|
|
146
190
|
exports.createVar = createVar
|
|
147
191
|
exports.css = css
|
|
148
192
|
exports.cx = cx
|
|
193
|
+
exports.defineTokens = defineTokens
|
|
149
194
|
exports.globalStyle = globalStyle
|
|
150
195
|
exports.keyframes = keyframes
|
package/index.d.ts
CHANGED
|
@@ -40,6 +40,22 @@ export type ClassValue =
|
|
|
40
40
|
|
|
41
41
|
export type CSSVariableStyles = Record<string, string | number>
|
|
42
42
|
|
|
43
|
+
export type TokenPrimitive = string | number | boolean | null | undefined
|
|
44
|
+
|
|
45
|
+
export type TokenValue =
|
|
46
|
+
| TokenPrimitive
|
|
47
|
+
| { readonly [key: string]: TokenValue }
|
|
48
|
+
| readonly TokenValue[]
|
|
49
|
+
|
|
50
|
+
export type DeepReadonlyToken<T> =
|
|
51
|
+
T extends TokenPrimitive
|
|
52
|
+
? T
|
|
53
|
+
: T extends readonly unknown[]
|
|
54
|
+
? { readonly [K in keyof T]: DeepReadonlyToken<T[K]> }
|
|
55
|
+
: T extends object
|
|
56
|
+
? { readonly [K in keyof T]: DeepReadonlyToken<T[K]> }
|
|
57
|
+
: never
|
|
58
|
+
|
|
43
59
|
/**
|
|
44
60
|
* Declares a scoped class. Wake replaces this template tag with a ClassName
|
|
45
61
|
* and extracts its CSS during compilation.
|
|
@@ -70,6 +86,9 @@ export declare function globalStyle(
|
|
|
70
86
|
...interpolations: readonly CSSInterpolation[]
|
|
71
87
|
): void
|
|
72
88
|
|
|
89
|
+
/** Marks a recursively pure token structure as deeply immutable for safe ESM propagation. */
|
|
90
|
+
export declare function defineTokens<const T extends TokenValue>(value: T): DeepReadonlyToken<T>
|
|
91
|
+
|
|
73
92
|
/** Creates a realm-unique `var(--custom-property)` reference. */
|
|
74
93
|
export declare function createVar(debugName?: string): CSSVar
|
|
75
94
|
|
package/index.mjs
CHANGED
|
@@ -25,6 +25,50 @@ export function globalStyle() {
|
|
|
25
25
|
return compilerOnly('globalStyle')
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export function defineTokens(value) {
|
|
29
|
+
if (value === null || typeof value !== 'object') {
|
|
30
|
+
throw new TypeError('@crab-dev/css: defineTokens expects a plain object or array.')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const pending = [value]
|
|
34
|
+
const seen = new WeakSet()
|
|
35
|
+
while (pending.length > 0) {
|
|
36
|
+
const current = pending.pop()
|
|
37
|
+
if (seen.has(current)) continue
|
|
38
|
+
seen.add(current)
|
|
39
|
+
|
|
40
|
+
const prototype = Object.getPrototypeOf(current)
|
|
41
|
+
const plainObject = prototype === null || Object.getPrototypeOf(prototype) === null
|
|
42
|
+
if (!Array.isArray(current) && !plainObject) {
|
|
43
|
+
throw new TypeError('@crab-dev/css: defineTokens accepts only plain objects and arrays.')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const descriptors = Object.getOwnPropertyDescriptors(current)
|
|
47
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
48
|
+
if (typeof key === 'symbol') {
|
|
49
|
+
throw new TypeError('@crab-dev/css: defineTokens does not accept symbol keys.')
|
|
50
|
+
}
|
|
51
|
+
const descriptor = descriptors[key]
|
|
52
|
+
if ('get' in descriptor || 'set' in descriptor) {
|
|
53
|
+
throw new TypeError('@crab-dev/css: defineTokens does not accept accessors.')
|
|
54
|
+
}
|
|
55
|
+
const item = descriptor.value
|
|
56
|
+
if (item === null) {
|
|
57
|
+
continue
|
|
58
|
+
} else if (typeof item === 'object') {
|
|
59
|
+
pending.push(item)
|
|
60
|
+
} else if (
|
|
61
|
+
!['string', 'number', 'boolean', 'undefined'].includes(typeof item) ||
|
|
62
|
+
(typeof item === 'number' && !Number.isFinite(item))
|
|
63
|
+
) {
|
|
64
|
+
throw new TypeError('@crab-dev/css: defineTokens contains an unsupported value.')
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
Object.freeze(current)
|
|
68
|
+
}
|
|
69
|
+
return value
|
|
70
|
+
}
|
|
71
|
+
|
|
28
72
|
export function cx(...values) {
|
|
29
73
|
const classes = []
|
|
30
74
|
const pending = values.slice().reverse()
|