zyzz 0.0.6 → 0.0.7

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 (48) hide show
  1. package/README.md +26 -1
  2. package/dist/Style.d.ts +3 -3
  3. package/dist/Style.d.ts.map +1 -1
  4. package/dist/Style.js +12 -1
  5. package/dist/Style.js.map +1 -1
  6. package/dist/internal/Authoring.d.ts.map +1 -1
  7. package/dist/internal/Authoring.js +14 -0
  8. package/dist/internal/Authoring.js.map +1 -1
  9. package/dist/internal/Binding.d.ts +7 -0
  10. package/dist/internal/Binding.d.ts.map +1 -1
  11. package/dist/internal/Binding.js.map +1 -1
  12. package/dist/internal/Configuration.d.ts +1 -1
  13. package/dist/internal/Configuration.d.ts.map +1 -1
  14. package/dist/internal/Configuration.js +7 -6
  15. package/dist/internal/Configuration.js.map +1 -1
  16. package/dist/internal/Theme.d.ts +1 -1
  17. package/dist/internal/Theme.d.ts.map +1 -1
  18. package/dist/internal/Theme.js.map +1 -1
  19. package/dist/internal/Token.d.ts +3 -1
  20. package/dist/internal/Token.d.ts.map +1 -1
  21. package/dist/internal/Token.js +28 -3
  22. package/dist/internal/Token.js.map +1 -1
  23. package/dist/internal/Value.d.ts +22 -0
  24. package/dist/internal/Value.d.ts.map +1 -1
  25. package/dist/internal/Value.js +76 -9
  26. package/dist/internal/Value.js.map +1 -1
  27. package/dist/next/internal/Project.d.ts +28 -0
  28. package/dist/next/internal/Project.d.ts.map +1 -0
  29. package/dist/next/internal/Project.js +249 -0
  30. package/dist/next/internal/Project.js.map +1 -0
  31. package/dist/next/loader.d.ts +2 -5
  32. package/dist/next/loader.d.ts.map +1 -1
  33. package/dist/next/loader.js +11 -150
  34. package/dist/next/loader.js.map +1 -1
  35. package/dist/variants.d.ts +1 -1
  36. package/dist/variants.d.ts.map +1 -1
  37. package/dist/variants.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/Style.ts +42 -8
  40. package/src/internal/Authoring.ts +21 -0
  41. package/src/internal/Binding.ts +30 -0
  42. package/src/internal/Configuration.ts +19 -13
  43. package/src/internal/Theme.ts +5 -1
  44. package/src/internal/Token.ts +34 -3
  45. package/src/internal/Value.ts +180 -10
  46. package/src/next/internal/Project.ts +337 -0
  47. package/src/next/loader.ts +19 -196
  48. package/src/variants.ts +1 -0
