From 4d21e088243b2ab40217cdcd1909e983ab24e13b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 27 Aug 2026 19:24:51 +1000 Subject: [PATCH] Reorganise React source layout (#518) --- .github/workflows/visual-regression.yml | 7 +- README.md | 3 +- .../docs/components/actions/button.mdx | 4 +- .../docs/components/actions/icon-button.mdx | 4 +- .../content/docs/components/actions/link.mdx | 4 +- .../components/feedback/loading-skeleton.mdx | 6 +- .../components/feedback/loading-spinner.mdx | 4 +- .../docs/components/forms/checkbox.mdx | 4 +- .../docs/components/forms/combobox-field.mdx | 4 +- .../docs/components/forms/text-field.mdx | 4 +- .../content/docs/components/layout/box.mdx | 4 +- .../components/layout/visually-hidden.mdx | 4 +- .../docs/components/primitives/button.mdx | 4 +- .../docs/components/primitives/checkbox.mdx | 10 +- .../docs/components/primitives/combobox.mdx | 24 +-- .../docs/components/primitives/field.mdx | 10 +- .../components/primitives/input-group.mdx | 10 +- .../docs/components/typography/blockquote.mdx | 4 +- .../docs/components/typography/code.mdx | 4 +- .../content/docs/components/typography/em.mdx | 4 +- .../docs/components/typography/emoji.mdx | 4 +- .../docs/components/typography/heading.mdx | 8 +- .../docs/components/typography/kbd.mdx | 4 +- .../docs/components/typography/numeral.mdx | 4 +- .../docs/components/typography/quote.mdx | 4 +- .../docs/components/typography/strong.mdx | 4 +- .../docs/components/typography/text.mdx | 4 +- .../content/docs/components/visuals/icon.mdx | 10 +- apps/docs/source.config.ts | 4 +- apps/docs/src/lib/check-docs.test.ts | 30 +-- .../src/lib/component-doc-contract.test.ts | 4 +- .../docs/src/lib/component-guide-inventory.ts | 24 ++- .../src/lib/component-props-contract.test.ts | 78 ++++--- apps/docs/src/lib/component-props-contract.ts | 51 ++++- apps/docs/src/lib/docs-frontmatter.test.ts | 4 +- .../docs/src/lib/generate-props-pages.test.ts | 20 +- apps/docs/src/lib/storybook.test.ts | 4 +- apps/docs/vite.config.ts | 5 +- docs/COMPONENTS.md | 6 +- docs/DOCUMENTATION.md | 8 +- docs/STYLING.md | 109 +++++----- docs/TESTING.md | 6 +- docs/VISUAL_TESTING.md | 2 +- knip.config.ts | 6 +- packages/@luke-ui/react/.storybook/main.ts | 6 +- packages/@luke-ui/react/AGENTS.md | 26 ++- packages/@luke-ui/react/package.json | 64 +++--- .../scripts/visual-regression-contract.ts | 12 +- .../scripts/visual-regression-lib.test.ts | 2 +- .../react/scripts/visual-regression-lib.ts | 2 +- .../react/scripts/visual-regression-runner.ts | 15 +- .../@luke-ui/react/src/blockquote/index.ts | 2 - packages/@luke-ui/react/src/box/index.ts | 1 - packages/@luke-ui/react/src/button/index.ts | 2 - packages/@luke-ui/react/src/checkbox/index.ts | 2 - packages/@luke-ui/react/src/code/index.ts | 2 - .../react/src/combobox-field/index.ts | 1 - .../blockquote/blockquote.browser.test.tsx | 2 +- .../blockquote/blockquote.stories.tsx | 2 +- .../src/{ => core}/blockquote/blockquote.tsx | 2 +- .../src/{ => core}/blockquote/recipe.css.ts | 2 +- .../src/{ => core}/box/box.browser.test.tsx | 2 +- .../react/src/{ => core}/box/box.stories.tsx | 2 +- .../@luke-ui/react/src/{ => core}/box/box.tsx | 2 +- .../src/{ => core}/box/box.visual.test.tsx | 4 +- .../{ => core}/button/button.browser.test.tsx | 2 +- .../src/{ => core}/button/button.stories.tsx | 4 +- .../react/src/{ => core}/button/button.tsx | 0 .../{ => core}/button/button.visual.test.tsx | 4 +- .../react/src/{ => core}/button/styles.css.ts | 2 +- .../checkbox/checkbox.browser.test.tsx | 2 +- .../{ => core}/checkbox/checkbox.stories.tsx | 2 +- .../src/{ => core}/checkbox/checkbox.tsx | 0 .../checkbox/checkbox.visual.test.tsx | 6 +- .../src/{ => core}/code/code.browser.test.tsx | 2 +- .../src/{ => core}/code/code.stories.tsx | 2 +- .../react/src/{ => core}/code/code.tsx | 2 +- .../react/src/{ => core}/code/recipe.css.ts | 2 +- .../combobox-field.browser.test.tsx | 2 +- .../combobox-field/combobox-field.stories.tsx | 2 +- .../combobox-field/combobox-field.tsx | 2 +- .../combobox-field.visual.test.tsx | 6 +- .../src/{ => core}/conformance/helpers.ts | 0 .../{ => core}/conformance/manifest.test.ts | 15 +- .../src/{ => core}/conformance/manifest.ts | 0 .../src/{ => core}/em/em.browser.test.tsx | 2 +- .../react/src/{ => core}/em/em.stories.tsx | 2 +- .../@luke-ui/react/src/{ => core}/em/em.tsx | 2 +- .../react/src/{ => core}/em/styles.css.ts | 0 .../{ => core}/emoji/emoji.browser.test.tsx | 2 +- .../src/{ => core}/emoji/emoji.stories.tsx | 2 +- .../react/src/{ => core}/emoji/emoji.tsx | 0 .../{ => core}/emoji/emoji.visual.test.tsx | 4 +- .../{ => core}/heading/heading-context.tsx | 0 .../heading/heading.browser.test.tsx | 2 +- .../{ => core}/heading/heading.stories.tsx | 2 +- .../react/src/{ => core}/heading/heading.tsx | 0 .../heading/heading.visual.test.tsx | 2 +- .../icon-button/icon-button.browser.test.tsx | 2 +- .../icon-button/icon-button.stories.tsx | 2 +- .../{ => core}/icon-button/icon-button.tsx | 2 +- .../icon-button/icon-button.visual.test.tsx | 2 +- .../src/{ => core}/icon-button/recipe.css.ts | 2 +- .../src/{ => core}/icon/icon-size-context.tsx | 0 .../src/{ => core}/icon/icon.stories.tsx | 2 +- .../react/src/{ => core}/icon/icon.tsx | 6 +- .../src/{ => core}/icon/icon.visual.test.tsx | 4 +- .../react/src/{ => core}/icon/recipe.css.ts | 2 +- .../src/{ => core}/kbd/kbd.browser.test.tsx | 2 +- .../react/src/{ => core}/kbd/kbd.stories.tsx | 2 +- .../@luke-ui/react/src/{ => core}/kbd/kbd.tsx | 2 +- .../react/src/{ => core}/kbd/recipe.css.ts | 4 +- .../src/{ => core}/link/link.browser.test.tsx | 2 +- .../src/{ => core}/link/link.stories.tsx | 2 +- .../react/src/{ => core}/link/link.tsx | 2 +- .../src/{ => core}/link/link.visual.test.tsx | 2 +- .../react/src/{ => core}/link/recipe.css.ts | 2 +- .../loading-skeleton.browser.test.tsx | 2 +- .../loading-skeleton.stories.tsx | 2 +- .../loading-skeleton/loading-skeleton.tsx | 4 +- .../loading-skeleton.visual.test.tsx | 6 +- .../{ => core}/loading-skeleton/styles.css.ts | 2 +- .../loading-spinner.browser.test.tsx | 2 +- .../loading-spinner.stories.tsx | 2 +- .../loading-spinner/loading-spinner.tsx | 0 .../loading-spinner.visual.test.tsx | 4 +- .../{ => core}/loading-spinner/recipe.css.ts | 2 +- .../numeral/numeral.browser.test.tsx | 2 +- .../{ => core}/numeral/numeral.stories.tsx | 2 +- .../react/src/{ => core}/numeral/numeral.tsx | 0 .../numeral/numeral.visual.test.tsx | 2 +- .../{ => core}/overlays/mobile-overlay.css.ts | 2 +- .../{ => core}/overlays/mobile-overlay.tsx | 4 +- .../overlays/use-is-mobile-device.ts | 2 +- .../primitives/button/button.browser.test.tsx | 2 +- .../{ => core}/primitives/button/button.tsx | 2 +- .../primitives/button/recipe.css.ts | 4 +- .../primitives/checkbox/checkbox.tsx | 0 .../primitives/checkbox/recipe.css.ts | 4 +- .../primitives/combobox/clear-button.tsx | 0 .../primitives/combobox/empty-state.tsx | 0 .../primitives/combobox/input-group.tsx | 0 .../{ => core}/primitives/combobox/input.tsx | 0 .../{ => core}/primitives/combobox/item.tsx | 0 .../primitives/combobox/listbox.tsx | 0 .../primitives/combobox/popover.tsx | 4 +- .../{ => core}/primitives/combobox/root.tsx | 0 .../primitives/combobox/section.tsx | 0 .../primitives/combobox/size-context.tsx | 0 .../primitives/combobox/styles.css.ts | 4 +- .../primitives/combobox/trigger.tsx | 0 .../primitives/field/description.tsx | 0 .../src/{ => core}/primitives/field/error.tsx | 0 .../src/{ => core}/primitives/field/field.tsx | 0 .../field/is-invalid-from-error-message.ts | 0 .../src/{ => core}/primitives/field/label.tsx | 0 .../{ => core}/primitives/field/recipe.css.ts | 2 +- .../src/{ => core}/primitives/field/root.tsx | 0 .../primitives/input-group/input-group.tsx | 0 .../primitives/input-group/recipe.css.ts | 4 +- .../{ => core}/quote/quote.browser.test.tsx | 2 +- .../src/{ => core}/quote/quote.stories.tsx | 2 +- .../react/src/{ => core}/quote/quote.tsx | 0 .../src/{ => core}/sizing/button-sizing.ts | 0 .../src/{ => core}/sizing/combobox-sizing.ts | 0 .../src/{ => core}/sizing/control-size.ts | 0 .../src/{ => core}/sizing/icon-sizing.ts | 0 .../{ => core}/strong/strong.browser.test.tsx | 2 +- .../src/{ => core}/strong/strong.stories.tsx | 2 +- .../react/src/{ => core}/strong/strong.tsx | 2 +- .../react/src/{ => core}/strong/styles.css.ts | 2 +- .../styles/class-selector.ts} | 6 +- .../react/src/{ => core}/styles/focus-ring.ts | 0 .../react/src/{ => core}/styles/index.css.ts | 0 .../src/{ => core}/styles/input-states.ts | 0 .../{ => core}/styles/invalid-indicator.ts | 4 +- .../{ => core}/styles/layered-style.css.ts | 0 .../react/src/{ => core}/styles/layers.css.ts | 0 .../src/{ => core}/styles/layers.test.ts | 0 .../src/{ => core}/styles/modules.css.ts | 0 .../src/{ => core}/styles/overlay-motion.ts | 2 +- .../{ => core}/styles/package-exports.test.ts | 26 +-- .../src/{ => core}/styles/recipe-engine.ts | 0 .../{ => core}/styles/recipe.browser.test.ts | 0 .../{ => core}/styles/recipe.fixtures.css.ts | 0 .../react/src/{ => core}/styles/recipe.ts | 2 +- .../react/src/{ => core}/styles/reset.css.ts | 5 +- .../src/{ => core}/styles/spinner-overlay.ts | 0 .../styles/stylesheet-contract.test.ts | 14 +- .../{ => core}/styles/stylesheet-size.test.ts | 2 +- .../src/{ => core}/styles/theme-root.css.ts | 5 +- .../styles/utilities-emitted.test-d.ts | 15 +- .../styles/utilities.browser.test.ts | 8 +- .../src/{ => core}/styles/utilities.css.ts | 10 +- .../{ => core}/styles/utilities.stories.tsx | 4 +- .../src/{ => core}/styles/utilities.test-d.ts | 2 +- .../src/{ => core}/styles/utilities.test.ts | 0 .../react/src/{ => core}/stylesheet.css.ts | 0 .../test-utils/get-described-text.ts | 0 .../test-utils/mock-screen-width.ts | 0 .../src/{ => core}/test-utils/render-setup.ts | 0 .../test-utils/render.browser.test.tsx | 4 +- .../src/{ => core}/test-utils/render.tsx | 10 +- .../test-utils/visual-capture-id.test.ts | 0 .../test-utils/visual-capture-id.ts | 0 .../src/{ => core}/test-utils/visual-setup.ts | 0 .../src/{ => core}/test-utils/visual.tsx | 0 .../test-utils/wait-for-overlay-enter.ts | 0 .../text-field/text-field.browser.test.tsx | 4 +- .../text-field/text-field.stories.tsx | 2 +- .../src/{ => core}/text-field/text-field.tsx | 0 .../text-field/text-field.visual.test.tsx | 8 +- .../react/src/{ => core}/text/recipe.css.ts | 6 +- .../src/{ => core}/text/text.browser.test.tsx | 2 +- .../src/{ => core}/text/text.stories.tsx | 6 +- .../react/src/{ => core}/text/text.tsx | 4 +- .../src/{ => core}/text/text.visual.test.tsx | 4 +- .../src/{ => core}/types/distributive-omit.ts | 0 .../{ => core}/types/documented-rac-props.ts | 0 .../react/src/{ => core}/types/icon-size.ts | 0 .../react/src/{ => core}/types/prettify.ts | 0 ...se-synchronize-animations.browser.test.tsx | 0 .../use-synchronize-animations.ts | 0 .../{ => core}/visually-hidden/recipe.css.ts | 0 .../visually-hidden.browser.test.tsx | 2 +- .../visually-hidden.stories.tsx | 2 +- .../visually-hidden/visually-hidden.tsx | 2 +- packages/@luke-ui/react/src/em/index.ts | 1 - packages/@luke-ui/react/src/emoji/index.ts | 1 - .../@luke-ui/react/src/exports/blockquote.ts | 2 + packages/@luke-ui/react/src/exports/box.ts | 1 + packages/@luke-ui/react/src/exports/button.ts | 2 + .../@luke-ui/react/src/exports/checkbox.ts | 5 + packages/@luke-ui/react/src/exports/code.ts | 2 + .../react/src/exports/combobox-field.ts | 1 + packages/@luke-ui/react/src/exports/em.ts | 1 + packages/@luke-ui/react/src/exports/emoji.ts | 1 + .../@luke-ui/react/src/exports/heading.ts | 12 ++ .../@luke-ui/react/src/exports/icon-button.ts | 2 + .../src/{icon/index.ts => exports/icon.ts} | 4 +- packages/@luke-ui/react/src/exports/kbd.ts | 2 + packages/@luke-ui/react/src/exports/link.ts | 2 + .../index.ts => exports/loading-skeleton.ts} | 2 +- .../react/src/exports/loading-spinner.ts | 8 + .../{numeral/index.ts => exports/numeral.ts} | 2 +- .../react/src/exports/primitives/button.ts | 5 + .../primitives/checkbox.ts} | 7 +- .../react/src/exports/primitives/combobox.ts | 23 ++ .../index.ts => exports/primitives/field.ts} | 4 +- .../primitives/input-group.ts} | 4 +- packages/@luke-ui/react/src/exports/quote.ts | 1 + packages/@luke-ui/react/src/exports/strong.ts | 1 + packages/@luke-ui/react/src/exports/styles.ts | 2 + .../@luke-ui/react/src/exports/text-field.ts | 1 + packages/@luke-ui/react/src/exports/text.ts | 2 + packages/@luke-ui/react/src/exports/theme.ts | 1 + .../react/src/exports/themes/paper.ts | 1 + .../react/src/exports/themes/tactile.ts | 1 + packages/@luke-ui/react/src/exports/utils.ts | 1 + .../react/src/exports/visually-hidden.ts | 5 + packages/@luke-ui/react/src/heading/index.ts | 9 - .../@luke-ui/react/src/icon-button/index.ts | 2 - packages/@luke-ui/react/src/kbd/index.ts | 2 - packages/@luke-ui/react/src/link/index.ts | 2 - .../react/src/loading-spinner/index.ts | 2 - .../react/src/module-boundaries.test.ts | 203 ++++++++++++++++++ .../react/src/primitives/button/index.ts | 2 - .../react/src/primitives/combobox/index.ts | 20 -- packages/@luke-ui/react/src/quote/index.ts | 1 - .../@luke-ui/react/src/shared/class-names.ts | 5 + .../react/src/{ => shared}/utils/utils.ts | 0 packages/@luke-ui/react/src/strong/index.ts | 1 - packages/@luke-ui/react/src/styles/index.ts | 2 - .../@luke-ui/react/src/text-field/index.ts | 1 - packages/@luke-ui/react/src/text/index.ts | 2 - .../{themes => theme/bundles}/paper/index.ts | 0 .../bundles}/paper/theme-class-name.ts | 2 +- .../{themes => theme/bundles}/paper/theme.ts | 4 +- .../bundles}/tactile/index.ts | 0 .../bundles}/tactile/theme-class-name.ts | 2 +- .../bundles}/tactile/theme.ts | 4 +- .../bundles}/theme-bundle.test.ts | 4 +- .../{themes => theme/bundles}/themes.test.ts | 16 +- .../react/src/theme/cascade.browser.test.ts | 4 +- .../@luke-ui/react/src/theme/define-theme.ts | 2 +- .../react/src/theme/stylesheet.test.ts | 4 +- .../src/theme/theme-diagnostics-inspector.tsx | 2 +- .../react/src/theme/theme.browser.test.tsx | 10 +- packages/@luke-ui/react/src/theme/theme.ts | 4 +- .../src/theme/token-board.visual.test.tsx | 4 +- packages/@luke-ui/react/src/utils/index.ts | 9 - .../react/src/visually-hidden/index.ts | 2 - packages/@luke-ui/react/vite.config.ts | 12 +- packages/@luke-ui/react/vitest.config.ts | 11 +- .../src/apply-component-creation-plan.test.ts | 37 ++-- .../src/component-creation-plan.test.ts | 117 ++++++++-- .../src/component-creation-plan.ts | 36 ++-- vite.config.ts | 59 ++++- 298 files changed, 1115 insertions(+), 634 deletions(-) delete mode 100644 packages/@luke-ui/react/src/blockquote/index.ts delete mode 100644 packages/@luke-ui/react/src/box/index.ts delete mode 100644 packages/@luke-ui/react/src/button/index.ts delete mode 100644 packages/@luke-ui/react/src/checkbox/index.ts delete mode 100644 packages/@luke-ui/react/src/code/index.ts delete mode 100644 packages/@luke-ui/react/src/combobox-field/index.ts rename packages/@luke-ui/react/src/{ => core}/blockquote/blockquote.browser.test.tsx (90%) rename packages/@luke-ui/react/src/{ => core}/blockquote/blockquote.stories.tsx (90%) rename packages/@luke-ui/react/src/{ => core}/blockquote/blockquote.tsx (94%) rename packages/@luke-ui/react/src/{ => core}/blockquote/recipe.css.ts (91%) rename packages/@luke-ui/react/src/{ => core}/box/box.browser.test.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/box/box.stories.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/box/box.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/box/box.visual.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/button/button.browser.test.tsx (95%) rename packages/@luke-ui/react/src/{ => core}/button/button.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/button/button.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/button/button.visual.test.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/button/styles.css.ts (94%) rename packages/@luke-ui/react/src/{ => core}/checkbox/checkbox.browser.test.tsx (99%) rename packages/@luke-ui/react/src/{ => core}/checkbox/checkbox.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/checkbox/checkbox.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/checkbox/checkbox.visual.test.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/code/code.browser.test.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/code/code.stories.tsx (88%) rename packages/@luke-ui/react/src/{ => core}/code/code.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/code/recipe.css.ts (92%) rename packages/@luke-ui/react/src/{ => core}/combobox-field/combobox-field.browser.test.tsx (99%) rename packages/@luke-ui/react/src/{ => core}/combobox-field/combobox-field.stories.tsx (99%) rename packages/@luke-ui/react/src/{ => core}/combobox-field/combobox-field.tsx (99%) rename packages/@luke-ui/react/src/{ => core}/combobox-field/combobox-field.visual.test.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/conformance/helpers.ts (100%) rename packages/@luke-ui/react/src/{ => core}/conformance/manifest.test.ts (94%) rename packages/@luke-ui/react/src/{ => core}/conformance/manifest.ts (100%) rename packages/@luke-ui/react/src/{ => core}/em/em.browser.test.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/em/em.stories.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/em/em.tsx (95%) rename packages/@luke-ui/react/src/{ => core}/em/styles.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/emoji/emoji.browser.test.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/emoji/emoji.stories.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/emoji/emoji.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/emoji/emoji.visual.test.tsx (93%) rename packages/@luke-ui/react/src/{ => core}/heading/heading-context.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/heading/heading.browser.test.tsx (95%) rename packages/@luke-ui/react/src/{ => core}/heading/heading.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/heading/heading.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/heading/heading.visual.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/icon-button/icon-button.browser.test.tsx (94%) rename packages/@luke-ui/react/src/{ => core}/icon-button/icon-button.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/icon-button/icon-button.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/icon-button/icon-button.visual.test.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/icon-button/recipe.css.ts (96%) rename packages/@luke-ui/react/src/{ => core}/icon/icon-size-context.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/icon/icon.stories.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/icon/icon.tsx (95%) rename packages/@luke-ui/react/src/{ => core}/icon/icon.visual.test.tsx (94%) rename packages/@luke-ui/react/src/{ => core}/icon/recipe.css.ts (94%) rename packages/@luke-ui/react/src/{ => core}/kbd/kbd.browser.test.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/kbd/kbd.stories.tsx (90%) rename packages/@luke-ui/react/src/{ => core}/kbd/kbd.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/kbd/recipe.css.ts (88%) rename packages/@luke-ui/react/src/{ => core}/link/link.browser.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/link/link.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/link/link.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/link/link.visual.test.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/link/recipe.css.ts (97%) rename packages/@luke-ui/react/src/{ => core}/loading-skeleton/loading-skeleton.browser.test.tsx (88%) rename packages/@luke-ui/react/src/{ => core}/loading-skeleton/loading-skeleton.stories.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/loading-skeleton/loading-skeleton.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/loading-skeleton/loading-skeleton.visual.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/loading-skeleton/styles.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/loading-spinner/loading-spinner.browser.test.tsx (88%) rename packages/@luke-ui/react/src/{ => core}/loading-spinner/loading-spinner.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/loading-spinner/loading-spinner.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/loading-spinner/loading-spinner.visual.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/loading-spinner/recipe.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/numeral/numeral.browser.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/numeral/numeral.stories.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/numeral/numeral.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/numeral/numeral.visual.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/overlays/mobile-overlay.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/overlays/mobile-overlay.tsx (95%) rename packages/@luke-ui/react/src/{ => core}/overlays/use-is-mobile-device.ts (94%) rename packages/@luke-ui/react/src/{ => core}/primitives/button/button.browser.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/primitives/button/button.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/primitives/button/recipe.css.ts (97%) rename packages/@luke-ui/react/src/{ => core}/primitives/checkbox/checkbox.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/checkbox/recipe.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/clear-button.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/empty-state.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/input-group.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/input.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/item.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/listbox.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/popover.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/root.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/section.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/size-context.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/styles.css.ts (99%) rename packages/@luke-ui/react/src/{ => core}/primitives/combobox/trigger.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/description.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/error.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/field.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/is-invalid-from-error-message.ts (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/label.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/recipe.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/primitives/field/root.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/input-group/input-group.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/primitives/input-group/recipe.css.ts (98%) rename packages/@luke-ui/react/src/{ => core}/quote/quote.browser.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/quote/quote.stories.tsx (93%) rename packages/@luke-ui/react/src/{ => core}/quote/quote.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/sizing/button-sizing.ts (100%) rename packages/@luke-ui/react/src/{ => core}/sizing/combobox-sizing.ts (100%) rename packages/@luke-ui/react/src/{ => core}/sizing/control-size.ts (100%) rename packages/@luke-ui/react/src/{ => core}/sizing/icon-sizing.ts (100%) rename packages/@luke-ui/react/src/{ => core}/strong/strong.browser.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/strong/strong.stories.tsx (92%) rename packages/@luke-ui/react/src/{ => core}/strong/strong.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/strong/styles.css.ts (75%) rename packages/@luke-ui/react/src/{styles/class-names.ts => core/styles/class-selector.ts} (54%) rename packages/@luke-ui/react/src/{ => core}/styles/focus-ring.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/index.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/input-states.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/invalid-indicator.ts (95%) rename packages/@luke-ui/react/src/{ => core}/styles/layered-style.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/layers.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/layers.test.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/modules.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/overlay-motion.ts (95%) rename packages/@luke-ui/react/src/{ => core}/styles/package-exports.test.ts (66%) rename packages/@luke-ui/react/src/{ => core}/styles/recipe-engine.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/recipe.browser.test.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/recipe.fixtures.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/recipe.ts (99%) rename packages/@luke-ui/react/src/{ => core}/styles/reset.css.ts (93%) rename packages/@luke-ui/react/src/{ => core}/styles/spinner-overlay.ts (100%) rename packages/@luke-ui/react/src/{ => core}/styles/stylesheet-contract.test.ts (97%) rename packages/@luke-ui/react/src/{ => core}/styles/stylesheet-size.test.ts (88%) rename packages/@luke-ui/react/src/{ => core}/styles/theme-root.css.ts (59%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities-emitted.test-d.ts (92%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities.browser.test.ts (93%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities.css.ts (95%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities.test-d.ts (98%) rename packages/@luke-ui/react/src/{ => core}/styles/utilities.test.ts (100%) rename packages/@luke-ui/react/src/{ => core}/stylesheet.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/get-described-text.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/mock-screen-width.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/render-setup.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/render.browser.test.tsx (88%) rename packages/@luke-ui/react/src/{ => core}/test-utils/render.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/test-utils/visual-capture-id.test.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/visual-capture-id.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/visual-setup.ts (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/visual.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/test-utils/wait-for-overlay-enter.ts (100%) rename packages/@luke-ui/react/src/{ => core}/text-field/text-field.browser.test.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/text-field/text-field.stories.tsx (98%) rename packages/@luke-ui/react/src/{ => core}/text-field/text-field.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/text-field/text-field.visual.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/text/recipe.css.ts (96%) rename packages/@luke-ui/react/src/{ => core}/text/text.browser.test.tsx (91%) rename packages/@luke-ui/react/src/{ => core}/text/text.stories.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/text/text.tsx (97%) rename packages/@luke-ui/react/src/{ => core}/text/text.visual.test.tsx (96%) rename packages/@luke-ui/react/src/{ => core}/types/distributive-omit.ts (100%) rename packages/@luke-ui/react/src/{ => core}/types/documented-rac-props.ts (100%) rename packages/@luke-ui/react/src/{ => core}/types/icon-size.ts (100%) rename packages/@luke-ui/react/src/{ => core}/types/prettify.ts (100%) rename packages/@luke-ui/react/src/{ => core}/use-synchronize-animations/use-synchronize-animations.browser.test.tsx (100%) rename packages/@luke-ui/react/src/{ => core}/use-synchronize-animations/use-synchronize-animations.ts (100%) rename packages/@luke-ui/react/src/{ => core}/visually-hidden/recipe.css.ts (100%) rename packages/@luke-ui/react/src/{ => core}/visually-hidden/visually-hidden.browser.test.tsx (89%) rename packages/@luke-ui/react/src/{ => core}/visually-hidden/visually-hidden.stories.tsx (93%) rename packages/@luke-ui/react/src/{ => core}/visually-hidden/visually-hidden.tsx (96%) delete mode 100644 packages/@luke-ui/react/src/em/index.ts delete mode 100644 packages/@luke-ui/react/src/emoji/index.ts create mode 100644 packages/@luke-ui/react/src/exports/blockquote.ts create mode 100644 packages/@luke-ui/react/src/exports/box.ts create mode 100644 packages/@luke-ui/react/src/exports/button.ts create mode 100644 packages/@luke-ui/react/src/exports/checkbox.ts create mode 100644 packages/@luke-ui/react/src/exports/code.ts create mode 100644 packages/@luke-ui/react/src/exports/combobox-field.ts create mode 100644 packages/@luke-ui/react/src/exports/em.ts create mode 100644 packages/@luke-ui/react/src/exports/emoji.ts create mode 100644 packages/@luke-ui/react/src/exports/heading.ts create mode 100644 packages/@luke-ui/react/src/exports/icon-button.ts rename packages/@luke-ui/react/src/{icon/index.ts => exports/icon.ts} (65%) create mode 100644 packages/@luke-ui/react/src/exports/kbd.ts create mode 100644 packages/@luke-ui/react/src/exports/link.ts rename packages/@luke-ui/react/src/{loading-skeleton/index.ts => exports/loading-skeleton.ts} (68%) create mode 100644 packages/@luke-ui/react/src/exports/loading-spinner.ts rename packages/@luke-ui/react/src/{numeral/index.ts => exports/numeral.ts} (75%) create mode 100644 packages/@luke-ui/react/src/exports/primitives/button.ts rename packages/@luke-ui/react/src/{primitives/checkbox/index.ts => exports/primitives/checkbox.ts} (52%) create mode 100644 packages/@luke-ui/react/src/exports/primitives/combobox.ts rename packages/@luke-ui/react/src/{primitives/field/index.ts => exports/primitives/field.ts} (56%) rename packages/@luke-ui/react/src/{primitives/input-group/index.ts => exports/primitives/input-group.ts} (69%) create mode 100644 packages/@luke-ui/react/src/exports/quote.ts create mode 100644 packages/@luke-ui/react/src/exports/strong.ts create mode 100644 packages/@luke-ui/react/src/exports/styles.ts create mode 100644 packages/@luke-ui/react/src/exports/text-field.ts create mode 100644 packages/@luke-ui/react/src/exports/text.ts create mode 100644 packages/@luke-ui/react/src/exports/theme.ts create mode 100644 packages/@luke-ui/react/src/exports/themes/paper.ts create mode 100644 packages/@luke-ui/react/src/exports/themes/tactile.ts create mode 100644 packages/@luke-ui/react/src/exports/utils.ts create mode 100644 packages/@luke-ui/react/src/exports/visually-hidden.ts delete mode 100644 packages/@luke-ui/react/src/heading/index.ts delete mode 100644 packages/@luke-ui/react/src/icon-button/index.ts delete mode 100644 packages/@luke-ui/react/src/kbd/index.ts delete mode 100644 packages/@luke-ui/react/src/link/index.ts delete mode 100644 packages/@luke-ui/react/src/loading-spinner/index.ts create mode 100644 packages/@luke-ui/react/src/module-boundaries.test.ts delete mode 100644 packages/@luke-ui/react/src/primitives/button/index.ts delete mode 100644 packages/@luke-ui/react/src/primitives/combobox/index.ts delete mode 100644 packages/@luke-ui/react/src/quote/index.ts create mode 100644 packages/@luke-ui/react/src/shared/class-names.ts rename packages/@luke-ui/react/src/{ => shared}/utils/utils.ts (100%) delete mode 100644 packages/@luke-ui/react/src/strong/index.ts delete mode 100644 packages/@luke-ui/react/src/styles/index.ts delete mode 100644 packages/@luke-ui/react/src/text-field/index.ts delete mode 100644 packages/@luke-ui/react/src/text/index.ts rename packages/@luke-ui/react/src/{themes => theme/bundles}/paper/index.ts (100%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/paper/theme-class-name.ts (84%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/paper/theme.ts (65%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/tactile/index.ts (100%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/tactile/theme-class-name.ts (84%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/tactile/theme.ts (66%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/theme-bundle.test.ts (97%) rename packages/@luke-ui/react/src/{themes => theme/bundles}/themes.test.ts (82%) delete mode 100644 packages/@luke-ui/react/src/utils/index.ts delete mode 100644 packages/@luke-ui/react/src/visually-hidden/index.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 6f46aef1..918f9306 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -42,9 +42,10 @@ jobs: visual-main-${{ runner.os }}-${{ runner.arch }}-${{ github.event.pull_request.base.sha || github.event.before }}-${{ hashFiles('pnpm-lock.yaml', 'packages/@luke-ui/react/vitest.config.ts', - 'packages/@luke-ui/react/src/test-utils/render-setup.ts', - 'packages/@luke-ui/react/src/test-utils/render.tsx', - 'packages/@luke-ui/react/src/test-utils/visual-setup.ts') }} + 'packages/@luke-ui/react/src/core/stylesheet.css.ts', + 'packages/@luke-ui/react/src/core/test-utils/render-setup.ts', + 'packages/@luke-ui/react/src/core/test-utils/render.tsx', + 'packages/@luke-ui/react/src/core/test-utils/visual-setup.ts') }} - name: Compare visual captures run: corepack pnpm test:visual env: diff --git a/README.md b/README.md index 88713bd0..eb5c6986 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ Useful repo commands: `@luke-ui/react` contains the public React package. - Theme contract and compiler: `packages/@luke-ui/react/src/theme/`. -- Styles: `packages/@luke-ui/react/src/styles/`. +- Styles: `packages/@luke-ui/react/src/core/styles/`. +- Shared low-level primitives: `packages/@luke-ui/react/src/shared/`. - Build output: `packages/@luke-ui/react/dist/stylesheet.css`. ## Docs diff --git a/apps/docs/content/docs/components/actions/button.mdx b/apps/docs/content/docs/components/actions/button.mdx index 5588dcbf..694e40d2 100644 --- a/apps/docs/content/docs/components/actions/button.mdx +++ b/apps/docs/content/docs/components/actions/button.mdx @@ -1,11 +1,11 @@ --- title: Button description: A labelled control for actions in an interface. -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts reactAria: https://react-spectrum.adobe.com/react-aria/Button.html props: - name: ButtonProps - path: packages/@luke-ui/react/src/button/button.tsx + path: packages/@luke-ui/react/src/core/button/button.tsx --- `Button` triggers an action, such as saving a form, opening a dialog, or deleting an item. Use diff --git a/apps/docs/content/docs/components/actions/icon-button.mdx b/apps/docs/content/docs/components/actions/icon-button.mdx index 09fd65e4..573317b7 100644 --- a/apps/docs/content/docs/components/actions/icon-button.mdx +++ b/apps/docs/content/docs/components/actions/icon-button.mdx @@ -1,11 +1,11 @@ --- title: Icon Button description: A compact icon-only control for an action. -source: packages/@luke-ui/react/src/icon-button +source: packages/@luke-ui/react/src/exports/icon-button.ts reactAria: https://react-spectrum.adobe.com/react-aria/Button.html props: - name: IconButtonProps - path: packages/@luke-ui/react/src/icon-button/icon-button.tsx + path: packages/@luke-ui/react/src/core/icon-button/icon-button.tsx --- `IconButton` triggers a familiar compact action, such as closing a panel, deleting a row, or adding diff --git a/apps/docs/content/docs/components/actions/link.mdx b/apps/docs/content/docs/components/actions/link.mdx index 45605c8d..57104e5e 100644 --- a/apps/docs/content/docs/components/actions/link.mdx +++ b/apps/docs/content/docs/components/actions/link.mdx @@ -1,11 +1,11 @@ --- title: Link description: A styled link for inline and standalone navigation. -source: packages/@luke-ui/react/src/link +source: packages/@luke-ui/react/src/exports/link.ts reactAria: https://react-spectrum.adobe.com/react-aria/Link.html props: - name: LinkProps - path: packages/@luke-ui/react/src/link/link.tsx + path: packages/@luke-ui/react/src/core/link/link.tsx --- `Link` navigates to another URL or route. Use [`Button`](/components/actions/button) for an action diff --git a/apps/docs/content/docs/components/feedback/loading-skeleton.mdx b/apps/docs/content/docs/components/feedback/loading-skeleton.mdx index b61aefb3..31188957 100644 --- a/apps/docs/content/docs/components/feedback/loading-skeleton.mdx +++ b/apps/docs/content/docs/components/feedback/loading-skeleton.mdx @@ -1,12 +1,12 @@ --- title: Loading Skeleton description: Replaces content with a placeholder that keeps its shape while it loads. -source: packages/@luke-ui/react/src/loading-skeleton +source: packages/@luke-ui/react/src/exports/loading-skeleton.ts props: - name: LoadingSkeletonProps - path: packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.tsx + path: packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.tsx - name: LoadingSkeletonProviderProps - path: packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.tsx + path: packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.tsx --- Use `LoadingSkeleton` when content is loading but its layout is known. Wrap the content that will diff --git a/apps/docs/content/docs/components/feedback/loading-spinner.mdx b/apps/docs/content/docs/components/feedback/loading-spinner.mdx index 36b5941a..a4ae7d95 100644 --- a/apps/docs/content/docs/components/feedback/loading-spinner.mdx +++ b/apps/docs/content/docs/components/feedback/loading-spinner.mdx @@ -1,10 +1,10 @@ --- title: Loading Spinner description: Animated indicator for work that is still in progress. -source: packages/@luke-ui/react/src/loading-spinner +source: packages/@luke-ui/react/src/exports/loading-spinner.ts props: - name: LoadingSpinnerProps - path: packages/@luke-ui/react/src/loading-spinner/loading-spinner.tsx + path: packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.tsx --- `LoadingSpinner` shows that work is in progress. Use it on its own for a small loading region. Wrap diff --git a/apps/docs/content/docs/components/forms/checkbox.mdx b/apps/docs/content/docs/components/forms/checkbox.mdx index 1afc5497..96afad03 100644 --- a/apps/docs/content/docs/components/forms/checkbox.mdx +++ b/apps/docs/content/docs/components/forms/checkbox.mdx @@ -2,11 +2,11 @@ title: Checkbox description: Lets someone select an independent option, with optional supporting and validation text. -source: packages/@luke-ui/react/src/checkbox +source: packages/@luke-ui/react/src/exports/checkbox.ts reactAria: https://react-spectrum.adobe.com/react-aria/Checkbox.html props: - name: CheckboxProps - path: packages/@luke-ui/react/src/checkbox/checkbox.tsx + path: packages/@luke-ui/react/src/core/checkbox/checkbox.tsx --- Use `Checkbox` when someone can choose an option independently of nearby controls. Pass diff --git a/apps/docs/content/docs/components/forms/combobox-field.mdx b/apps/docs/content/docs/components/forms/combobox-field.mdx index eabe4de7..871e5846 100644 --- a/apps/docs/content/docs/components/forms/combobox-field.mdx +++ b/apps/docs/content/docs/components/forms/combobox-field.mdx @@ -1,11 +1,11 @@ --- title: Combobox Field description: Searchable single-select field with label, validation, and option list. -source: packages/@luke-ui/react/src/combobox-field +source: packages/@luke-ui/react/src/exports/combobox-field.ts reactAria: https://react-spectrum.adobe.com/react-aria/ComboBox.html props: - name: ComboboxFieldProps - path: packages/@luke-ui/react/src/combobox-field/combobox-field.tsx + path: packages/@luke-ui/react/src/core/combobox-field/combobox-field.tsx --- Use `ComboboxField` when someone needs to search for or choose one option from a list. It combines diff --git a/apps/docs/content/docs/components/forms/text-field.mdx b/apps/docs/content/docs/components/forms/text-field.mdx index 81a1a3da..607032d0 100644 --- a/apps/docs/content/docs/components/forms/text-field.mdx +++ b/apps/docs/content/docs/components/forms/text-field.mdx @@ -2,11 +2,11 @@ title: Text Field description: Captures a single line of text with a label, validation, and an optional prefix or suffix. -source: packages/@luke-ui/react/src/text-field +source: packages/@luke-ui/react/src/exports/text-field.ts reactAria: https://react-spectrum.adobe.com/react-aria/TextField.html props: - name: TextFieldProps - path: packages/@luke-ui/react/src/text-field/text-field.tsx + path: packages/@luke-ui/react/src/core/text-field/text-field.tsx --- Use `TextField` to collect a single line of text. It combines an input with a label, description, diff --git a/apps/docs/content/docs/components/layout/box.mdx b/apps/docs/content/docs/components/layout/box.mdx index 7fdfe806..f2c4efa2 100644 --- a/apps/docs/content/docs/components/layout/box.mdx +++ b/apps/docs/content/docs/components/layout/box.mdx @@ -1,10 +1,10 @@ --- title: Box description: A layout element with responsive Luke UI layout properties. -source: packages/@luke-ui/react/src/box +source: packages/@luke-ui/react/src/exports/box.ts props: - name: BoxProps - path: packages/@luke-ui/react/src/box/box.tsx + path: packages/@luke-ui/react/src/core/box/box.tsx --- `Box` accepts Luke UI layout properties and renders a `div` by default. It places content in a diff --git a/apps/docs/content/docs/components/layout/visually-hidden.mdx b/apps/docs/content/docs/components/layout/visually-hidden.mdx index 8d77dcc3..e25ad020 100644 --- a/apps/docs/content/docs/components/layout/visually-hidden.mdx +++ b/apps/docs/content/docs/components/layout/visually-hidden.mdx @@ -1,10 +1,10 @@ --- title: Visually Hidden description: Hide content visually and keep it available to assistive technology. -source: packages/@luke-ui/react/src/visually-hidden +source: packages/@luke-ui/react/src/exports/visually-hidden.ts props: - name: VisuallyHiddenProps - path: packages/@luke-ui/react/src/visually-hidden/visually-hidden.tsx + path: packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.tsx --- Use `VisuallyHidden` to hide content from sight. The content stays in the accessibility tree and the diff --git a/apps/docs/content/docs/components/primitives/button.mdx b/apps/docs/content/docs/components/primitives/button.mdx index aee51405..2c2186df 100644 --- a/apps/docs/content/docs/components/primitives/button.mdx +++ b/apps/docs/content/docs/components/primitives/button.mdx @@ -1,11 +1,11 @@ --- title: Button primitive description: Lower-level button styles and behaviour for custom control layouts. -source: packages/@luke-ui/react/src/primitives/button +source: packages/@luke-ui/react/src/exports/primitives/button.ts reactAria: https://react-spectrum.adobe.com/react-aria/Button.html props: - name: ButtonProps - path: packages/@luke-ui/react/src/primitives/button/button.tsx + path: packages/@luke-ui/react/src/core/primitives/button/button.tsx --- Use the button primitive to build a custom button pattern. It provides Luke UI button styles and diff --git a/apps/docs/content/docs/components/primitives/checkbox.mdx b/apps/docs/content/docs/components/primitives/checkbox.mdx index e75719bb..6f3cc159 100644 --- a/apps/docs/content/docs/components/primitives/checkbox.mdx +++ b/apps/docs/content/docs/components/primitives/checkbox.mdx @@ -1,17 +1,17 @@ --- title: Checkbox primitive description: Lower-level checkbox anatomy for custom form control layouts. -source: packages/@luke-ui/react/src/primitives/checkbox +source: packages/@luke-ui/react/src/exports/primitives/checkbox.ts reactAria: https://react-spectrum.adobe.com/react-aria/Checkbox.html props: - name: CheckboxProps - path: packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx + path: packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx - name: CheckboxContentProps - path: packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx + path: packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx - name: CheckboxControlProps - path: packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx + path: packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx - name: CheckboxIndicatorProps - path: packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx + path: packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx --- Use the Checkbox primitive when you need a custom label layout or extra content. For normal diff --git a/apps/docs/content/docs/components/primitives/combobox.mdx b/apps/docs/content/docs/components/primitives/combobox.mdx index 50de0edc..36e9ba59 100644 --- a/apps/docs/content/docs/components/primitives/combobox.mdx +++ b/apps/docs/content/docs/components/primitives/combobox.mdx @@ -1,31 +1,31 @@ --- title: Combobox primitives description: Lower-level parts for composing a searchable single-select combobox. -source: packages/@luke-ui/react/src/primitives/combobox +source: packages/@luke-ui/react/src/exports/primitives/combobox.ts reactAria: https://react-spectrum.adobe.com/react-aria/ComboBox.html props: - name: ComboboxRootProps - path: packages/@luke-ui/react/src/primitives/combobox/root.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/root.tsx - name: ComboboxInputGroupProps - path: packages/@luke-ui/react/src/primitives/combobox/input-group.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/input-group.tsx - name: ComboboxInputProps - path: packages/@luke-ui/react/src/primitives/combobox/input.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/input.tsx - name: ComboboxClearButtonProps - path: packages/@luke-ui/react/src/primitives/combobox/clear-button.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/clear-button.tsx - name: ComboboxTriggerProps - path: packages/@luke-ui/react/src/primitives/combobox/trigger.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/trigger.tsx - name: ComboboxPopoverProps - path: packages/@luke-ui/react/src/primitives/combobox/popover.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/popover.tsx - name: ComboboxListBoxProps - path: packages/@luke-ui/react/src/primitives/combobox/listbox.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/listbox.tsx - name: ComboboxItemProps - path: packages/@luke-ui/react/src/primitives/combobox/item.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/item.tsx - name: ComboboxLoadMoreItemProps - path: packages/@luke-ui/react/src/primitives/combobox/item.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/item.tsx - name: ComboboxSectionProps - path: packages/@luke-ui/react/src/primitives/combobox/section.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/section.tsx - name: ComboboxEmptyStateProps - path: packages/@luke-ui/react/src/primitives/combobox/empty-state.tsx + path: packages/@luke-ui/react/src/core/primitives/combobox/empty-state.tsx --- Use these primitives when [`ComboboxField`](/components/forms/combobox-field) does not fit your diff --git a/apps/docs/content/docs/components/primitives/field.mdx b/apps/docs/content/docs/components/primitives/field.mdx index de491a78..37360ad0 100644 --- a/apps/docs/content/docs/components/primitives/field.mdx +++ b/apps/docs/content/docs/components/primitives/field.mdx @@ -1,16 +1,16 @@ --- title: Field primitive description: Shared label, description, and validation parts for custom fields. -source: packages/@luke-ui/react/src/primitives/field +source: packages/@luke-ui/react/src/exports/primitives/field.ts props: - name: FieldProps - path: packages/@luke-ui/react/src/primitives/field/field.tsx + path: packages/@luke-ui/react/src/core/primitives/field/field.tsx - name: FieldLabelProps - path: packages/@luke-ui/react/src/primitives/field/label.tsx + path: packages/@luke-ui/react/src/core/primitives/field/label.tsx - name: FieldDescriptionProps - path: packages/@luke-ui/react/src/primitives/field/description.tsx + path: packages/@luke-ui/react/src/core/primitives/field/description.tsx - name: FieldErrorProps - path: packages/@luke-ui/react/src/primitives/field/error.tsx + path: packages/@luke-ui/react/src/core/primitives/field/error.tsx --- Use the field primitives to build a custom field with Luke UI labels, descriptions, and validation diff --git a/apps/docs/content/docs/components/primitives/input-group.mdx b/apps/docs/content/docs/components/primitives/input-group.mdx index 75bd6076..bddf788d 100644 --- a/apps/docs/content/docs/components/primitives/input-group.mdx +++ b/apps/docs/content/docs/components/primitives/input-group.mdx @@ -1,16 +1,16 @@ --- title: InputGroup primitives description: Lower-level parts for composing a text input with content at either end. -source: packages/@luke-ui/react/src/primitives/input-group +source: packages/@luke-ui/react/src/exports/primitives/input-group.ts props: - name: InputGroupProps - path: packages/@luke-ui/react/src/primitives/input-group/input-group.tsx + path: packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx - name: InputGroupInputProps - path: packages/@luke-ui/react/src/primitives/input-group/input-group.tsx + path: packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx - name: InputGroupPrefixProps - path: packages/@luke-ui/react/src/primitives/input-group/input-group.tsx + path: packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx - name: InputGroupSuffixProps - path: packages/@luke-ui/react/src/primitives/input-group/input-group.tsx + path: packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx --- Use these primitives to build a custom text control. Reach for them when you need the styled input diff --git a/apps/docs/content/docs/components/typography/blockquote.mdx b/apps/docs/content/docs/components/typography/blockquote.mdx index effa8b39..e8f856e1 100644 --- a/apps/docs/content/docs/components/typography/blockquote.mdx +++ b/apps/docs/content/docs/components/typography/blockquote.mdx @@ -1,10 +1,10 @@ --- title: Blockquote description: Block-level quotation from another source. -source: packages/@luke-ui/react/src/blockquote +source: packages/@luke-ui/react/src/exports/blockquote.ts props: - name: BlockquoteProps - path: packages/@luke-ui/react/src/blockquote/blockquote.tsx + path: packages/@luke-ui/react/src/core/blockquote/blockquote.tsx --- `Blockquote` quotes another source as a block. It renders a native `blockquote` element, which diff --git a/apps/docs/content/docs/components/typography/code.mdx b/apps/docs/content/docs/components/typography/code.mdx index 1427f4e9..b8658744 100644 --- a/apps/docs/content/docs/components/typography/code.mdx +++ b/apps/docs/content/docs/components/typography/code.mdx @@ -1,10 +1,10 @@ --- title: Code description: Marks text to signify a short fragment of computer code. -source: packages/@luke-ui/react/src/code +source: packages/@luke-ui/react/src/exports/code.ts props: - name: CodeProps - path: packages/@luke-ui/react/src/code/code.tsx + path: packages/@luke-ui/react/src/core/code/code.tsx --- `Code` marks a short fragment of computer code. It renders a native `code` element, which conveys diff --git a/apps/docs/content/docs/components/typography/em.mdx b/apps/docs/content/docs/components/typography/em.mdx index 2c29a588..7a6f1438 100644 --- a/apps/docs/content/docs/components/typography/em.mdx +++ b/apps/docs/content/docs/components/typography/em.mdx @@ -1,10 +1,10 @@ --- title: Em description: Marks text to stress emphasis. -source: packages/@luke-ui/react/src/em +source: packages/@luke-ui/react/src/exports/em.ts props: - name: EmProps - path: packages/@luke-ui/react/src/em/em.tsx + path: packages/@luke-ui/react/src/core/em/em.tsx --- `Em` stresses a word or phrase, changing how the sentence reads. It renders a native `em` element, diff --git a/apps/docs/content/docs/components/typography/emoji.mdx b/apps/docs/content/docs/components/typography/emoji.mdx index 48df66bc..054df531 100644 --- a/apps/docs/content/docs/components/typography/emoji.mdx +++ b/apps/docs/content/docs/components/typography/emoji.mdx @@ -1,10 +1,10 @@ --- title: Emoji description: Emoji rendering with a reliable screen reader label. -source: packages/@luke-ui/react/src/emoji +source: packages/@luke-ui/react/src/exports/emoji.ts props: - name: EmojiProps - path: packages/@luke-ui/react/src/emoji/emoji.tsx + path: packages/@luke-ui/react/src/core/emoji/emoji.tsx --- `Emoji` renders an emoji with a reliable accessible name. It inherits its size from the surrounding diff --git a/apps/docs/content/docs/components/typography/heading.mdx b/apps/docs/content/docs/components/typography/heading.mdx index 77736f01..f172cbd0 100644 --- a/apps/docs/content/docs/components/typography/heading.mdx +++ b/apps/docs/content/docs/components/typography/heading.mdx @@ -1,14 +1,14 @@ --- title: Heading description: Semantic heading with automatic level management. -source: packages/@luke-ui/react/src/heading +source: packages/@luke-ui/react/src/exports/heading.ts props: - name: HeadingProps - path: packages/@luke-ui/react/src/heading/heading.tsx + path: packages/@luke-ui/react/src/core/heading/heading.tsx - name: HeadingLevelsProps - path: packages/@luke-ui/react/src/heading/heading-context.tsx + path: packages/@luke-ui/react/src/core/heading/heading-context.tsx - name: HeadingLevelsRenderProps - path: packages/@luke-ui/react/src/heading/heading-context.tsx + path: packages/@luke-ui/react/src/core/heading/heading-context.tsx --- `Heading` renders a semantic section heading. It reads its level from `HeadingLevels` context. Set diff --git a/apps/docs/content/docs/components/typography/kbd.mdx b/apps/docs/content/docs/components/typography/kbd.mdx index f59cd580..2f592233 100644 --- a/apps/docs/content/docs/components/typography/kbd.mdx +++ b/apps/docs/content/docs/components/typography/kbd.mdx @@ -1,10 +1,10 @@ --- title: Kbd description: Represents keyboard input or a hotkey. -source: packages/@luke-ui/react/src/kbd +source: packages/@luke-ui/react/src/exports/kbd.ts props: - name: KbdProps - path: packages/@luke-ui/react/src/kbd/kbd.tsx + path: packages/@luke-ui/react/src/core/kbd/kbd.tsx --- `Kbd` marks keyboard input, such as a shortcut or a key name. It renders a native `kbd` element, diff --git a/apps/docs/content/docs/components/typography/numeral.mdx b/apps/docs/content/docs/components/typography/numeral.mdx index dbb6ad1a..3610e913 100644 --- a/apps/docs/content/docs/components/typography/numeral.mdx +++ b/apps/docs/content/docs/components/typography/numeral.mdx @@ -1,10 +1,10 @@ --- title: Numeral description: Locale-aware number formatting powered by Intl.NumberFormat. -source: packages/@luke-ui/react/src/numeral +source: packages/@luke-ui/react/src/exports/numeral.ts props: - name: NumeralProps - path: packages/@luke-ui/react/src/numeral/numeral.tsx + path: packages/@luke-ui/react/src/core/numeral/numeral.tsx --- `Numeral` formats a number with `Intl.NumberFormat`. It reads the active locale from React Aria's diff --git a/apps/docs/content/docs/components/typography/quote.mdx b/apps/docs/content/docs/components/typography/quote.mdx index e66be4ab..dddc5641 100644 --- a/apps/docs/content/docs/components/typography/quote.mdx +++ b/apps/docs/content/docs/components/typography/quote.mdx @@ -1,10 +1,10 @@ --- title: Quote description: Short inline quotation. -source: packages/@luke-ui/react/src/quote +source: packages/@luke-ui/react/src/exports/quote.ts props: - name: QuoteProps - path: packages/@luke-ui/react/src/quote/quote.tsx + path: packages/@luke-ui/react/src/core/quote/quote.tsx --- `Quote` marks a short quotation inside a sentence. It renders a native `q` element, which conveys diff --git a/apps/docs/content/docs/components/typography/strong.mdx b/apps/docs/content/docs/components/typography/strong.mdx index 5ba01efd..835aaee7 100644 --- a/apps/docs/content/docs/components/typography/strong.mdx +++ b/apps/docs/content/docs/components/typography/strong.mdx @@ -1,10 +1,10 @@ --- title: Strong description: Marks text to signify strong importance. -source: packages/@luke-ui/react/src/strong +source: packages/@luke-ui/react/src/exports/strong.ts props: - name: StrongProps - path: packages/@luke-ui/react/src/strong/strong.tsx + path: packages/@luke-ui/react/src/core/strong/strong.tsx --- `Strong` marks text as important. It renders a native `strong` element, which conveys importance to diff --git a/apps/docs/content/docs/components/typography/text.mdx b/apps/docs/content/docs/components/typography/text.mdx index 273d0f6c..60f9520b 100644 --- a/apps/docs/content/docs/components/typography/text.mdx +++ b/apps/docs/content/docs/components/typography/text.mdx @@ -1,10 +1,10 @@ --- title: Text description: Styled text with semantic typography styles and colour roles. -source: packages/@luke-ui/react/src/text +source: packages/@luke-ui/react/src/exports/text.ts props: - name: TextProps - path: packages/@luke-ui/react/src/text/text.tsx + path: packages/@luke-ui/react/src/core/text/text.tsx --- `Text` applies Luke UI typography without adding heading semantics. It needs a Luke UI theme root. diff --git a/apps/docs/content/docs/components/visuals/icon.mdx b/apps/docs/content/docs/components/visuals/icon.mdx index fba79a26..f11e8af7 100644 --- a/apps/docs/content/docs/components/visuals/icon.mdx +++ b/apps/docs/content/docs/components/visuals/icon.mdx @@ -1,16 +1,16 @@ --- title: Icon description: SVG icon component backed by the generated spritesheet. -source: packages/@luke-ui/react/src/icon +source: packages/@luke-ui/react/src/exports/icon.ts props: - name: IconProps - path: packages/@luke-ui/react/src/icon/icon.tsx + path: packages/@luke-ui/react/src/core/icon/icon.tsx - name: CreateIconOptions - path: packages/@luke-ui/react/src/icon/icon.tsx + path: packages/@luke-ui/react/src/core/icon/icon.tsx - name: CustomIconProps - path: packages/@luke-ui/react/src/icon/icon.tsx + path: packages/@luke-ui/react/src/core/icon/icon.tsx - name: IconSpritesheetProviderProps - path: packages/@luke-ui/react/src/icon/icon.tsx + path: packages/@luke-ui/react/src/core/icon/icon.tsx --- `Icon` renders a symbol from the generated Luke UI spritesheet. It needs an diff --git a/apps/docs/source.config.ts b/apps/docs/source.config.ts index fb52c996..63706419 100644 --- a/apps/docs/source.config.ts +++ b/apps/docs/source.config.ts @@ -26,14 +26,14 @@ export const docs = defineDocs({ z.object({ /** Exported type name to render, e.g. `ButtonProps`. */ name: z.string(), - /** Repo-relative path to the file exporting `name`, e.g. `packages/@luke-ui/react/src/button/button.tsx`. */ + /** Repo-relative path to the file exporting `name`, e.g. `packages/@luke-ui/react/src/core/button/button.tsx`. */ path: z.string(), }), ) .optional(), /** Full URL to this component's React Aria Components docs page, when it genuinely wraps one. */ reactAria: z.string().optional(), - /** Repo-relative path to this component's source, e.g. `packages/@luke-ui/react/src/button`. */ + /** Repo-relative path to this component's public module, e.g. `packages/@luke-ui/react/src/exports/button.ts`. */ source: z.string().optional(), }), }, diff --git a/apps/docs/src/lib/check-docs.test.ts b/apps/docs/src/lib/check-docs.test.ts index d04b982b..3202bfbe 100644 --- a/apps/docs/src/lib/check-docs.test.ts +++ b/apps/docs/src/lib/check-docs.test.ts @@ -44,7 +44,7 @@ Run the install command. components: { 'actions/button.mdx': `--- title: Button -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts --- @@ -76,7 +76,7 @@ test('reports heading vocabulary, order, and required Accessibility issues', () components: { 'actions/button.mdx': `--- title: Button -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts --- @@ -95,7 +95,7 @@ Use Button for actions. `, 'visuals/icon.mdx': `--- title: Icon -source: packages/@luke-ui/react/src/icon +source: packages/@luke-ui/react/src/exports/icon.ts --- @@ -159,7 +159,7 @@ title: Layout components: { 'layout/box.mdx': `--- title: Box -source: packages/@luke-ui/react/src/box +source: packages/@luke-ui/react/src/exports/box.ts --- @@ -179,7 +179,7 @@ test('does not report a shared-example issue for a page referencing the same exa components: { 'layout/box.mdx': `--- title: Box -source: packages/@luke-ui/react/src/box +source: packages/@luke-ui/react/src/exports/box.ts --- @@ -516,7 +516,7 @@ test('finds no extra docs issues in the docs app beyond the baseline', () => { const INVENTORY_GUIDE = `--- title: Button -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts --- @@ -653,7 +653,7 @@ test('reports category metadata that does not match the root metadata', () => { 'actions/button.mdx': INVENTORY_GUIDE, 'actions/link.mdx': `--- title: Link -source: packages/@luke-ui/react/src/link +source: packages/@luke-ui/react/src/exports/link.ts --- @@ -676,15 +676,15 @@ test('reports a guide source that is not a public package entry point', () => { const paths = inventoryFixture({ packageExports: ['./link'] }); expect(findDocsIssues(paths)).toEqual([ - 'component-guide-inventory: actions/button.mdx: source "packages/@luke-ui/react/src/button" is not a public package entry point (expected export "./button" in @luke-ui/react)', + 'component-guide-inventory: actions/button.mdx: source "packages/@luke-ui/react/src/exports/button.ts" is not a public package entry point (expected export "./button" in @luke-ui/react)', ]); }); -test('reports a source directory with no index.ts', () => { +test('reports a missing exports module', () => { const paths = inventoryFixture({ sourceDirs: [] }); expect(findDocsIssues(paths)).toEqual([ - 'component-guide-inventory: actions/button.mdx: source "packages/@luke-ui/react/src/button" has no packages/@luke-ui/react/src/button/index.ts', + 'component-guide-inventory: actions/button.mdx: source "packages/@luke-ui/react/src/exports/button.ts" does not exist', ]); }); @@ -696,7 +696,7 @@ function createDocsFixture(input: { metadata?: Record; /** Export keys for the fake `@luke-ui/react` manifest. */ packageExports?: ReadonlyArray; - /** Subpaths under the fake package `src` that get an `index.ts`. */ + /** Subpaths under the fake package `src/exports` that get a `.ts` module. */ sourceDirs?: ReadonlyArray; }): DocsCheckPaths { const directory = mkdtempSync(join(tmpdir(), 'luke-ui-check-docs-')); @@ -738,9 +738,9 @@ function createDocsFixture(input: { writeFileSync(reactPackageJsonPath, JSON.stringify({ exports, name: '@luke-ui/react' })); for (const subpath of input.sourceDirs ?? guideSources) { - const sourceDir = join(reactPackageDir, 'src', subpath); - mkdirSync(sourceDir, { recursive: true }); - writeFileSync(join(sourceDir, 'index.ts'), 'export {};\n'); + const sourcePath = join(reactPackageDir, 'src', 'exports', `${subpath}.ts`); + mkdirSync(join(sourcePath, '..'), { recursive: true }); + writeFileSync(sourcePath, 'export {};\n'); } return { @@ -790,5 +790,5 @@ function defaultMetadata(guidePaths: ReadonlyArray): Record const docsDir = createDocsFixture({ guide: `--- title: Status Badge -source: packages/@luke-ui/react/src/status-badge +source: packages/@luke-ui/react/src/exports/status-badge.ts --- @@ -33,7 +33,7 @@ test('reports placeholders and the wrong primary example', () => { guide: `--- title: Status Badge description: Status Badge component. -source: packages/@luke-ui/react/src/status-badge +source: packages/@luke-ui/react/src/exports/status-badge.ts --- \`StatusBadge\` from \`@luke-ui/react/status-badge\`. diff --git a/apps/docs/src/lib/component-guide-inventory.ts b/apps/docs/src/lib/component-guide-inventory.ts index b2801974..bd601871 100644 --- a/apps/docs/src/lib/component-guide-inventory.ts +++ b/apps/docs/src/lib/component-guide-inventory.ts @@ -2,7 +2,7 @@ import { existsSync, readdirSync, readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { parseComponentFrontmatter, readFrontmatter } from './docs-frontmatter.js'; -const SOURCE_PREFIX = 'packages/@luke-ui/react/src/'; +const EXPORTS_PREFIX = 'packages/@luke-ui/react/src/exports/'; /** One authored component guide, keyed by the slug the navigation uses. */ interface ComponentGuide { @@ -158,8 +158,8 @@ function categoryDirectories(componentsDir: string): Array { } /** - * A guide `source` must name a public package entry point and a real source directory with an - * `index.ts`, so a documented component is one a developer can import. + * A guide `source` must name a public package entry point and its exports module, so a documented + * component is one a developer can import. */ export function findGuideSourceIssues( inventory: ComponentGuideInventory, @@ -172,14 +172,20 @@ export function findGuideSourceIssues( const { relativePath, source } = guide; if (source === undefined) continue; - if (!source.startsWith(SOURCE_PREFIX)) { + if (!source.startsWith(EXPORTS_PREFIX)) { issues.push( - `${relativePath}: source "${source}" is not a public package entry point (expected a path under ${SOURCE_PREFIX})`, + `${relativePath}: source "${source}" is not a public package entry point (expected a path under ${EXPORTS_PREFIX})`, ); continue; } - const subpath = source.slice(SOURCE_PREFIX.length); + const exportPath = source.slice(EXPORTS_PREFIX.length); + if (!exportPath.endsWith('.ts')) { + issues.push(`${relativePath}: source "${source}" must name an exports .ts module`); + continue; + } + + const subpath = exportPath.slice(0, -'.ts'.length); if (!exportSet.has(`./${subpath}`)) { issues.push( `${relativePath}: source "${source}" is not a public package entry point (expected export "./${subpath}" in @luke-ui/react)`, @@ -187,9 +193,9 @@ export function findGuideSourceIssues( continue; } - const indexPath = resolve(reactPackageDir, 'src', subpath, 'index.ts'); - if (!existsSync(indexPath)) { - issues.push(`${relativePath}: source "${source}" has no ${source}/index.ts`); + const sourcePath = resolve(reactPackageDir, 'src', 'exports', exportPath); + if (!existsSync(sourcePath)) { + issues.push(`${relativePath}: source "${source}" does not exist`); } } diff --git a/apps/docs/src/lib/component-props-contract.test.ts b/apps/docs/src/lib/component-props-contract.test.ts index 88367fe5..e3f9973e 100644 --- a/apps/docs/src/lib/component-props-contract.test.ts +++ b/apps/docs/src/lib/component-props-contract.test.ts @@ -24,7 +24,21 @@ test('requires component props from an exported component parameter', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', + ]); +}); + +test('follows an export-all module into core', () => { + const fixture = createFixture({ + entry: "export * from './button.js';\n", + files: { + 'button.ts': + 'export interface ButtonProps {}\nexport function Button(props: ButtonProps) {}\n', + }, + }); + + expect(issues(fixture)).toEqual([ + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -38,7 +52,7 @@ test('requires provider props from an exported provider parameter', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ProviderProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ProviderProps" in props frontmatter', ]); }); @@ -53,8 +67,8 @@ test('requires factory options and the factory return type', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "CreateThingOptions" in props frontmatter', - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "CreatedThing" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "CreateThingOptions" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "CreatedThing" in props frontmatter', ]); }); @@ -68,7 +82,7 @@ test('requires a render callback value', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "RenderProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "RenderProps" in props frontmatter', ]); }); @@ -82,7 +96,7 @@ test('requires a named object generic constraint', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ThingProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ThingProps" in props frontmatter', ]); }); @@ -96,8 +110,8 @@ test('requires contracts from a multipart entry point', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "RootProps" in props frontmatter', - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ItemProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "RootProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ItemProps" in props frontmatter', ]); }); @@ -113,7 +127,7 @@ test('requires contracts re-exported through an intermediate local module', () = }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "FieldLabelProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "FieldLabelProps" in props frontmatter', ]); }); @@ -129,7 +143,7 @@ test('follows aliased values and types through an intermediate re-export', () => }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -143,7 +157,7 @@ test('follows an aliased value through an intermediate re-export', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" has an unsupported exported signature "Button"', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" has an unsupported exported signature "Button"', ]); }); @@ -158,7 +172,7 @@ test('follows an aliased type through an intermediate re-export', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -175,7 +189,7 @@ test('follows aliased values and types through multiple local re-exports', () => }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -191,7 +205,7 @@ test('requires a public imported type from another local module', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -209,7 +223,7 @@ test('uses the public alias for an imported type from another local module', () }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -294,7 +308,7 @@ test('requires an indirect alias that resolves to an object type', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -320,7 +334,7 @@ test('requires a parameterised alias that resolves to an object type', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -346,7 +360,7 @@ test('requires a chained parameterised alias that resolves to an object type', ( }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', ]); }); @@ -363,7 +377,7 @@ test('classifies private aliases using the module that declared them', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "RootProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "RootProps" in props frontmatter', ]); }); @@ -395,7 +409,7 @@ test('requires a public object type when a local leaf type has the same name', ( }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "SharedProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "SharedProps" in props frontmatter', ]); }); @@ -410,7 +424,7 @@ test('inspects only the re-exported callable when local modules share a value na }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "RootProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "RootProps" in props frontmatter', ]); }); @@ -424,7 +438,7 @@ test('reports an unsupported relevant exported signature', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" has an unsupported exported signature "Button"', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" has an unsupported exported signature "Button"', ]); }); @@ -440,13 +454,13 @@ test('does not treat a typed data constant as an unsupported signature', () => { expect(issues(fixture)).toEqual([]); }); -test('does not follow public re-exports outside the entry directory', () => { +test('does not follow public re-exports outside the implementation directory', () => { const fixture = createFixture({ entry: "export { Icon, iconNames, type IconProps } from './icon.js';\n", files: { 'icon.ts': - "import { iconNames } from '../generated/icon-data.js';\nexport { iconNames };\nexport interface IconProps {}\nexport function Icon(props: IconProps) {}\n", - '../generated/icon-data.ts': + "import { iconNames } from '../../generated/icon-data.js';\nexport { iconNames };\nexport interface IconProps {}\nexport function Icon(props: IconProps) {}\n", + '../../generated/icon-data.ts': 'export declare const iconNames: (props: IconProps) => unknown;\n', }, props: ['IconProps'], @@ -466,8 +480,8 @@ test('reports frontmatter props that the entry point does not export', () => { }); expect(issues(fixture)).toEqual([ - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" requires public object contract "ButtonProps" in props frontmatter', - 'actions/button.mdx: entry point "packages/@luke-ui/react/src/button/index.ts" does not export props frontmatter type "MissingProps"', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" requires public object contract "ButtonProps" in props frontmatter', + 'actions/button.mdx: entry point "packages/@luke-ui/react/src/exports/button.ts" does not export props frontmatter type "MissingProps"', ]); }); @@ -481,15 +495,17 @@ function createFixture(input: { const componentsDir = join(directory, 'content', 'components'); const reactPackageDir = join(directory, 'react-package'); - const sourceDir = join(reactPackageDir, 'src', 'button'); + const sourceDir = join(reactPackageDir, 'src', 'core', 'button'); + const exportsDir = join(reactPackageDir, 'src', 'exports'); mkdirSync(componentsDir, { recursive: true }); mkdirSync(sourceDir, { recursive: true }); + mkdirSync(exportsDir, { recursive: true }); writeFileSync(join(componentsDir, 'meta.json'), '{"pages":["---Actions---","actions/button"]}'); writeFileSync( join(reactPackageDir, 'package.json'), '{"exports":{"./button":"./dist/button/index.js"}}', ); - writeFileSync(join(sourceDir, 'index.ts'), input.entry); + writeFileSync(join(exportsDir, 'button.ts'), input.entry.replaceAll("'./", "'../core/button/")); for (const [path, contents] of Object.entries(input.files)) { const filePath = join(sourceDir, path); @@ -498,7 +514,7 @@ function createFixture(input: { } const props = input.props ?? []; - const guide = `---\ntitle: Button\nsource: packages/@luke-ui/react/src/button\n${renderProps(props)}---\n`; + const guide = `---\ntitle: Button\nsource: packages/@luke-ui/react/src/exports/button.ts\n${renderProps(props)}---\n`; return { inventory: buildComponentGuideInventory({ componentsDir, @@ -511,7 +527,7 @@ function createFixture(input: { function renderProps(props: ReadonlyArray): string { if (props.length === 0) return ''; - return `props:\n${props.map((name) => ` - name: ${name}\n path: packages/@luke-ui/react/src/button/button.ts`).join('\n')}\n`; + return `props:\n${props.map((name) => ` - name: ${name}\n path: packages/@luke-ui/react/src/core/button/button.ts`).join('\n')}\n`; } function issues(fixture: { diff --git a/apps/docs/src/lib/component-props-contract.ts b/apps/docs/src/lib/component-props-contract.ts index 2a2f4bdc..26a3ac65 100644 --- a/apps/docs/src/lib/component-props-contract.ts +++ b/apps/docs/src/lib/component-props-contract.ts @@ -3,7 +3,7 @@ import { dirname, resolve, sep } from 'node:path'; import { parseSync } from 'oxc-parser'; import type { ComponentGuideInventory } from './component-guide-inventory.js'; -const SOURCE_PREFIX = 'packages/@luke-ui/react/src/'; +const EXPORTS_PREFIX = 'packages/@luke-ui/react/src/exports/'; interface AstNode { readonly type: string; @@ -28,13 +28,13 @@ export function findComponentPropsContractIssues( const issues: Array = []; for (const guide of inventory.guides) { - if (guide.source === undefined || !guide.source.startsWith(SOURCE_PREFIX)) continue; + if (guide.source === undefined || !guide.source.startsWith(EXPORTS_PREFIX)) continue; const entryPath = resolve( reactPackageDir, 'src', - guide.source.slice(SOURCE_PREFIX.length), - 'index.ts', + 'exports', + guide.source.slice(EXPORTS_PREFIX.length), ); if (!existsSync(entryPath)) continue; @@ -71,7 +71,7 @@ function findGuideIssues( } } - const entryPoint = `${source}/index.ts`; + const entryPoint = source; const documented = new Set(frontmatterProps.map((entry) => entry.name)); const issues: Array = []; @@ -98,9 +98,21 @@ function findGuideIssues( return issues; } -// Public names may be imported and re-exported by an intermediate file. Follow those local -// modules so the callable signature that is inspected is the one consumers actually import. -// Stay inside the entry directory so generated data modules are not scanned. +// Entries live under `src/exports/` (some nested, such as `src/exports/primitives/button.ts`), so +// the traversal boundary is the `src/core` directory that sits alongside `exports` in the source +// tree, found by walking up from the entry module to the `exports` segment. +function srcDir(entryPath: string): string { + const segments = entryPath.split(sep); + const exportsIndex = segments.lastIndexOf('exports'); + if (exportsIndex === -1) { + throw new Error(`Expected an entry path under "src/exports/", received "${entryPath}".`); + } + return segments.slice(0, exportsIndex).join(sep); +} + +// Public names may be imported and re-exported by an intermediate file. Follow the public module +// and implementation modules so the callable signature that is inspected is the one consumers +// actually import. Do not follow other local modules such as generated data. function localContractModules( entryModule: ParsedModule, ): Array<{ module: ParsedModule } & PublicExports> { @@ -108,7 +120,7 @@ function localContractModules( const entryExports = exportedNames(entryModule.program); modules.set(entryModule.path, { module: entryModule, ...entryExports }); const pending = [entryModule.path]; - const entryDir = dirname(entryModule.path); + const coreDir = resolve(srcDir(entryModule.path), 'core'); while (pending.length > 0) { const path = pending.pop(); @@ -125,7 +137,7 @@ function localContractModules( } const targetPath = target.path; - if (targetPath.endsWith('.css.ts') || !isInsideDirectory(targetPath, entryDir)) continue; + if (targetPath.endsWith('.css.ts') || !isInsideDirectory(targetPath, coreDir)) continue; const module = parseModule(targetPath); if (module === undefined) continue; @@ -160,6 +172,12 @@ function localReexports( const reexports = new Map(); for (const statement of body(module.program)) { + if (statement.type === 'ExportAllDeclaration') { + const source = literalString(statement.source); + if (source === undefined || !source.startsWith('.')) continue; + addWildcardReexport(reexports, resolveLocalModule(module.path, source)); + continue; + } if (statement.type !== 'ExportNamedDeclaration') continue; const source = literalString(statement.source); @@ -193,6 +211,19 @@ function localReexports( return [...reexports.values()]; } +function addWildcardReexport( + reexports: Map, + path: string, +): void { + const module = parseModule(path); + if (module === undefined) return; + + const exported = exportedNames(module.program); + const reexport = reexports.get(path) ?? { path, types: new Map(), values: new Map() }; + mergePublicExports(reexport, exported); + reexports.set(path, reexport); +} + function addReexportedNames( reexports: Map, path: string, diff --git a/apps/docs/src/lib/docs-frontmatter.test.ts b/apps/docs/src/lib/docs-frontmatter.test.ts index 9436ba37..c249e0a4 100644 --- a/apps/docs/src/lib/docs-frontmatter.test.ts +++ b/apps/docs/src/lib/docs-frontmatter.test.ts @@ -8,14 +8,14 @@ title: Button description: A labelled control for actions. -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts --- Body. `), ).toEqual({ description: 'A labelled control for actions.', - source: 'packages/@luke-ui/react/src/button', + source: 'packages/@luke-ui/react/src/exports/button.ts', title: 'Button', }); }); diff --git a/apps/docs/src/lib/generate-props-pages.test.ts b/apps/docs/src/lib/generate-props-pages.test.ts index 2911e677..b02b320b 100644 --- a/apps/docs/src/lib/generate-props-pages.test.ts +++ b/apps/docs/src/lib/generate-props-pages.test.ts @@ -17,11 +17,11 @@ test('renders a single-entry Props page without a type heading', () => { const frontmatter = parseComponentFrontmatter(`--- title: Button description: A labelled control for actions in an interface. -source: packages/@luke-ui/react/src/button +source: packages/@luke-ui/react/src/exports/button.ts reactAria: https://react-spectrum.adobe.com/react-aria/Button.html props: - name: ButtonProps - path: packages/@luke-ui/react/src/button/button.tsx + path: packages/@luke-ui/react/src/core/button/button.tsx --- `); @@ -29,9 +29,9 @@ props: expect(page).toContain('## Props'); expect(page).not.toContain('### '); expect(page).toContain( - '', + '', ); - expect(page).toContain('source: packages/@luke-ui/react/src/button'); + expect(page).toContain('source: packages/@luke-ui/react/src/exports/button.ts'); expect(page).not.toContain('props:'); }); @@ -39,12 +39,12 @@ test('renders a multi-entry Props page with a heading per entry', () => { const frontmatter = parseComponentFrontmatter(`--- title: Field primitive description: Shared label, description, and validation parts for custom fields. -source: packages/@luke-ui/react/src/primitives/field +source: packages/@luke-ui/react/src/exports/primitives/field.ts props: - name: FieldProps - path: packages/@luke-ui/react/src/primitives/field/field.tsx + path: packages/@luke-ui/react/src/core/primitives/field/field.tsx - name: FieldLabelProps - path: packages/@luke-ui/react/src/primitives/field/label.tsx + path: packages/@luke-ui/react/src/core/primitives/field/label.tsx --- `); @@ -52,9 +52,7 @@ props: expect(page).toContain('## Props'); expect(page).toContain('### FieldProps'); expect(page).toContain('### FieldLabelProps'); - expect(page).toContain( - '', - ); + expect(page).toContain('\tpath="packages/@luke-ui/react/src/core/primitives/field/field.tsx"'); expect(page).toContain('name="FieldLabelProps"'); expect(page.indexOf('### FieldProps')).toBeLessThan(page.indexOf('### FieldLabelProps')); }); @@ -95,7 +93,7 @@ const SCRATCH_GUIDE = `--- title: Button props: - name: ButtonProps - path: packages/@luke-ui/react/src/button/button.tsx + path: packages/@luke-ui/react/src/core/button/button.tsx --- Guide body. diff --git a/apps/docs/src/lib/storybook.test.ts b/apps/docs/src/lib/storybook.test.ts index dd6c680d..9910a44d 100644 --- a/apps/docs/src/lib/storybook.test.ts +++ b/apps/docs/src/lib/storybook.test.ts @@ -6,7 +6,7 @@ test('links component guides to their Storybook docs', () => { getStorybookStoryUrl( 'components/actions/icon-button.mdx', '/', - 'packages/@luke-ui/react/src/icon-button', + 'packages/@luke-ui/react/src/exports/icon-button.ts', ), ).toBe('http://localhost:6006/?path=/docs/actions-iconbutton--docs'); }); @@ -20,7 +20,7 @@ test('does not link component props pages to Storybook', () => { getStorybookStoryUrl( 'components/actions/icon-button/props.mdx', '/', - 'packages/@luke-ui/react/src/icon-button', + 'packages/@luke-ui/react/src/exports/icon-button.ts', ), ).toBeNull(); }); diff --git a/apps/docs/vite.config.ts b/apps/docs/vite.config.ts index c1ef09ab..a4808bd0 100644 --- a/apps/docs/vite.config.ts +++ b/apps/docs/vite.config.ts @@ -145,7 +145,10 @@ export default defineConfig(async () => { resolve: { alias: { '#recipe-engine': fileURLToPath( - new URL('../../packages/@luke-ui/react/src/styles/recipe-engine.ts', import.meta.url), + new URL( + '../../packages/@luke-ui/react/src/core/styles/recipe-engine.ts', + import.meta.url, + ), ), }, tsconfigPaths: true, diff --git a/docs/COMPONENTS.md b/docs/COMPONENTS.md index 8efa3a6b..9daa9fcc 100644 --- a/docs/COMPONENTS.md +++ b/docs/COMPONENTS.md @@ -31,8 +31,10 @@ pnpm run generate:component --args Primitive scaffolding is not handled by the component generator. -Public subpath `index.ts` files are export-only. A component's implementation lives in a named -sibling file. Support modules and multi-part primitives each keep their own file. +Public subpath modules live under `src/exports/`. Single-segment subpaths use a flat module such as +`src/exports/button.ts`. Grouped subpaths use nested modules such as +`src/exports/primitives/button.ts`. A component's implementation lives under `src/core/`. Support +modules and multi-part primitives each keep their own file. The component creation rules live in `packages/turbo-generators/src/component-creation-plan.ts`. That module owns component-name validation, documentation groups, conformance contracts, and diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index 71b93508..201371e8 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -332,10 +332,10 @@ fit. Keep primitive pages at the end of the Components area. `apps/docs/content/docs/components/meta.json` is the source for component category and order. List every component guide there once. Each category `meta.json` must list the same components as its slice of that file, in the same order. A leftover category file with no remaining guides or root -entries is stale. A guide's `source` must name a public entry point in -`packages/@luke-ui/react/package.json`, and that directory needs an `index.ts`. `check:docs` -enforces all of this, so a guide cannot leave the navigation or point at a path a developer cannot -import. +entries is stale. A guide's `source` must name the explicit module under +`packages/@luke-ui/react/src/exports/` for a public entry point in +`packages/@luke-ui/react/package.json`. `check:docs` enforces all of this, so a guide cannot leave +the navigation or point at a path a developer cannot import. ## MDX page structure diff --git a/docs/STYLING.md b/docs/STYLING.md index 574662bd..416f1942 100644 --- a/docs/STYLING.md +++ b/docs/STYLING.md @@ -10,48 +10,51 @@ with no class and no JS required. Neither step injects styles at runtime. ## Structure -- `styles/index.css.ts`: stylesheet graph in cascade order — layers, reset, theme root, style +Paths below are rooted in `packages/@luke-ui/react/src/`. Core style, primitive, overlay, and +utility modules live under `core/`. Theme modules live under `theme/`. + +- `core/styles/index.css.ts`: stylesheet graph in cascade order — layers, reset, theme root, style modules, utilities. -- `styles/reset.css.ts`: reset scoped to `.luke-ui-reset`. -- `styles/theme-root.css.ts`: base typography and text colour scoped to `.luke-ui-theme`. -- `styles/modules.css.ts`: the committed stylesheet registry. It explicitly imports every colocated - `recipe.css.ts` and `styles.css.ts` that participates in the shipped stylesheet, plus primitive - and overlay style modules. Keep the list in code-point order by path for deterministic output. - Named layers make cross-layer priority explicit. Specificity and source order still matter within - a layer. -- `styles/recipe.ts`: the internal `recipe()` engine shared by every component recipe, plus the +- `core/styles/reset.css.ts`: reset scoped to `.luke-ui-reset`. +- `core/styles/theme-root.css.ts`: base typography and text colour scoped to `.luke-ui-theme`. +- `core/styles/modules.css.ts`: the committed stylesheet registry. It explicitly imports every + colocated `recipe.css.ts` and `styles.css.ts` that participates in the shipped stylesheet, plus + primitive and overlay style modules. Keep the list in code-point order by path for deterministic + output. Named layers make cross-layer priority explicit. Specificity and source order still matter + within a layer. +- `core/styles/recipe.ts`: the internal `recipe()` engine shared by every component recipe, plus the `RecipeSelection` helper that derives a recipe's variant type. -- `styles/input-states.ts`: the shared field control-state selectors (`composeInputStateSelectors`, - `descendantDisabledSelector`) that field recipes compose. It is named `.ts`, not `.css.ts`, - because it emits no CSS. Each field recipe's `.css.ts` module composes its plain data and - functions. -- `styles/invalid-indicator.ts`: the shared invalid-state `exclamationTriangle` icon, rendered as a - CSS mask in two sizes. `invalidIndicatorIcon` (plus `invalidIndicatorIconForcedColors`) is the - in-control icon `primitives/combobox/styles.css.ts` applies under its own invalid selector's - `::after` — the border stays at its resting 1px there, since the icon is already the non-colour - cue. It renders as the pseudo-element's own last DOM child, so the style gives its trailing - affordances (the combobox clear button and trigger) a flex `order` ahead of the icon's default - `order: 0`, so it lands right after the field's text content and before them, matching the +- `core/styles/input-states.ts`: the shared field control-state selectors + (`composeInputStateSelectors`, `descendantDisabledSelector`) that field recipes compose. It is + named `.ts`, not `.css.ts`, because it emits no CSS. Each field recipe's `.css.ts` module composes + its plain data and functions. +- `core/styles/invalid-indicator.ts`: the shared invalid-state `exclamationTriangle` icon, rendered + as a CSS mask in two sizes. `invalidIndicatorIcon` (plus `invalidIndicatorIconForcedColors`) is + the in-control icon `core/primitives/combobox/styles.css.ts` applies under its own invalid + selector's `::after` — the border stays at its resting 1px there, since the icon is already the + non-colour cue. It renders as the pseudo-element's own last DOM child, so the style gives its + trailing affordances (the combobox clear button and trigger) a flex `order` ahead of the icon's + default `order: 0`, so it lands right after the field's text content and before them, matching the Spectrum reference this ordering is drawn from. `invalidMessageIcon` is the smaller, - message-leading variant `primitives/field/recipe.css.ts` draws on its `message` slot, switched on - by `primitives/checkbox/recipe.css.ts` alone: `Checkbox`'s own box has no room for an in-control - icon without floating past the label, so its icon moves to the message and its box keeps a `2px` - border as its own non-colour cue instead. Named `.ts` for the same reason as `input-states.ts`: it - emits no CSS of its own, only plain style-rule data each recipe composes. -- `primitives/input-group/recipe.css.ts` draws the same glyph, but as a real `Icon` element on its - own `invalidIndicator` slot rather than a mask: `InputGroup` (`primitives/input-group/`) reads - React Aria's `Group` `isInvalid` render prop and renders the icon itself, so an invalid control - cannot be composed without a non-colour cue. The recipe owns only the icon's colour and margins — - `Icon` owns its box, and `IconSizeProvider` (`FIELD_CONTROL_ICON_SIZE`) owns its per-size step — - and gives the `suffix` slot the same `order: 1` for the same Spectrum ordering. Combobox's control - is not a plain `Group` with that state to hand, so it stays CSS-driven. -- `overlays/mobile-overlay.css.ts`: the backdrop, tray, and dialog styles `MobileOverlay` renders - for the mobile combobox tray, based on Apache-2.0 React Spectrum's `Tray.tsx` and + message-leading variant `core/primitives/field/recipe.css.ts` draws on its `message` slot, + switched on by `core/primitives/checkbox/recipe.css.ts` alone: `Checkbox`'s own box has no room + for an in-control icon without floating past the label, so its icon moves to the message and its + box keeps a `2px` border as its own non-colour cue instead. Named `.ts` for the same reason as + `input-states.ts`: it emits no CSS of its own, only plain style-rule data each recipe composes. +- `core/primitives/input-group/recipe.css.ts` draws the same glyph, but as a real `Icon` element on + its own `invalidIndicator` slot rather than a mask: `InputGroup` (`core/primitives/input-group/`) + reads React Aria's `Group` `isInvalid` render prop and renders the icon itself, so an invalid + control cannot be composed without a non-colour cue. The recipe owns only the icon's colour and + margins — `Icon` owns its box, and `IconSizeProvider` (`FIELD_CONTROL_ICON_SIZE`) owns its + per-size step — and gives the `suffix` slot the same `order: 1` for the same Spectrum ordering. + Combobox's control is not a plain `Group` with that state to hand, so it stays CSS-driven. +- `core/overlays/mobile-overlay.css.ts`: the backdrop, tray, and dialog styles `MobileOverlay` + renders for the mobile combobox tray, based on Apache-2.0 React Spectrum's `Tray.tsx` and `tray/index.css`. -- `overlays/`: the private mobile tray plumbing. `mobile-overlay.tsx` wraps React Aria's +- `core/overlays/`: the private mobile tray plumbing. `mobile-overlay.tsx` wraps React Aria's `ModalOverlay`, `Modal`, and `Dialog` for the combobox tray. `use-is-mobile-device.ts` reads the device screen width, not the viewport width, to decide when a combobox switches to it. -- `styles/`: layout utilities, most exported from `@luke-ui/react/styles`. +- `core/styles/`: layout utilities, most exported from `@luke-ui/react/styles`. - `theme/contract.ts`: the theme token tree, the mode-family declaration, `--luke-*` variable naming, and the source-owned `typeStyles` typography keys. - `theme/path-record.ts`: the typed `[path, value]` record constructor value producers use so @@ -96,12 +99,12 @@ with no class and no JS required. Neither step injects styles at runtime. class never drags in the compiler or a foundation. - `theme/foundations/tactile.ts` and `theme/foundations/paper.ts`: each bundled theme's `defineTheme(...)` input, kept as separate leaf modules so importing one never pulls in the other. -- `themes/tactile/` and `themes/paper/`: each theme's public entrypoint, exported from +- `theme/bundles/tactile/` and `theme/bundles/paper/`: each theme's public entrypoint, exported from `@luke-ui/react/themes/tactile` and `@luke-ui/react/themes/paper`. Each exports its own `themeClassName` identity class and its `theme` (the public `ThemeInput` a consumer can read, copy, or spread). The class comes from a per-theme `theme-class-name.ts` leaf holding the name as a literal, so importing the class alone leaves the foundation out of a consumer's bundle. - `themes/theme-bundle.test.ts` proves that with a real bundler run. + `theme/bundles/theme-bundle.test.ts` proves that with a real bundler run. - `scripts/build-themes.ts`: writes the bundled theme stylesheets to `dist/themes//stylesheet.css`, alongside the entrypoint `vp pack` emits there. @@ -180,13 +183,13 @@ Specificity and source order still decide conflicts within a layer. | `recipes` | Component styles, variants, and compound variants. | | `utilities` | One-off layout and override escape hatches. | -Use `styleInLayer` and `globalStyleInLayer` from `styles/layered-style.css.ts` to place a plain +Use `styleInLayer` and `globalStyleInLayer` from `core/styles/layered-style.css.ts` to place a plain Vanilla Extract style for a recipe with no variants in a named layer (see -`loading-skeleton/styles.css.ts`). A variant-driven recipe instead calls `recipe()` from -`styles/recipe.ts`, which wraps every base, variant, and compound-variant style it is given in the -`recipes` layer. A recipe can still pre-build a static `base` with `styleInLayer('recipes', …)` and -hand the resulting class string to `recipe()`, which passes a string value through unchanged rather -than wrapping it again. +`core/loading-skeleton/styles.css.ts`). A variant-driven recipe instead calls `recipe()` from +`core/styles/recipe.ts`, which wraps every base, variant, and compound-variant style it is given in +the `recipes` layer. A recipe can still pre-build a static `base` with `styleInLayer('recipes', …)` +and hand the resulting class string to `recipe()`, which passes a string value through unchanged +rather than wrapping it again. Text's Capsize trim declarations use logical properties for the pseudo-element margins and are authored as one of the Text recipe's `recipe()` compound-variant styles, so they remain owned by @@ -220,10 +223,10 @@ Colocate recipe files beside their owner: - `recipe.css.ts` — public recipe contract - `styles.css.ts` — private implementation styling -Every recipe is built with the internal `recipe()` engine from `styles/recipe.ts`. It is not part of -the public package entry. Component authors inside `@luke-ui/react` use it to define a new recipe. -Consumers call the built recipe functions it returns (`buttonRecipe`, `textRecipe`, and so on). -`recipe()` wraps every base, variant, and compound-variant style it is given in the `recipes` +Every recipe is built with the internal `recipe()` engine from `core/styles/recipe.ts`. It is not +part of the public package entry. Component authors inside `@luke-ui/react` use it to define a new +recipe. Consumers call the built recipe functions it returns (`buttonRecipe`, `textRecipe`, and so +on). `recipe()` wraps every base, variant, and compound-variant style it is given in the `recipes` cascade layer itself, so a recipe author does not add layering by hand. ### Single-part recipes @@ -244,7 +247,7 @@ export const buttonRecipe = recipe({ }); ``` -See `primitives/button/recipe.css.ts` for the full recipe this abbreviates. +See `core/primitives/button/recipe.css.ts` for the full recipe this abbreviates. ### Slotted recipes @@ -265,7 +268,7 @@ const { label, message, root } = fieldRecipe({ tone: 'description' }); ; ``` -See `primitives/field/recipe.css.ts` for a complete public slotted recipe. Apply +See `core/primitives/field/recipe.css.ts` for a complete public slotted recipe. Apply `as const satisfies SlottedConfigInput` at the definition site: `as const` preserves the literal slot names and variant values `recipe()` infers, and `satisfies` type-checks every slot and variant style against `StyleRule` where it is written. @@ -288,9 +291,9 @@ with the recipe. ### Shared input-state selectors -`InputGroup` and Combobox styling (`primitives/input-group/recipe.css.ts`, -`primitives/combobox/styles.css.ts`) share one definition of what "hovered", "focused", "disabled", -"invalid", and "read-only" mean for a control, from `styles/input-states.ts`: +`InputGroup` and Combobox styling (`core/primitives/input-group/recipe.css.ts`, +`core/primitives/combobox/styles.css.ts`) share one definition of what "hovered", "focused", +"disabled", "invalid", and "read-only" mean for a control, from `core/styles/input-states.ts`: ```ts import { composeInputStateSelectors, descendantDisabledSelector } from './input-states.js'; diff --git a/docs/TESTING.md b/docs/TESTING.md index 111864f3..7f2ce9e4 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -18,7 +18,7 @@ Use the smallest surface that can falsify the intention. Do not add a new test flavour for a component. Recipe tests are infrastructure tests, not a component-testing category. If a recipe implementation needs a browser to test its own machinery, -keep that test under `src/styles/` and exclude it from these component rules. +keep that test under `src/core/styles/` and exclude it from these component rules. ## Component tests @@ -109,8 +109,8 @@ menu, focus a control, or reveal a loading state for documentation or an accessi ## Visual tests -Visual tests use `render()` from `src/test-utils/render.tsx` and capture helpers from -`src/test-utils/visual.tsx`. The visual test is not a story and does not reuse a story as its +Visual tests use `render()` from `src/core/test-utils/render.tsx` and capture helpers from +`src/core/test-utils/visual.tsx`. The visual test is not a story and does not reuse a story as its fixture. Each component with a meaningful visual surface gets one kitchen-sink fixture: diff --git a/docs/VISUAL_TESTING.md b/docs/VISUAL_TESTING.md index 69aeb2c3..9acf6a2d 100644 --- a/docs/VISUAL_TESTING.md +++ b/docs/VISUAL_TESTING.md @@ -15,7 +15,7 @@ current working tree, including uncommitted changes. Fetch before running when y remote commit. Set `VISUAL_BASE_REF` to use another local ref, such as `upstream/main` in a fork. The command compares matching capture IDs and writes a self-contained report to `.artifacts/visual-regression/report/index.html`. Capture identity, harness files, and that artifact -directory are defined in `packages/@luke-ui/react/src/test-utils/visual-capture-id.ts` and +directory are defined in `packages/@luke-ui/react/src/core/test-utils/visual-capture-id.ts` and `packages/@luke-ui/react/scripts/visual-regression-contract.ts`. GitHub Actions cannot import those modules. A unit test fails if the workflow lists a different harness file or artifact path. `vitest.config.ts` is copied into the base worktree, so it also cannot import those modules. A unit diff --git a/knip.config.ts b/knip.config.ts index 60a4ff45..d35b2b05 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -24,9 +24,9 @@ export default { 'packages/@luke-ui/react': { entry: [ 'src/**/*.stories.tsx', - 'src/**/index.ts', - 'src/stylesheet.css.ts', - 'src/styles/index.css.ts', + 'src/exports/**/*.ts', + 'src/core/stylesheet.css.ts', + 'src/core/styles/index.css.ts', 'scripts/**/*.ts', ], project: ['src/**/*.{ts,tsx}'], diff --git a/packages/@luke-ui/react/.storybook/main.ts b/packages/@luke-ui/react/.storybook/main.ts index 2030372d..4a44f9f7 100644 --- a/packages/@luke-ui/react/.storybook/main.ts +++ b/packages/@luke-ui/react/.storybook/main.ts @@ -36,7 +36,7 @@ export default defineMain({ config.resolve.alias = [ { find: '#recipe-engine', - replacement: resolve(srcDir, 'styles/recipe-engine.ts'), + replacement: resolve(srcDir, 'core/styles/recipe-engine.ts'), }, { find: /^@luke-ui\/react\/spritesheet\.svg(\?.*)?$/, @@ -44,11 +44,11 @@ export default defineMain({ }, { find: /^@luke-ui\/react\/stylesheet\.css(\?.*)?$/, - replacement: `${resolve(srcDir, 'stylesheet.css.ts')}$1`, + replacement: `${resolve(srcDir, 'core/stylesheet.css.ts')}$1`, }, { find: /^@luke-ui\/react\/(.+?)(\?.*)?$/, - replacement: `${srcDir}/$1$2`, + replacement: `${resolve(srcDir, 'exports')}/$1$2`, }, ...existingAliases, ]; diff --git a/packages/@luke-ui/react/AGENTS.md b/packages/@luke-ui/react/AGENTS.md index 2563b2da..14b23686 100644 --- a/packages/@luke-ui/react/AGENTS.md +++ b/packages/@luke-ui/react/AGENTS.md @@ -3,8 +3,9 @@ - Do not hand-edit `.generated/entries.ts` or `package.json#exports`. `vp pack` generates entries and updates exports during build. The `stylesheet` build entry is excluded from the public export map via `exports.exclude` in `vite.config.ts`. Vanilla Extract serializes recipes to - `#recipe-engine`; pack, Vitest, Storybook, and the docs app alias that specifier to source. Pack - then bundles a relative runtime chunk. The specifier is not a public package subpath. + `#recipe-engine`; pack, Vitest, Storybook, and the docs app alias that specifier to + `src/core/styles/recipe-engine.ts`. Pack then bundles a relative runtime chunk. The specifier is + not a public package subpath. - When adding a component, use `pnpm generate:component` from the repo root. Do not create component files by hand. The generator updates the style-module registry, conformance manifest, and docs wiring. @@ -14,19 +15,34 @@ - React Compiler is enabled. Do not use `useCallback` or `useMemo` unless there is a specific reason the compiler cannot handle. +## Source structure + +- `src/core/` holds implementation, styles, test helpers, and their co-located tests, stories, and + fixtures. +- `src/exports/` holds the thin public modules that pack publishes for each package subpath. +- `src/theme/` holds the theme compiler, foundations, and bundled themes in `bundles/`. +- `src/shared/` holds low-level primitives with no domain of their own, such as the class-name + constants and the typed object helpers. It exists so a zone never has to duplicate a value or + reach across a forbidden edge to use one. It stays deliberately small. Do not put component code, + theme compiler logic, recipes, or other domain-specific implementation there. + +`exports` may import from `core`, `theme`, and `shared`. `core` may import from `theme` and +`shared`. `theme` may import from `shared`. `shared` imports from no other zone. + ## Component structure -A component directory contains: +`src/exports/` is the only barrel layer in the package. A component directory holds implementation, +styles, tests, and stories, and its matching `src/exports/` module re-exports directly from those +files. A component directory contains: - `[component].stories.tsx`: Storybook documentation and render/a11y fixtures - `[component].browser.test.tsx`: component behaviour, conformance, and the integration tripwire - `[component].visual.test.tsx`: visual regression captures when the component has a visual surface - `.tsx`: component implementation -- `index.ts`: export-only public subpath barrel - `recipe.css.ts`: public recipe contract (scaffolded by the generator) - `styles.css.ts`: private implementation styling when needed -Lower-level composition APIs live under `src/primitives/*` and export from +Lower-level composition APIs live under `src/core/primitives/*` and export from `@luke-ui/react/primitives/*`. See [`docs/COMPONENTS.md`](../../docs/COMPONENTS.md). ## Exported prop types diff --git a/packages/@luke-ui/react/package.json b/packages/@luke-ui/react/package.json index d86fe052..f468ba1a 100644 --- a/packages/@luke-ui/react/package.json +++ b/packages/@luke-ui/react/package.json @@ -19,37 +19,37 @@ "./dist/themes/*/stylesheet.css" ], "exports": { - "./blockquote": "./dist/blockquote/index.js", - "./box": "./dist/box/index.js", - "./button": "./dist/button/index.js", - "./checkbox": "./dist/checkbox/index.js", - "./code": "./dist/code/index.js", - "./combobox-field": "./dist/combobox-field/index.js", - "./em": "./dist/em/index.js", - "./emoji": "./dist/emoji/index.js", - "./heading": "./dist/heading/index.js", - "./icon": "./dist/icon/index.js", - "./icon-button": "./dist/icon-button/index.js", - "./kbd": "./dist/kbd/index.js", - "./link": "./dist/link/index.js", - "./loading-skeleton": "./dist/loading-skeleton/index.js", - "./loading-spinner": "./dist/loading-spinner/index.js", - "./numeral": "./dist/numeral/index.js", - "./primitives/button": "./dist/primitives/button/index.js", - "./primitives/checkbox": "./dist/primitives/checkbox/index.js", - "./primitives/combobox": "./dist/primitives/combobox/index.js", - "./primitives/field": "./dist/primitives/field/index.js", - "./primitives/input-group": "./dist/primitives/input-group/index.js", - "./quote": "./dist/quote/index.js", - "./strong": "./dist/strong/index.js", - "./styles": "./dist/styles/index.js", - "./text": "./dist/text/index.js", - "./text-field": "./dist/text-field/index.js", - "./theme": "./dist/theme/index.js", - "./themes/paper": "./dist/themes/paper/index.js", - "./themes/tactile": "./dist/themes/tactile/index.js", - "./utils": "./dist/utils/index.js", - "./visually-hidden": "./dist/visually-hidden/index.js", + "./blockquote": "./dist/blockquote.js", + "./box": "./dist/box.js", + "./button": "./dist/button.js", + "./checkbox": "./dist/checkbox.js", + "./code": "./dist/code.js", + "./combobox-field": "./dist/combobox-field.js", + "./em": "./dist/em.js", + "./emoji": "./dist/emoji.js", + "./heading": "./dist/heading.js", + "./icon": "./dist/icon.js", + "./icon-button": "./dist/icon-button.js", + "./kbd": "./dist/kbd.js", + "./link": "./dist/link.js", + "./loading-skeleton": "./dist/loading-skeleton.js", + "./loading-spinner": "./dist/loading-spinner.js", + "./numeral": "./dist/numeral.js", + "./primitives/button": "./dist/primitives/button.js", + "./primitives/checkbox": "./dist/primitives/checkbox.js", + "./primitives/combobox": "./dist/primitives/combobox.js", + "./primitives/field": "./dist/primitives/field.js", + "./primitives/input-group": "./dist/primitives/input-group.js", + "./quote": "./dist/quote.js", + "./strong": "./dist/strong.js", + "./styles": "./dist/styles.js", + "./text": "./dist/text.js", + "./text-field": "./dist/text-field.js", + "./theme": "./dist/theme.js", + "./themes/paper": "./dist/themes/paper.js", + "./themes/tactile": "./dist/themes/tactile.js", + "./utils": "./dist/utils.js", + "./visually-hidden": "./dist/visually-hidden.js", "./package.json": "./package.json", "./stylesheet.css": "./dist/stylesheet.css", "./spritesheet.svg": "./dist/spritesheet.svg", @@ -63,7 +63,7 @@ "build": "pnpm run build:tsdown", "build:storybook": "storybook build", "build:tsdown": "vp pack", - "check:barrels": "unbarrelify --check --skip 'src/*/index.ts' --skip 'src/primitives/*/index.ts' --skip 'src/themes/*/index.ts'", + "check:barrels": "unbarrelify --check --skip 'src/exports/**/*.ts' --skip 'src/theme/index.ts' --skip 'src/theme/bundles/*/index.ts'", "check:format": "vp fmt . --check", "check:lint": "vp lint . --type-aware", "check:story-plays": "tsx scripts/check-story-plays.ts", diff --git a/packages/@luke-ui/react/scripts/visual-regression-contract.ts b/packages/@luke-ui/react/scripts/visual-regression-contract.ts index 731b084f..ac9c9053 100644 --- a/packages/@luke-ui/react/scripts/visual-regression-contract.ts +++ b/packages/@luke-ui/react/scripts/visual-regression-contract.ts @@ -3,12 +3,16 @@ import { fileURLToPath } from 'node:url'; export const VISUAL_LOCKFILE = 'pnpm-lock.yaml'; -/** Files that shape how a capture renders. Hashed into the cache key and copied into the base worktree. */ +/** Marks a baseline worktree that can accept the current visual harness. */ +export const VISUAL_HARNESS_LAYOUT_FILE = 'packages/@luke-ui/react/src/core/stylesheet.css.ts'; + +/** Files that shape how a capture renders. Hashed into the cache key and copied into compatible base worktrees. */ export const VISUAL_HARNESS_FILES = [ 'packages/@luke-ui/react/vitest.config.ts', - 'packages/@luke-ui/react/src/test-utils/render-setup.ts', - 'packages/@luke-ui/react/src/test-utils/render.tsx', - 'packages/@luke-ui/react/src/test-utils/visual-setup.ts', + VISUAL_HARNESS_LAYOUT_FILE, + 'packages/@luke-ui/react/src/core/test-utils/render-setup.ts', + 'packages/@luke-ui/react/src/core/test-utils/render.tsx', + 'packages/@luke-ui/react/src/core/test-utils/visual-setup.ts', ] as const; export const VISUAL_CACHE_HASH_FILES = [VISUAL_LOCKFILE, ...VISUAL_HARNESS_FILES]; diff --git a/packages/@luke-ui/react/scripts/visual-regression-lib.test.ts b/packages/@luke-ui/react/scripts/visual-regression-lib.test.ts index 49977e3b..f78eb075 100644 --- a/packages/@luke-ui/react/scripts/visual-regression-lib.test.ts +++ b/packages/@luke-ui/react/scripts/visual-regression-lib.test.ts @@ -6,7 +6,7 @@ import { expect, test } from 'vite-plus/test'; import { formatVisualCaptureName, formatVisualViewport, -} from '../src/test-utils/visual-capture-id.js'; +} from '../src/core/test-utils/visual-capture-id.js'; import { assertCapturesPainted, compareCaptures, renderReport } from './visual-regression-lib.js'; const png = (red: number) => { diff --git a/packages/@luke-ui/react/scripts/visual-regression-lib.ts b/packages/@luke-ui/react/scripts/visual-regression-lib.ts index 06ed56dd..dcd88955 100644 --- a/packages/@luke-ui/react/scripts/visual-regression-lib.ts +++ b/packages/@luke-ui/react/scripts/visual-regression-lib.ts @@ -2,7 +2,7 @@ import path from 'node:path'; import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'; import pixelmatch from 'pixelmatch'; import { PNG } from 'pngjs'; -import { parseVisualCaptureIdentity } from '../src/test-utils/visual-capture-id.js'; +import { parseVisualCaptureIdentity } from '../src/core/test-utils/visual-capture-id.js'; export type VisualResult = { id: string; diff --git a/packages/@luke-ui/react/scripts/visual-regression-runner.ts b/packages/@luke-ui/react/scripts/visual-regression-runner.ts index 0f8ce8d6..fa4ec7c5 100644 --- a/packages/@luke-ui/react/scripts/visual-regression-runner.ts +++ b/packages/@luke-ui/react/scripts/visual-regression-runner.ts @@ -7,6 +7,7 @@ import { VISUAL_CACHE_HASH_FILES, VISUAL_CAPTURE_DIR_ENV, VISUAL_HARNESS_FILES, + VISUAL_HARNESS_LAYOUT_FILE, visualArtifactsRoot, visualPackageRoot, visualRepoRootFromPackage, @@ -130,7 +131,7 @@ export async function runVisualRegression(io: VisualRegressionIo = createVisualR } async function capture(io: VisualRegressionIo, worktree: string, target: string) { - if (worktree !== io.repoRoot) { + if (worktree !== io.repoRoot && (await hasCurrentVisualHarnessLayout(io, worktree))) { await Promise.all( VISUAL_HARNESS_FILES.map(async (file) => { const destination = path.join(worktree, file); @@ -167,3 +168,15 @@ async function capture(io: VisualRegressionIo, worktree: string, target: string) { ...io.env, [VISUAL_CAPTURE_DIR_ENV]: target }, ); } + +async function hasCurrentVisualHarnessLayout( + io: VisualRegressionIo, + worktree: string, +): Promise { + try { + await io.readFile(path.join(worktree, VISUAL_HARNESS_LAYOUT_FILE)); + return true; + } catch { + return false; + } +} diff --git a/packages/@luke-ui/react/src/blockquote/index.ts b/packages/@luke-ui/react/src/blockquote/index.ts deleted file mode 100644 index 0ecc08ac..00000000 --- a/packages/@luke-ui/react/src/blockquote/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Blockquote, type BlockquoteProps } from './blockquote.js'; -export { type BlockquoteRecipeVariants, blockquoteRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/box/index.ts b/packages/@luke-ui/react/src/box/index.ts deleted file mode 100644 index 4cc734ce..00000000 --- a/packages/@luke-ui/react/src/box/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Box, type BoxProps } from './box.js'; diff --git a/packages/@luke-ui/react/src/button/index.ts b/packages/@luke-ui/react/src/button/index.ts deleted file mode 100644 index 58bbed93..00000000 --- a/packages/@luke-ui/react/src/button/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { type ButtonRecipeVariants, buttonRecipe } from '../primitives/button/recipe.css.js'; -export { Button, type ButtonProps } from './button.js'; diff --git a/packages/@luke-ui/react/src/checkbox/index.ts b/packages/@luke-ui/react/src/checkbox/index.ts deleted file mode 100644 index 8550f049..00000000 --- a/packages/@luke-ui/react/src/checkbox/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { type CheckboxRecipeVariants, checkboxRecipe } from '../primitives/checkbox/recipe.css.js'; -export { Checkbox, type CheckboxProps } from './checkbox.js'; diff --git a/packages/@luke-ui/react/src/code/index.ts b/packages/@luke-ui/react/src/code/index.ts deleted file mode 100644 index 52c9543c..00000000 --- a/packages/@luke-ui/react/src/code/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Code, type CodeProps } from './code.js'; -export { type CodeRecipeVariants, codeRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/combobox-field/index.ts b/packages/@luke-ui/react/src/combobox-field/index.ts deleted file mode 100644 index c66a32c6..00000000 --- a/packages/@luke-ui/react/src/combobox-field/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { ComboboxField, type ComboboxFieldProps } from './combobox-field.js'; diff --git a/packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx b/packages/@luke-ui/react/src/core/blockquote/blockquote.browser.test.tsx similarity index 90% rename from packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx rename to packages/@luke-ui/react/src/core/blockquote/blockquote.browser.test.tsx index 4661fbce..0c669f86 100644 --- a/packages/@luke-ui/react/src/blockquote/blockquote.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/blockquote/blockquote.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Blockquote } from './index.js'; +import { Blockquote } from './blockquote.js'; testConformance({ path: 'blockquote', diff --git a/packages/@luke-ui/react/src/blockquote/blockquote.stories.tsx b/packages/@luke-ui/react/src/core/blockquote/blockquote.stories.tsx similarity index 90% rename from packages/@luke-ui/react/src/blockquote/blockquote.stories.tsx rename to packages/@luke-ui/react/src/core/blockquote/blockquote.stories.tsx index fdd2fd58..2c6103c5 100644 --- a/packages/@luke-ui/react/src/blockquote/blockquote.stories.tsx +++ b/packages/@luke-ui/react/src/core/blockquote/blockquote.stories.tsx @@ -1,6 +1,6 @@ import { Blockquote } from '@luke-ui/react/blockquote'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Blockquote, diff --git a/packages/@luke-ui/react/src/blockquote/blockquote.tsx b/packages/@luke-ui/react/src/core/blockquote/blockquote.tsx similarity index 94% rename from packages/@luke-ui/react/src/blockquote/blockquote.tsx rename to packages/@luke-ui/react/src/core/blockquote/blockquote.tsx index 7a33c805..053f24df 100644 --- a/packages/@luke-ui/react/src/blockquote/blockquote.tsx +++ b/packages/@luke-ui/react/src/core/blockquote/blockquote.tsx @@ -1,8 +1,8 @@ +import { cx } from '../../shared/utils/utils.js'; import type { TextProps } from '../text/text.js'; import { Text } from '../text/text.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { blockquoteRecipe } from './recipe.css.js'; type _BlockquoteOmit = DistributiveOmit; diff --git a/packages/@luke-ui/react/src/blockquote/recipe.css.ts b/packages/@luke-ui/react/src/core/blockquote/recipe.css.ts similarity index 91% rename from packages/@luke-ui/react/src/blockquote/recipe.css.ts rename to packages/@luke-ui/react/src/core/blockquote/recipe.css.ts index f3899f54..1f7a12c4 100644 --- a/packages/@luke-ui/react/src/blockquote/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/blockquote/recipe.css.ts @@ -1,7 +1,7 @@ +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; const base = styleInLayer('recipes', { borderInlineStart: `3px solid ${vars.color.border.decorative}`, diff --git a/packages/@luke-ui/react/src/box/box.browser.test.tsx b/packages/@luke-ui/react/src/core/box/box.browser.test.tsx similarity index 97% rename from packages/@luke-ui/react/src/box/box.browser.test.tsx rename to packages/@luke-ui/react/src/core/box/box.browser.test.tsx index 733fcc59..f3853f86 100644 --- a/packages/@luke-ui/react/src/box/box.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/box/box.browser.test.tsx @@ -1,7 +1,7 @@ import { expect, test } from 'vite-plus/test'; import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Box } from './index.js'; +import { Box } from './box.js'; testConformance({ path: 'box', diff --git a/packages/@luke-ui/react/src/box/box.stories.tsx b/packages/@luke-ui/react/src/core/box/box.stories.tsx similarity index 96% rename from packages/@luke-ui/react/src/box/box.stories.tsx rename to packages/@luke-ui/react/src/core/box/box.stories.tsx index 45276490..4840abe6 100644 --- a/packages/@luke-ui/react/src/box/box.stories.tsx +++ b/packages/@luke-ui/react/src/core/box/box.stories.tsx @@ -2,7 +2,7 @@ import type { BoxProps } from '@luke-ui/react/box'; import { Box } from '@luke-ui/react/box'; import { vars } from '@luke-ui/react/theme'; import type { ComponentPropsWithRef } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Box, diff --git a/packages/@luke-ui/react/src/box/box.tsx b/packages/@luke-ui/react/src/core/box/box.tsx similarity index 98% rename from packages/@luke-ui/react/src/box/box.tsx rename to packages/@luke-ui/react/src/core/box/box.tsx index 363955bd..8af7e1ff 100644 --- a/packages/@luke-ui/react/src/box/box.tsx +++ b/packages/@luke-ui/react/src/core/box/box.tsx @@ -1,9 +1,9 @@ import type { HTMLAttributes, JSX, ReactElement, Ref, RefCallback } from 'react'; +import { mergeProps } from '../../shared/utils/utils.js'; import type { SprinklesProps } from '../styles/utilities.css.js'; import { createSprinkles } from '../styles/utilities.css.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { mergeProps } from '../utils/utils.js'; /** Props for `Box`. */ export type BoxProps = Prettify<_BoxElementProps | _BoxRenderProps>; diff --git a/packages/@luke-ui/react/src/box/box.visual.test.tsx b/packages/@luke-ui/react/src/core/box/box.visual.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/box/box.visual.test.tsx rename to packages/@luke-ui/react/src/core/box/box.visual.test.tsx index f230c163..dc60f09a 100644 --- a/packages/@luke-ui/react/src/box/box.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/box/box.visual.test.tsx @@ -1,8 +1,8 @@ import { test } from 'vite-plus/test'; +import { vars } from '../../theme/index.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisualAppearance } from '../test-utils/visual.js'; -import { vars } from '../theme/index.js'; -import { Box } from './index.js'; +import { Box } from './box.js'; for (const appearance of visualAppearances) { test(`layout: ${appearance.theme} ${appearance.mode}`, async () => { diff --git a/packages/@luke-ui/react/src/button/button.browser.test.tsx b/packages/@luke-ui/react/src/core/button/button.browser.test.tsx similarity index 95% rename from packages/@luke-ui/react/src/button/button.browser.test.tsx rename to packages/@luke-ui/react/src/core/button/button.browser.test.tsx index c971f097..b302de78 100644 --- a/packages/@luke-ui/react/src/button/button.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/button/button.browser.test.tsx @@ -1,7 +1,7 @@ import { expect } from 'vite-plus/test'; import { testConformance, testIntegration } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Button } from './index.js'; +import { Button } from './button.js'; testConformance({ path: 'button', diff --git a/packages/@luke-ui/react/src/button/button.stories.tsx b/packages/@luke-ui/react/src/core/button/button.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/button/button.stories.tsx rename to packages/@luke-ui/react/src/core/button/button.stories.tsx index 757ec094..9dee82f1 100644 --- a/packages/@luke-ui/react/src/button/button.stories.tsx +++ b/packages/@luke-ui/react/src/core/button/button.stories.tsx @@ -3,8 +3,8 @@ import { Button } from '@luke-ui/react/button'; import { Icon } from '@luke-ui/react/icon'; import type { CSSProperties } from 'react'; import { fn } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; -import { vars } from '../theme/index.js'; +import preview from '../../../.storybook/preview.js'; +import { vars } from '../../theme/index.js'; const meta = preview.meta({ component: Button, diff --git a/packages/@luke-ui/react/src/button/button.tsx b/packages/@luke-ui/react/src/core/button/button.tsx similarity index 100% rename from packages/@luke-ui/react/src/button/button.tsx rename to packages/@luke-ui/react/src/core/button/button.tsx diff --git a/packages/@luke-ui/react/src/button/button.visual.test.tsx b/packages/@luke-ui/react/src/core/button/button.visual.test.tsx similarity index 97% rename from packages/@luke-ui/react/src/button/button.visual.test.tsx rename to packages/@luke-ui/react/src/core/button/button.visual.test.tsx index 20238401..460b31b1 100644 --- a/packages/@luke-ui/react/src/button/button.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/button/button.visual.test.tsx @@ -1,6 +1,6 @@ import { test } from 'vite-plus/test'; import { page, userEvent } from 'vite-plus/test/context'; -import { Icon } from '../icon/index.js'; +import { Icon } from '../icon/icon.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, @@ -10,7 +10,7 @@ import { Grid, variantValuesFor, } from '../test-utils/visual.js'; -import { Button } from './index.js'; +import { Button } from './button.js'; const tones = variantValuesFor()(['neutral', 'accent', 'danger']); const appearances = variantValuesFor()(['solid', 'subtle', 'ghost']); diff --git a/packages/@luke-ui/react/src/button/styles.css.ts b/packages/@luke-ui/react/src/core/button/styles.css.ts similarity index 94% rename from packages/@luke-ui/react/src/button/styles.css.ts rename to packages/@luke-ui/react/src/core/button/styles.css.ts index 0ec4d16f..68af3c5e 100644 --- a/packages/@luke-ui/react/src/button/styles.css.ts +++ b/packages/@luke-ui/react/src/core/button/styles.css.ts @@ -1,7 +1,7 @@ +import { vars } from '../../theme/contract.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; import { spinnerOverlayBase } from '../styles/spinner-overlay.js'; -import { vars } from '../theme/contract.css.js'; export const buttonContent = recipe({ base: { diff --git a/packages/@luke-ui/react/src/checkbox/checkbox.browser.test.tsx b/packages/@luke-ui/react/src/core/checkbox/checkbox.browser.test.tsx similarity index 99% rename from packages/@luke-ui/react/src/checkbox/checkbox.browser.test.tsx rename to packages/@luke-ui/react/src/core/checkbox/checkbox.browser.test.tsx index 92899455..46c4784f 100644 --- a/packages/@luke-ui/react/src/checkbox/checkbox.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/checkbox/checkbox.browser.test.tsx @@ -2,7 +2,7 @@ import { expect, test } from 'vite-plus/test'; import { cdp, page } from 'vite-plus/test/context'; import { testConformance, testIntegration } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Checkbox } from './index.js'; +import { Checkbox } from './checkbox.js'; testConformance({ path: 'checkbox', diff --git a/packages/@luke-ui/react/src/checkbox/checkbox.stories.tsx b/packages/@luke-ui/react/src/core/checkbox/checkbox.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/checkbox/checkbox.stories.tsx rename to packages/@luke-ui/react/src/core/checkbox/checkbox.stories.tsx index 9d3c69b8..23be6caa 100644 --- a/packages/@luke-ui/react/src/checkbox/checkbox.stories.tsx +++ b/packages/@luke-ui/react/src/core/checkbox/checkbox.stories.tsx @@ -2,7 +2,7 @@ import { Checkbox } from '@luke-ui/react/checkbox'; import type { CSSProperties } from 'react'; import { Form } from 'react-aria-components/Form'; import { userEvent, within } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const stackStyle = { display: 'flex', diff --git a/packages/@luke-ui/react/src/checkbox/checkbox.tsx b/packages/@luke-ui/react/src/core/checkbox/checkbox.tsx similarity index 100% rename from packages/@luke-ui/react/src/checkbox/checkbox.tsx rename to packages/@luke-ui/react/src/core/checkbox/checkbox.tsx diff --git a/packages/@luke-ui/react/src/checkbox/checkbox.visual.test.tsx b/packages/@luke-ui/react/src/core/checkbox/checkbox.visual.test.tsx similarity index 97% rename from packages/@luke-ui/react/src/checkbox/checkbox.visual.test.tsx rename to packages/@luke-ui/react/src/core/checkbox/checkbox.visual.test.tsx index 41fec9bf..9309406b 100644 --- a/packages/@luke-ui/react/src/checkbox/checkbox.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/checkbox/checkbox.visual.test.tsx @@ -1,6 +1,7 @@ import { test } from 'vite-plus/test'; import type { Locator } from 'vite-plus/test/context'; import { page, userEvent } from 'vite-plus/test/context'; +import { typeStyles } from '../../theme/contract.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, @@ -9,9 +10,8 @@ import { focusViaKeyboard, Stack, } from '../test-utils/visual.js'; -import { Text } from '../text/index.js'; -import { typeStyles } from '../theme/contract.js'; -import { Checkbox } from './index.js'; +import { Text } from '../text/text.js'; +import { Checkbox } from './checkbox.js'; test('kitchen sink', async () => { for (const appearance of visualAppearances) { diff --git a/packages/@luke-ui/react/src/code/code.browser.test.tsx b/packages/@luke-ui/react/src/core/code/code.browser.test.tsx similarity index 92% rename from packages/@luke-ui/react/src/code/code.browser.test.tsx rename to packages/@luke-ui/react/src/core/code/code.browser.test.tsx index 29004c2d..f74ff2c1 100644 --- a/packages/@luke-ui/react/src/code/code.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/code/code.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Code } from './index.js'; +import { Code } from './code.js'; testConformance({ path: 'code', diff --git a/packages/@luke-ui/react/src/code/code.stories.tsx b/packages/@luke-ui/react/src/core/code/code.stories.tsx similarity index 88% rename from packages/@luke-ui/react/src/code/code.stories.tsx rename to packages/@luke-ui/react/src/core/code/code.stories.tsx index 2944238a..0a92cbd5 100644 --- a/packages/@luke-ui/react/src/code/code.stories.tsx +++ b/packages/@luke-ui/react/src/core/code/code.stories.tsx @@ -1,6 +1,6 @@ import { Code } from '@luke-ui/react/code'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Code, diff --git a/packages/@luke-ui/react/src/code/code.tsx b/packages/@luke-ui/react/src/core/code/code.tsx similarity index 92% rename from packages/@luke-ui/react/src/code/code.tsx rename to packages/@luke-ui/react/src/core/code/code.tsx index 0ae31a3a..4ccbf537 100644 --- a/packages/@luke-ui/react/src/code/code.tsx +++ b/packages/@luke-ui/react/src/core/code/code.tsx @@ -1,6 +1,6 @@ +import { cx } from '../../shared/utils/utils.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { codeRecipe } from './recipe.css.js'; type _CodeOmit = DistributiveOmit, never>; diff --git a/packages/@luke-ui/react/src/code/recipe.css.ts b/packages/@luke-ui/react/src/core/code/recipe.css.ts similarity index 92% rename from packages/@luke-ui/react/src/code/recipe.css.ts rename to packages/@luke-ui/react/src/core/code/recipe.css.ts index 93868be4..9cb3f08d 100644 --- a/packages/@luke-ui/react/src/code/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/code/recipe.css.ts @@ -1,7 +1,7 @@ +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; const base = styleInLayer('recipes', { backgroundColor: vars.color.surface.recessed, diff --git a/packages/@luke-ui/react/src/combobox-field/combobox-field.browser.test.tsx b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.browser.test.tsx similarity index 99% rename from packages/@luke-ui/react/src/combobox-field/combobox-field.browser.test.tsx rename to packages/@luke-ui/react/src/core/combobox-field/combobox-field.browser.test.tsx index 0501dd2e..00dbc9ee 100644 --- a/packages/@luke-ui/react/src/combobox-field/combobox-field.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.browser.test.tsx @@ -9,7 +9,7 @@ import { ComboboxRoot } from '../primitives/combobox/root.js'; import { mockScreenWidth } from '../test-utils/mock-screen-width.js'; import { render } from '../test-utils/render.js'; import { waitForOverlayEnter } from '../test-utils/wait-for-overlay-enter.js'; -import { ComboboxField } from './index.js'; +import { ComboboxField } from './combobox-field.js'; type CountryItem = { id: string; diff --git a/packages/@luke-ui/react/src/combobox-field/combobox-field.stories.tsx b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.stories.tsx similarity index 99% rename from packages/@luke-ui/react/src/combobox-field/combobox-field.stories.tsx rename to packages/@luke-ui/react/src/core/combobox-field/combobox-field.stories.tsx index 3dd4119c..6f5d3f64 100644 --- a/packages/@luke-ui/react/src/combobox-field/combobox-field.stories.tsx +++ b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.stories.tsx @@ -7,7 +7,7 @@ import type { Key } from 'react-aria-components/Breadcrumbs'; import { Form } from 'react-aria-components/Form'; import { useAsyncList } from 'react-aria-components/useAsyncList'; import { userEvent, within } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: ComboboxField, diff --git a/packages/@luke-ui/react/src/combobox-field/combobox-field.tsx b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.tsx similarity index 99% rename from packages/@luke-ui/react/src/combobox-field/combobox-field.tsx rename to packages/@luke-ui/react/src/core/combobox-field/combobox-field.tsx index 07836c0a..37129e9e 100644 --- a/packages/@luke-ui/react/src/combobox-field/combobox-field.tsx +++ b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.tsx @@ -9,6 +9,7 @@ import { LabelContext } from 'react-aria-components/Label'; import { PopoverContext } from 'react-aria-components/Popover'; import { composeRenderProps } from 'react-aria-components/composeRenderProps'; import { useSlottedContext } from 'react-aria-components/slots'; +import { cx } from '../../shared/utils/utils.js'; import { Icon } from '../icon/icon.js'; import { LoadingSpinner } from '../loading-spinner/loading-spinner.js'; import { MobileOverlay } from '../overlays/mobile-overlay.js'; @@ -35,7 +36,6 @@ import { } from '../primitives/field/field.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; type ComboboxLoadingState = 'error' | 'filtering' | 'idle' | 'loading' | 'loadingMore' | 'sorting'; diff --git a/packages/@luke-ui/react/src/combobox-field/combobox-field.visual.test.tsx b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.visual.test.tsx similarity index 98% rename from packages/@luke-ui/react/src/combobox-field/combobox-field.visual.test.tsx rename to packages/@luke-ui/react/src/core/combobox-field/combobox-field.visual.test.tsx index 104f0ab0..781932c3 100644 --- a/packages/@luke-ui/react/src/combobox-field/combobox-field.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/combobox-field/combobox-field.visual.test.tsx @@ -1,7 +1,7 @@ import { expect, test } from 'vite-plus/test'; import { page, userEvent } from 'vite-plus/test/context'; -import { Icon } from '../icon/index.js'; -import { LoadingSpinner } from '../loading-spinner/index.js'; +import { Icon } from '../icon/icon.js'; +import { LoadingSpinner } from '../loading-spinner/loading-spinner.js'; import { ComboboxInputGroup } from '../primitives/combobox/input-group.js'; import { ComboboxInput } from '../primitives/combobox/input.js'; import { ComboboxItem, ComboboxLoadMoreItem } from '../primitives/combobox/item.js'; @@ -21,7 +21,7 @@ import { Stack, } from '../test-utils/visual.js'; import { waitForOverlayEnter } from '../test-utils/wait-for-overlay-enter.js'; -import { ComboboxField } from './index.js'; +import { ComboboxField } from './combobox-field.js'; type CountryItem = { id: string; diff --git a/packages/@luke-ui/react/src/conformance/helpers.ts b/packages/@luke-ui/react/src/core/conformance/helpers.ts similarity index 100% rename from packages/@luke-ui/react/src/conformance/helpers.ts rename to packages/@luke-ui/react/src/core/conformance/helpers.ts diff --git a/packages/@luke-ui/react/src/conformance/manifest.test.ts b/packages/@luke-ui/react/src/core/conformance/manifest.test.ts similarity index 94% rename from packages/@luke-ui/react/src/conformance/manifest.test.ts rename to packages/@luke-ui/react/src/core/conformance/manifest.test.ts index d8d10b39..f1e26618 100644 --- a/packages/@luke-ui/react/src/conformance/manifest.test.ts +++ b/packages/@luke-ui/react/src/core/conformance/manifest.test.ts @@ -6,8 +6,9 @@ import { expect, test } from 'vite-plus/test'; import type { ComponentTestManifestEntry } from './manifest.js'; import { componentTestManifest } from './manifest.js'; -const sourceRoot = resolve(import.meta.dirname, '..'); -const packageRoot = resolve(sourceRoot, '..'); +const coreRoot = resolve(import.meta.dirname, '..'); +const exportsRoot = resolve(coreRoot, '../exports'); +const packageRoot = resolve(coreRoot, '../..'); // Public subpaths that are not normal component entrypoints, so they are exempt from the // conformance manifest. `theme` is deliberately absent from this set: it has its own manifest @@ -34,7 +35,7 @@ function getExportPaths() { if (!exportPath.startsWith('./') || exportPath.includes('*')) continue; const sourcePath = exportPath.slice(2); if (NON_COMPONENT_EXPORTS.has(sourcePath)) continue; - if (existsSync(resolve(sourceRoot, sourcePath, 'index.ts'))) { + if (existsSync(resolve(exportsRoot, `${sourcePath}.ts`))) { paths.push(sourcePath); } } @@ -45,7 +46,7 @@ function getExportPaths() { function componentFile(path: string, suffix: 'browser.test.tsx' | 'visual.test.tsx') { const basename = path.split('/').at(-1); if (basename == null) throw new Error(`Invalid component test path: ${path}`); - return resolve(sourceRoot, path, `${basename}.${suffix}`); + return resolve(coreRoot, path, `${basename}.${suffix}`); } function hasHelperCall(source: string, helperName: string, path: string): boolean { @@ -137,10 +138,14 @@ test('covers every public component entrypoint exactly once', () => { expect(new Set(paths).size).toBe(paths.length); expect([...paths].sort()).toEqual(getExportPaths()); for (const path of paths) { - expect(existsSync(resolve(sourceRoot, path, 'index.ts'))).toBe(true); + expect(existsSync(packageExportModule(path))).toBe(true); } }); +function packageExportModule(path: string): string { + return resolve(exportsRoot, `${path}.ts`); +} + test('browser tests invoke the helpers required by their manifest entry', () => { expect( getBrowserCoverageErrors(componentTestManifest, (path) => { diff --git a/packages/@luke-ui/react/src/conformance/manifest.ts b/packages/@luke-ui/react/src/core/conformance/manifest.ts similarity index 100% rename from packages/@luke-ui/react/src/conformance/manifest.ts rename to packages/@luke-ui/react/src/core/conformance/manifest.ts diff --git a/packages/@luke-ui/react/src/em/em.browser.test.tsx b/packages/@luke-ui/react/src/core/em/em.browser.test.tsx similarity index 92% rename from packages/@luke-ui/react/src/em/em.browser.test.tsx rename to packages/@luke-ui/react/src/core/em/em.browser.test.tsx index f15e9e06..b4b23369 100644 --- a/packages/@luke-ui/react/src/em/em.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/em/em.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Em } from './index.js'; +import { Em } from './em.js'; testConformance({ path: 'em', diff --git a/packages/@luke-ui/react/src/em/em.stories.tsx b/packages/@luke-ui/react/src/core/em/em.stories.tsx similarity index 92% rename from packages/@luke-ui/react/src/em/em.stories.tsx rename to packages/@luke-ui/react/src/core/em/em.stories.tsx index 02a50c1f..aa1e071c 100644 --- a/packages/@luke-ui/react/src/em/em.stories.tsx +++ b/packages/@luke-ui/react/src/core/em/em.stories.tsx @@ -1,6 +1,6 @@ import { Em } from '@luke-ui/react/em'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Em, diff --git a/packages/@luke-ui/react/src/em/em.tsx b/packages/@luke-ui/react/src/core/em/em.tsx similarity index 95% rename from packages/@luke-ui/react/src/em/em.tsx rename to packages/@luke-ui/react/src/core/em/em.tsx index 081c8e5f..67990121 100644 --- a/packages/@luke-ui/react/src/em/em.tsx +++ b/packages/@luke-ui/react/src/core/em/em.tsx @@ -1,8 +1,8 @@ +import { cx } from '../../shared/utils/utils.js'; import type { TextProps } from '../text/text.js'; import { Text } from '../text/text.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { em } from './styles.css.js'; interface EmStyleProps { diff --git a/packages/@luke-ui/react/src/em/styles.css.ts b/packages/@luke-ui/react/src/core/em/styles.css.ts similarity index 100% rename from packages/@luke-ui/react/src/em/styles.css.ts rename to packages/@luke-ui/react/src/core/em/styles.css.ts diff --git a/packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx b/packages/@luke-ui/react/src/core/emoji/emoji.browser.test.tsx similarity index 92% rename from packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx rename to packages/@luke-ui/react/src/core/emoji/emoji.browser.test.tsx index aa3a08f0..935cc29e 100644 --- a/packages/@luke-ui/react/src/emoji/emoji.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/emoji/emoji.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Emoji } from './index.js'; +import { Emoji } from './emoji.js'; testConformance({ path: 'emoji', diff --git a/packages/@luke-ui/react/src/emoji/emoji.stories.tsx b/packages/@luke-ui/react/src/core/emoji/emoji.stories.tsx similarity index 96% rename from packages/@luke-ui/react/src/emoji/emoji.stories.tsx rename to packages/@luke-ui/react/src/core/emoji/emoji.stories.tsx index add990f8..c6481c00 100644 --- a/packages/@luke-ui/react/src/emoji/emoji.stories.tsx +++ b/packages/@luke-ui/react/src/core/emoji/emoji.stories.tsx @@ -2,7 +2,7 @@ import type { EmojiProps } from '@luke-ui/react/emoji'; import { Emoji } from '@luke-ui/react/emoji'; import { Text } from '@luke-ui/react/text'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Emoji, diff --git a/packages/@luke-ui/react/src/emoji/emoji.tsx b/packages/@luke-ui/react/src/core/emoji/emoji.tsx similarity index 100% rename from packages/@luke-ui/react/src/emoji/emoji.tsx rename to packages/@luke-ui/react/src/core/emoji/emoji.tsx diff --git a/packages/@luke-ui/react/src/emoji/emoji.visual.test.tsx b/packages/@luke-ui/react/src/core/emoji/emoji.visual.test.tsx similarity index 93% rename from packages/@luke-ui/react/src/emoji/emoji.visual.test.tsx rename to packages/@luke-ui/react/src/core/emoji/emoji.visual.test.tsx index c2dc487a..56b7f787 100644 --- a/packages/@luke-ui/react/src/emoji/emoji.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/emoji/emoji.visual.test.tsx @@ -2,8 +2,8 @@ import type { CSSProperties } from 'react'; import { test } from 'vite-plus/test'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisualAppearance, Stack } from '../test-utils/visual.js'; -import { Text } from '../text/index.js'; -import { Emoji } from './index.js'; +import { Text } from '../text/text.js'; +import { Emoji } from './emoji.js'; const stackStyle = { display: 'flex', diff --git a/packages/@luke-ui/react/src/heading/heading-context.tsx b/packages/@luke-ui/react/src/core/heading/heading-context.tsx similarity index 100% rename from packages/@luke-ui/react/src/heading/heading-context.tsx rename to packages/@luke-ui/react/src/core/heading/heading-context.tsx diff --git a/packages/@luke-ui/react/src/heading/heading.browser.test.tsx b/packages/@luke-ui/react/src/core/heading/heading.browser.test.tsx similarity index 95% rename from packages/@luke-ui/react/src/heading/heading.browser.test.tsx rename to packages/@luke-ui/react/src/core/heading/heading.browser.test.tsx index 43d27226..b61c7d27 100644 --- a/packages/@luke-ui/react/src/heading/heading.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/heading/heading.browser.test.tsx @@ -1,7 +1,7 @@ import { expect, test } from 'vite-plus/test'; import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Heading, HeadingLevels, useHeadingLevel } from './index.js'; +import { Heading, HeadingLevels, useHeadingLevel } from './heading.js'; testConformance({ path: 'heading', diff --git a/packages/@luke-ui/react/src/heading/heading.stories.tsx b/packages/@luke-ui/react/src/core/heading/heading.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/heading/heading.stories.tsx rename to packages/@luke-ui/react/src/core/heading/heading.stories.tsx index a64ee971..dd7016d0 100644 --- a/packages/@luke-ui/react/src/heading/heading.stories.tsx +++ b/packages/@luke-ui/react/src/core/heading/heading.stories.tsx @@ -1,7 +1,7 @@ import type { HeadingProps } from '@luke-ui/react/heading'; import { Heading } from '@luke-ui/react/heading'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Heading, diff --git a/packages/@luke-ui/react/src/heading/heading.tsx b/packages/@luke-ui/react/src/core/heading/heading.tsx similarity index 100% rename from packages/@luke-ui/react/src/heading/heading.tsx rename to packages/@luke-ui/react/src/core/heading/heading.tsx diff --git a/packages/@luke-ui/react/src/heading/heading.visual.test.tsx b/packages/@luke-ui/react/src/core/heading/heading.visual.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/heading/heading.visual.test.tsx rename to packages/@luke-ui/react/src/core/heading/heading.visual.test.tsx index f4520e1b..22506c7d 100644 --- a/packages/@luke-ui/react/src/heading/heading.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/heading/heading.visual.test.tsx @@ -6,7 +6,7 @@ import { Stack, variantValuesFor, } from '../test-utils/visual.js'; -import { Heading } from './index.js'; +import { Heading } from './heading.js'; const levels = variantValuesFor()([1, 2, 3, 4, 5, 6]); diff --git a/packages/@luke-ui/react/src/icon-button/icon-button.browser.test.tsx b/packages/@luke-ui/react/src/core/icon-button/icon-button.browser.test.tsx similarity index 94% rename from packages/@luke-ui/react/src/icon-button/icon-button.browser.test.tsx rename to packages/@luke-ui/react/src/core/icon-button/icon-button.browser.test.tsx index 941de384..8e7586d4 100644 --- a/packages/@luke-ui/react/src/icon-button/icon-button.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/icon-button/icon-button.browser.test.tsx @@ -1,7 +1,7 @@ import { expect } from 'vite-plus/test'; import { testConformance, testIntegration } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { IconButton } from './index.js'; +import { IconButton } from './icon-button.js'; testConformance({ path: 'icon-button', diff --git a/packages/@luke-ui/react/src/icon-button/icon-button.stories.tsx b/packages/@luke-ui/react/src/core/icon-button/icon-button.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/icon-button/icon-button.stories.tsx rename to packages/@luke-ui/react/src/core/icon-button/icon-button.stories.tsx index a113402a..45e6101a 100644 --- a/packages/@luke-ui/react/src/icon-button/icon-button.stories.tsx +++ b/packages/@luke-ui/react/src/core/icon-button/icon-button.stories.tsx @@ -2,7 +2,7 @@ import type { IconButtonProps } from '@luke-ui/react/icon-button'; import { IconButton } from '@luke-ui/react/icon-button'; import type { CSSProperties } from 'react'; import { fn } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: IconButton, diff --git a/packages/@luke-ui/react/src/icon-button/icon-button.tsx b/packages/@luke-ui/react/src/core/icon-button/icon-button.tsx similarity index 97% rename from packages/@luke-ui/react/src/icon-button/icon-button.tsx rename to packages/@luke-ui/react/src/core/icon-button/icon-button.tsx index af51a654..1808467d 100644 --- a/packages/@luke-ui/react/src/icon-button/icon-button.tsx +++ b/packages/@luke-ui/react/src/core/icon-button/icon-button.tsx @@ -1,5 +1,6 @@ import type { JSX } from 'react'; import { composeRenderProps } from 'react-aria-components/composeRenderProps'; +import { cx } from '../../shared/utils/utils.js'; import type { IconName } from '../icon/icon.js'; import { Icon } from '../icon/icon.js'; import type { ButtonProps as PrimitiveButtonProps } from '../primitives/button/button.js'; @@ -7,7 +8,6 @@ import { Button } from '../primitives/button/button.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { DocumentedPressProps } from '../types/documented-rac-props.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import type { IconButtonRecipeVariants } from './recipe.css.js'; import { iconButtonIcon, iconButtonRecipe, iconButtonReset } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/icon-button/icon-button.visual.test.tsx b/packages/@luke-ui/react/src/core/icon-button/icon-button.visual.test.tsx similarity index 98% rename from packages/@luke-ui/react/src/icon-button/icon-button.visual.test.tsx rename to packages/@luke-ui/react/src/core/icon-button/icon-button.visual.test.tsx index 1ff05fa7..21f88128 100644 --- a/packages/@luke-ui/react/src/icon-button/icon-button.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/icon-button/icon-button.visual.test.tsx @@ -8,7 +8,7 @@ import { focusViaKeyboard, Grid, } from '../test-utils/visual.js'; -import { IconButton } from './index.js'; +import { IconButton } from './icon-button.js'; test('kitchen sink', async () => { for (const appearance of visualAppearances) { diff --git a/packages/@luke-ui/react/src/icon-button/recipe.css.ts b/packages/@luke-ui/react/src/core/icon-button/recipe.css.ts similarity index 96% rename from packages/@luke-ui/react/src/icon-button/recipe.css.ts rename to packages/@luke-ui/react/src/core/icon-button/recipe.css.ts index 0b4a4ca2..994ae2c6 100644 --- a/packages/@luke-ui/react/src/icon-button/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/icon-button/recipe.css.ts @@ -1,7 +1,7 @@ +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; export const iconButtonReset = styleInLayer('recipes', { '@media': { diff --git a/packages/@luke-ui/react/src/icon/icon-size-context.tsx b/packages/@luke-ui/react/src/core/icon/icon-size-context.tsx similarity index 100% rename from packages/@luke-ui/react/src/icon/icon-size-context.tsx rename to packages/@luke-ui/react/src/core/icon/icon-size-context.tsx diff --git a/packages/@luke-ui/react/src/icon/icon.stories.tsx b/packages/@luke-ui/react/src/core/icon/icon.stories.tsx similarity index 98% rename from packages/@luke-ui/react/src/icon/icon.stories.tsx rename to packages/@luke-ui/react/src/core/icon/icon.stories.tsx index b3ee837e..3a32240a 100644 --- a/packages/@luke-ui/react/src/icon/icon.stories.tsx +++ b/packages/@luke-ui/react/src/core/icon/icon.stories.tsx @@ -4,7 +4,7 @@ import { createIcon, Icon, iconNames } from '@luke-ui/react/icon'; import type { TextProps } from '@luke-ui/react/text'; import { vars } from '@luke-ui/react/theme'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Icon, diff --git a/packages/@luke-ui/react/src/icon/icon.tsx b/packages/@luke-ui/react/src/core/icon/icon.tsx similarity index 95% rename from packages/@luke-ui/react/src/icon/icon.tsx rename to packages/@luke-ui/react/src/core/icon/icon.tsx index 6312515d..b0891a56 100644 --- a/packages/@luke-ui/react/src/icon/icon.tsx +++ b/packages/@luke-ui/react/src/core/icon/icon.tsx @@ -1,15 +1,15 @@ import type { JSX, ReactNode, SVGAttributes } from 'react'; import { createContext, useContext } from 'react'; -import { iconNames, iconViewBoxes } from '../../.generated/icon-data.js'; +import { iconNames, iconViewBoxes } from '../../../.generated/icon-data.js'; +import { cx } from '../../shared/utils/utils.js'; import { ICON_VIEWBOX } from '../sizing/icon-sizing.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { useIconSizeContext } from './icon-size-context.js'; import type { IconRecipeVariants } from './recipe.css.js'; import { iconRecipe } from './recipe.css.js'; -export type { IconName } from '../../.generated/icon-data.js'; +export type { IconName } from '../../../.generated/icon-data.js'; export { IconSizeProvider } from './icon-size-context.js'; export { iconNames, iconViewBoxes }; diff --git a/packages/@luke-ui/react/src/icon/icon.visual.test.tsx b/packages/@luke-ui/react/src/core/icon/icon.visual.test.tsx similarity index 94% rename from packages/@luke-ui/react/src/icon/icon.visual.test.tsx rename to packages/@luke-ui/react/src/core/icon/icon.visual.test.tsx index 2b1f034f..abf6b6c5 100644 --- a/packages/@luke-ui/react/src/icon/icon.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/icon/icon.visual.test.tsx @@ -1,5 +1,6 @@ import type { CSSProperties } from 'react'; import { test } from 'vite-plus/test'; +import { vars } from '../../theme/index.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, @@ -7,8 +8,7 @@ import { Stack, variantValuesFor, } from '../test-utils/visual.js'; -import { vars } from '../theme/index.js'; -import { Icon } from './index.js'; +import { Icon } from './icon.js'; const rowStyle = { alignItems: 'center', diff --git a/packages/@luke-ui/react/src/icon/recipe.css.ts b/packages/@luke-ui/react/src/core/icon/recipe.css.ts similarity index 94% rename from packages/@luke-ui/react/src/icon/recipe.css.ts rename to packages/@luke-ui/react/src/core/icon/recipe.css.ts index 23a38f7f..fdab23c2 100644 --- a/packages/@luke-ui/react/src/icon/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/icon/recipe.css.ts @@ -1,6 +1,6 @@ +import { vars } from '../../theme/contract.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; /** Shared size dimensions for Icon and LoadingSpinner (icon-aligned sizing). */ export const iconSizeVariants = { diff --git a/packages/@luke-ui/react/src/kbd/kbd.browser.test.tsx b/packages/@luke-ui/react/src/core/kbd/kbd.browser.test.tsx similarity index 92% rename from packages/@luke-ui/react/src/kbd/kbd.browser.test.tsx rename to packages/@luke-ui/react/src/core/kbd/kbd.browser.test.tsx index 53701230..2c1304ff 100644 --- a/packages/@luke-ui/react/src/kbd/kbd.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/kbd/kbd.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Kbd } from './index.js'; +import { Kbd } from './kbd.js'; testConformance({ path: 'kbd', diff --git a/packages/@luke-ui/react/src/kbd/kbd.stories.tsx b/packages/@luke-ui/react/src/core/kbd/kbd.stories.tsx similarity index 90% rename from packages/@luke-ui/react/src/kbd/kbd.stories.tsx rename to packages/@luke-ui/react/src/core/kbd/kbd.stories.tsx index 3ecce2be..ee69a293 100644 --- a/packages/@luke-ui/react/src/kbd/kbd.stories.tsx +++ b/packages/@luke-ui/react/src/core/kbd/kbd.stories.tsx @@ -1,6 +1,6 @@ import { Kbd } from '@luke-ui/react/kbd'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Kbd, diff --git a/packages/@luke-ui/react/src/kbd/kbd.tsx b/packages/@luke-ui/react/src/core/kbd/kbd.tsx similarity index 92% rename from packages/@luke-ui/react/src/kbd/kbd.tsx rename to packages/@luke-ui/react/src/core/kbd/kbd.tsx index 13367f27..7bf2c556 100644 --- a/packages/@luke-ui/react/src/kbd/kbd.tsx +++ b/packages/@luke-ui/react/src/core/kbd/kbd.tsx @@ -1,6 +1,6 @@ +import { cx } from '../../shared/utils/utils.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { kbdRecipe } from './recipe.css.js'; type _KbdOmit = DistributiveOmit, never>; diff --git a/packages/@luke-ui/react/src/kbd/recipe.css.ts b/packages/@luke-ui/react/src/core/kbd/recipe.css.ts similarity index 88% rename from packages/@luke-ui/react/src/kbd/recipe.css.ts rename to packages/@luke-ui/react/src/core/kbd/recipe.css.ts index 77c278e9..b760854b 100644 --- a/packages/@luke-ui/react/src/kbd/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/kbd/recipe.css.ts @@ -1,8 +1,8 @@ +import { vars } from '../../theme/contract.css.js'; +import { FONT_METRIC_SCALE } from '../../theme/font-metric-scale.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; -import { FONT_METRIC_SCALE } from '../theme/font-metric-scale.js'; const base = styleInLayer('recipes', { alignItems: 'center', diff --git a/packages/@luke-ui/react/src/link/link.browser.test.tsx b/packages/@luke-ui/react/src/core/link/link.browser.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/link/link.browser.test.tsx rename to packages/@luke-ui/react/src/core/link/link.browser.test.tsx index c07c4524..ddedfd70 100644 --- a/packages/@luke-ui/react/src/link/link.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/link/link.browser.test.tsx @@ -1,7 +1,7 @@ import { expect } from 'vite-plus/test'; import { testConformance, testIntegration } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Link } from './index.js'; +import { Link } from './link.js'; testConformance({ path: 'link', diff --git a/packages/@luke-ui/react/src/link/link.stories.tsx b/packages/@luke-ui/react/src/core/link/link.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/link/link.stories.tsx rename to packages/@luke-ui/react/src/core/link/link.stories.tsx index fd6fc895..88d71508 100644 --- a/packages/@luke-ui/react/src/link/link.stories.tsx +++ b/packages/@luke-ui/react/src/core/link/link.stories.tsx @@ -2,7 +2,7 @@ import type { LinkProps } from '@luke-ui/react/link'; import { Link } from '@luke-ui/react/link'; import type { CSSProperties } from 'react'; import { fn } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Link, diff --git a/packages/@luke-ui/react/src/link/link.tsx b/packages/@luke-ui/react/src/core/link/link.tsx similarity index 96% rename from packages/@luke-ui/react/src/link/link.tsx rename to packages/@luke-ui/react/src/core/link/link.tsx index b7da3642..d6969095 100644 --- a/packages/@luke-ui/react/src/link/link.tsx +++ b/packages/@luke-ui/react/src/core/link/link.tsx @@ -2,10 +2,10 @@ import type { JSX } from 'react'; import type { LinkProps as RacLinkProps } from 'react-aria-components/Link'; import { Link as RacLink } from 'react-aria-components/Link'; import { composeRenderProps } from 'react-aria-components/composeRenderProps'; +import { cx } from '../../shared/utils/utils.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { DocumentedLinkProps } from '../types/documented-rac-props.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import type { LinkRecipeVariants } from './recipe.css.js'; import { linkRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/link/link.visual.test.tsx b/packages/@luke-ui/react/src/core/link/link.visual.test.tsx similarity index 98% rename from packages/@luke-ui/react/src/link/link.visual.test.tsx rename to packages/@luke-ui/react/src/core/link/link.visual.test.tsx index de12a0ee..eef47da9 100644 --- a/packages/@luke-ui/react/src/link/link.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/link/link.visual.test.tsx @@ -9,7 +9,7 @@ import { Grid, Stack, } from '../test-utils/visual.js'; -import { Link } from './index.js'; +import { Link } from './link.js'; test('kitchen sink', async () => { for (const appearance of visualAppearances) { diff --git a/packages/@luke-ui/react/src/link/recipe.css.ts b/packages/@luke-ui/react/src/core/link/recipe.css.ts similarity index 97% rename from packages/@luke-ui/react/src/link/recipe.css.ts rename to packages/@luke-ui/react/src/core/link/recipe.css.ts index b30e0ae1..ae2cb6e1 100644 --- a/packages/@luke-ui/react/src/link/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/link/recipe.css.ts @@ -1,7 +1,7 @@ +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; const base = styleInLayer('recipes', { '@media': { diff --git a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.browser.test.tsx b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.browser.test.tsx similarity index 88% rename from packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.browser.test.tsx rename to packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.browser.test.tsx index 22bf53d6..c6b3266f 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { LoadingSkeleton } from './index.js'; +import { LoadingSkeleton } from './loading-skeleton.js'; testConformance({ path: 'loading-skeleton', diff --git a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.stories.tsx b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.stories.tsx similarity index 98% rename from packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.stories.tsx rename to packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.stories.tsx index 6e9a7622..778980f9 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.stories.tsx +++ b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.stories.tsx @@ -3,7 +3,7 @@ import { LoadingSkeleton, LoadingSkeletonProvider } from '@luke-ui/react/loading import { Text } from '@luke-ui/react/text'; import { TextField } from '@luke-ui/react/text-field'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: LoadingSkeleton, diff --git a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.tsx b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.tsx similarity index 96% rename from packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.tsx rename to packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.tsx index f677c2f0..82ab40b2 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.tsx +++ b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.tsx @@ -1,9 +1,9 @@ import type { ComponentProps, ElementType, JSX, ReactNode } from 'react'; import { createContext, isValidElement, useContext } from 'react'; -import { vars } from '../theme/contract.css.js'; +import { cx } from '../../shared/utils/utils.js'; +import { vars } from '../../theme/contract.css.js'; import type { Prettify } from '../types/prettify.js'; import { useSynchronizeAnimations } from '../use-synchronize-animations/use-synchronize-animations.js'; -import { cx } from '../utils/utils.js'; import { loadingSkeletonClassName, skeletonAnimationName, diff --git a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.visual.test.tsx b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.visual.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.visual.test.tsx rename to packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.visual.test.tsx index 25845e42..118bda48 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/loading-skeleton.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/loading-skeleton/loading-skeleton.visual.test.tsx @@ -1,10 +1,10 @@ import type { CSSProperties } from 'react'; import { test } from 'vite-plus/test'; -import { Button } from '../button/index.js'; +import { Button } from '../button/button.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, captureVisualAppearance, Stack } from '../test-utils/visual.js'; -import { TextField } from '../text-field/index.js'; -import { LoadingSkeleton } from './index.js'; +import { TextField } from '../text-field/text-field.js'; +import { LoadingSkeleton } from './loading-skeleton.js'; test('text and component placeholders', async () => { const { locator } = render( diff --git a/packages/@luke-ui/react/src/loading-skeleton/styles.css.ts b/packages/@luke-ui/react/src/core/loading-skeleton/styles.css.ts similarity index 98% rename from packages/@luke-ui/react/src/loading-skeleton/styles.css.ts rename to packages/@luke-ui/react/src/core/loading-skeleton/styles.css.ts index 6aab278b..01428ecb 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/styles.css.ts +++ b/packages/@luke-ui/react/src/core/loading-skeleton/styles.css.ts @@ -1,7 +1,7 @@ import type { StyleRule } from '@vanilla-extract/css'; import { fallbackVar, keyframes } from '@vanilla-extract/css'; +import { vars } from '../../theme/contract.css.js'; import { globalStyleInLayer, styleInLayer } from '../styles/layered-style.css.js'; -import { vars } from '../theme/contract.css.js'; /** * @internal diff --git a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.browser.test.tsx b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.browser.test.tsx similarity index 88% rename from packages/@luke-ui/react/src/loading-spinner/loading-spinner.browser.test.tsx rename to packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.browser.test.tsx index dceceb30..f9554b19 100644 --- a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { LoadingSpinner } from './index.js'; +import { LoadingSpinner } from './loading-spinner.js'; testConformance({ path: 'loading-spinner', diff --git a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.stories.tsx b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/loading-spinner/loading-spinner.stories.tsx rename to packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.stories.tsx index 5751f1d2..b3733755 100644 --- a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.stories.tsx +++ b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.stories.tsx @@ -4,7 +4,7 @@ import { vars } from '@luke-ui/react/theme'; import type { CSSProperties } from 'react'; import { useState } from 'react'; import { userEvent } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: LoadingSpinner, diff --git a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.tsx b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.tsx similarity index 100% rename from packages/@luke-ui/react/src/loading-spinner/loading-spinner.tsx rename to packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.tsx diff --git a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.visual.test.tsx b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.visual.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/loading-spinner/loading-spinner.visual.test.tsx rename to packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.visual.test.tsx index c2c5e0a3..0e1eac93 100644 --- a/packages/@luke-ui/react/src/loading-spinner/loading-spinner.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/loading-spinner/loading-spinner.visual.test.tsx @@ -1,5 +1,6 @@ import type { CSSProperties, ReactNode } from 'react'; import { test } from 'vite-plus/test'; +import { vars } from '../../theme/index.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, @@ -7,8 +8,7 @@ import { Stack, variantValuesFor, } from '../test-utils/visual.js'; -import { vars } from '../theme/index.js'; -import { LoadingSpinner } from './index.js'; +import { LoadingSpinner } from './loading-spinner.js'; const rowStyle = { alignItems: 'center', diff --git a/packages/@luke-ui/react/src/loading-spinner/recipe.css.ts b/packages/@luke-ui/react/src/core/loading-spinner/recipe.css.ts similarity index 98% rename from packages/@luke-ui/react/src/loading-spinner/recipe.css.ts rename to packages/@luke-ui/react/src/core/loading-spinner/recipe.css.ts index 6a82ab24..c06374bd 100644 --- a/packages/@luke-ui/react/src/loading-spinner/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/loading-spinner/recipe.css.ts @@ -1,9 +1,9 @@ import { keyframes } from '@vanilla-extract/css'; +import { vars } from '../../theme/contract.css.js'; import { iconSizeVariants } from '../icon/recipe.css.js'; import type { RecipeSelection, SlottedConfigInput } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; import { spinnerOverlayBase } from '../styles/spinner-overlay.js'; -import { vars } from '../theme/contract.css.js'; const rotationDuration = '1.2s'; const rubberBandDuration = '2s'; diff --git a/packages/@luke-ui/react/src/numeral/numeral.browser.test.tsx b/packages/@luke-ui/react/src/core/numeral/numeral.browser.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/numeral/numeral.browser.test.tsx rename to packages/@luke-ui/react/src/core/numeral/numeral.browser.test.tsx index 285e75c7..fa29eb6f 100644 --- a/packages/@luke-ui/react/src/numeral/numeral.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/numeral/numeral.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Numeral } from './index.js'; +import { Numeral } from './numeral.js'; testConformance({ path: 'numeral', diff --git a/packages/@luke-ui/react/src/numeral/numeral.stories.tsx b/packages/@luke-ui/react/src/core/numeral/numeral.stories.tsx similarity index 98% rename from packages/@luke-ui/react/src/numeral/numeral.stories.tsx rename to packages/@luke-ui/react/src/core/numeral/numeral.stories.tsx index 4ad48c5b..ecd002b3 100644 --- a/packages/@luke-ui/react/src/numeral/numeral.stories.tsx +++ b/packages/@luke-ui/react/src/core/numeral/numeral.stories.tsx @@ -3,7 +3,7 @@ import type { NumeralProps } from '@luke-ui/react/numeral'; import { Numeral } from '@luke-ui/react/numeral'; import { Text } from '@luke-ui/react/text'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Numeral, diff --git a/packages/@luke-ui/react/src/numeral/numeral.tsx b/packages/@luke-ui/react/src/core/numeral/numeral.tsx similarity index 100% rename from packages/@luke-ui/react/src/numeral/numeral.tsx rename to packages/@luke-ui/react/src/core/numeral/numeral.tsx diff --git a/packages/@luke-ui/react/src/numeral/numeral.visual.test.tsx b/packages/@luke-ui/react/src/core/numeral/numeral.visual.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/numeral/numeral.visual.test.tsx rename to packages/@luke-ui/react/src/core/numeral/numeral.visual.test.tsx index 3eda5620..e75bc9e6 100644 --- a/packages/@luke-ui/react/src/numeral/numeral.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/numeral/numeral.visual.test.tsx @@ -2,7 +2,7 @@ import type { CSSProperties } from 'react'; import { test } from 'vite-plus/test'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisualAppearance, Stack } from '../test-utils/visual.js'; -import { Numeral } from './index.js'; +import { Numeral } from './numeral.js'; const rowStyle = { display: 'flex', diff --git a/packages/@luke-ui/react/src/overlays/mobile-overlay.css.ts b/packages/@luke-ui/react/src/core/overlays/mobile-overlay.css.ts similarity index 98% rename from packages/@luke-ui/react/src/overlays/mobile-overlay.css.ts rename to packages/@luke-ui/react/src/core/overlays/mobile-overlay.css.ts index 02c7a356..a413331d 100644 --- a/packages/@luke-ui/react/src/overlays/mobile-overlay.css.ts +++ b/packages/@luke-ui/react/src/core/overlays/mobile-overlay.css.ts @@ -1,7 +1,7 @@ import { createVar } from '@vanilla-extract/css'; +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import { overlayEnterTransition, overlayExitTransition } from '../styles/overlay-motion.js'; -import { vars } from '../theme/contract.css.js'; const trayPaddingBlockEnd = createVar(); diff --git a/packages/@luke-ui/react/src/overlays/mobile-overlay.tsx b/packages/@luke-ui/react/src/core/overlays/mobile-overlay.tsx similarity index 95% rename from packages/@luke-ui/react/src/overlays/mobile-overlay.tsx rename to packages/@luke-ui/react/src/core/overlays/mobile-overlay.tsx index 8de1967f..3f688b42 100644 --- a/packages/@luke-ui/react/src/overlays/mobile-overlay.tsx +++ b/packages/@luke-ui/react/src/core/overlays/mobile-overlay.tsx @@ -2,8 +2,8 @@ import type { JSX, ReactNode, Ref } from 'react'; import type { DialogProps } from 'react-aria-components/Dialog'; import { Dialog, OverlayTriggerStateContext } from 'react-aria-components/Dialog'; import { Modal, ModalOverlay } from 'react-aria-components/Modal'; -import { rootClassName } from '../theme/theme.js'; -import { cx } from '../utils/utils.js'; +import { cx } from '../../shared/utils/utils.js'; +import { rootClassName } from '../../theme/theme.js'; import { mobileDialog, mobileModal, mobileOverlay } from './mobile-overlay.css.js'; interface MobileOverlayProps { diff --git a/packages/@luke-ui/react/src/overlays/use-is-mobile-device.ts b/packages/@luke-ui/react/src/core/overlays/use-is-mobile-device.ts similarity index 94% rename from packages/@luke-ui/react/src/overlays/use-is-mobile-device.ts rename to packages/@luke-ui/react/src/core/overlays/use-is-mobile-device.ts index e6aabf04..edfedd58 100644 --- a/packages/@luke-ui/react/src/overlays/use-is-mobile-device.ts +++ b/packages/@luke-ui/react/src/core/overlays/use-is-mobile-device.ts @@ -1,5 +1,5 @@ import { useSyncExternalStore } from 'react'; -import { breakpoints } from '../theme/breakpoints.js'; +import { breakpoints } from '../../theme/breakpoints.js'; /** * Reports whether the current device is a mobile one, so an overlay can switch to diff --git a/packages/@luke-ui/react/src/primitives/button/button.browser.test.tsx b/packages/@luke-ui/react/src/core/primitives/button/button.browser.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/primitives/button/button.browser.test.tsx rename to packages/@luke-ui/react/src/core/primitives/button/button.browser.test.tsx index 13a96cf6..71126102 100644 --- a/packages/@luke-ui/react/src/primitives/button/button.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/primitives/button/button.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../../conformance/helpers.js'; import { render } from '../../test-utils/render.js'; -import { Button } from './index.js'; +import { Button } from './button.js'; testConformance({ path: 'primitives/button', diff --git a/packages/@luke-ui/react/src/primitives/button/button.tsx b/packages/@luke-ui/react/src/core/primitives/button/button.tsx similarity index 97% rename from packages/@luke-ui/react/src/primitives/button/button.tsx rename to packages/@luke-ui/react/src/core/primitives/button/button.tsx index 8a26ce62..bfc27afe 100644 --- a/packages/@luke-ui/react/src/primitives/button/button.tsx +++ b/packages/@luke-ui/react/src/core/primitives/button/button.tsx @@ -2,10 +2,10 @@ import type { JSX } from 'react'; import type { ButtonProps as RacButtonProps } from 'react-aria-components/Button'; import { Button as RacButton } from 'react-aria-components/Button'; import { composeRenderProps } from 'react-aria-components/composeRenderProps'; +import { cx } from '../../../shared/utils/utils.js'; import { IconSizeProvider } from '../../icon/icon-size-context.js'; import { BUTTON_ICON_SIZE } from '../../sizing/button-sizing.js'; import type { Prettify } from '../../types/prettify.js'; -import { cx } from '../../utils/utils.js'; import type { ButtonRecipeVariants } from './recipe.css.js'; import { buttonRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/primitives/button/recipe.css.ts b/packages/@luke-ui/react/src/core/primitives/button/recipe.css.ts similarity index 97% rename from packages/@luke-ui/react/src/primitives/button/recipe.css.ts rename to packages/@luke-ui/react/src/core/primitives/button/recipe.css.ts index 11b0da9f..20ab4df5 100644 --- a/packages/@luke-ui/react/src/primitives/button/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/primitives/button/recipe.css.ts @@ -1,8 +1,8 @@ +import { vars } from '../../../theme/contract.css.js'; +import { FONT_METRIC_SCALE } from '../../../theme/font-metric-scale.js'; import { styleInLayer } from '../../styles/layered-style.css.js'; import type { RecipeSelection } from '../../styles/recipe.js'; import { recipe } from '../../styles/recipe.js'; -import { vars } from '../../theme/contract.css.js'; -import { FONT_METRIC_SCALE } from '../../theme/font-metric-scale.js'; const base = styleInLayer('recipes', { '@media': { diff --git a/packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx b/packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/checkbox/checkbox.tsx rename to packages/@luke-ui/react/src/core/primitives/checkbox/checkbox.tsx diff --git a/packages/@luke-ui/react/src/primitives/checkbox/recipe.css.ts b/packages/@luke-ui/react/src/core/primitives/checkbox/recipe.css.ts similarity index 98% rename from packages/@luke-ui/react/src/primitives/checkbox/recipe.css.ts rename to packages/@luke-ui/react/src/core/primitives/checkbox/recipe.css.ts index 7943953a..4c844d56 100644 --- a/packages/@luke-ui/react/src/primitives/checkbox/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/primitives/checkbox/recipe.css.ts @@ -1,10 +1,10 @@ import { createVar, fallbackVar } from '@vanilla-extract/css'; +import { vars } from '../../../theme/contract.css.js'; +import { FONT_METRIC_SCALE } from '../../../theme/font-metric-scale.js'; import { focusRing } from '../../styles/focus-ring.js'; import type { RecipeSelection, SlottedConfigInput } from '../../styles/recipe.js'; import { recipe } from '../../styles/recipe.js'; import { textLineHeight } from '../../text/recipe.css.js'; -import { vars } from '../../theme/contract.css.js'; -import { FONT_METRIC_SCALE } from '../../theme/font-metric-scale.js'; import { fieldMessageIcon, fieldMessageIndent } from '../field/recipe.css.js'; const checkboxControlSize = createVar(); diff --git a/packages/@luke-ui/react/src/primitives/combobox/clear-button.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/clear-button.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/clear-button.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/clear-button.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/empty-state.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/empty-state.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/empty-state.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/empty-state.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/input-group.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/input-group.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/input-group.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/input-group.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/input.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/input.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/input.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/input.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/item.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/item.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/item.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/item.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/listbox.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/listbox.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/listbox.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/listbox.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/popover.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/popover.tsx similarity index 92% rename from packages/@luke-ui/react/src/primitives/combobox/popover.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/popover.tsx index 5c9712f3..b040492a 100644 --- a/packages/@luke-ui/react/src/primitives/combobox/popover.tsx +++ b/packages/@luke-ui/react/src/core/primitives/combobox/popover.tsx @@ -2,10 +2,10 @@ import type { JSX, Ref } from 'react'; import type { PopoverProps as RacPopoverProps } from 'react-aria-components/ComboBox'; import { Popover as RacPopover } from 'react-aria-components/ComboBox'; import { composeRenderProps } from 'react-aria-components/composeRenderProps'; -import { rootClassName } from '../../theme/theme.js'; +import { cx } from '../../../shared/utils/utils.js'; +import { rootClassName } from '../../../theme/theme.js'; import type { DistributiveOmit } from '../../types/distributive-omit.js'; import type { Prettify } from '../../types/prettify.js'; -import { cx } from '../../utils/utils.js'; import { comboboxRecipe } from './styles.css.js'; type _ComboboxPopoverOmit = DistributiveOmit; diff --git a/packages/@luke-ui/react/src/primitives/combobox/root.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/root.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/root.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/root.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/section.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/section.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/section.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/section.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/size-context.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/size-context.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/size-context.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/size-context.tsx diff --git a/packages/@luke-ui/react/src/primitives/combobox/styles.css.ts b/packages/@luke-ui/react/src/core/primitives/combobox/styles.css.ts similarity index 99% rename from packages/@luke-ui/react/src/primitives/combobox/styles.css.ts rename to packages/@luke-ui/react/src/core/primitives/combobox/styles.css.ts index 3e085a47..7c4d7a96 100644 --- a/packages/@luke-ui/react/src/primitives/combobox/styles.css.ts +++ b/packages/@luke-ui/react/src/core/primitives/combobox/styles.css.ts @@ -1,5 +1,7 @@ import type { StyleRule } from '@vanilla-extract/css'; import { createVar } from '@vanilla-extract/css'; +import { vars } from '../../../theme/contract.css.js'; +import { FONT_METRIC_SCALE } from '../../../theme/font-metric-scale.js'; import { FIELD_CONTROL_ICON_SIZE } from '../../sizing/control-size.js'; import { focusRing } from '../../styles/focus-ring.js'; import { @@ -14,8 +16,6 @@ import { styleInLayer } from '../../styles/layered-style.css.js'; import { overlayEnterTransition, overlayExitTransition } from '../../styles/overlay-motion.js'; import type { SlottedConfigInput } from '../../styles/recipe.js'; import { recipe } from '../../styles/recipe.js'; -import { vars } from '../../theme/contract.css.js'; -import { FONT_METRIC_SCALE } from '../../theme/font-metric-scale.js'; // Set per `size` variant on `inputGroup` below, from `FIELD_CONTROL_ICON_SIZE`, so the invalid // `::after` icon matches the trigger/clear chevrons at each size instead of a constant. diff --git a/packages/@luke-ui/react/src/primitives/combobox/trigger.tsx b/packages/@luke-ui/react/src/core/primitives/combobox/trigger.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/combobox/trigger.tsx rename to packages/@luke-ui/react/src/core/primitives/combobox/trigger.tsx diff --git a/packages/@luke-ui/react/src/primitives/field/description.tsx b/packages/@luke-ui/react/src/core/primitives/field/description.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/description.tsx rename to packages/@luke-ui/react/src/core/primitives/field/description.tsx diff --git a/packages/@luke-ui/react/src/primitives/field/error.tsx b/packages/@luke-ui/react/src/core/primitives/field/error.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/error.tsx rename to packages/@luke-ui/react/src/core/primitives/field/error.tsx diff --git a/packages/@luke-ui/react/src/primitives/field/field.tsx b/packages/@luke-ui/react/src/core/primitives/field/field.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/field.tsx rename to packages/@luke-ui/react/src/core/primitives/field/field.tsx diff --git a/packages/@luke-ui/react/src/primitives/field/is-invalid-from-error-message.ts b/packages/@luke-ui/react/src/core/primitives/field/is-invalid-from-error-message.ts similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/is-invalid-from-error-message.ts rename to packages/@luke-ui/react/src/core/primitives/field/is-invalid-from-error-message.ts diff --git a/packages/@luke-ui/react/src/primitives/field/label.tsx b/packages/@luke-ui/react/src/core/primitives/field/label.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/label.tsx rename to packages/@luke-ui/react/src/core/primitives/field/label.tsx diff --git a/packages/@luke-ui/react/src/primitives/field/recipe.css.ts b/packages/@luke-ui/react/src/core/primitives/field/recipe.css.ts similarity index 98% rename from packages/@luke-ui/react/src/primitives/field/recipe.css.ts rename to packages/@luke-ui/react/src/core/primitives/field/recipe.css.ts index 43b5f839..6cc896d0 100644 --- a/packages/@luke-ui/react/src/primitives/field/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/primitives/field/recipe.css.ts @@ -1,11 +1,11 @@ import { createVar, fallbackVar } from '@vanilla-extract/css'; +import { vars } from '../../../theme/contract.css.js'; import { invalidIndicatorIconForcedColors, invalidMessageIcon, } from '../../styles/invalid-indicator.js'; import type { RecipeSelection, SlottedConfigInput } from '../../styles/recipe.js'; import { recipe } from '../../styles/recipe.js'; -import { vars } from '../../theme/contract.css.js'; const dataDisabledSelector = '[data-disabled="true"]'; const dataRequiredSelector = '[data-required="true"]'; diff --git a/packages/@luke-ui/react/src/primitives/field/root.tsx b/packages/@luke-ui/react/src/core/primitives/field/root.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/field/root.tsx rename to packages/@luke-ui/react/src/core/primitives/field/root.tsx diff --git a/packages/@luke-ui/react/src/primitives/input-group/input-group.tsx b/packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx similarity index 100% rename from packages/@luke-ui/react/src/primitives/input-group/input-group.tsx rename to packages/@luke-ui/react/src/core/primitives/input-group/input-group.tsx diff --git a/packages/@luke-ui/react/src/primitives/input-group/recipe.css.ts b/packages/@luke-ui/react/src/core/primitives/input-group/recipe.css.ts similarity index 98% rename from packages/@luke-ui/react/src/primitives/input-group/recipe.css.ts rename to packages/@luke-ui/react/src/core/primitives/input-group/recipe.css.ts index c059333c..3a988e84 100644 --- a/packages/@luke-ui/react/src/primitives/input-group/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/primitives/input-group/recipe.css.ts @@ -1,3 +1,5 @@ +import { vars } from '../../../theme/contract.css.js'; +import { FONT_METRIC_SCALE } from '../../../theme/font-metric-scale.js'; import { focusRing } from '../../styles/focus-ring.js'; import { composeInputStateSelectors, @@ -5,8 +7,6 @@ import { } from '../../styles/input-states.js'; import type { RecipeSelection, SlottedConfigInput } from '../../styles/recipe.js'; import { recipe } from '../../styles/recipe.js'; -import { vars } from '../../theme/contract.css.js'; -import { FONT_METRIC_SCALE } from '../../theme/font-metric-scale.js'; const { disabled, focusWithin, hover, invalid, invalidFocusWithin, readOnly, readOnlyFocusWithin } = composeInputStateSelectors(); diff --git a/packages/@luke-ui/react/src/quote/quote.browser.test.tsx b/packages/@luke-ui/react/src/core/quote/quote.browser.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/quote/quote.browser.test.tsx rename to packages/@luke-ui/react/src/core/quote/quote.browser.test.tsx index 98c6c865..e42749f6 100644 --- a/packages/@luke-ui/react/src/quote/quote.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/quote/quote.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Quote } from './index.js'; +import { Quote } from './quote.js'; testConformance({ path: 'quote', diff --git a/packages/@luke-ui/react/src/quote/quote.stories.tsx b/packages/@luke-ui/react/src/core/quote/quote.stories.tsx similarity index 93% rename from packages/@luke-ui/react/src/quote/quote.stories.tsx rename to packages/@luke-ui/react/src/core/quote/quote.stories.tsx index 5457f4cd..e72ee154 100644 --- a/packages/@luke-ui/react/src/quote/quote.stories.tsx +++ b/packages/@luke-ui/react/src/core/quote/quote.stories.tsx @@ -1,6 +1,6 @@ import { Quote } from '@luke-ui/react/quote'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Quote, diff --git a/packages/@luke-ui/react/src/quote/quote.tsx b/packages/@luke-ui/react/src/core/quote/quote.tsx similarity index 100% rename from packages/@luke-ui/react/src/quote/quote.tsx rename to packages/@luke-ui/react/src/core/quote/quote.tsx diff --git a/packages/@luke-ui/react/src/sizing/button-sizing.ts b/packages/@luke-ui/react/src/core/sizing/button-sizing.ts similarity index 100% rename from packages/@luke-ui/react/src/sizing/button-sizing.ts rename to packages/@luke-ui/react/src/core/sizing/button-sizing.ts diff --git a/packages/@luke-ui/react/src/sizing/combobox-sizing.ts b/packages/@luke-ui/react/src/core/sizing/combobox-sizing.ts similarity index 100% rename from packages/@luke-ui/react/src/sizing/combobox-sizing.ts rename to packages/@luke-ui/react/src/core/sizing/combobox-sizing.ts diff --git a/packages/@luke-ui/react/src/sizing/control-size.ts b/packages/@luke-ui/react/src/core/sizing/control-size.ts similarity index 100% rename from packages/@luke-ui/react/src/sizing/control-size.ts rename to packages/@luke-ui/react/src/core/sizing/control-size.ts diff --git a/packages/@luke-ui/react/src/sizing/icon-sizing.ts b/packages/@luke-ui/react/src/core/sizing/icon-sizing.ts similarity index 100% rename from packages/@luke-ui/react/src/sizing/icon-sizing.ts rename to packages/@luke-ui/react/src/core/sizing/icon-sizing.ts diff --git a/packages/@luke-ui/react/src/strong/strong.browser.test.tsx b/packages/@luke-ui/react/src/core/strong/strong.browser.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/strong/strong.browser.test.tsx rename to packages/@luke-ui/react/src/core/strong/strong.browser.test.tsx index a852439e..688c236b 100644 --- a/packages/@luke-ui/react/src/strong/strong.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/strong/strong.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Strong } from './index.js'; +import { Strong } from './strong.js'; testConformance({ path: 'strong', diff --git a/packages/@luke-ui/react/src/strong/strong.stories.tsx b/packages/@luke-ui/react/src/core/strong/strong.stories.tsx similarity index 92% rename from packages/@luke-ui/react/src/strong/strong.stories.tsx rename to packages/@luke-ui/react/src/core/strong/strong.stories.tsx index 06e47f36..6d952d3e 100644 --- a/packages/@luke-ui/react/src/strong/strong.stories.tsx +++ b/packages/@luke-ui/react/src/core/strong/strong.stories.tsx @@ -1,6 +1,6 @@ import { Strong } from '@luke-ui/react/strong'; import { Text } from '@luke-ui/react/text'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: Strong, diff --git a/packages/@luke-ui/react/src/strong/strong.tsx b/packages/@luke-ui/react/src/core/strong/strong.tsx similarity index 96% rename from packages/@luke-ui/react/src/strong/strong.tsx rename to packages/@luke-ui/react/src/core/strong/strong.tsx index 0381f6c7..ec90dbf1 100644 --- a/packages/@luke-ui/react/src/strong/strong.tsx +++ b/packages/@luke-ui/react/src/core/strong/strong.tsx @@ -1,8 +1,8 @@ +import { cx } from '../../shared/utils/utils.js'; import type { TextProps } from '../text/text.js'; import { Text } from '../text/text.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { strong } from './styles.css.js'; interface StrongStyleProps { diff --git a/packages/@luke-ui/react/src/strong/styles.css.ts b/packages/@luke-ui/react/src/core/strong/styles.css.ts similarity index 75% rename from packages/@luke-ui/react/src/strong/styles.css.ts rename to packages/@luke-ui/react/src/core/strong/styles.css.ts index 26d626f2..c823f39f 100644 --- a/packages/@luke-ui/react/src/strong/styles.css.ts +++ b/packages/@luke-ui/react/src/core/strong/styles.css.ts @@ -1,5 +1,5 @@ +import { vars } from '../../theme/contract.css.js'; import { styleInLayer } from '../styles/layered-style.css.js'; -import { vars } from '../theme/contract.css.js'; export const strong = styleInLayer('recipes', { fontWeight: vars.font.weight.emphasis, diff --git a/packages/@luke-ui/react/src/styles/class-names.ts b/packages/@luke-ui/react/src/core/styles/class-selector.ts similarity index 54% rename from packages/@luke-ui/react/src/styles/class-names.ts rename to packages/@luke-ui/react/src/core/styles/class-selector.ts index 5093c2d0..bd6f204c 100644 --- a/packages/@luke-ui/react/src/styles/class-names.ts +++ b/packages/@luke-ui/react/src/core/styles/class-selector.ts @@ -1,8 +1,4 @@ -export const lukeUiClassNames = { - resetRoot: 'luke-ui-reset', - themeRoot: 'luke-ui-theme', -} as const; - +/** Builds a CSS class selector (`.foo`) from a class name, typed to preserve the literal. */ export function classSelector(className: TClassName): `.${TClassName}` { return `.${className}`; } diff --git a/packages/@luke-ui/react/src/styles/focus-ring.ts b/packages/@luke-ui/react/src/core/styles/focus-ring.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/focus-ring.ts rename to packages/@luke-ui/react/src/core/styles/focus-ring.ts diff --git a/packages/@luke-ui/react/src/styles/index.css.ts b/packages/@luke-ui/react/src/core/styles/index.css.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/index.css.ts rename to packages/@luke-ui/react/src/core/styles/index.css.ts diff --git a/packages/@luke-ui/react/src/styles/input-states.ts b/packages/@luke-ui/react/src/core/styles/input-states.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/input-states.ts rename to packages/@luke-ui/react/src/core/styles/input-states.ts diff --git a/packages/@luke-ui/react/src/styles/invalid-indicator.ts b/packages/@luke-ui/react/src/core/styles/invalid-indicator.ts similarity index 95% rename from packages/@luke-ui/react/src/styles/invalid-indicator.ts rename to packages/@luke-ui/react/src/core/styles/invalid-indicator.ts index 8c83d788..2a2219c9 100644 --- a/packages/@luke-ui/react/src/styles/invalid-indicator.ts +++ b/packages/@luke-ui/react/src/core/styles/invalid-indicator.ts @@ -1,6 +1,6 @@ import type { StyleRule } from '@vanilla-extract/css'; -import { iconMaskUrls } from '../../.generated/icon-mask-data.js'; -import { vars } from '../theme/contract.css.js'; +import { iconMaskUrls } from '../../../.generated/icon-mask-data.js'; +import { vars } from '../../theme/contract.css.js'; /** Renders the `exclamationTriangle` icon as a CSS mask `::before`/`::after`, at `size`. */ function invalidIconMask(size: string) { diff --git a/packages/@luke-ui/react/src/styles/layered-style.css.ts b/packages/@luke-ui/react/src/core/styles/layered-style.css.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/layered-style.css.ts rename to packages/@luke-ui/react/src/core/styles/layered-style.css.ts diff --git a/packages/@luke-ui/react/src/styles/layers.css.ts b/packages/@luke-ui/react/src/core/styles/layers.css.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/layers.css.ts rename to packages/@luke-ui/react/src/core/styles/layers.css.ts diff --git a/packages/@luke-ui/react/src/styles/layers.test.ts b/packages/@luke-ui/react/src/core/styles/layers.test.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/layers.test.ts rename to packages/@luke-ui/react/src/core/styles/layers.test.ts diff --git a/packages/@luke-ui/react/src/styles/modules.css.ts b/packages/@luke-ui/react/src/core/styles/modules.css.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/modules.css.ts rename to packages/@luke-ui/react/src/core/styles/modules.css.ts diff --git a/packages/@luke-ui/react/src/styles/overlay-motion.ts b/packages/@luke-ui/react/src/core/styles/overlay-motion.ts similarity index 95% rename from packages/@luke-ui/react/src/styles/overlay-motion.ts rename to packages/@luke-ui/react/src/core/styles/overlay-motion.ts index 32b9d277..3bd41ac2 100644 --- a/packages/@luke-ui/react/src/styles/overlay-motion.ts +++ b/packages/@luke-ui/react/src/core/styles/overlay-motion.ts @@ -1,4 +1,4 @@ -import { vars } from '../theme/contract.css.js'; +import { vars } from '../../theme/contract.css.js'; /* * Overlays take the enter and exit duration roles rather than the in-place feedback one. Entry diff --git a/packages/@luke-ui/react/src/styles/package-exports.test.ts b/packages/@luke-ui/react/src/core/styles/package-exports.test.ts similarity index 66% rename from packages/@luke-ui/react/src/styles/package-exports.test.ts rename to packages/@luke-ui/react/src/core/styles/package-exports.test.ts index 970cb6c8..c40c3592 100644 --- a/packages/@luke-ui/react/src/styles/package-exports.test.ts +++ b/packages/@luke-ui/react/src/core/styles/package-exports.test.ts @@ -1,6 +1,6 @@ import { readFile } from 'node:fs/promises'; import { expect, test } from 'vite-plus/test'; -import packageJson from '../../package.json' with { type: 'json' }; +import packageJson from '../../../package.json' with { type: 'json' }; const absentExportPaths = [ './button/primitive', @@ -18,17 +18,17 @@ const absentExportPaths = [ ] as const; const presentExportPaths = { - './box': './dist/box/index.js', - './theme': './dist/theme/index.js', - './themes/tactile': './dist/themes/tactile/index.js', - './themes/paper': './dist/themes/paper/index.js', - './styles': './dist/styles/index.js', + './box': './dist/box.js', + './theme': './dist/theme.js', + './themes/tactile': './dist/themes/tactile.js', + './themes/paper': './dist/themes/paper.js', + './styles': './dist/styles.js', './stylesheet.css': './dist/stylesheet.css', - './primitives/button': './dist/primitives/button/index.js', - './primitives/checkbox': './dist/primitives/checkbox/index.js', - './primitives/combobox': './dist/primitives/combobox/index.js', - './primitives/field': './dist/primitives/field/index.js', - './primitives/input-group': './dist/primitives/input-group/index.js', + './primitives/button': './dist/primitives/button.js', + './primitives/checkbox': './dist/primitives/checkbox.js', + './primitives/combobox': './dist/primitives/combobox.js', + './primitives/field': './dist/primitives/field.js', + './primitives/input-group': './dist/primitives/input-group.js', } as const; test('publishes only the final styling entrypoints', () => { @@ -46,9 +46,9 @@ test('publishes only the final styling entrypoints', () => { }); test('bundles recipe runtime through a relative chunk, not a package import', async () => { - const source = await readFile(new URL('../../dist/blockquote/index.js', import.meta.url), 'utf8'); + const source = await readFile(new URL('../../../dist/blockquote.js', import.meta.url), 'utf8'); expect(source).not.toContain('#recipe-engine'); - expect(source).toMatch(/createSingleRecipe[\s\S]*from ["']\.\.\//); + expect(source).toMatch(/createSingleRecipe[\s\S]*from ["']\.\//); }); test('requires react-aria-components as a peer dependency', () => { diff --git a/packages/@luke-ui/react/src/styles/recipe-engine.ts b/packages/@luke-ui/react/src/core/styles/recipe-engine.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/recipe-engine.ts rename to packages/@luke-ui/react/src/core/styles/recipe-engine.ts diff --git a/packages/@luke-ui/react/src/styles/recipe.browser.test.ts b/packages/@luke-ui/react/src/core/styles/recipe.browser.test.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/recipe.browser.test.ts rename to packages/@luke-ui/react/src/core/styles/recipe.browser.test.ts diff --git a/packages/@luke-ui/react/src/styles/recipe.fixtures.css.ts b/packages/@luke-ui/react/src/core/styles/recipe.fixtures.css.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/recipe.fixtures.css.ts rename to packages/@luke-ui/react/src/core/styles/recipe.fixtures.css.ts diff --git a/packages/@luke-ui/react/src/styles/recipe.ts b/packages/@luke-ui/react/src/core/styles/recipe.ts similarity index 99% rename from packages/@luke-ui/react/src/styles/recipe.ts rename to packages/@luke-ui/react/src/core/styles/recipe.ts index 1745d566..48aa2596 100644 --- a/packages/@luke-ui/react/src/styles/recipe.ts +++ b/packages/@luke-ui/react/src/core/styles/recipe.ts @@ -1,8 +1,8 @@ import type { StyleRule } from '@vanilla-extract/css'; import { addFunctionSerializer } from '@vanilla-extract/css/functionSerializer'; import { recipe as vanillaRecipe } from '@vanilla-extract/recipes'; +import { cx } from '../../shared/utils/utils.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; -import { cx } from '../utils/utils.js'; /** * `recipe()` styling helper for Vanilla Extract. diff --git a/packages/@luke-ui/react/src/styles/reset.css.ts b/packages/@luke-ui/react/src/core/styles/reset.css.ts similarity index 93% rename from packages/@luke-ui/react/src/styles/reset.css.ts rename to packages/@luke-ui/react/src/core/styles/reset.css.ts index 8c6b6da6..17e5c9dd 100644 --- a/packages/@luke-ui/react/src/styles/reset.css.ts +++ b/packages/@luke-ui/react/src/core/styles/reset.css.ts @@ -1,5 +1,6 @@ -import { vars } from '../theme/contract.css.js'; -import { classSelector, lukeUiClassNames } from './class-names.js'; +import { lukeUiClassNames } from '../../shared/class-names.js'; +import { vars } from '../../theme/contract.css.js'; +import { classSelector } from './class-selector.js'; import { focusRing } from './focus-ring.js'; import { globalStyleInLayer } from './layered-style.css.js'; diff --git a/packages/@luke-ui/react/src/styles/spinner-overlay.ts b/packages/@luke-ui/react/src/core/styles/spinner-overlay.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/spinner-overlay.ts rename to packages/@luke-ui/react/src/core/styles/spinner-overlay.ts diff --git a/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts b/packages/@luke-ui/react/src/core/styles/stylesheet-contract.test.ts similarity index 97% rename from packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts rename to packages/@luke-ui/react/src/core/styles/stylesheet-contract.test.ts index b23e8987..05525dae 100644 --- a/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts +++ b/packages/@luke-ui/react/src/core/styles/stylesheet-contract.test.ts @@ -3,8 +3,8 @@ import type { AtRule, Root, Rule } from 'postcss'; import { parse } from 'postcss'; import selectorParser from 'postcss-selector-parser'; import { expect, test } from 'vite-plus/test'; -import type { TypeStyle } from '../theme/contract.js'; -import { typeStyles } from '../theme/contract.js'; +import type { TypeStyle } from '../../theme/contract.js'; +import { typeStyles } from '../../theme/contract.js'; const retainedLayerNames = ['reset', 'theme', 'recipes', 'utilities'] as const; const retainedLayerNameSet = new Set(retainedLayerNames); @@ -17,7 +17,10 @@ type LineClampClasses = { }; test('builds the public stylesheet with the retained layer contract', async () => { - const stylesheet = await readFile(new URL('../../dist/stylesheet.css', import.meta.url), 'utf8'); + const stylesheet = await readFile( + new URL('../../../dist/stylesheet.css', import.meta.url), + 'utf8', + ); const icon = await import('@luke-ui/react/icon'); const text = await import('@luke-ui/react/text'); const styles = await import('@luke-ui/react/styles'); @@ -106,7 +109,10 @@ for (const [name, mutate] of stylesheetMutations) { } test('queries responsive conditions on the logical inline axis', async () => { - const stylesheet = await readFile(new URL('../../dist/stylesheet.css', import.meta.url), 'utf8'); + const stylesheet = await readFile( + new URL('../../../dist/stylesheet.css', import.meta.url), + 'utf8', + ); // The containers are `container-type: inline-size`, which answers inline-axis queries. A // physical `width` query only agrees with that in a horizontal writing mode. diff --git a/packages/@luke-ui/react/src/styles/stylesheet-size.test.ts b/packages/@luke-ui/react/src/core/styles/stylesheet-size.test.ts similarity index 88% rename from packages/@luke-ui/react/src/styles/stylesheet-size.test.ts rename to packages/@luke-ui/react/src/core/styles/stylesheet-size.test.ts index 381cfbe2..d719ef30 100644 --- a/packages/@luke-ui/react/src/styles/stylesheet-size.test.ts +++ b/packages/@luke-ui/react/src/core/styles/stylesheet-size.test.ts @@ -9,7 +9,7 @@ import { expect, test } from 'vite-plus/test'; const maximumGzipBytes = 12_000; test('keeps the public stylesheet within its size budget', async () => { - const stylesheet = await readFile(new URL('../../dist/stylesheet.css', import.meta.url)); + const stylesheet = await readFile(new URL('../../../dist/stylesheet.css', import.meta.url)); expect(gzipSync(stylesheet, { level: 9 }).byteLength, 'gzip stylesheet size').toBeLessThanOrEqual( maximumGzipBytes, diff --git a/packages/@luke-ui/react/src/styles/theme-root.css.ts b/packages/@luke-ui/react/src/core/styles/theme-root.css.ts similarity index 59% rename from packages/@luke-ui/react/src/styles/theme-root.css.ts rename to packages/@luke-ui/react/src/core/styles/theme-root.css.ts index 0dff29c0..303f6175 100644 --- a/packages/@luke-ui/react/src/styles/theme-root.css.ts +++ b/packages/@luke-ui/react/src/core/styles/theme-root.css.ts @@ -1,5 +1,6 @@ -import { vars } from '../theme/contract.css.js'; -import { classSelector, lukeUiClassNames } from './class-names.js'; +import { lukeUiClassNames } from '../../shared/class-names.js'; +import { vars } from '../../theme/contract.css.js'; +import { classSelector } from './class-selector.js'; import { globalStyleInLayer } from './layered-style.css.js'; globalStyleInLayer('theme', classSelector(lukeUiClassNames.themeRoot), { diff --git a/packages/@luke-ui/react/src/styles/utilities-emitted.test-d.ts b/packages/@luke-ui/react/src/core/styles/utilities-emitted.test-d.ts similarity index 92% rename from packages/@luke-ui/react/src/styles/utilities-emitted.test-d.ts rename to packages/@luke-ui/react/src/core/styles/utilities-emitted.test-d.ts index e71d1f88..02e58eb9 100644 --- a/packages/@luke-ui/react/src/styles/utilities-emitted.test-d.ts +++ b/packages/@luke-ui/react/src/core/styles/utilities-emitted.test-d.ts @@ -2,20 +2,17 @@ import { readFile } from 'node:fs/promises'; import { expect, test } from 'vite-plus/test'; -import { breakpoints } from '../theme/breakpoints.js'; -import { SEMANTIC_ROLES } from '../theme/contrast-policy.js'; +import { breakpoints } from '../../theme/breakpoints.js'; +import { SEMANTIC_ROLES } from '../../theme/contrast-policy.js'; -/** Reads the content-hashed declaration chunk referenced by `box/index.d.ts`. */ +/** Reads the content-hashed declaration chunk referenced by `box.d.ts`. */ async function readUtilitiesDeclaration(): Promise { - const boxDeclaration = await readFile( - new URL('../../dist/box/index.d.ts', import.meta.url), - 'utf8', - ); + const boxDeclaration = await readFile(new URL('../../../dist/box.d.ts', import.meta.url), 'utf8'); const chunk = /utilities\.css-[A-Za-z0-9_-]+/.exec(boxDeclaration)?.[0]; if (chunk === undefined) { - throw new Error('Could not find the utilities declaration chunk from dist/box/index.d.ts'); + throw new Error('Could not find the utilities declaration chunk from dist/box.d.ts'); } - return readFile(new URL(`../../dist/${chunk}.d.ts`, import.meta.url), 'utf8'); + return readFile(new URL(`../../../dist/${chunk}.d.ts`, import.meta.url), 'utf8'); } /** Extracts one property's declared type while balancing braces. */ diff --git a/packages/@luke-ui/react/src/styles/utilities.browser.test.ts b/packages/@luke-ui/react/src/core/styles/utilities.browser.test.ts similarity index 93% rename from packages/@luke-ui/react/src/styles/utilities.browser.test.ts rename to packages/@luke-ui/react/src/core/styles/utilities.browser.test.ts index ed3364d2..b40c1832 100644 --- a/packages/@luke-ui/react/src/styles/utilities.browser.test.ts +++ b/packages/@luke-ui/react/src/core/styles/utilities.browser.test.ts @@ -1,10 +1,10 @@ -import '../../dist/themes/tactile/stylesheet.css'; +import '../../../dist/themes/tactile/stylesheet.css'; import '../stylesheet.css.js'; import { afterEach, expect, test } from 'vite-plus/test'; import { page } from 'vite-plus/test/context'; -import { breakpoints } from '../theme/breakpoints.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; -import { mergeProps } from '../utils/index.js'; +import { mergeProps } from '../../shared/utils/utils.js'; +import { breakpoints } from '../../theme/breakpoints.js'; +import { themeClassName as tactileThemeClassName } from '../../theme/bundles/tactile/index.js'; import { createSprinkles } from './utilities.css.js'; const mounted: Array = []; diff --git a/packages/@luke-ui/react/src/styles/utilities.css.ts b/packages/@luke-ui/react/src/core/styles/utilities.css.ts similarity index 95% rename from packages/@luke-ui/react/src/styles/utilities.css.ts rename to packages/@luke-ui/react/src/core/styles/utilities.css.ts index 976225a1..c7101fcd 100644 --- a/packages/@luke-ui/react/src/styles/utilities.css.ts +++ b/packages/@luke-ui/react/src/core/styles/utilities.css.ts @@ -1,10 +1,10 @@ import type { SprinklesFn } from '@luke-ui/rainbow-sprinkles'; import { defineProperties, defineSprinkles } from '@luke-ui/rainbow-sprinkles'; -import { breakpoints } from '../theme/breakpoints.js'; -import { vars } from '../theme/contract.css.js'; -import type { SpaceStep } from '../theme/contract.js'; -import { SEMANTIC_ROLES } from '../theme/contrast-policy.js'; -import { typedEntries } from '../utils/index.js'; +import { typedEntries } from '../../shared/utils/utils.js'; +import { breakpoints } from '../../theme/breakpoints.js'; +import { vars } from '../../theme/contract.css.js'; +import type { SpaceStep } from '../../theme/contract.js'; +import { SEMANTIC_ROLES } from '../../theme/contrast-policy.js'; import { layers } from './layers.css.js'; function fromBreakpoint(minimumInlineSize: number) { diff --git a/packages/@luke-ui/react/src/styles/utilities.stories.tsx b/packages/@luke-ui/react/src/core/styles/utilities.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/styles/utilities.stories.tsx rename to packages/@luke-ui/react/src/core/styles/utilities.stories.tsx index bf4189e6..b574903d 100644 --- a/packages/@luke-ui/react/src/styles/utilities.stories.tsx +++ b/packages/@luke-ui/react/src/core/styles/utilities.stories.tsx @@ -2,8 +2,8 @@ import { Button } from '@luke-ui/react/button'; import { createSprinkles } from '@luke-ui/react/styles'; import { mergeProps } from '@luke-ui/react/utils'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; -import { vars } from '../theme/index.js'; +import preview from '../../../.storybook/preview.js'; +import { vars } from '../../theme/index.js'; const meta = preview.meta({ title: 'Foundation/Utilities', diff --git a/packages/@luke-ui/react/src/styles/utilities.test-d.ts b/packages/@luke-ui/react/src/core/styles/utilities.test-d.ts similarity index 98% rename from packages/@luke-ui/react/src/styles/utilities.test-d.ts rename to packages/@luke-ui/react/src/core/styles/utilities.test-d.ts index bb1bb621..41dab941 100644 --- a/packages/@luke-ui/react/src/styles/utilities.test-d.ts +++ b/packages/@luke-ui/react/src/core/styles/utilities.test-d.ts @@ -1,7 +1,7 @@ /** Type-level assertions against the utility props emitted for package consumers. */ import { assertType, expectTypeOf, test } from 'vite-plus/test'; -import type { BoxProps } from '../../dist/box/index.js'; +import type { BoxProps } from '../../../dist/box.js'; type UtilityProps = NonNullable; diff --git a/packages/@luke-ui/react/src/styles/utilities.test.ts b/packages/@luke-ui/react/src/core/styles/utilities.test.ts similarity index 100% rename from packages/@luke-ui/react/src/styles/utilities.test.ts rename to packages/@luke-ui/react/src/core/styles/utilities.test.ts diff --git a/packages/@luke-ui/react/src/stylesheet.css.ts b/packages/@luke-ui/react/src/core/stylesheet.css.ts similarity index 100% rename from packages/@luke-ui/react/src/stylesheet.css.ts rename to packages/@luke-ui/react/src/core/stylesheet.css.ts diff --git a/packages/@luke-ui/react/src/test-utils/get-described-text.ts b/packages/@luke-ui/react/src/core/test-utils/get-described-text.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/get-described-text.ts rename to packages/@luke-ui/react/src/core/test-utils/get-described-text.ts diff --git a/packages/@luke-ui/react/src/test-utils/mock-screen-width.ts b/packages/@luke-ui/react/src/core/test-utils/mock-screen-width.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/mock-screen-width.ts rename to packages/@luke-ui/react/src/core/test-utils/mock-screen-width.ts diff --git a/packages/@luke-ui/react/src/test-utils/render-setup.ts b/packages/@luke-ui/react/src/core/test-utils/render-setup.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/render-setup.ts rename to packages/@luke-ui/react/src/core/test-utils/render-setup.ts diff --git a/packages/@luke-ui/react/src/test-utils/render.browser.test.tsx b/packages/@luke-ui/react/src/core/test-utils/render.browser.test.tsx similarity index 88% rename from packages/@luke-ui/react/src/test-utils/render.browser.test.tsx rename to packages/@luke-ui/react/src/core/test-utils/render.browser.test.tsx index 00d537b3..0784f100 100644 --- a/packages/@luke-ui/react/src/test-utils/render.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/test-utils/render.browser.test.tsx @@ -1,6 +1,6 @@ import { expect, test } from 'vite-plus/test'; -import { themeClassName as paperThemeClassName } from '../themes/paper/index.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; +import { themeClassName as paperThemeClassName } from '../../theme/bundles/paper/index.js'; +import { themeClassName as tactileThemeClassName } from '../../theme/bundles/tactile/index.js'; import { render, visualAppearances } from './render.js'; test('renders every bundled identity and explicit colour mode independently', () => { diff --git a/packages/@luke-ui/react/src/test-utils/render.tsx b/packages/@luke-ui/react/src/core/test-utils/render.tsx similarity index 91% rename from packages/@luke-ui/react/src/test-utils/render.tsx rename to packages/@luke-ui/react/src/core/test-utils/render.tsx index 773eb095..fa7f82a5 100644 --- a/packages/@luke-ui/react/src/test-utils/render.tsx +++ b/packages/@luke-ui/react/src/core/test-utils/render.tsx @@ -12,11 +12,11 @@ import type { Locator } from 'vite-plus/test/context'; import { page, userEvent } from 'vite-plus/test/context'; // The generated spritesheet is emitted to `dist/` by the `generate` task, which // both `build` and `test` depend on, so it is always present when tests run. -import spritesheetHref from '../../dist/spritesheet.svg?url'; -import { IconSpritesheetProvider } from '../icon/index.js'; -import { rootClassName, vars } from '../theme/index.js'; -import { themeClassName as paperThemeClassName } from '../themes/paper/index.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; +import spritesheetHref from '../../../dist/spritesheet.svg?url'; +import { themeClassName as paperThemeClassName } from '../../theme/bundles/paper/index.js'; +import { themeClassName as tactileThemeClassName } from '../../theme/bundles/tactile/index.js'; +import { rootClassName, vars } from '../../theme/index.js'; +import { IconSpritesheetProvider } from '../icon/icon.js'; const mounted: Array<{ container: HTMLElement; root: Root }> = []; diff --git a/packages/@luke-ui/react/src/test-utils/visual-capture-id.test.ts b/packages/@luke-ui/react/src/core/test-utils/visual-capture-id.test.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/visual-capture-id.test.ts rename to packages/@luke-ui/react/src/core/test-utils/visual-capture-id.test.ts diff --git a/packages/@luke-ui/react/src/test-utils/visual-capture-id.ts b/packages/@luke-ui/react/src/core/test-utils/visual-capture-id.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/visual-capture-id.ts rename to packages/@luke-ui/react/src/core/test-utils/visual-capture-id.ts diff --git a/packages/@luke-ui/react/src/test-utils/visual-setup.ts b/packages/@luke-ui/react/src/core/test-utils/visual-setup.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/visual-setup.ts rename to packages/@luke-ui/react/src/core/test-utils/visual-setup.ts diff --git a/packages/@luke-ui/react/src/test-utils/visual.tsx b/packages/@luke-ui/react/src/core/test-utils/visual.tsx similarity index 100% rename from packages/@luke-ui/react/src/test-utils/visual.tsx rename to packages/@luke-ui/react/src/core/test-utils/visual.tsx diff --git a/packages/@luke-ui/react/src/test-utils/wait-for-overlay-enter.ts b/packages/@luke-ui/react/src/core/test-utils/wait-for-overlay-enter.ts similarity index 100% rename from packages/@luke-ui/react/src/test-utils/wait-for-overlay-enter.ts rename to packages/@luke-ui/react/src/core/test-utils/wait-for-overlay-enter.ts diff --git a/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx b/packages/@luke-ui/react/src/core/text-field/text-field.browser.test.tsx similarity index 98% rename from packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx rename to packages/@luke-ui/react/src/core/text-field/text-field.browser.test.tsx index d4677df7..0aa578c8 100644 --- a/packages/@luke-ui/react/src/text-field/text-field.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/text-field/text-field.browser.test.tsx @@ -2,10 +2,10 @@ import { createRef } from 'react'; import { expect, test } from 'vite-plus/test'; import { page, userEvent } from 'vite-plus/test/context'; import { testConformance, testIntegration } from '../conformance/helpers.js'; -import { InputGroup, InputGroupInput } from '../primitives/input-group/index.js'; +import { InputGroup, InputGroupInput } from '../primitives/input-group/input-group.js'; import { getDescribedText } from '../test-utils/get-described-text.js'; import { render } from '../test-utils/render.js'; -import { TextField } from './index.js'; +import { TextField } from './text-field.js'; testConformance({ path: 'text-field', diff --git a/packages/@luke-ui/react/src/text-field/text-field.stories.tsx b/packages/@luke-ui/react/src/core/text-field/text-field.stories.tsx similarity index 98% rename from packages/@luke-ui/react/src/text-field/text-field.stories.tsx rename to packages/@luke-ui/react/src/core/text-field/text-field.stories.tsx index dc8cf070..9b8ed7a6 100644 --- a/packages/@luke-ui/react/src/text-field/text-field.stories.tsx +++ b/packages/@luke-ui/react/src/core/text-field/text-field.stories.tsx @@ -11,7 +11,7 @@ import type { CSSProperties } from 'react'; import { useState } from 'react'; import { Form } from 'react-aria-components/Form'; import { userEvent, within } from 'storybook/test'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: TextField, diff --git a/packages/@luke-ui/react/src/text-field/text-field.tsx b/packages/@luke-ui/react/src/core/text-field/text-field.tsx similarity index 100% rename from packages/@luke-ui/react/src/text-field/text-field.tsx rename to packages/@luke-ui/react/src/core/text-field/text-field.tsx diff --git a/packages/@luke-ui/react/src/text-field/text-field.visual.test.tsx b/packages/@luke-ui/react/src/core/text-field/text-field.visual.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/text-field/text-field.visual.test.tsx rename to packages/@luke-ui/react/src/core/text-field/text-field.visual.test.tsx index ac43f7c3..3e443649 100644 --- a/packages/@luke-ui/react/src/text-field/text-field.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/text-field/text-field.visual.test.tsx @@ -1,13 +1,13 @@ import { test } from 'vite-plus/test'; import { page, userEvent } from 'vite-plus/test/context'; -import { Button } from '../button/index.js'; -import { Icon } from '../icon/index.js'; +import { Button } from '../button/button.js'; +import { Icon } from '../icon/icon.js'; import { InputGroup, InputGroupInput, InputGroupPrefix, InputGroupSuffix, -} from '../primitives/input-group/index.js'; +} from '../primitives/input-group/input-group.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, @@ -16,7 +16,7 @@ import { focusViaKeyboard, Stack, } from '../test-utils/visual.js'; -import { TextField } from './index.js'; +import { TextField } from './text-field.js'; test('kitchen sink', async () => { for (const appearance of visualAppearances) { diff --git a/packages/@luke-ui/react/src/text/recipe.css.ts b/packages/@luke-ui/react/src/core/text/recipe.css.ts similarity index 96% rename from packages/@luke-ui/react/src/text/recipe.css.ts rename to packages/@luke-ui/react/src/core/text/recipe.css.ts index 79efd439..027248c8 100644 --- a/packages/@luke-ui/react/src/text/recipe.css.ts +++ b/packages/@luke-ui/react/src/core/text/recipe.css.ts @@ -1,11 +1,11 @@ import type { ComplexStyleRule } from '@vanilla-extract/css'; import { createVar } from '@vanilla-extract/css'; +import { vars } from '../../theme/contract.css.js'; +import type { FontWeightRole, TypeStyle } from '../../theme/contract.js'; +import { fontWeightRoles, typeStyles } from '../../theme/contract.js'; import { styleInLayer } from '../styles/layered-style.css.js'; import type { RecipeSelection } from '../styles/recipe.js'; import { recipe } from '../styles/recipe.js'; -import { vars } from '../theme/contract.css.js'; -import type { FontWeightRole, TypeStyle } from '../theme/contract.js'; -import { fontWeightRoles, typeStyles } from '../theme/contract.js'; import { visuallyHiddenStyle } from '../visually-hidden/recipe.css.js'; const lineClampNone = {} satisfies ComplexStyleRule; diff --git a/packages/@luke-ui/react/src/text/text.browser.test.tsx b/packages/@luke-ui/react/src/core/text/text.browser.test.tsx similarity index 91% rename from packages/@luke-ui/react/src/text/text.browser.test.tsx rename to packages/@luke-ui/react/src/core/text/text.browser.test.tsx index 674c002a..fd67354d 100644 --- a/packages/@luke-ui/react/src/text/text.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/text/text.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { Text } from './index.js'; +import { Text } from './text.js'; testConformance({ path: 'text', diff --git a/packages/@luke-ui/react/src/text/text.stories.tsx b/packages/@luke-ui/react/src/core/text/text.stories.tsx similarity index 97% rename from packages/@luke-ui/react/src/text/text.stories.tsx rename to packages/@luke-ui/react/src/core/text/text.stories.tsx index 529ed96a..692db341 100644 --- a/packages/@luke-ui/react/src/text/text.stories.tsx +++ b/packages/@luke-ui/react/src/core/text/text.stories.tsx @@ -3,9 +3,9 @@ import { Text } from '@luke-ui/react/text'; import { mergeProps } from '@luke-ui/react/utils'; import { VisuallyHidden as VisuallyHiddenText } from '@luke-ui/react/visually-hidden'; import type { CSSProperties } from 'react'; -import preview from '../../.storybook/preview.js'; -import { createSprinkles } from '../styles/index.js'; -import { typeStyles, vars } from '../theme/index.js'; +import preview from '../../../.storybook/preview.js'; +import { typeStyles, vars } from '../../theme/index.js'; +import { createSprinkles } from '../styles/utilities.css.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; const meta = preview.meta({ diff --git a/packages/@luke-ui/react/src/text/text.tsx b/packages/@luke-ui/react/src/core/text/text.tsx similarity index 97% rename from packages/@luke-ui/react/src/text/text.tsx rename to packages/@luke-ui/react/src/core/text/text.tsx index 1b92b00f..16537b90 100644 --- a/packages/@luke-ui/react/src/text/text.tsx +++ b/packages/@luke-ui/react/src/core/text/text.tsx @@ -1,8 +1,8 @@ import { Text as RacText } from 'react-aria-components/Text'; -import { typeStyleWeightRole } from '../theme/contract.js'; +import { cx } from '../../shared/utils/utils.js'; +import { typeStyleWeightRole } from '../../theme/contract.js'; import type { DistributiveOmit } from '../types/distributive-omit.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import type { TextRecipeVariants } from './recipe.css.js'; import { textRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/text/text.visual.test.tsx b/packages/@luke-ui/react/src/core/text/text.visual.test.tsx similarity index 96% rename from packages/@luke-ui/react/src/text/text.visual.test.tsx rename to packages/@luke-ui/react/src/core/text/text.visual.test.tsx index 2b489150..c47b7bbf 100644 --- a/packages/@luke-ui/react/src/text/text.visual.test.tsx +++ b/packages/@luke-ui/react/src/core/text/text.visual.test.tsx @@ -1,9 +1,9 @@ import type { CSSProperties } from 'react'; import { test } from 'vite-plus/test'; +import { typeStyles } from '../../theme/contract.js'; import { render, visualAppearances } from '../test-utils/render.js'; import { captureVisual, captureVisualAppearance, Stack } from '../test-utils/visual.js'; -import { typeStyles } from '../theme/contract.js'; -import { Text } from './index.js'; +import { Text } from './text.js'; const rowStyle = { alignItems: 'baseline', diff --git a/packages/@luke-ui/react/src/types/distributive-omit.ts b/packages/@luke-ui/react/src/core/types/distributive-omit.ts similarity index 100% rename from packages/@luke-ui/react/src/types/distributive-omit.ts rename to packages/@luke-ui/react/src/core/types/distributive-omit.ts diff --git a/packages/@luke-ui/react/src/types/documented-rac-props.ts b/packages/@luke-ui/react/src/core/types/documented-rac-props.ts similarity index 100% rename from packages/@luke-ui/react/src/types/documented-rac-props.ts rename to packages/@luke-ui/react/src/core/types/documented-rac-props.ts diff --git a/packages/@luke-ui/react/src/types/icon-size.ts b/packages/@luke-ui/react/src/core/types/icon-size.ts similarity index 100% rename from packages/@luke-ui/react/src/types/icon-size.ts rename to packages/@luke-ui/react/src/core/types/icon-size.ts diff --git a/packages/@luke-ui/react/src/types/prettify.ts b/packages/@luke-ui/react/src/core/types/prettify.ts similarity index 100% rename from packages/@luke-ui/react/src/types/prettify.ts rename to packages/@luke-ui/react/src/core/types/prettify.ts diff --git a/packages/@luke-ui/react/src/use-synchronize-animations/use-synchronize-animations.browser.test.tsx b/packages/@luke-ui/react/src/core/use-synchronize-animations/use-synchronize-animations.browser.test.tsx similarity index 100% rename from packages/@luke-ui/react/src/use-synchronize-animations/use-synchronize-animations.browser.test.tsx rename to packages/@luke-ui/react/src/core/use-synchronize-animations/use-synchronize-animations.browser.test.tsx diff --git a/packages/@luke-ui/react/src/use-synchronize-animations/use-synchronize-animations.ts b/packages/@luke-ui/react/src/core/use-synchronize-animations/use-synchronize-animations.ts similarity index 100% rename from packages/@luke-ui/react/src/use-synchronize-animations/use-synchronize-animations.ts rename to packages/@luke-ui/react/src/core/use-synchronize-animations/use-synchronize-animations.ts diff --git a/packages/@luke-ui/react/src/visually-hidden/recipe.css.ts b/packages/@luke-ui/react/src/core/visually-hidden/recipe.css.ts similarity index 100% rename from packages/@luke-ui/react/src/visually-hidden/recipe.css.ts rename to packages/@luke-ui/react/src/core/visually-hidden/recipe.css.ts diff --git a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.browser.test.tsx b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.browser.test.tsx similarity index 89% rename from packages/@luke-ui/react/src/visually-hidden/visually-hidden.browser.test.tsx rename to packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.browser.test.tsx index 11b44990..56e04b58 100644 --- a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.browser.test.tsx +++ b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.browser.test.tsx @@ -1,6 +1,6 @@ import { testConformance } from '../conformance/helpers.js'; import { render } from '../test-utils/render.js'; -import { VisuallyHidden } from './index.js'; +import { VisuallyHidden } from './visually-hidden.js'; testConformance({ path: 'visually-hidden', diff --git a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.stories.tsx b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.stories.tsx similarity index 93% rename from packages/@luke-ui/react/src/visually-hidden/visually-hidden.stories.tsx rename to packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.stories.tsx index cc5758d4..97b802e3 100644 --- a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.stories.tsx +++ b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.stories.tsx @@ -1,5 +1,5 @@ import { VisuallyHidden } from '@luke-ui/react/visually-hidden'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: VisuallyHidden, diff --git a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.tsx b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.tsx similarity index 96% rename from packages/@luke-ui/react/src/visually-hidden/visually-hidden.tsx rename to packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.tsx index 7b227a45..bfadc937 100644 --- a/packages/@luke-ui/react/src/visually-hidden/visually-hidden.tsx +++ b/packages/@luke-ui/react/src/core/visually-hidden/visually-hidden.tsx @@ -1,7 +1,7 @@ import type { ComponentPropsWithRef, JSX } from 'react'; import { Text as RacText } from 'react-aria-components/Text'; +import { cx } from '../../shared/utils/utils.js'; import type { Prettify } from '../types/prettify.js'; -import { cx } from '../utils/utils.js'; import { visuallyHiddenRecipe } from './recipe.css.js'; type _VisuallyHiddenProps = ComponentPropsWithRef; diff --git a/packages/@luke-ui/react/src/em/index.ts b/packages/@luke-ui/react/src/em/index.ts deleted file mode 100644 index 8f6e5d70..00000000 --- a/packages/@luke-ui/react/src/em/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Em, type EmProps } from './em.js'; diff --git a/packages/@luke-ui/react/src/emoji/index.ts b/packages/@luke-ui/react/src/emoji/index.ts deleted file mode 100644 index 4ceb9217..00000000 --- a/packages/@luke-ui/react/src/emoji/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Emoji, type EmojiProps } from './emoji.js'; diff --git a/packages/@luke-ui/react/src/exports/blockquote.ts b/packages/@luke-ui/react/src/exports/blockquote.ts new file mode 100644 index 00000000..f68fe19c --- /dev/null +++ b/packages/@luke-ui/react/src/exports/blockquote.ts @@ -0,0 +1,2 @@ +export { Blockquote, type BlockquoteProps } from '../core/blockquote/blockquote.js'; +export { type BlockquoteRecipeVariants, blockquoteRecipe } from '../core/blockquote/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/box.ts b/packages/@luke-ui/react/src/exports/box.ts new file mode 100644 index 00000000..d4912d34 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/box.ts @@ -0,0 +1 @@ +export { Box, type BoxProps } from '../core/box/box.js'; diff --git a/packages/@luke-ui/react/src/exports/button.ts b/packages/@luke-ui/react/src/exports/button.ts new file mode 100644 index 00000000..cf149aca --- /dev/null +++ b/packages/@luke-ui/react/src/exports/button.ts @@ -0,0 +1,2 @@ +export { type ButtonRecipeVariants, buttonRecipe } from '../core/primitives/button/recipe.css.js'; +export { Button, type ButtonProps } from '../core/button/button.js'; diff --git a/packages/@luke-ui/react/src/exports/checkbox.ts b/packages/@luke-ui/react/src/exports/checkbox.ts new file mode 100644 index 00000000..f61c4edd --- /dev/null +++ b/packages/@luke-ui/react/src/exports/checkbox.ts @@ -0,0 +1,5 @@ +export { + type CheckboxRecipeVariants, + checkboxRecipe, +} from '../core/primitives/checkbox/recipe.css.js'; +export { Checkbox, type CheckboxProps } from '../core/checkbox/checkbox.js'; diff --git a/packages/@luke-ui/react/src/exports/code.ts b/packages/@luke-ui/react/src/exports/code.ts new file mode 100644 index 00000000..55f1ef9a --- /dev/null +++ b/packages/@luke-ui/react/src/exports/code.ts @@ -0,0 +1,2 @@ +export { Code, type CodeProps } from '../core/code/code.js'; +export { type CodeRecipeVariants, codeRecipe } from '../core/code/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/combobox-field.ts b/packages/@luke-ui/react/src/exports/combobox-field.ts new file mode 100644 index 00000000..0aa252b3 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/combobox-field.ts @@ -0,0 +1 @@ +export { ComboboxField, type ComboboxFieldProps } from '../core/combobox-field/combobox-field.js'; diff --git a/packages/@luke-ui/react/src/exports/em.ts b/packages/@luke-ui/react/src/exports/em.ts new file mode 100644 index 00000000..8456b684 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/em.ts @@ -0,0 +1 @@ +export { Em, type EmProps } from '../core/em/em.js'; diff --git a/packages/@luke-ui/react/src/exports/emoji.ts b/packages/@luke-ui/react/src/exports/emoji.ts new file mode 100644 index 00000000..dd700c1b --- /dev/null +++ b/packages/@luke-ui/react/src/exports/emoji.ts @@ -0,0 +1 @@ +export { Emoji, type EmojiProps } from '../core/emoji/emoji.js'; diff --git a/packages/@luke-ui/react/src/exports/heading.ts b/packages/@luke-ui/react/src/exports/heading.ts new file mode 100644 index 00000000..13b861cc --- /dev/null +++ b/packages/@luke-ui/react/src/exports/heading.ts @@ -0,0 +1,12 @@ +export { + Heading, + type HeadingLevel, + HeadingLevels, + type HeadingProps, + type HeadingTag, + useHeadingLevel, +} from '../core/heading/heading.js'; +export type { + HeadingLevelsProps, + HeadingLevelsRenderProps, +} from '../core/heading/heading-context.js'; diff --git a/packages/@luke-ui/react/src/exports/icon-button.ts b/packages/@luke-ui/react/src/exports/icon-button.ts new file mode 100644 index 00000000..c8406e4e --- /dev/null +++ b/packages/@luke-ui/react/src/exports/icon-button.ts @@ -0,0 +1,2 @@ +export { IconButton, type IconButtonProps } from '../core/icon-button/icon-button.js'; +export { type IconButtonRecipeVariants, iconButtonRecipe } from '../core/icon-button/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/icon/index.ts b/packages/@luke-ui/react/src/exports/icon.ts similarity index 65% rename from packages/@luke-ui/react/src/icon/index.ts rename to packages/@luke-ui/react/src/exports/icon.ts index 2ffeb606..7041c70b 100644 --- a/packages/@luke-ui/react/src/icon/index.ts +++ b/packages/@luke-ui/react/src/exports/icon.ts @@ -10,5 +10,5 @@ export { type IconSpritesheetProviderProps, iconNames, iconViewBoxes, -} from './icon.js'; -export { type IconRecipeVariants, iconRecipe } from './recipe.css.js'; +} from '../core/icon/icon.js'; +export { type IconRecipeVariants, iconRecipe } from '../core/icon/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/kbd.ts b/packages/@luke-ui/react/src/exports/kbd.ts new file mode 100644 index 00000000..74cfb88e --- /dev/null +++ b/packages/@luke-ui/react/src/exports/kbd.ts @@ -0,0 +1,2 @@ +export { Kbd, type KbdProps } from '../core/kbd/kbd.js'; +export { type KbdRecipeVariants, kbdRecipe } from '../core/kbd/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/link.ts b/packages/@luke-ui/react/src/exports/link.ts new file mode 100644 index 00000000..3f1b0ddf --- /dev/null +++ b/packages/@luke-ui/react/src/exports/link.ts @@ -0,0 +1,2 @@ +export { Link, type LinkProps } from '../core/link/link.js'; +export { type LinkRecipeVariants, linkRecipe } from '../core/link/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/loading-skeleton/index.ts b/packages/@luke-ui/react/src/exports/loading-skeleton.ts similarity index 68% rename from packages/@luke-ui/react/src/loading-skeleton/index.ts rename to packages/@luke-ui/react/src/exports/loading-skeleton.ts index d117ff93..e71ae414 100644 --- a/packages/@luke-ui/react/src/loading-skeleton/index.ts +++ b/packages/@luke-ui/react/src/exports/loading-skeleton.ts @@ -3,4 +3,4 @@ export { type LoadingSkeletonProps, LoadingSkeletonProvider, type LoadingSkeletonProviderProps, -} from './loading-skeleton.js'; +} from '../core/loading-skeleton/loading-skeleton.js'; diff --git a/packages/@luke-ui/react/src/exports/loading-spinner.ts b/packages/@luke-ui/react/src/exports/loading-spinner.ts new file mode 100644 index 00000000..aa3af4a0 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/loading-spinner.ts @@ -0,0 +1,8 @@ +export { + LoadingSpinner, + type LoadingSpinnerProps, +} from '../core/loading-spinner/loading-spinner.js'; +export { + type LoadingSpinnerRecipeVariants, + loadingSpinnerRecipe, +} from '../core/loading-spinner/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/numeral/index.ts b/packages/@luke-ui/react/src/exports/numeral.ts similarity index 75% rename from packages/@luke-ui/react/src/numeral/index.ts rename to packages/@luke-ui/react/src/exports/numeral.ts index 25a589f3..d3efe12f 100644 --- a/packages/@luke-ui/react/src/numeral/index.ts +++ b/packages/@luke-ui/react/src/exports/numeral.ts @@ -4,4 +4,4 @@ export { type NumeralFormat, type NumeralPrecision, type NumeralProps, -} from './numeral.js'; +} from '../core/numeral/numeral.js'; diff --git a/packages/@luke-ui/react/src/exports/primitives/button.ts b/packages/@luke-ui/react/src/exports/primitives/button.ts new file mode 100644 index 00000000..bceea966 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/primitives/button.ts @@ -0,0 +1,5 @@ +export { Button, type ButtonProps } from '../../core/primitives/button/button.js'; +export { + type ButtonRecipeVariants, + buttonRecipe, +} from '../../core/primitives/button/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/primitives/checkbox/index.ts b/packages/@luke-ui/react/src/exports/primitives/checkbox.ts similarity index 52% rename from packages/@luke-ui/react/src/primitives/checkbox/index.ts rename to packages/@luke-ui/react/src/exports/primitives/checkbox.ts index ede3d0a0..03aaa1b2 100644 --- a/packages/@luke-ui/react/src/primitives/checkbox/index.ts +++ b/packages/@luke-ui/react/src/exports/primitives/checkbox.ts @@ -7,5 +7,8 @@ export { CheckboxIndicator, type CheckboxIndicatorProps, type CheckboxProps, -} from './checkbox.js'; -export { type CheckboxRecipeVariants, checkboxRecipe } from './recipe.css.js'; +} from '../../core/primitives/checkbox/checkbox.js'; +export { + type CheckboxRecipeVariants, + checkboxRecipe, +} from '../../core/primitives/checkbox/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/primitives/combobox.ts b/packages/@luke-ui/react/src/exports/primitives/combobox.ts new file mode 100644 index 00000000..0e196577 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/primitives/combobox.ts @@ -0,0 +1,23 @@ +export type { ComboboxClearButtonProps } from '../../core/primitives/combobox/clear-button.js'; +export { ComboboxClearButton } from '../../core/primitives/combobox/clear-button.js'; +export type { ComboboxEmptyStateProps } from '../../core/primitives/combobox/empty-state.js'; +export { ComboboxEmptyState } from '../../core/primitives/combobox/empty-state.js'; +export type { ComboboxInputProps } from '../../core/primitives/combobox/input.js'; +export { ComboboxInput } from '../../core/primitives/combobox/input.js'; +export type { ComboboxInputGroupProps } from '../../core/primitives/combobox/input-group.js'; +export { ComboboxInputGroup } from '../../core/primitives/combobox/input-group.js'; +export type { + ComboboxItemProps, + ComboboxLoadMoreItemProps, +} from '../../core/primitives/combobox/item.js'; +export { ComboboxItem, ComboboxLoadMoreItem } from '../../core/primitives/combobox/item.js'; +export type { ComboboxListBoxProps } from '../../core/primitives/combobox/listbox.js'; +export { ComboboxListBox } from '../../core/primitives/combobox/listbox.js'; +export type { ComboboxPopoverProps } from '../../core/primitives/combobox/popover.js'; +export { ComboboxPopover } from '../../core/primitives/combobox/popover.js'; +export type { ComboboxRootProps, ComboboxSize } from '../../core/primitives/combobox/root.js'; +export { ComboboxRoot } from '../../core/primitives/combobox/root.js'; +export type { ComboboxSectionProps } from '../../core/primitives/combobox/section.js'; +export { ComboboxSection } from '../../core/primitives/combobox/section.js'; +export type { ComboboxTriggerProps } from '../../core/primitives/combobox/trigger.js'; +export { ComboboxTrigger } from '../../core/primitives/combobox/trigger.js'; diff --git a/packages/@luke-ui/react/src/primitives/field/index.ts b/packages/@luke-ui/react/src/exports/primitives/field.ts similarity index 56% rename from packages/@luke-ui/react/src/primitives/field/index.ts rename to packages/@luke-ui/react/src/exports/primitives/field.ts index 9fd8ac64..cd8e99fc 100644 --- a/packages/@luke-ui/react/src/primitives/field/index.ts +++ b/packages/@luke-ui/react/src/exports/primitives/field.ts @@ -8,5 +8,5 @@ export { type FieldLabelProps, type FieldNecessityIndicator, type FieldProps, -} from './field.js'; -export { type FieldRecipeVariants, fieldRecipe } from './recipe.css.js'; +} from '../../core/primitives/field/field.js'; +export { type FieldRecipeVariants, fieldRecipe } from '../../core/primitives/field/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/primitives/input-group/index.ts b/packages/@luke-ui/react/src/exports/primitives/input-group.ts similarity index 69% rename from packages/@luke-ui/react/src/primitives/input-group/index.ts rename to packages/@luke-ui/react/src/exports/primitives/input-group.ts index 3e9d7a59..2a77b574 100644 --- a/packages/@luke-ui/react/src/primitives/input-group/index.ts +++ b/packages/@luke-ui/react/src/exports/primitives/input-group.ts @@ -7,9 +7,9 @@ export { type InputGroupProps, InputGroupSuffix, type InputGroupSuffixProps, -} from './input-group.js'; +} from '../../core/primitives/input-group/input-group.js'; export { type InputGroupRecipeVariants, type InputGroupSize, inputGroupRecipe, -} from './recipe.css.js'; +} from '../../core/primitives/input-group/recipe.css.js'; diff --git a/packages/@luke-ui/react/src/exports/quote.ts b/packages/@luke-ui/react/src/exports/quote.ts new file mode 100644 index 00000000..c5f01da7 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/quote.ts @@ -0,0 +1 @@ +export { Quote, type QuoteProps } from '../core/quote/quote.js'; diff --git a/packages/@luke-ui/react/src/exports/strong.ts b/packages/@luke-ui/react/src/exports/strong.ts new file mode 100644 index 00000000..b5364606 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/strong.ts @@ -0,0 +1 @@ +export { Strong, type StrongProps } from '../core/strong/strong.js'; diff --git a/packages/@luke-ui/react/src/exports/styles.ts b/packages/@luke-ui/react/src/exports/styles.ts new file mode 100644 index 00000000..dc7d1e81 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/styles.ts @@ -0,0 +1,2 @@ +export type { SprinklesProps } from '../core/styles/utilities.css.js'; +export { createSprinkles } from '../core/styles/utilities.css.js'; diff --git a/packages/@luke-ui/react/src/exports/text-field.ts b/packages/@luke-ui/react/src/exports/text-field.ts new file mode 100644 index 00000000..9c5ed61b --- /dev/null +++ b/packages/@luke-ui/react/src/exports/text-field.ts @@ -0,0 +1 @@ +export { TextField, type TextFieldProps } from '../core/text-field/text-field.js'; diff --git a/packages/@luke-ui/react/src/exports/text.ts b/packages/@luke-ui/react/src/exports/text.ts new file mode 100644 index 00000000..4f868a52 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/text.ts @@ -0,0 +1,2 @@ +export { type TextRecipeVariants, textRecipe } from '../core/text/recipe.css.js'; +export { Text, type TextProps } from '../core/text/text.js'; diff --git a/packages/@luke-ui/react/src/exports/theme.ts b/packages/@luke-ui/react/src/exports/theme.ts new file mode 100644 index 00000000..a14d2c2d --- /dev/null +++ b/packages/@luke-ui/react/src/exports/theme.ts @@ -0,0 +1 @@ +export * from '../theme/index.js'; diff --git a/packages/@luke-ui/react/src/exports/themes/paper.ts b/packages/@luke-ui/react/src/exports/themes/paper.ts new file mode 100644 index 00000000..0bf587cc --- /dev/null +++ b/packages/@luke-ui/react/src/exports/themes/paper.ts @@ -0,0 +1 @@ +export * from '../../theme/bundles/paper/index.js'; diff --git a/packages/@luke-ui/react/src/exports/themes/tactile.ts b/packages/@luke-ui/react/src/exports/themes/tactile.ts new file mode 100644 index 00000000..c8fcdda7 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/themes/tactile.ts @@ -0,0 +1 @@ +export * from '../../theme/bundles/tactile/index.js'; diff --git a/packages/@luke-ui/react/src/exports/utils.ts b/packages/@luke-ui/react/src/exports/utils.ts new file mode 100644 index 00000000..053842ef --- /dev/null +++ b/packages/@luke-ui/react/src/exports/utils.ts @@ -0,0 +1 @@ +export * from '../shared/utils/utils.js'; diff --git a/packages/@luke-ui/react/src/exports/visually-hidden.ts b/packages/@luke-ui/react/src/exports/visually-hidden.ts new file mode 100644 index 00000000..b76b9099 --- /dev/null +++ b/packages/@luke-ui/react/src/exports/visually-hidden.ts @@ -0,0 +1,5 @@ +export { visuallyHiddenRecipe } from '../core/visually-hidden/recipe.css.js'; +export { + VisuallyHidden, + type VisuallyHiddenProps, +} from '../core/visually-hidden/visually-hidden.js'; diff --git a/packages/@luke-ui/react/src/heading/index.ts b/packages/@luke-ui/react/src/heading/index.ts deleted file mode 100644 index 71885e3a..00000000 --- a/packages/@luke-ui/react/src/heading/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { - Heading, - type HeadingLevel, - HeadingLevels, - type HeadingProps, - type HeadingTag, - useHeadingLevel, -} from './heading.js'; -export type { HeadingLevelsProps, HeadingLevelsRenderProps } from './heading-context.js'; diff --git a/packages/@luke-ui/react/src/icon-button/index.ts b/packages/@luke-ui/react/src/icon-button/index.ts deleted file mode 100644 index d51997aa..00000000 --- a/packages/@luke-ui/react/src/icon-button/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { IconButton, type IconButtonProps } from './icon-button.js'; -export { type IconButtonRecipeVariants, iconButtonRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/kbd/index.ts b/packages/@luke-ui/react/src/kbd/index.ts deleted file mode 100644 index f467185e..00000000 --- a/packages/@luke-ui/react/src/kbd/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Kbd, type KbdProps } from './kbd.js'; -export { type KbdRecipeVariants, kbdRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/link/index.ts b/packages/@luke-ui/react/src/link/index.ts deleted file mode 100644 index fd4b1850..00000000 --- a/packages/@luke-ui/react/src/link/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Link, type LinkProps } from './link.js'; -export { type LinkRecipeVariants, linkRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/loading-spinner/index.ts b/packages/@luke-ui/react/src/loading-spinner/index.ts deleted file mode 100644 index a754ab79..00000000 --- a/packages/@luke-ui/react/src/loading-spinner/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { LoadingSpinner, type LoadingSpinnerProps } from './loading-spinner.js'; -export { type LoadingSpinnerRecipeVariants, loadingSpinnerRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/module-boundaries.test.ts b/packages/@luke-ui/react/src/module-boundaries.test.ts new file mode 100644 index 00000000..17b825d9 --- /dev/null +++ b/packages/@luke-ui/react/src/module-boundaries.test.ts @@ -0,0 +1,203 @@ +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { access, mkdir, mkdtemp, readdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { afterEach, expect, test } from 'vite-plus/test'; + +const sourceRoot = fileURLToPath(new URL('./', import.meta.url)); +const sourceZones = new Set(['core', 'exports', 'shared', 'theme']); +const sourceFilePattern = /\.(?:ts|tsx)$/; +const excludedSourcePattern = /(?:\.test\.|\.stories\.|__fixtures__)/; +const relativeImportPattern = /(?:from\s+|import\s+)(['"])(\.\.?\/[^'"]+)\1/g; + +type SourceZone = 'core' | 'exports' | 'shared' | 'theme'; + +// The zones a source zone may import from. A missing key is a compile error, not a runtime +// `undefined`, so a new zone cannot silently bypass enforcement. +const MAY_IMPORT: Record> = { + core: ['theme', 'shared'], + exports: ['core', 'theme', 'shared'], + shared: [], + theme: ['shared'], +}; + +const testDirectories: Array = []; + +afterEach(async () => { + await Promise.all( + testDirectories.map((directory) => rm(directory, { force: true, recursive: true })), + ); + testDirectories.length = 0; +}); + +test('keeps source imports flowing between core, exports, shared, and theme', async () => { + const sourceFiles = await collectSourceFiles(sourceRoot); + const violations = ( + await Promise.all( + sourceFiles.map((sourceFile) => findBoundaryViolations(sourceRoot, sourceFile)), + ) + ).flat(); + + expect(violations).toEqual([]); +}); + +// The dependency graph, driven against synthetic zone pairs rather than files on disk, so every +// forbidden edge is proven to fail if `isForbiddenEdge` stopped reporting it, and every allowed +// edge is proven not to false-positive. Exhaustive over all 16 zone pairs so no pair can be +// silently omitted in future. +test.for([ + ['shared', 'core'], + ['shared', 'theme'], + ['shared', 'exports'], + ['core', 'exports'], + ['theme', 'core'], + ['theme', 'exports'], +] as const)('reports %s -> %s as a violation', ([sourceZone, targetZone]) => { + expect(isForbiddenEdge(sourceZone, targetZone)).toBe(true); +}); + +test.for([ + ['exports', 'core'], + ['exports', 'theme'], + ['exports', 'shared'], + ['core', 'shared'], + ['core', 'theme'], + ['theme', 'shared'], +] as const)('allows %s -> %s', ([sourceZone, targetZone]) => { + expect(isForbiddenEdge(sourceZone, targetZone)).toBe(false); +}); + +test.for([ + ['core', 'core'], + ['exports', 'exports'], + ['shared', 'shared'], + ['theme', 'theme'], +] as const)('allows %s -> %s (same zone)', ([sourceZone, targetZone]) => { + expect(isForbiddenEdge(sourceZone, targetZone)).toBe(false); +}); + +// The synthetic cases above call `isForbiddenEdge` directly with string literals, so they cannot +// catch a regression in the code that actually walks the tree. Drive the real scanning path +// (`collectSourceFiles`, `resolveRelativeImport`, `sourceZoneFor`) against an on-disk fixture so a +// broken zone classification (for example `sourceZoneFor` no longer recognising `theme`) fails this +// test instead of silently reporting zero violations everywhere. +test('detects a real on-disk forbidden import via the file-walking scanner', async () => { + const root = await createFixtureRoot({ + 'theme/offender.ts': "import { helper } from '../exports/helper.js';\n", + 'exports/helper.ts': 'export const helper = 1;\n', + }); + + const sourceFiles = await collectSourceFiles(root); + const violations = ( + await Promise.all(sourceFiles.map((sourceFile) => findBoundaryViolations(root, sourceFile))) + ).flat(); + + expect(violations).toEqual([`theme/offender.ts -> ../exports/helper.js`]); +}); + +test('reports no violations for a real on-disk allowed import via the file-walking scanner', async () => { + const root = await createFixtureRoot({ + 'theme/consumer.ts': "import { helper } from '../shared/helper.js';\n", + 'shared/helper.ts': 'export const helper = 1;\n', + }); + + const sourceFiles = await collectSourceFiles(root); + const violations = ( + await Promise.all(sourceFiles.map((sourceFile) => findBoundaryViolations(root, sourceFile))) + ).flat(); + + expect(violations).toEqual([]); +}); + +async function createFixtureRoot(files: Record): Promise { + const root = await mkdtemp(path.join(tmpdir(), 'module-boundaries-')); + testDirectories.push(root); + + await Promise.all( + Object.entries(files).map(async ([relativePath, contents]) => { + const filePath = path.join(root, relativePath); + await mkdir(path.dirname(filePath), { recursive: true }); + await writeFile(filePath, contents, 'utf8'); + }), + ); + + return root; +} + +/** + * Whether an import from `sourceZone` to `targetZone` breaks the dependency graph. See + * {@link MAY_IMPORT} for the allow-list each zone is checked against. + */ +function isForbiddenEdge(sourceZone: SourceZone, targetZone: SourceZone): boolean { + return sourceZone !== targetZone && !MAY_IMPORT[sourceZone].includes(targetZone); +} + +async function findBoundaryViolations(root: string, sourceFile: string): Promise> { + const sourceZone = sourceZoneFor(root, sourceFile); + if (sourceZone === undefined) return []; + + const source = await readFile(sourceFile, 'utf8'); + const imports = [...source.matchAll(relativeImportPattern)].flatMap((match) => { + const specifier = match[2]; + return specifier === undefined ? [] : [specifier]; + }); + const targetFiles = await Promise.all( + imports.map((specifier) => resolveRelativeImport(root, sourceFile, specifier)), + ); + + return targetFiles.flatMap((targetFile, index) => { + const targetZone = sourceZoneFor(root, targetFile); + if (targetZone === undefined) return []; + + const specifier = imports[index]; + if (specifier === undefined) return []; + if (!isForbiddenEdge(sourceZone, targetZone)) return []; + + return [`${path.relative(root, sourceFile)} -> ${specifier}`]; + }); +} + +async function collectSourceFiles(directory: string): Promise> { + const entries = await readdir(directory, { withFileTypes: true }); + const files = await Promise.all( + entries.map(async (entry) => { + const entryPath = path.join(directory, entry.name); + if (entry.isDirectory()) return collectSourceFiles(entryPath); + if (!sourceFilePattern.test(entry.name) || excludedSourcePattern.test(entryPath)) return []; + return [entryPath]; + }), + ); + + return files.flat(); +} + +async function resolveRelativeImport( + root: string, + sourceFile: string, + specifier: string, +): Promise { + const target = path.resolve(path.dirname(sourceFile), specifier.split(/[?#]/)[0] ?? specifier); + if (!target.startsWith(`${root}${path.sep}`)) return target; + + const candidates = [target, target.replace(/\.js$/, '.ts'), target.replace(/\.js$/, '.tsx')]; + + const resolvedCandidates = await Promise.all( + candidates.map(async (candidate) => { + try { + await access(candidate); + return candidate; + } catch { + return undefined; + } + }), + ); + const resolved = resolvedCandidates.find((candidate) => candidate !== undefined); + if (resolved !== undefined) return resolved; + + throw new Error(`Could not resolve ${specifier} from ${path.relative(root, sourceFile)}.`); +} + +function sourceZoneFor(root: string, filePath: string): SourceZone | undefined { + const [zone] = path.relative(root, filePath).split(path.sep); + return zone !== undefined && sourceZones.has(zone) ? (zone as SourceZone) : undefined; +} diff --git a/packages/@luke-ui/react/src/primitives/button/index.ts b/packages/@luke-ui/react/src/primitives/button/index.ts deleted file mode 100644 index fcc5457f..00000000 --- a/packages/@luke-ui/react/src/primitives/button/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Button, type ButtonProps } from './button.js'; -export { type ButtonRecipeVariants, buttonRecipe } from './recipe.css.js'; diff --git a/packages/@luke-ui/react/src/primitives/combobox/index.ts b/packages/@luke-ui/react/src/primitives/combobox/index.ts deleted file mode 100644 index e8bf209a..00000000 --- a/packages/@luke-ui/react/src/primitives/combobox/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -export type { ComboboxClearButtonProps } from './clear-button.js'; -export { ComboboxClearButton } from './clear-button.js'; -export type { ComboboxEmptyStateProps } from './empty-state.js'; -export { ComboboxEmptyState } from './empty-state.js'; -export type { ComboboxInputProps } from './input.js'; -export { ComboboxInput } from './input.js'; -export type { ComboboxInputGroupProps } from './input-group.js'; -export { ComboboxInputGroup } from './input-group.js'; -export type { ComboboxItemProps, ComboboxLoadMoreItemProps } from './item.js'; -export { ComboboxItem, ComboboxLoadMoreItem } from './item.js'; -export type { ComboboxListBoxProps } from './listbox.js'; -export { ComboboxListBox } from './listbox.js'; -export type { ComboboxPopoverProps } from './popover.js'; -export { ComboboxPopover } from './popover.js'; -export type { ComboboxRootProps, ComboboxSize } from './root.js'; -export { ComboboxRoot } from './root.js'; -export type { ComboboxSectionProps } from './section.js'; -export { ComboboxSection } from './section.js'; -export type { ComboboxTriggerProps } from './trigger.js'; -export { ComboboxTrigger } from './trigger.js'; diff --git a/packages/@luke-ui/react/src/quote/index.ts b/packages/@luke-ui/react/src/quote/index.ts deleted file mode 100644 index 99f84939..00000000 --- a/packages/@luke-ui/react/src/quote/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Quote, type QuoteProps } from './quote.js'; diff --git a/packages/@luke-ui/react/src/shared/class-names.ts b/packages/@luke-ui/react/src/shared/class-names.ts new file mode 100644 index 00000000..b23e4c59 --- /dev/null +++ b/packages/@luke-ui/react/src/shared/class-names.ts @@ -0,0 +1,5 @@ +/** The class-name constants both `core` and `theme` must agree on. */ +export const lukeUiClassNames = { + resetRoot: 'luke-ui-reset', + themeRoot: 'luke-ui-theme', +} as const; diff --git a/packages/@luke-ui/react/src/utils/utils.ts b/packages/@luke-ui/react/src/shared/utils/utils.ts similarity index 100% rename from packages/@luke-ui/react/src/utils/utils.ts rename to packages/@luke-ui/react/src/shared/utils/utils.ts diff --git a/packages/@luke-ui/react/src/strong/index.ts b/packages/@luke-ui/react/src/strong/index.ts deleted file mode 100644 index fa0dee63..00000000 --- a/packages/@luke-ui/react/src/strong/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Strong, type StrongProps } from './strong.js'; diff --git a/packages/@luke-ui/react/src/styles/index.ts b/packages/@luke-ui/react/src/styles/index.ts deleted file mode 100644 index 428a2efa..00000000 --- a/packages/@luke-ui/react/src/styles/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type { SprinklesProps } from './utilities.css.js'; -export { createSprinkles } from './utilities.css.js'; diff --git a/packages/@luke-ui/react/src/text-field/index.ts b/packages/@luke-ui/react/src/text-field/index.ts deleted file mode 100644 index 80e7dede..00000000 --- a/packages/@luke-ui/react/src/text-field/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { TextField, type TextFieldProps } from './text-field.js'; diff --git a/packages/@luke-ui/react/src/text/index.ts b/packages/@luke-ui/react/src/text/index.ts deleted file mode 100644 index 108b0153..00000000 --- a/packages/@luke-ui/react/src/text/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { type TextRecipeVariants, textRecipe } from './recipe.css.js'; -export { Text, type TextProps } from './text.js'; diff --git a/packages/@luke-ui/react/src/themes/paper/index.ts b/packages/@luke-ui/react/src/theme/bundles/paper/index.ts similarity index 100% rename from packages/@luke-ui/react/src/themes/paper/index.ts rename to packages/@luke-ui/react/src/theme/bundles/paper/index.ts diff --git a/packages/@luke-ui/react/src/themes/paper/theme-class-name.ts b/packages/@luke-ui/react/src/theme/bundles/paper/theme-class-name.ts similarity index 84% rename from packages/@luke-ui/react/src/themes/paper/theme-class-name.ts rename to packages/@luke-ui/react/src/theme/bundles/paper/theme-class-name.ts index 69fd2c2f..52e76c4e 100644 --- a/packages/@luke-ui/react/src/themes/paper/theme-class-name.ts +++ b/packages/@luke-ui/react/src/theme/bundles/paper/theme-class-name.ts @@ -1,6 +1,6 @@ /** Kept apart from `./index.ts` so the class never depends on the foundation. */ -import { getThemeClassName } from '../../theme/theme-class-name.js'; +import { getThemeClassName } from '../../theme-class-name.js'; const PAPER_THEME_NAME = 'paper'; diff --git a/packages/@luke-ui/react/src/themes/paper/theme.ts b/packages/@luke-ui/react/src/theme/bundles/paper/theme.ts similarity index 65% rename from packages/@luke-ui/react/src/themes/paper/theme.ts rename to packages/@luke-ui/react/src/theme/bundles/paper/theme.ts index 20b2a2a6..63b70e4b 100644 --- a/packages/@luke-ui/react/src/themes/paper/theme.ts +++ b/packages/@luke-ui/react/src/theme/bundles/paper/theme.ts @@ -1,5 +1,5 @@ -import type { ThemeInput } from '../../theme/define-theme.js'; -import { paperTheme } from '../../theme/foundations/paper.js'; +import type { ThemeInput } from '../../define-theme.js'; +import { paperTheme } from '../../foundations/paper.js'; /** * Paper's `defineTheme` input, the materially minimal bundled theme: a flat, hairline-bordered diff --git a/packages/@luke-ui/react/src/themes/tactile/index.ts b/packages/@luke-ui/react/src/theme/bundles/tactile/index.ts similarity index 100% rename from packages/@luke-ui/react/src/themes/tactile/index.ts rename to packages/@luke-ui/react/src/theme/bundles/tactile/index.ts diff --git a/packages/@luke-ui/react/src/themes/tactile/theme-class-name.ts b/packages/@luke-ui/react/src/theme/bundles/tactile/theme-class-name.ts similarity index 84% rename from packages/@luke-ui/react/src/themes/tactile/theme-class-name.ts rename to packages/@luke-ui/react/src/theme/bundles/tactile/theme-class-name.ts index a8fe6704..c0e74aee 100644 --- a/packages/@luke-ui/react/src/themes/tactile/theme-class-name.ts +++ b/packages/@luke-ui/react/src/theme/bundles/tactile/theme-class-name.ts @@ -1,6 +1,6 @@ /** Kept apart from `./index.ts` so the class never depends on the foundation. */ -import { getThemeClassName } from '../../theme/theme-class-name.js'; +import { getThemeClassName } from '../../theme-class-name.js'; const TACTILE_THEME_NAME = 'tactile'; diff --git a/packages/@luke-ui/react/src/themes/tactile/theme.ts b/packages/@luke-ui/react/src/theme/bundles/tactile/theme.ts similarity index 66% rename from packages/@luke-ui/react/src/themes/tactile/theme.ts rename to packages/@luke-ui/react/src/theme/bundles/tactile/theme.ts index 74a4bbaf..02bf08e0 100644 --- a/packages/@luke-ui/react/src/themes/tactile/theme.ts +++ b/packages/@luke-ui/react/src/theme/bundles/tactile/theme.ts @@ -1,5 +1,5 @@ -import type { ThemeInput } from '../../theme/define-theme.js'; -import { tactileTheme } from '../../theme/foundations/tactile.js'; +import type { ThemeInput } from '../../define-theme.js'; +import { tactileTheme } from '../../foundations/tactile.js'; /** * Tactile's `defineTheme` input, the Luke UI default: a teal accent, a neutral near-white light diff --git a/packages/@luke-ui/react/src/themes/theme-bundle.test.ts b/packages/@luke-ui/react/src/theme/bundles/theme-bundle.test.ts similarity index 97% rename from packages/@luke-ui/react/src/themes/theme-bundle.test.ts rename to packages/@luke-ui/react/src/theme/bundles/theme-bundle.test.ts index a00a8440..1fa9c014 100644 --- a/packages/@luke-ui/react/src/themes/theme-bundle.test.ts +++ b/packages/@luke-ui/react/src/theme/bundles/theme-bundle.test.ts @@ -1,5 +1,5 @@ /** - * Bundles a one-line entry against the built `dist/themes//index.js`, so it measures what a + * Bundles a one-line entry against the built `dist/themes/.js`, so it measures what a * consumer's bundler keeps. The identity class must not derive from `Theme.name`, which would * make the foundation a prerequisite of the class string. A multi-layer CSS value must stay a * concatenated literal, not `[...].join(', ')`, because a joined value survives dead-code @@ -48,7 +48,7 @@ async function runThemeExportBuild( themeName: keyof typeof FOUNDATION_MARKERS, exportName: 'theme' | 'themeClassName', ): Promise { - const entrypoint = new URL(`../../dist/themes/${themeName}/index.js`, import.meta.url); + const entrypoint = new URL(`../../../dist/themes/${themeName}.js`, import.meta.url); const source = `export { ${exportName} } from ${JSON.stringify(fileURLToPath(entrypoint))};`; const result = await build({ build: { diff --git a/packages/@luke-ui/react/src/themes/themes.test.ts b/packages/@luke-ui/react/src/theme/bundles/themes.test.ts similarity index 82% rename from packages/@luke-ui/react/src/themes/themes.test.ts rename to packages/@luke-ui/react/src/theme/bundles/themes.test.ts index e35af80e..506db912 100644 --- a/packages/@luke-ui/react/src/themes/themes.test.ts +++ b/packages/@luke-ui/react/src/theme/bundles/themes.test.ts @@ -1,7 +1,7 @@ import { readFile } from 'node:fs/promises'; import { describe, expect, it } from 'vite-plus/test'; -import packageJson from '../../package.json' with { type: 'json' }; -import { getThemeClassName } from '../theme/theme-class-name.js'; +import packageJson from '../../../package.json' with { type: 'json' }; +import { getThemeClassName } from '../theme-class-name.js'; import { themeClassName as paperThemeClassName, theme as paperThemeInput } from './paper/index.js'; import { themeClassName as tactileThemeClassName, @@ -9,19 +9,19 @@ import { } from './tactile/index.js'; const themeArtifacts = { - paper: new URL('../../dist/themes/paper/stylesheet.css', import.meta.url), - tactile: new URL('../../dist/themes/tactile/stylesheet.css', import.meta.url), + paper: new URL('../../../dist/themes/paper/stylesheet.css', import.meta.url), + tactile: new URL('../../../dist/themes/tactile/stylesheet.css', import.meta.url), } as const; const themeEntrypoints = { - paper: new URL('../../dist/themes/paper/index.js', import.meta.url), - tactile: new URL('../../dist/themes/tactile/index.js', import.meta.url), + paper: new URL('../../../dist/themes/paper.js', import.meta.url), + tactile: new URL('../../../dist/themes/tactile.js', import.meta.url), } as const; describe('bundled theme package exports', () => { it('publishes a per-theme entrypoint and stylesheet, with no combined barrel', () => { - expect(packageJson.exports['./themes/tactile']).toBe('./dist/themes/tactile/index.js'); - expect(packageJson.exports['./themes/paper']).toBe('./dist/themes/paper/index.js'); + expect(packageJson.exports['./themes/tactile']).toBe('./dist/themes/tactile.js'); + expect(packageJson.exports['./themes/paper']).toBe('./dist/themes/paper.js'); expect(packageJson.exports['./themes/tactile/stylesheet.css']).toBe( './dist/themes/tactile/stylesheet.css', ); diff --git a/packages/@luke-ui/react/src/theme/cascade.browser.test.ts b/packages/@luke-ui/react/src/theme/cascade.browser.test.ts index 8bbb877a..a45ff42e 100644 --- a/packages/@luke-ui/react/src/theme/cascade.browser.test.ts +++ b/packages/@luke-ui/react/src/theme/cascade.browser.test.ts @@ -12,9 +12,9 @@ import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test'; import { cdp } from 'vite-plus/test/context'; import paperCss from '../../dist/themes/paper/stylesheet.css?inline'; import tactileCss from '../../dist/themes/tactile/stylesheet.css?inline'; -import { themeClassName as paperThemeClassName } from '../themes/paper/index.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; import { extractValue, splitBlocks } from './__fixtures__/theme-css.js'; +import { themeClassName as paperThemeClassName } from './bundles/paper/index.js'; +import { themeClassName as tactileThemeClassName } from './bundles/tactile/index.js'; const themeCss = { paper: paperCss, tactile: tactileCss } as const; type ThemeName = keyof typeof themeCss; diff --git a/packages/@luke-ui/react/src/theme/define-theme.ts b/packages/@luke-ui/react/src/theme/define-theme.ts index 1e90e5b3..9cdd5499 100644 --- a/packages/@luke-ui/react/src/theme/define-theme.ts +++ b/packages/@luke-ui/react/src/theme/define-theme.ts @@ -6,7 +6,7 @@ * backdrop) into {@link Oklch} values the foundation carries. */ -import { typedEntries, typedFromEntries } from '../utils/index.js'; +import { typedEntries, typedFromEntries } from '../shared/utils/utils.js'; import { buildTheme, ThemeContrastError } from './build-theme.js'; import type { Oklch } from './color.js'; import { gamutMapOklch, parseColor } from './color.js'; diff --git a/packages/@luke-ui/react/src/theme/stylesheet.test.ts b/packages/@luke-ui/react/src/theme/stylesheet.test.ts index cf7d964a..8f95890e 100644 --- a/packages/@luke-ui/react/src/theme/stylesheet.test.ts +++ b/packages/@luke-ui/react/src/theme/stylesheet.test.ts @@ -1,6 +1,4 @@ import { describe, expect, it } from 'vite-plus/test'; -import { themeClassName as paperThemeClassName } from '../themes/paper/index.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; import { extractValue, paperFoundation, @@ -8,6 +6,8 @@ import { tactileFoundation, } from './__fixtures__/theme-css.js'; import { buildTheme } from './build-theme.js'; +import { themeClassName as paperThemeClassName } from './bundles/paper/index.js'; +import { themeClassName as tactileThemeClassName } from './bundles/tactile/index.js'; import { flattenThemeContract, partitionContractPairs, diff --git a/packages/@luke-ui/react/src/theme/theme-diagnostics-inspector.tsx b/packages/@luke-ui/react/src/theme/theme-diagnostics-inspector.tsx index d1fe411d..08d6d914 100644 --- a/packages/@luke-ui/react/src/theme/theme-diagnostics-inspector.tsx +++ b/packages/@luke-ui/react/src/theme/theme-diagnostics-inspector.tsx @@ -1,6 +1,6 @@ import type { CSSProperties, ReactNode } from 'react'; import { useState } from 'react'; -import { typedKeys } from '../utils/index.js'; +import { typedKeys } from '../shared/utils/utils.js'; import { compileTheme } from './build-theme.js'; import type { Oklch } from './color.js'; import { formatOklch } from './color.js'; diff --git a/packages/@luke-ui/react/src/theme/theme.browser.test.tsx b/packages/@luke-ui/react/src/theme/theme.browser.test.tsx index a9f0b4b1..993ea7e2 100644 --- a/packages/@luke-ui/react/src/theme/theme.browser.test.tsx +++ b/packages/@luke-ui/react/src/theme/theme.browser.test.tsx @@ -4,11 +4,11 @@ import type { Root } from 'react-dom/client'; import { createRoot } from 'react-dom/client'; import { afterEach, expect, test } from 'vite-plus/test'; import { cdp, page, userEvent } from 'vite-plus/test/context'; -import { Button } from '../button/index.js'; -import { ComboboxField } from '../combobox-field/index.js'; -import { IconSpritesheetProvider } from '../icon/index.js'; -import { ComboboxItem } from '../primitives/combobox/item.js'; -import { themeClassName as tactileThemeClassName } from '../themes/tactile/index.js'; +import { Button } from '../core/button/button.js'; +import { ComboboxField } from '../core/combobox-field/combobox-field.js'; +import { IconSpritesheetProvider } from '../core/icon/icon.js'; +import { ComboboxItem } from '../core/primitives/combobox/item.js'; +import { themeClassName as tactileThemeClassName } from './bundles/tactile/index.js'; const mounted: Array<{ container: HTMLElement; root: Root }> = []; const scopes: Array = []; diff --git a/packages/@luke-ui/react/src/theme/theme.ts b/packages/@luke-ui/react/src/theme/theme.ts index 0177dead..c58c8ad7 100644 --- a/packages/@luke-ui/react/src/theme/theme.ts +++ b/packages/@luke-ui/react/src/theme/theme.ts @@ -1,5 +1,5 @@ -import { lukeUiClassNames } from '../styles/class-names.js'; -import { cx } from '../utils/utils.js'; +import { lukeUiClassNames } from '../shared/class-names.js'; +import { cx } from '../shared/utils/utils.js'; /** * Applies the descendant CSS reset and the base Luke UI typography and theme layer. It carries no diff --git a/packages/@luke-ui/react/src/theme/token-board.visual.test.tsx b/packages/@luke-ui/react/src/theme/token-board.visual.test.tsx index 3b28b135..8d489f4d 100644 --- a/packages/@luke-ui/react/src/theme/token-board.visual.test.tsx +++ b/packages/@luke-ui/react/src/theme/token-board.visual.test.tsx @@ -1,6 +1,6 @@ import { expect, test } from 'vite-plus/test'; -import { render, visualAppearances } from '../test-utils/render.js'; -import { captureVisualAppearance } from '../test-utils/visual.js'; +import { render, visualAppearances } from '../core/test-utils/render.js'; +import { captureVisualAppearance } from '../core/test-utils/visual.js'; import { TokenBoard } from './token-board.js'; // Captures every contract leaf, colour and non-colour alike, for both bundled themes and diff --git a/packages/@luke-ui/react/src/utils/index.ts b/packages/@luke-ui/react/src/utils/index.ts deleted file mode 100644 index 6ffde591..00000000 --- a/packages/@luke-ui/react/src/utils/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export { - cx, - mergeProps, - type ObjectEntry, - pxToRem, - typedEntries, - typedFromEntries, - typedKeys, -} from './utils.js'; diff --git a/packages/@luke-ui/react/src/visually-hidden/index.ts b/packages/@luke-ui/react/src/visually-hidden/index.ts deleted file mode 100644 index 6a6b9b60..00000000 --- a/packages/@luke-ui/react/src/visually-hidden/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { visuallyHiddenRecipe } from './recipe.css.js'; -export { VisuallyHidden, type VisuallyHiddenProps } from './visually-hidden.js'; diff --git a/packages/@luke-ui/react/vite.config.ts b/packages/@luke-ui/react/vite.config.ts index d073da50..15ec3b99 100644 --- a/packages/@luke-ui/react/vite.config.ts +++ b/packages/@luke-ui/react/vite.config.ts @@ -7,7 +7,9 @@ import { readdir, rm } from 'node:fs/promises'; import { defineConfig } from 'vite-plus'; import packageJson from './package.json' with { type: 'json' }; -const recipeEngineSource = fileURLToPath(new URL('./src/styles/recipe-engine.ts', import.meta.url)); +const recipeEngineSource = fileURLToPath( + new URL('./src/core/styles/recipe-engine.ts', import.meta.url), +); const workspaceRoot = fileURLToPath(new URL('../../../', import.meta.url)); const distDir = fileURLToPath(new URL('dist/', import.meta.url)); const preservedDistFiles = new Set(['spritesheet.svg', 'docs', 'themes']); @@ -57,10 +59,10 @@ export default defineConfig({ }, dts: true, entry: { - stylesheet: 'src/stylesheet.css.ts', - '*': ['src/*/index.ts'], - 'primitives/*': ['src/primitives/*/index.ts'], - 'themes/*': ['src/themes/*/index.ts'], + stylesheet: 'src/core/stylesheet.css.ts', + '*': ['src/exports/*.ts'], + 'primitives/*': ['src/exports/primitives/*.ts'], + 'themes/*': ['src/exports/themes/*.ts'], }, exports: { customExports: Object.fromEntries( diff --git a/packages/@luke-ui/react/vitest.config.ts b/packages/@luke-ui/react/vitest.config.ts index 64da0be8..eb9f4d64 100644 --- a/packages/@luke-ui/react/vitest.config.ts +++ b/packages/@luke-ui/react/vitest.config.ts @@ -8,7 +8,9 @@ import { playwright } from 'vite-plus/test/browser-playwright'; const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); const configDir = path.join(dirname, '.storybook'); -const recipeEngineSource = fileURLToPath(new URL('./src/styles/recipe-engine.ts', import.meta.url)); +const recipeEngineSource = fileURLToPath( + new URL('./src/core/styles/recipe-engine.ts', import.meta.url), +); // This file is copied into a git worktree at an older revision. It cannot import // TypeScript that does not exist there, so it has no relative imports of the // visual-regression contract. Keep the capture-dir literals below in sync with @@ -71,7 +73,7 @@ export default defineConfig({ }, include: ['src/**/*.browser.test.{ts,tsx}'], name: 'browser', - setupFiles: ['./src/test-utils/render-setup.ts'], + setupFiles: ['./src/core/test-utils/render-setup.ts'], }, }, { @@ -146,7 +148,10 @@ export default defineConfig({ }, include: ['src/**/*.visual.test.{ts,tsx}'], name: 'visual', - setupFiles: ['./src/test-utils/render-setup.ts', './src/test-utils/visual-setup.ts'], + setupFiles: [ + './src/core/test-utils/render-setup.ts', + './src/core/test-utils/visual-setup.ts', + ], }, }, ], diff --git a/packages/turbo-generators/src/apply-component-creation-plan.test.ts b/packages/turbo-generators/src/apply-component-creation-plan.test.ts index 1d9edf5e..d5cb99ac 100644 --- a/packages/turbo-generators/src/apply-component-creation-plan.test.ts +++ b/packages/turbo-generators/src/apply-component-creation-plan.test.ts @@ -23,16 +23,25 @@ describe('createComponent', () => { await createComponent(root, answers); await expect( - readFile(join(root, 'packages/@luke-ui/react/src/status-badge/index.ts'), 'utf8'), + readFile(join(root, 'packages/@luke-ui/react/src/exports/status-badge.ts'), 'utf8'), ).resolves.toBe( [ - "export { StatusBadge, type StatusBadgeProps } from './status-badge.js';", - "export { statusBadgeRecipe, type StatusBadgeRecipeVariants } from './recipe.css.js';", + "export { StatusBadge, type StatusBadgeProps } from '../core/status-badge/status-badge.js';", + 'export {', + '\ttype StatusBadgeRecipeVariants,', + '\tstatusBadgeRecipe,', + "} from '../core/status-badge/recipe.css.js';", '', ].join('\n'), ); await expect( - readFile(join(root, 'packages/@luke-ui/react/src/status-badge/status-badge.tsx'), 'utf8'), + readFile(join(root, 'packages/@luke-ui/react/src/core/status-badge/index.ts'), 'utf8'), + ).rejects.toMatchObject({ code: 'ENOENT' }); + await expect( + readFile( + join(root, 'packages/@luke-ui/react/src/core/status-badge/status-badge.tsx'), + 'utf8', + ), ).resolves.toContain('export function StatusBadge'); await expect(readJson(root, 'apps/docs/content/docs/components/meta.json')).resolves.toEqual({ pages: ['actions', 'feedback'], @@ -66,7 +75,7 @@ describe('createComponent', () => { ); await expect( readFile( - join(root, 'packages/@luke-ui/react/src/status-badge/component-test-registration.ts'), + join(root, 'packages/@luke-ui/react/src/core/status-badge/component-test-registration.ts'), 'utf8', ), ).rejects.toMatchObject({ code: 'ENOENT' }); @@ -110,7 +119,7 @@ describe('createComponent', () => { }); const browserTest = await readFile( - join(root, 'packages/@luke-ui/react/src/date-field/date-field.browser.test.tsx'), + join(root, 'packages/@luke-ui/react/src/core/date-field/date-field.browser.test.tsx'), 'utf8', ); expect(browserTest).toContain('testConformance'); @@ -133,7 +142,7 @@ describe('createComponent', () => { }); const browserTest = await readFile( - join(root, 'packages/@luke-ui/react/src/date-field/date-field.browser.test.tsx'), + join(root, 'packages/@luke-ui/react/src/core/date-field/date-field.browser.test.tsx'), 'utf8', ); expect(browserTest).toContain('testConformance'); @@ -154,7 +163,7 @@ describe('createComponent', () => { }); const browserTest = await readFile( - join(root, 'packages/@luke-ui/react/src/mark/mark.browser.test.tsx'), + join(root, 'packages/@luke-ui/react/src/core/mark/mark.browser.test.tsx'), 'utf8', ); expect(browserTest).toContain("test('Mark renders its root element'"); @@ -175,7 +184,7 @@ describe('createComponent', () => { expect( await readFile( - join(root, 'packages/@luke-ui/react/src/action-chip/action-chip.browser.test.tsx'), + join(root, 'packages/@luke-ui/react/src/core/action-chip/action-chip.browser.test.tsx'), 'utf8', ), ).toContain("testIntegration('action-chip', async"); @@ -195,7 +204,7 @@ describe('createComponent', () => { await expect( readFile( - join(root, 'packages/@luke-ui/react/src/date-field/date-field.visual.test.tsx'), + join(root, 'packages/@luke-ui/react/src/core/date-field/date-field.visual.test.tsx'), 'utf8', ), ).rejects.toMatchObject({ code: 'ENOENT' }); @@ -229,16 +238,16 @@ describe('createComponent', () => { }); }); -const modulesRegistryPath = 'packages/@luke-ui/react/src/styles/modules.css.ts'; -const manifestPath = 'packages/@luke-ui/react/src/conformance/manifest.ts'; +const modulesRegistryPath = 'packages/@luke-ui/react/src/core/styles/modules.css.ts'; +const manifestPath = 'packages/@luke-ui/react/src/core/conformance/manifest.ts'; async function createRepositoryFixture(options?: { modulesRegistry?: string }): Promise { const root = await mkdtemp(join(tmpdir(), 'component-plan-')); roots.push(root); await mkdir(join(root, 'apps/docs/content/docs/components'), { recursive: true }); - await mkdir(join(root, 'packages/@luke-ui/react/src/styles'), { recursive: true }); - await mkdir(join(root, 'packages/@luke-ui/react/src/conformance'), { recursive: true }); + await mkdir(join(root, 'packages/@luke-ui/react/src/core/styles'), { recursive: true }); + await mkdir(join(root, 'packages/@luke-ui/react/src/core/conformance'), { recursive: true }); await writeFile( join(root, 'apps/docs/content/docs/components/meta.json'), diff --git a/packages/turbo-generators/src/component-creation-plan.test.ts b/packages/turbo-generators/src/component-creation-plan.test.ts index 340c1c4f..9dac07b7 100644 --- a/packages/turbo-generators/src/component-creation-plan.test.ts +++ b/packages/turbo-generators/src/component-creation-plan.test.ts @@ -1,3 +1,6 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { access } from 'node:fs/promises'; import { parseSync } from 'oxc-parser'; import { describe, expect, it } from 'vite-plus/test'; import { ZodError } from 'zod'; @@ -8,6 +11,8 @@ import { parseComponentAnswers, } from './component-creation-plan.js'; +const repoRoot = fileURLToPath(new URL('../../..', import.meta.url)); + const validAnswers = { docsGroup: 'feedback', name: 'StatusBadge', @@ -48,13 +53,16 @@ describe('createComponentPlan', () => { expect(plan.files.map((file) => file.path).sort()).toEqual([ 'apps/docs/content/docs/components/feedback/status-badge.mdx', 'apps/docs/src/examples/status-badge/basic.tsx', - 'packages/@luke-ui/react/src/status-badge/index.ts', - 'packages/@luke-ui/react/src/status-badge/recipe.css.ts', - 'packages/@luke-ui/react/src/status-badge/status-badge.browser.test.tsx', - 'packages/@luke-ui/react/src/status-badge/status-badge.stories.tsx', - 'packages/@luke-ui/react/src/status-badge/status-badge.tsx', - 'packages/@luke-ui/react/src/status-badge/status-badge.visual.test.tsx', + 'packages/@luke-ui/react/src/core/status-badge/recipe.css.ts', + 'packages/@luke-ui/react/src/core/status-badge/status-badge.browser.test.tsx', + 'packages/@luke-ui/react/src/core/status-badge/status-badge.stories.tsx', + 'packages/@luke-ui/react/src/core/status-badge/status-badge.tsx', + 'packages/@luke-ui/react/src/core/status-badge/status-badge.visual.test.tsx', + 'packages/@luke-ui/react/src/exports/status-badge.ts', ]); + expect(plan.files.map((file) => file.path)).not.toContainEqual( + expect.stringMatching(/core\/status-badge\/index\.ts$/), + ); expect(plan).not.toHaveProperty('jsonEdits'); expect(plan).not.toHaveProperty('sortedImportEdits'); expect(plan).not.toHaveProperty('textFileInserts'); @@ -62,26 +70,62 @@ describe('createComponentPlan', () => { const recipeSource = plan.files.find((file) => file.path.endsWith('/status-badge/recipe.css.ts'), )?.contents; - const indexSource = plan.files.find((file) => + const componentSource = plan.files.find((file) => file.path.endsWith('/status-badge/status-badge.tsx'), )?.contents; - const barrelSource = plan.files.find((file) => - file.path.endsWith('/status-badge/index.ts'), + const packageExportSource = plan.files.find((file) => + file.path.endsWith('/exports/status-badge.ts'), )?.contents; - expect(indexSource).not.toContain('export { statusBadgeRecipe'); - expect(barrelSource).toContain( - "export { StatusBadge, type StatusBadgeProps } from './status-badge.js';", + expect(componentSource).not.toContain('export { statusBadgeRecipe'); + expect(packageExportSource).toContain( + "export { StatusBadge, type StatusBadgeProps } from '../core/status-badge/status-badge.js';", ); - expect(barrelSource).toContain( - "export { statusBadgeRecipe, type StatusBadgeRecipeVariants } from './recipe.css.js';", + expect(packageExportSource).toContain( + "export { type StatusBadgeRecipeVariants, statusBadgeRecipe } from '../core/status-badge/recipe.css.js';", ); + expect(packageExportSource).not.toContain("from './index.js'"); expect(recipeSource).toContain('export const statusBadgeRecipe = recipe({'); expect(recipeSource).toContain( 'export type StatusBadgeRecipeVariants = RecipeSelection;', ); }); + it('emits relative imports that resolve to real files already in the repo', async () => { + const plan = createComponentPlan(validAnswers); + + const violations = ( + await Promise.all(plan.files.map((file) => findUnresolvedImports(file))) + ).flat(); + + expect(violations).toEqual([]); + }); + + it('plans no core barrel and names implementation modules directly', () => { + const plan = createComponentPlan(validAnswers); + + expect(plan.files.map((file) => file.path)).not.toContainEqual( + expect.stringMatching(/\/core\/status-badge\/index\.ts$/), + ); + + const packageExportSource = plan.files.find((file) => + file.path.endsWith('/exports/status-badge.ts'), + )?.contents; + if (packageExportSource === undefined) { + throw new Error('Expected the scaffold to write a package export module.'); + } + expect(packageExportSource).toContain("from '../core/status-badge/status-badge.js'"); + expect(packageExportSource).toContain("from '../core/status-badge/recipe.css.js'"); + expect(packageExportSource).not.toContain('index.js'); + + for (const testPath of ['status-badge.browser.test.tsx', 'status-badge.visual.test.tsx']) { + const testSource = plan.files.find((file) => file.path.endsWith(testPath))?.contents; + if (testSource === undefined) throw new Error(`Expected the scaffold to write ${testPath}.`); + expect(testSource).toContain("from './status-badge.js'"); + expect(testSource).not.toContain("from './index.js'"); + } + }); + it('rejects invalid component names before file writes', () => { expect(() => { return createComponentPlan({ @@ -103,7 +147,7 @@ describe('createComponentPlan', () => { expect(frontmatter.props).toEqual([ { name: 'StatusBadgeProps', - path: 'packages/@luke-ui/react/src/status-badge/status-badge.tsx', + path: 'packages/@luke-ui/react/src/core/status-badge/status-badge.tsx', }, ]); }); @@ -147,3 +191,46 @@ describe('createComponentPlan', () => { expect(testFile.contents).toContain('getTarget'); }); }); + +/** + * Resolves each relative import in a generated file against the repo tree and reports the ones + * that don't exist. Imports into the component's own not-yet-created directory (`./index.js`, + * `./recipe.css.js`, and similar) are skipped since the plan writes those files together; every + * other relative import must already resolve to a real file on disk. + */ +async function findUnresolvedImports(file: { + contents: string; + path: string; +}): Promise> { + if (!/\.tsx?$/.test(file.path)) return []; + + const generatedDirectory = path.dirname(path.join(repoRoot, file.path)); + const lang = file.path.endsWith('.tsx') ? 'tsx' : 'ts'; + const parsed = parseSync(file.path, file.contents, { lang }); + const specifiers = parsed.module.staticImports + .map((staticImport) => staticImport.moduleRequest.value) + .filter((specifier) => specifier.startsWith('.')); + + const resolutions = await Promise.all( + specifiers.map(async (specifier) => { + if (specifier.startsWith('./')) return undefined; + + const target = path.resolve(generatedDirectory, specifier); + const candidates = [target, target.replace(/\.js$/, '.ts'), target.replace(/\.js$/, '.tsx')]; + const found = await Promise.all( + candidates.map(async (candidate) => { + try { + await access(candidate); + return true; + } catch { + return false; + } + }), + ); + if (found.some(Boolean)) return undefined; + return `${file.path} -> ${specifier}`; + }), + ); + + return resolutions.filter((resolution): resolution is string => resolution !== undefined); +} diff --git a/packages/turbo-generators/src/component-creation-plan.ts b/packages/turbo-generators/src/component-creation-plan.ts index 53a28496..77b9abae 100644 --- a/packages/turbo-generators/src/component-creation-plan.ts +++ b/packages/turbo-generators/src/component-creation-plan.ts @@ -133,15 +133,15 @@ export function createComponentWork(input: ParsedComponentAnswers): ComponentCre recipeName, variantsType, }), - path: `packages/@luke-ui/react/src/${name}/${name}.tsx`, + path: `packages/@luke-ui/react/src/core/${name}/${name}.tsx`, }, { - contents: renderComponentBarrel({ name, pascalName, recipeName, variantsType }), - path: `packages/@luke-ui/react/src/${name}/index.ts`, + contents: renderPackageExport({ name, pascalName, recipeName, variantsType }), + path: `packages/@luke-ui/react/src/exports/${name}.ts`, }, { contents: renderRecipe({ recipeName, variantsType }), - path: `packages/@luke-ui/react/src/${name}/recipe.css.ts`, + path: `packages/@luke-ui/react/src/core/${name}/recipe.css.ts`, }, { contents: renderComponentTest({ @@ -150,11 +150,11 @@ export function createComponentWork(input: ParsedComponentAnswers): ComponentCre name, pascalName, }), - path: `packages/@luke-ui/react/src/${name}/${name}.browser.test.tsx`, + path: `packages/@luke-ui/react/src/core/${name}/${name}.browser.test.tsx`, }, { contents: renderPackageStory({ docsGroup, name, pascalName }), - path: `packages/@luke-ui/react/src/${name}/${name}.stories.tsx`, + path: `packages/@luke-ui/react/src/core/${name}/${name}.stories.tsx`, }, { contents: renderHostedExample({ name, pascalName }), @@ -169,7 +169,7 @@ export function createComponentWork(input: ParsedComponentAnswers): ComponentCre if (input.visualCoverage) { files.push({ contents: renderVisualTest({ name, pascalName }), - path: `packages/@luke-ui/react/src/${name}/${name}.visual.test.tsx`, + path: `packages/@luke-ui/react/src/core/${name}/${name}.visual.test.tsx`, }); } @@ -201,7 +201,7 @@ export function createComponentWork(input: ParsedComponentAnswers): ComponentCre { kind: 'sorted-import', line: `import '../${name}/recipe.css.js';`, - path: 'packages/@luke-ui/react/src/styles/modules.css.ts', + path: 'packages/@luke-ui/react/src/core/styles/modules.css.ts', }, ], textFileInserts: [ @@ -211,7 +211,7 @@ export function createComponentWork(input: ParsedComponentAnswers): ComponentCre `\t['${pascalName}', '${name}', ${formatConformanceList(conformance)}, '${integrationTripwire}', '${visualApplicability}'],`, ], marker: '].map(([name, path, conformance, integrationTripwire, visualApplicability]) => ({', - path: 'packages/@luke-ui/react/src/conformance/manifest.ts', + path: 'packages/@luke-ui/react/src/core/conformance/manifest.ts', }, ], }; @@ -247,7 +247,7 @@ function renderComponentSource(input: { variantsType: string; }): string { return `import type { ComponentProps, JSX } from 'react'; -import { cx } from '../utils/utils.js'; +import { cx } from '../../shared/utils/utils.js'; import { ${input.recipeName} } from './recipe.css.js'; /** Props for \`${input.pascalName}\`. */ @@ -261,14 +261,14 @@ export function ${input.pascalName}(props: ${input.pascalName}Props): JSX.Elemen `; } -function renderComponentBarrel(input: { +function renderPackageExport(input: { name: string; pascalName: string; recipeName: string; variantsType: string; }): string { - return `export { ${input.pascalName}, type ${input.pascalName}Props } from './${input.name}.js'; -export { ${input.recipeName}, type ${input.variantsType} } from './recipe.css.js'; + return `export { ${input.pascalName}, type ${input.pascalName}Props } from '../core/${input.name}/${input.name}.js'; +export { type ${input.variantsType}, ${input.recipeName} } from '../core/${input.name}/recipe.css.js'; `; } @@ -278,7 +278,7 @@ function renderPackageStory(input: { pascalName: string; }): string { return `import { ${input.pascalName} } from '@luke-ui/react/${input.name}'; -import preview from '../../.storybook/preview.js'; +import preview from '../../../.storybook/preview.js'; const meta = preview.meta({ component: ${input.pascalName}, @@ -330,7 +330,7 @@ function renderComponentTest(input: { ? [`import { ${helperImports.join(', ')} } from '../conformance/helpers.js';`] : []), "import { render } from '../test-utils/render.js';", - `import { ${input.pascalName} } from './index.js';`, + `import { ${input.pascalName} } from './${input.name}.js';`, ]; const renderComponent = `render(<${input.pascalName} {...props}>Content)`; @@ -386,7 +386,7 @@ function renderVisualTest(input: { name: string; pascalName: string }): string { return `import { test } from 'vite-plus/test'; import { render } from '../test-utils/render.js'; import { captureVisual, Grid } from '../test-utils/visual.js'; -import { ${input.pascalName} } from './index.js'; +import { ${input.pascalName} } from './${input.name}.js'; test('kitchen sink', async () => { const { locator } = render( @@ -408,10 +408,10 @@ function renderHostedDocsPage(input: { }): string { return `--- title: ${input.displayName} -source: packages/@luke-ui/react/src/${input.name} +source: packages/@luke-ui/react/src/exports/${input.name}.ts props: - name: ${input.pascalName}Props - path: packages/@luke-ui/react/src/${input.name}/${input.name}.tsx + path: packages/@luke-ui/react/src/core/${input.name}/${input.name}.tsx ---