@charcuterie/ui 0.1.0 → 0.2.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.
Files changed (106) hide show
  1. package/README.md +9 -0
  2. package/dist/Accordion/Accordion.d.ts +82 -0
  3. package/dist/Accordion/Accordion.d.ts.map +1 -0
  4. package/dist/Accordion/Accordion.js +108 -0
  5. package/dist/Accordion/Accordion.js.map +1 -0
  6. package/dist/Accordion/AccordionSection.d.ts +28 -0
  7. package/dist/Accordion/AccordionSection.d.ts.map +1 -0
  8. package/dist/Accordion/AccordionSection.js +53 -0
  9. package/dist/Accordion/AccordionSection.js.map +1 -0
  10. package/dist/Field/Field.d.ts +69 -0
  11. package/dist/Field/Field.d.ts.map +1 -0
  12. package/dist/Field/Field.js +73 -0
  13. package/dist/Field/Field.js.map +1 -0
  14. package/dist/FileDropZone/FileDropZone.d.ts +63 -0
  15. package/dist/FileDropZone/FileDropZone.d.ts.map +1 -0
  16. package/dist/FileDropZone/FileDropZone.js +122 -0
  17. package/dist/FileDropZone/FileDropZone.js.map +1 -0
  18. package/dist/LogViewer/LogViewer.d.ts +88 -0
  19. package/dist/LogViewer/LogViewer.d.ts.map +1 -0
  20. package/dist/LogViewer/LogViewer.js +104 -0
  21. package/dist/LogViewer/LogViewer.js.map +1 -0
  22. package/dist/Menu/Menu.d.ts +81 -0
  23. package/dist/Menu/Menu.d.ts.map +1 -0
  24. package/dist/Menu/Menu.js +199 -0
  25. package/dist/Menu/Menu.js.map +1 -0
  26. package/dist/Menu/MenuAction.d.ts +24 -0
  27. package/dist/Menu/MenuAction.d.ts.map +1 -0
  28. package/dist/Menu/MenuAction.js +37 -0
  29. package/dist/Menu/MenuAction.js.map +1 -0
  30. package/dist/Select/Select.d.ts +87 -0
  31. package/dist/Select/Select.d.ts.map +1 -0
  32. package/dist/Select/Select.js +51 -0
  33. package/dist/Select/Select.js.map +1 -0
  34. package/dist/SortableTableHeader/SortableTableHeader.d.ts +61 -0
  35. package/dist/SortableTableHeader/SortableTableHeader.d.ts.map +1 -0
  36. package/dist/SortableTableHeader/SortableTableHeader.js +56 -0
  37. package/dist/SortableTableHeader/SortableTableHeader.js.map +1 -0
  38. package/dist/Toast/Toast.d.ts +54 -0
  39. package/dist/Toast/Toast.d.ts.map +1 -0
  40. package/dist/Toast/Toast.js +121 -0
  41. package/dist/Toast/Toast.js.map +1 -0
  42. package/dist/Toast/ToastRegion.d.ts +57 -0
  43. package/dist/Toast/ToastRegion.d.ts.map +1 -0
  44. package/dist/Toast/ToastRegion.js +41 -0
  45. package/dist/Toast/ToastRegion.js.map +1 -0
  46. package/dist/Toast/toastLifecycle.d.ts +34 -0
  47. package/dist/Toast/toastLifecycle.d.ts.map +1 -0
  48. package/dist/Toast/toastLifecycle.js +25 -0
  49. package/dist/Toast/toastLifecycle.js.map +1 -0
  50. package/dist/Tooltip/Tooltip.d.ts +68 -0
  51. package/dist/Tooltip/Tooltip.d.ts.map +1 -0
  52. package/dist/Tooltip/Tooltip.js +104 -0
  53. package/dist/Tooltip/Tooltip.js.map +1 -0
  54. package/dist/index.d.ts +22 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +11 -0
  57. package/dist/index.js.map +1 -1
  58. package/dist/mountStory.testHelpers.d.ts +0 -11
  59. package/dist/mountStory.testHelpers.d.ts.map +1 -1
  60. package/dist/mountStory.testHelpers.js +27 -0
  61. package/dist/mountStory.testHelpers.js.map +1 -1
  62. package/package.json +3 -3
  63. package/src/Accordion/Accordion.mdx +83 -0
  64. package/src/Accordion/Accordion.stories.tsx +146 -0
  65. package/src/Accordion/Accordion.test.tsx +163 -0
  66. package/src/Accordion/Accordion.tsx +182 -0
  67. package/src/Accordion/AccordionSection.tsx +163 -0
  68. package/src/Field/Field.mdx +74 -0
  69. package/src/Field/Field.stories.tsx +144 -0
  70. package/src/Field/Field.test.tsx +142 -0
  71. package/src/Field/Field.tsx +163 -0
  72. package/src/FileDropZone/FileDropZone.mdx +78 -0
  73. package/src/FileDropZone/FileDropZone.stories.tsx +136 -0
  74. package/src/FileDropZone/FileDropZone.test.tsx +230 -0
  75. package/src/FileDropZone/FileDropZone.tsx +234 -0
  76. package/src/LogViewer/LogViewer.mdx +88 -0
  77. package/src/LogViewer/LogViewer.stories.tsx +135 -0
  78. package/src/LogViewer/LogViewer.test.tsx +173 -0
  79. package/src/LogViewer/LogViewer.tsx +195 -0
  80. package/src/Menu/Menu.mdx +103 -0
  81. package/src/Menu/Menu.stories.tsx +161 -0
  82. package/src/Menu/Menu.test.tsx +202 -0
  83. package/src/Menu/Menu.tsx +300 -0
  84. package/src/Menu/MenuAction.tsx +95 -0
  85. package/src/Select/Select.mdx +74 -0
  86. package/src/Select/Select.stories.tsx +126 -0
  87. package/src/Select/Select.test.tsx +129 -0
  88. package/src/Select/Select.tsx +200 -0
  89. package/src/SortableTableHeader/SortableTableHeader.mdx +67 -0
  90. package/src/SortableTableHeader/SortableTableHeader.stories.tsx +207 -0
  91. package/src/SortableTableHeader/SortableTableHeader.test.tsx +180 -0
  92. package/src/SortableTableHeader/SortableTableHeader.tsx +131 -0
  93. package/src/Toast/Toast.mdx +84 -0
  94. package/src/Toast/Toast.stories.tsx +192 -0
  95. package/src/Toast/Toast.test.tsx +217 -0
  96. package/src/Toast/Toast.tsx +224 -0
  97. package/src/Toast/ToastRegion.tsx +98 -0
  98. package/src/Toast/toastLifecycle.ts +45 -0
  99. package/src/Tooltip/Tooltip.mdx +69 -0
  100. package/src/Tooltip/Tooltip.stories.tsx +104 -0
  101. package/src/Tooltip/Tooltip.test.tsx +138 -0
  102. package/src/Tooltip/Tooltip.tsx +169 -0
  103. package/src/index.ts +43 -0
  104. package/src/mountStory.testHelpers.ts +30 -0
  105. package/src/sourceRules.test.ts +221 -30
  106. package/CHANGELOG.md +0 -13