@@ -0,0 +1,337 @@
1
+ /** Retains filesystem snapshots and an incremental graph within a Next.js loader worker. @module */
2
+ import * as Contract from '../../compiler/internal/Contract.js'
3
+ import * as Fs from 'node:fs/promises'
4
+ import * as Graph from '../../compiler/Graph.js'
5
+ import type * as NativeFs from 'node:fs'
6
+ import * as Path from 'node:path'
7
+ import * as Reset from '../../node/Reset.js'
8
+ import * as Syntax from '../../compiler/internal/Syntax.js'
9
+
10
+ /** Resolution and dependency notifications supplied by the active loader invocation. */
11
+ export type Context = {
12
+ /** Track graph membership changes. */
13
+ addContextDependency(directory: string): void
14
+ /** Track source and contract edits. */
15
+ addDependency(file: string): void
16
+ /** Resolve with the active bundler's aliases and conditions. */
17
+ getResolve(
18
+ options: object,
19
+ ): (
20
+ directory: string,
21
+ specifier: string,
22
+ callback: (error: Error | null, file?: string | false) => void,
23
+ ) => void
24
+ /** Physical loader resource. */
25
+ resourcePath: string
26
+ }
27
+
28
+ /** Creates project state owned by a loader configuration in one worker process. */
29
+ export function create(options: create.Options) {
30
+ let compiler = Graph.create()
31
+ const directories = new Map<
32
+ string,
33
+ { items: NativeFs.Dirent[]; track: boolean; version: string }
34
+ >()
35
+ const files = new Map<string, { source: string; version: string }>()
36
+ const libraries = new Map<
37
+ string,
38
+ { dependencies: readonly (readonly string[])[]; source: string }
39
+ >()
40
+ const reset = options.reset ? Reset.read() : undefined
41
+ const syntax = new Map<
42
+ string,
43
+ { parsed: ReturnType<typeof Syntax.parse>; source: string }
44
+ >()
45
+ let pending = Promise.resolve()
46
+
47
+ return {
48
+ /** Compiles a fresh snapshot while retaining unchanged syntax and graph results. */
49
+ compile(context: Context, source?: string) {
50
+ // Loader calls may overlap, but only one immutable snapshot updates the compiler at a time.
51
+ const result = pending.then(async () => {
52
+ try {
53
+ return await compile(context, source)
54
+ } catch (error) {
55
+ // Failed discovery or extraction may leave a partial filesystem snapshot.
56
+ compiler = Graph.create()
57
+ directories.clear()
58
+ files.clear()
59
+ libraries.clear()
60
+ syntax.clear()
61
+ throw error
62
+ }
63
+ })
64
+
65
+ // A failed generation must not prevent recovery after the source is corrected.
66
+ pending = result.then(
67
+ () => {},
68
+ () => {},
69
+ )
70
+
71
+ return result
72
+ },
73
+ }
74
+
75
+ async function compile(context: Context, source?: string) {
76
+ const root = options.root
77
+ const programs = new Map<string, ReturnType<typeof Syntax.parse>>()
78
+ const seenDirectories = new Set<string>()
79
+ const seenFiles = new Set<string>()
80
+ async function read(file: string) {
81
+ seenFiles.add(file)
82
+ const version = await stamp(file)
83
+ let entry = files.get(file)
84
+ if (entry?.version !== version) {
85
+ entry = { source: await Fs.readFile(file, 'utf8'), version }
86
+ files.set(file, entry)
87
+ }
88
+ return entry.source
89
+ }
90
+
91
+ const modules: Record<string, string> = Object.create(null)
92
+ const imports: Record<
93
+ string,
94
+ Record<string, string | null>
95
+ > = Object.create(null)
96
+ const contracts: Record<string, string> = Object.create(null)
97
+ const resolve = context.getResolve({})
98
+ const id = (file: string) =>
99
+ `app/${Path.relative(root, file).split(Path.sep).join('/')}`
100
+ const eligible = (file: string) =>
101
+ /\.[cm]?[jt]sx?$/.test(file) &&
102
+ !/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(file) &&
103
+ !Path.relative(root, file)
104
+ .split(Path.sep)
105
+ .some(
106
+ (part) =>
107
+ part === '..' || part === 'node_modules' || part.startsWith('.'),
108
+ )
109
+
110
+ async function visit(file: string, text?: string): Promise<void> {
111
+ const name = id(file)
112
+ if (Object.hasOwn(modules, name)) return
113
+
114
+ context.addDependency(file)
115
+ seenFiles.add(file)
116
+ modules[name] = text ?? (await read(file))
117
+ const links: Record<string, string | null> = Object.create(null)
118
+ imports[name] = links
119
+ let entry = syntax.get(name)
120
+ if (entry?.source !== modules[name]) {
121
+ entry = {
122
+ source: modules[name]!,
123
+ parsed: Syntax.parse({ moduleId: name, source: modules[name]! }),
124
+ }
125
+ syntax.set(name, entry)
126
+ }
127
+ programs.set(name, entry.parsed)
128
+ const parsed = entry.parsed
129
+
130
+ for (const node of parsed.program.body) {
131
+ if (
132
+ (node.type !== 'ImportDeclaration' &&
133
+ node.type !== 'ExportNamedDeclaration' &&
134
+ node.type !== 'ExportAllDeclaration') ||
135
+ !node.source
136
+ )
137
+ continue
138
+ if (
139
+ node.type === 'ImportDeclaration'
140
+ ? node.importKind === 'type'
141
+ : node.exportKind === 'type'
142
+ )
143
+ continue
144
+
145
+ const specifier = node.source.value
146
+ links[specifier] = null
147
+ // Next owns this virtual module, which has no physical stylesheet contract.
148
+ if (specifier === 'next/root-params') continue
149
+ if (
150
+ specifier === 'zyzz' ||
151
+ (specifier.startsWith('zyzz/') && specifier !== 'zyzz/default') ||
152
+ specifier.startsWith('node:')
153
+ )
154
+ continue
155
+
156
+ const resolved = await new Promise<string | false | undefined>(
157
+ (accept, reject) => {
158
+ resolve(Path.dirname(file), specifier, (error, value) =>
159
+ error ? reject(error) : accept(value),
160
+ )
161
+ },
162
+ )
163
+ if (!resolved || !/\.[cm]?[jt]sx?$/.test(resolved)) continue
164
+ if (eligible(resolved)) {
165
+ links[specifier] = id(resolved)
166
+ await visit(resolved)
167
+ continue
168
+ }
169
+
170
+ const sidecar = `${resolved}.zyzz.json`
171
+ try {
172
+ contracts[resolved] = await read(sidecar)
173
+ context.addDependency(sidecar)
174
+ links[specifier] = resolved
175
+ } catch (error) {
176
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
177
+ }
178
+ }
179
+ }
180
+
181
+ async function discover(directory: string): Promise<void> {
182
+ const version = await stamp(directory)
183
+ let cached = directories.get(directory)
184
+ if (cached?.version !== version) {
185
+ const items = await Fs.readdir(directory, { withFileTypes: true })
186
+ cached = {
187
+ items,
188
+ track:
189
+ !items.some((item) => item.name === 'node_modules') ||
190
+ !(await loops(directory)),
191
+ version,
192
+ }
193
+ directories.set(directory, cached)
194
+ }
195
+ seenDirectories.add(directory)
196
+ const items = cached.items
197
+
198
+ // Linked package roots are tracked through children to avoid recursive dependency loops.
199
+ if (cached.track) context.addContextDependency(directory)
200
+
201
+ for (const item of items) {
202
+ if (
203
+ item.name.startsWith('.') ||
204
+ [
205
+ 'node_modules',
206
+ 'dist',
207
+ 'build',
208
+ 'coverage',
209
+ 'test',
210
+ 'tests',
211
+ '__tests__',
212
+ ].includes(item.name)
213
+ )
214
+ continue
215
+ const file = Path.join(directory, item.name)
216
+ if (item.isDirectory()) await discover(file)
217
+ else if (item.isFile() && eligible(file)) await visit(file)
218
+ }
219
+ }
220
+
221
+ if (source !== undefined) await visit(context.resourcePath, source)
222
+ await discover(root)
223
+ const loaded = new Set<string>()
224
+ async function dependencies(file: string): Promise<void> {
225
+ if (loaded.has(file)) return
226
+ loaded.add(file)
227
+
228
+ let library = libraries.get(file)
229
+ if (!library || library.source !== contracts[file]) {
230
+ library = {
231
+ source: contracts[file]!,
232
+ dependencies: Contract.read(
233
+ contracts[file]!,
234
+ new Map(),
235
+ file,
236
+ ).stylesheets.map((section) => section.dependency ?? []),
237
+ }
238
+ libraries.set(file, library)
239
+ }
240
+ for (const dependency of library.dependencies) {
241
+ let owner = file
242
+ for (const specifier of dependency) {
243
+ const target = await new Promise<string | false | undefined>(
244
+ (accept, reject) => {
245
+ resolve(Path.dirname(owner), specifier, (error, value) =>
246
+ error ? reject(error) : accept(value),
247
+ )
248
+ },
249
+ )
250
+ if (!target || !Path.isAbsolute(target))
251
+ throw new Error('Unable to resolve packed stylesheet dependency.')
252
+
253
+ const links = (imports[owner] ??= Object.create(null))
254
+ links[specifier] = target
255
+ if (!Object.hasOwn(contracts, target)) {
256
+ const sidecar = `${target}.zyzz.json`
257
+ contracts[target] = await read(sidecar)
258
+ context.addDependency(sidecar)
259
+ }
260
+ await dependencies(target)
261
+ owner = target
262
+ }
263
+ }
264
+ }
265
+ for (const file of Object.keys(contracts)) await dependencies(file)
266
+
267
+ const graph = compiler.compile({
268
+ [Syntax.cache]: programs,
269
+ contracts,
270
+ imports,
271
+ modules,
272
+ reset,
273
+ })
274
+
275
+ for (const file of files.keys())
276
+ if (!seenFiles.has(file)) files.delete(file)
277
+ for (const file of directories.keys())
278
+ if (!seenDirectories.has(file)) directories.delete(file)
279
+ for (const file of libraries.keys())
280
+ if (!Object.hasOwn(contracts, file)) libraries.delete(file)
281
+ for (const name of syntax.keys())
282
+ if (!programs.has(name)) syntax.delete(name)
283
+
284
+ return graph
285
+ }
286
+ }
287
+
288
+ /** Project cache settings. */
289
+ export declare namespace create {
290
+ /** Isolates source discovery and shared stylesheet options. */
291
+ type Options = {
292
+ /** Include the packaged reset in shared CSS. */
293
+ readonly reset?: boolean | undefined
294
+ /** Absolute application directory. */
295
+ readonly root: string
296
+ }
297
+ }
298
+
299
+ async function stamp(file: string) {
300
+ const stat = await Fs.stat(file, { bigint: true })
301
+ return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`
302
+ }
303
+
304
+ /** Whether an installed dependency links back to the directory or one of its ancestors. */
305
+ async function loops(directory: string): Promise<boolean> {
306
+ const installed = Path.join(directory, 'node_modules')
307
+ const entries = await Fs.readdir(installed, { withFileTypes: true }).catch(
308
+ () => [],
309
+ )
310
+ const candidates = (
311
+ await Promise.all(
312
+ entries.map(async (entry) => {
313
+ if (!entry.name.startsWith('@'))
314
+ return [Path.join(installed, entry.name)]
315
+
316
+ const scoped = Path.join(installed, entry.name)
317
+
318
+ return (await Fs.readdir(scoped).catch(() => [])).map((name) =>
319
+ Path.join(scoped, name),
320
+ )
321
+ }),
322
+ )
323
+ ).flat()
324
+
325
+ for (const candidate of candidates) {
326
+ const stat = await Fs.lstat(candidate).catch(() => undefined)
327
+ if (!stat?.isSymbolicLink()) continue
328
+
329
+ const target = await Fs.realpath(candidate).catch(() => undefined)
330
+ if (target === undefined) continue
331
+
332
+ const relative = Path.relative(target, directory)
333
+ if (!relative.startsWith('..') && !Path.isAbsolute(relative)) return true
334
+ }
335
+
336
+ return false
337
+ }
@@ -1,16 +1,11 @@
1
1
  /** Loads a physical source graph through Next.js resolution and emits cache-owned CSS. @module */
2
+ import * as AtRules from '../compiler/internal/AtRules.js'
2
3
  import * as Crypto from 'node:crypto'
3
4
  import * as Fs from 'node:fs/promises'
4
5
  import * as Path from 'node:path'
5
- import * as Parser from 'oxc-parser'
6
- import * as Graph from '../compiler/Graph.js'
7
- import * as Reset from '../node/Reset.js'
8
- import * as AtRules from '../compiler/internal/AtRules.js'
9
- import * as Contract from '../compiler/internal/Contract.js'
6
+ import * as Project from './internal/Project.js'
10
7
 
11
- type Context = {
12
- addContextDependency(directory: string): void
13
- addDependency(file: string): void
8
+ type Context = Project.Context & {
14
9
  async(): (error: Error | null, code?: string, map?: object) => void
15
10
  getOptions(): {
16
11
  bundler: string
@@ -18,16 +13,11 @@ type Context = {
18
13
  reset?: boolean | undefined
19
14
  root: string
20
15
  }
21
- getResolve(
22
- options: object,
23
- ): (
24
- directory: string,
25
- specifier: string,
26
- callback: (error: Error | null, file?: string | false) => void,
27
- ) => void
28
- resourcePath: string
29
16
  }
30
17
 
18
+ // Next owns loader worker lifetimes. Each project retains only its latest graph in that worker.
19
+ const projects = new Map<string, ReturnType<typeof Project.create>>()
20
+
31
21
  /** Loader-runner boundary used by Webpack and Turbopack. */
32
22
  export default function loader(this: Context, source: string): void {
33
23
  const callback = this.async()
@@ -42,14 +32,6 @@ async function compile(context: Context, source: string) {
42
32
  const options = context.getOptions()
43
33
  const root = options.root
44
34
  const directory = Path.resolve(root, '.zyzz', 'next')
45
- const modules: Record<string, string> = Object.create(null)
46
- const imports: Record<string, Record<string, string | null>> = Object.create(
47
- null,
48
- )
49
- const contracts: Record<string, string> = Object.create(null)
50
- const resolve = context.getResolve({})
51
- const id = (file: string) =>
52
- `app/${Path.relative(root, file).split(Path.sep).join('/')}`
53
35
  const eligible = (file: string) =>
54
36
  /\.[cm]?[jt]sx?$/.test(file) &&
55
37
  !/\.(?:d|test|test-d|bench|bench-d)\.[cm]?[jt]sx?$/.test(file) &&
@@ -71,144 +53,20 @@ async function compile(context: Context, source: string) {
71
53
  if (options.mode !== 'shared' && !eligible(context.resourcePath))
72
54
  return { code: source, map: undefined }
73
55
 
74
- async function visit(file: string, text?: string): Promise<void> {
75
- const name = id(file)
76
- if (Object.hasOwn(modules, name)) return
77
-
78
- context.addDependency(file)
79
- modules[name] = text ?? (await Fs.readFile(file, 'utf8'))
80
- const links: Record<string, string | null> = Object.create(null)
81
- imports[name] = links
82
- const parsed = Parser.parseSync(file, modules[name]!, {
83
- sourceType: 'module',
84
- })
85
-
86
- for (const node of parsed.program.body) {
87
- if (
88
- (node.type !== 'ImportDeclaration' &&
89
- node.type !== 'ExportNamedDeclaration' &&
90
- node.type !== 'ExportAllDeclaration') ||
91
- !node.source
92
- )
93
- continue
94
- if (
95
- node.type === 'ImportDeclaration'
96
- ? node.importKind === 'type'
97
- : node.exportKind === 'type'
98
- )
99
- continue
100
-
101
- const specifier = node.source.value
102
- links[specifier] = null
103
- // Next owns this virtual module, which has no physical stylesheet contract.
104
- if (specifier === 'next/root-params') continue
105
- if (
106
- specifier === 'zyzz' ||
107
- (specifier.startsWith('zyzz/') && specifier !== 'zyzz/default') ||
108
- specifier.startsWith('node:')
109
- )
110
- continue
111
-
112
- const resolved = await new Promise<string | false | undefined>(
113
- (accept, reject) => {
114
- resolve(Path.dirname(file), specifier, (error, value) =>
115
- error ? reject(error) : accept(value),
116
- )
117
- },
118
- )
119
- if (!resolved || !/\.[cm]?[jt]sx?$/.test(resolved)) continue
120
- if (eligible(resolved)) {
121
- links[specifier] = id(resolved)
122
- await visit(resolved)
123
- continue
124
- }
125
-
126
- const sidecar = `${resolved}.zyzz.json`
127
- try {
128
- contracts[resolved] = await Fs.readFile(sidecar, 'utf8')
129
- context.addDependency(sidecar)
130
- links[specifier] = resolved
131
- } catch (error) {
132
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
133
- }
134
- }
56
+ const key = JSON.stringify([root, options.bundler, options.reset ?? false])
57
+ let project = projects.get(key)
58
+ if (!project) {
59
+ project = Project.create(options)
60
+ projects.set(key, project)
135
61
  }
136
-
137
- async function discover(directory: string): Promise<void> {
138
- const items = await Fs.readdir(directory, { withFileTypes: true })
139
-
140
- // Recursive tracking of a package root follows every installed dependency.
141
- // A dependency symlink back to an ancestor, as a package linked from its
142
- // own repository, is rejected as a loop, so such a root is tracked through
143
- // its files and subdirectories instead.
144
- if (
145
- !items.some((item) => item.name === 'node_modules') ||
146
- !(await loops(directory))
147
- )
148
- context.addContextDependency(directory)
149
-
150
- for (const item of items) {
151
- if (
152
- item.name.startsWith('.') ||
153
- [
154
- 'node_modules',
155
- 'dist',
156
- 'build',
157
- 'coverage',
158
- 'test',
159
- 'tests',
160
- '__tests__',
161
- ].includes(item.name)
162
- )
163
- continue
164
- const file = Path.join(directory, item.name)
165
- if (item.isDirectory()) await discover(file)
166
- else if (item.isFile() && eligible(file)) await visit(file)
167
- }
168
- }
169
-
170
- if (options.mode !== 'shared') await visit(context.resourcePath, source)
171
- await discover(root)
172
- const loaded = new Set<string>()
173
- async function dependencies(file: string): Promise<void> {
174
- if (loaded.has(file)) return
175
- loaded.add(file)
176
-
177
- const metadata = Contract.read(contracts[file]!, new Map(), file)
178
- for (const section of metadata.stylesheets) {
179
- let owner = file
180
- for (const specifier of section.dependency ?? []) {
181
- const target = await new Promise<string | false | undefined>(
182
- (accept, reject) => {
183
- resolve(Path.dirname(owner), specifier, (error, value) =>
184
- error ? reject(error) : accept(value),
185
- )
186
- },
187
- )
188
- if (!target || !Path.isAbsolute(target))
189
- throw new Error('Unable to resolve packed stylesheet dependency.')
190
-
191
- const links = (imports[owner] ??= Object.create(null))
192
- links[specifier] = target
193
- if (!Object.hasOwn(contracts, target)) {
194
- const sidecar = `${target}.zyzz.json`
195
- contracts[target] = await Fs.readFile(sidecar, 'utf8')
196
- context.addDependency(sidecar)
197
- }
198
- await dependencies(target)
199
- owner = target
200
- }
201
- }
202
- }
203
- for (const file of Object.keys(contracts)) await dependencies(file)
204
-
205
- const graph = Graph.compile({
206
- contracts,
207
- imports,
208
- modules,
209
- reset: options.reset ? Reset.read() : undefined,
210
- })
211
- const output = graph.modules[id(context.resourcePath)]
62
+ const graph = await project.compile(
63
+ context,
64
+ options.mode === 'shared' ? undefined : source,
65
+ )
66
+ const output =
67
+ graph.modules[
68
+ `app/${Path.relative(root, context.resourcePath).split(Path.sep).join('/')}`
69
+ ]
212
70
 
213
71
  const assets = new Map<string, string>()
214
72
  for (const [placeholder, target] of Object.entries(
@@ -361,38 +219,3 @@ async function compile(context: Context, source: string) {
361
219
 
362
220
  return { code: `${output.code}\n${requests.join('\n')}`, map }
363
221
  }
364
-
365
- /** Whether an installed dependency links back to the directory or one of its ancestors. */
366
- async function loops(directory: string): Promise<boolean> {
367
- const installed = Path.join(directory, 'node_modules')
368
- const entries = await Fs.readdir(installed, { withFileTypes: true }).catch(
369
- () => [],
370
- )
371
- const candidates = (
372
- await Promise.all(
373
- entries.map(async (entry) => {
374
- if (!entry.name.startsWith('@'))
375
- return [Path.join(installed, entry.name)]
376
-
377
- const scoped = Path.join(installed, entry.name)
378
-
379
- return (await Fs.readdir(scoped).catch(() => [])).map((name) =>
380
- Path.join(scoped, name),
381
- )
382
- }),
383
- )
384
- ).flat()
385
-
386
- for (const candidate of candidates) {
387
- const stat = await Fs.lstat(candidate).catch(() => undefined)
388
- if (!stat?.isSymbolicLink()) continue
389
-
390
- const target = await Fs.realpath(candidate).catch(() => undefined)
391
- if (target === undefined) continue
392
-
393
- const relative = Path.relative(target, directory)
394
- if (!relative.startsWith('..') && !Path.isAbsolute(relative)) return true
395
- }
396
-
397
- return false
398
- }
package/src/variants.ts CHANGED
@@ -51,6 +51,7 @@ type CheckedChoice<
51
51
  values: values,
52
52
  ) => CheckedStyles<body, tokens, layers, mappings> &
53
53
  Binding.Checked<body> &
54
+ Binding.TokenSlots<body, values, tokens, mappings> &
54
55
  (string extends keyof values
55
56
  ? never
56
57
  : values extends Record<string, string | number> &