package/src/index.ts CHANGED
@@ -31,6 +31,12 @@
31
31
  * this package's README has always told it to do.
32
32
  */
33
33
 
34
+ export type {
35
+ AccordionHeadingLevel,
36
+ AccordionItem,
37
+ AccordionProps,
38
+ } from "./Accordion/Accordion.tsx"
39
+ export { Accordion } from "./Accordion/Accordion.tsx"
34
40
  export type {
35
41
  AlertProps,
36
42
  AlertSize,
@@ -60,6 +66,10 @@ export type {
60
66
  EmptyStateSize,
61
67
  } from "./EmptyState/EmptyState.tsx"
62
68
  export { EmptyState } from "./EmptyState/EmptyState.tsx"
69
+ export type { FieldProps } from "./Field/Field.tsx"
70
+ export { Field } from "./Field/Field.tsx"
71
+ export type { FileDropZoneProps } from "./FileDropZone/FileDropZone.tsx"
72
+ export { FileDropZone } from "./FileDropZone/FileDropZone.tsx"
63
73
  export type { IconButtonProps } from "./IconButton/IconButton.tsx"
64
74
  export { IconButton } from "./IconButton/IconButton.tsx"
65
75
  export type { IntentAppearance } from "./intentStyles.ts"
@@ -76,6 +86,11 @@ export type {
76
86
  LiveStatusIndicatorSize,
77
87
  } from "./LiveStatusIndicator/LiveStatusIndicator.tsx"
78
88
  export { LiveStatusIndicator } from "./LiveStatusIndicator/LiveStatusIndicator.tsx"
89
+ export type {
90
+ LogLine,
91
+ LogViewerProps,
92
+ } from "./LogViewer/LogViewer.tsx"
93
+ export { LogViewer } from "./LogViewer/LogViewer.tsx"
79
94
  export type {
80
95
  MediaTileProps,
81
96
  MediaTileRatio,
@@ -83,6 +98,11 @@ export type {
83
98
  export { MediaTile } from "./MediaTile/MediaTile.tsx"
84
99
  export type { MediaStatus } from "./MediaTile/mediaStatus.ts"
85
100
  export { mediaTransitions } from "./MediaTile/mediaStatus.ts"
101
+ export type {
102
+ MenuItem,
103
+ MenuProps,
104
+ } from "./Menu/Menu.tsx"
105
+ export { Menu } from "./Menu/Menu.tsx"
86
106
  export type {
87
107
  ModalProps,
88
108
  ModalSize,
@@ -106,11 +126,23 @@ export type {
106
126
  SegmentedItem,
107
127
  } from "./SegmentedControl/SegmentedControl.tsx"
108
128
  export { SegmentedControl } from "./SegmentedControl/SegmentedControl.tsx"
129
+ export type {
130
+ SelectItem,
131
+ SelectOption,
132
+ SelectOptionGroup,
133
+ SelectProps,
134
+ } from "./Select/Select.tsx"
135
+ export { Select } from "./Select/Select.tsx"
109
136
  export type {
110
137
  SkeletonProps,
111
138
  SkeletonShape,
112
139
  } from "./Skeleton/Skeleton.tsx"
113
140
  export { Skeleton } from "./Skeleton/Skeleton.tsx"
141
+ export type {
142
+ SortableTableHeaderProps,
143
+ SortDirection,
144
+ } from "./SortableTableHeader/SortableTableHeader.tsx"
145
+ export { SortableTableHeader } from "./SortableTableHeader/SortableTableHeader.tsx"
114
146
  export type { SpinnerProps } from "./Spinner/Spinner.tsx"
115
147
  export { Spinner } from "./Spinner/Spinner.tsx"
116
148
  export {
@@ -127,6 +159,17 @@ export type {
127
159
  TabsProps,
128
160
  } from "./Tabs/Tabs.tsx"
129
161
  export { Tabs } from "./Tabs/Tabs.tsx"
162
+ export type { ToastProps } from "./Toast/Toast.tsx"
163
+ export { Toast } from "./Toast/Toast.tsx"
164
+ export type {
165
+ ToastRecord,
166
+ ToastRegionProps,
167
+ } from "./Toast/ToastRegion.tsx"
168
+ export { ToastRegion } from "./Toast/ToastRegion.tsx"
169
+ export type { ToastStatus } from "./Toast/toastLifecycle.ts"
170
+ export { toastTransitions } from "./Toast/toastLifecycle.ts"
171
+ export type { TooltipProps } from "./Tooltip/Tooltip.tsx"
172
+ export { Tooltip } from "./Tooltip/Tooltip.tsx"
130
173
  export { toClassName } from "./toClassName.ts"
131
174
  export type { VisuallyHiddenProps } from "./VisuallyHidden/VisuallyHidden.tsx"
132
175
  export { VisuallyHidden } from "./VisuallyHidden/VisuallyHidden.tsx"
@@ -25,16 +25,46 @@ type MountableStory = {
25
25
  * drift from it. It is also what triggers the a11y addon's
26
26
  * `afterEach`, so every mount here is an axe run.
27
27
  */
28
+ /**
29
+ * Every canvas this helper has created, so the next mount can take
30
+ * the previous ones out of the document.
31
+ *
32
+ * They used to accumulate — one detached-but-attached `<div>` per
33
+ * test, for the whole file — and nothing noticed for three
34
+ * milestones, because every component up to M6 lays out **in
35
+ * flow**: a stale copy sits harmlessly below the live one and every
36
+ * query is scoped to its own canvas anyway.
37
+ *
38
+ * `ToastRegion` is the first `position: fixed` component in the
39
+ * library, and it broke that immediately. A previous test's toast
40
+ * stack is pinned to the same corner of the viewport as the live
41
+ * one, so `userEvent.click` — which clicks by **coordinates** —
42
+ * drove the wrong element while every query still returned the
43
+ * right one. The test failed with "the toast did not go away", one
44
+ * test after the toast it was actually clicking.
45
+ *
46
+ * The stale tree is removed rather than unmounted because portable
47
+ * stories expose no unmount. Detaching is enough: it takes the node
48
+ * out of the layout, which is the entire problem.
49
+ */
50
+ const mountedCanvases: HTMLElement[] = []
51
+
28
52
  export const mountStory = async (
29
53
  story: MountableStory,
30
54
  ): Promise<{
31
55
  canvas: Canvas
32
56
  canvasElement: HTMLElement
33
57
  }> => {
58
+ for (const previous of mountedCanvases.splice(0)) {
59
+ previous.remove()
60
+ }
61
+
34
62
  const canvasElement = document.createElement("div")
35
63
 
36
64
  document.body.append(canvasElement)
37
65
 
66
+ mountedCanvases.push(canvasElement)
67
+
38
68
  await story.run({ canvasElement })
39
69
 
40
70
  return { canvas: within(canvasElement), canvasElement }
@@ -1,14 +1,20 @@
1
1
  /**
2
- * The three rules about this package's source that no compiler and no
3
- * axe run can see.
2
+ * The rules about this package's source — and about the shape of the
3
+ * workspace around it — that no compiler and no axe run can see.
4
4
  *
5
- * All three come straight from the plan's verification list, and each
6
- * one is a thing the fleet does today that the library exists to
7
- * stop.
5
+ * They come straight from the plan's verification list, and each one
6
+ * is a thing the fleet does today that the library exists to stop.
7
+ *
8
+ * The last two reach past `@charcuterie/ui` into its siblings on
9
+ * purpose. `ui` sits at the top of the dependency graph, so it is the
10
+ * one package whose tests can see all three at once — which is why
11
+ * the plan's cross-package assertions live here rather than in a
12
+ * `packages/conformance` of their own
13
+ * ([decision](../../../docs/decisions/2026-07-31-conformance-is-not-a-package.md)).
8
14
  */
9
15
 
10
16
  import { readdir, readFile } from "node:fs/promises"
11
- import { join, relative, resolve } from "node:path"
17
+ import { dirname, join, relative, resolve } from "node:path"
12
18
 
13
19
  import { expect, test } from "vitest"
14
20
 
@@ -16,6 +22,24 @@ const sourceDirectory = resolve(import.meta.dirname)
16
22
 
17
23
  const packageDirectory = resolve(sourceDirectory, "..")
18
24
 
25
+ const packagesDirectory = resolve(packageDirectory, "..")
26
+
27
+ type PackageManifest = {
28
+ dependencies?: Record<string, string>
29
+ exports?: Record<string, string | { source?: string }>
30
+ peerDependencies?: Record<string, string>
31
+ }
32
+
33
+ const readManifest = async (
34
+ name: string,
35
+ ): Promise<PackageManifest> =>
36
+ JSON.parse(
37
+ await readFile(
38
+ join(packagesDirectory, name, "package.json"),
39
+ "utf8",
40
+ ),
41
+ ) as PackageManifest
42
+
19
43
  const getSourceFiles = async (
20
44
  directory: string,
21
45
  ): Promise<{ contents: string; file: string }[]> => {
@@ -250,12 +274,23 @@ test("the barrel is the only place components are re-exported", async () => {
250
274
  // than app-specific: `Alert` (rip-deck spells it four times, twice
251
275
  // with a byte-identical `TONE_CLASS`) and `SegmentedControl`.
252
276
  //
253
- // `TabTrigger` and `SegmentedOption` are deliberately not in this
254
- // count and not exported. Each is its own component's member —
255
- // its own file only because both registrations are effects and an
256
- // effect cannot run in a loop — and the `<Name>/<Name>.tsx`
257
- // pattern this regex matches is what keeps them out.
258
- expect(componentNames.length).toBe(16)
277
+ // M6 adds the nine P1 components: `Accordion`, `Field`,
278
+ // `FileDropZone`, `LogViewer`, `Menu`, `Select`,
279
+ // `SortableTableHeader`, `Toast`, and `Tooltip`.
280
+ //
281
+ // `TabTrigger`, `SegmentedOption`, `AccordionSection`, and
282
+ // `MenuAction` are deliberately not in this count and not
283
+ // exported. Each is its own component's member — its own file only
284
+ // because both registrations are effects and an effect cannot run
285
+ // in a loop — and the `<Name>/<Name>.tsx` pattern this regex
286
+ // matches is what keeps them out.
287
+ //
288
+ // `ToastRegion` is the one exception worth naming: it *is*
289
+ // exported, and it lives in `Toast/` rather than owning a
290
+ // directory because a region with no toasts in it is not a thing
291
+ // anybody renders. It is therefore outside this count by the same
292
+ // rule, and the barrel assertion below does not reach it.
293
+ expect(componentNames.length).toBe(25)
259
294
 
260
295
  for (const name of componentNames) {
261
296
  expect(barrel).toContain(`export { ${name} }`)
@@ -267,37 +302,193 @@ test("dependency direction is tokens ← logic ← ui", async () => {
267
302
  // depend on both; `logic` may depend on `tokens`; `tokens` depends
268
303
  // on nothing, which is what lets `castkit/packages/views` read a
269
304
  // colour without a React tree.
270
- const readPackage = async (name: string) =>
271
- JSON.parse(
272
- await readFile(
273
- join(packageDirectory, "..", name, "package.json"),
274
- "utf8",
275
- ),
276
- ) as {
277
- dependencies?: Record<string, string>
278
- peerDependencies?: Record<string, string>
279
- }
280
-
281
- const getCharcuterieDependencies = (manifest: {
282
- dependencies?: Record<string, string>
283
- peerDependencies?: Record<string, string>
284
- }) =>
305
+ const getCharcuterieDependencies = (
306
+ manifest: PackageManifest,
307
+ ) =>
285
308
  Object.keys({
286
309
  ...manifest.dependencies,
287
310
  ...manifest.peerDependencies,
288
311
  }).filter((name) => name.startsWith("@charcuterie/"))
289
312
 
290
313
  expect(
291
- getCharcuterieDependencies(await readPackage("tokens")),
314
+ getCharcuterieDependencies(
315
+ await readManifest("tokens"),
316
+ ),
292
317
  ).toEqual([])
293
318
 
294
319
  expect(
295
- getCharcuterieDependencies(await readPackage("logic")),
320
+ getCharcuterieDependencies(await readManifest("logic")),
296
321
  ).toEqual([])
297
322
 
298
323
  expect(
299
324
  getCharcuterieDependencies(
300
- await readPackage("ui"),
325
+ await readManifest("ui"),
301
326
  ).sort(),
302
327
  ).toEqual(["@charcuterie/logic", "@charcuterie/tokens"])
303
328
  })
329
+
330
+ /**
331
+ * What every published entry point is allowed to reach at runtime.
332
+ *
333
+ * This is what survives of the plan's `@charcuterie/conformance`
334
+ * package — "builds React19+TWv4 / Preact / Satori profiles" — once
335
+ * the three profiles are checked against what the library actually
336
+ * became. Two of them are already built and gated by name (`docs` is
337
+ * the React 19 + Tailwind v4 build; `logic`'s five-adapter suite is
338
+ * the Preact one), and the third has no components to render because
339
+ * the ePaper-safe subset was never built. What no gate covered is the
340
+ * claim each of those profiles actually rests on: **which runtime a
341
+ * consumer is forced to install by importing a given entry point.**
342
+ * That is the assertion below.
343
+ *
344
+ * Each of these numbers is load-bearing somewhere:
345
+ *
346
+ * - `tokens` reaches **nothing**. `castkit/packages/views` renders
347
+ * through Satori and must read a colour without a React tree.
348
+ * - `logic/core` reaches **nothing**, and `logic/preact` never
349
+ * reaches `react` — `preact/compat` is most of `slatecast`'s 60 KB
350
+ * gz budget, and an accidental `react` import there is a bundle
351
+ * blow-up that typechecks.
352
+ * - `ui/testing` reaches **nothing**, which is the promise
353
+ * `expectAgentDrivable.ts` makes in prose: it ships for consumers
354
+ * to hold *their* components to, and a published package must not
355
+ * drag a test framework into an app's dependency graph.
356
+ *
357
+ * A type-only import counts. It is erased from the bundle but not
358
+ * from the `.d.ts`, so a consumer still needs the package installed
359
+ * for their own typecheck to pass.
360
+ */
361
+ const ENTRY_POINT_RUNTIMES: Record<
362
+ string,
363
+ Record<string, string[]>
364
+ > = {
365
+ logic: {
366
+ ".": ["react"],
367
+ "./core": [],
368
+ "./jotai": ["jotai"],
369
+ "./preact": ["preact", "preact/hooks"],
370
+ "./signals": ["@preact/signals-core"],
371
+ },
372
+ tokens: {
373
+ ".": [],
374
+ "./epaper": [],
375
+ },
376
+ ui: {
377
+ ".": [
378
+ "@charcuterie/logic",
379
+ "@charcuterie/tokens",
380
+ "@floating-ui/react",
381
+ "react",
382
+ ],
383
+ "./testing": [],
384
+ "./tokens": ["@charcuterie/tokens"],
385
+ },
386
+ }
387
+
388
+ /**
389
+ * Every bare specifier reachable from `entryFile`, following relative
390
+ * imports and stopping at package boundaries.
391
+ *
392
+ * Every relative import in this workspace carries its extension, so
393
+ * "resolution" is a `join` — no `.js`/`index` guessing, and a typo
394
+ * fails loudly as a missing file rather than silently pruning the
395
+ * graph.
396
+ */
397
+ const collectRuntimeImports = async (entryFile: string) => {
398
+ const visited = new Set<string>()
399
+
400
+ const bareSpecifiers = new Set<string>()
401
+
402
+ const visit = async (file: string) => {
403
+ if (visited.has(file)) {
404
+ return
405
+ }
406
+
407
+ visited.add(file)
408
+
409
+ // Template literals go too. `createStatus`'s error message
410
+ // spells `from "${current}"`, which is prose about a state
411
+ // machine and not an import of a package called `${current}`.
412
+ const contents = stripComments(
413
+ await readFile(file, "utf8"),
414
+ ).replace(/`(?:[^`\\]|\\.)*`/g, "")
415
+
416
+ const specifiers = [
417
+ ...contents.matchAll(/\bfrom\s+"([^"]+)"/g),
418
+ ...contents.matchAll(/\bimport\s+"([^"]+)"/g),
419
+ ].map(([, specifier]) => specifier as string)
420
+
421
+ for (const specifier of specifiers) {
422
+ if (!specifier.startsWith(".")) {
423
+ bareSpecifiers.add(specifier)
424
+
425
+ continue
426
+ }
427
+
428
+ if (/\.css$/.test(specifier)) {
429
+ continue
430
+ }
431
+
432
+ await visit(resolve(dirname(file), specifier))
433
+ }
434
+ }
435
+
436
+ await visit(entryFile)
437
+
438
+ return [...bareSpecifiers].sort()
439
+ }
440
+
441
+ test.each(Object.keys(ENTRY_POINT_RUNTIMES))(
442
+ "every @charcuterie/%s entry point reaches only its own runtime",
443
+ async (packageName) => {
444
+ const manifest = await readManifest(packageName)
445
+
446
+ const entryPoints = Object.entries(
447
+ manifest.exports ?? {},
448
+ ).flatMap(([name, target]) =>
449
+ typeof target === "object" && target.source
450
+ ? [[name, target.source] as const]
451
+ : [],
452
+ )
453
+
454
+ // The table is exhaustive, so a new entry point cannot land
455
+ // without somebody deciding what it is allowed to pull in.
456
+ expect(
457
+ entryPoints.map(([name]) => name).sort(),
458
+ ).toEqual(
459
+ Object.keys(
460
+ ENTRY_POINT_RUNTIMES[packageName] ?? {},
461
+ ).sort(),
462
+ )
463
+
464
+ const declared = Object.keys({
465
+ ...manifest.dependencies,
466
+ ...manifest.peerDependencies,
467
+ })
468
+
469
+ for (const [name, source] of entryPoints) {
470
+ const reached = await collectRuntimeImports(
471
+ join(packagesDirectory, packageName, source),
472
+ )
473
+
474
+ expect({
475
+ entryPoint: `${packageName}${name.slice(1)}`,
476
+ reached,
477
+ }).toEqual({
478
+ entryPoint: `${packageName}${name.slice(1)}`,
479
+ reached: ENTRY_POINT_RUNTIMES[packageName]?.[name],
480
+ })
481
+
482
+ // And nothing reached is a phantom dependency —
483
+ // `preact/hooks` is declared as `preact`, which is the unit an
484
+ // install actually has.
485
+ for (const specifier of reached) {
486
+ const packageOfSpecifier = specifier.startsWith("@")
487
+ ? specifier.split("/").slice(0, 2).join("/")
488
+ : specifier.split("/")[0]
489
+
490
+ expect(declared).toContain(packageOfSpecifier)
491
+ }
492
+ }
493
+ },
494
+ )
package/CHANGELOG.md DELETED
@@ -1,13 +0,0 @@
1
- # @charcuterie/ui
2
-
3
- ## 0.1.0
4
-
5
- ### Minor Changes
6
-
7
- - Initial public release of the Charcuterie fleet library: the design tokens, the five state kinds (Visibility, VisibilityGroup, SinglePicker, MultiplePicker, RovingFocus, Status), the component set, and the shared ESLint + Biome configs.
8
-
9
- ### Patch Changes
10
-
11
- - Updated dependencies
12
- - @charcuterie/tokens@0.1.0
13
- - @charcuterie/logic@0.1.0