From cc1ae8ceb26fa0ee50515e2aef5a6c3e6a3dd54e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 3 Jul 2026 21:09:30 +1000 Subject: [PATCH] Deepen the package interface catalog (#58) --- .../docs-tools/src/discover-exports.ts | 23 ++++--------------- .../docs-tools/src/package-docs-catalog.ts | 6 +++++ .../react/scripts/generate-docs.test.ts | 12 ++++++++++ packages/@luke-ui/react/vite.config.ts | 10 ++++---- packages/@luke-ui/react/vitest.config.ts | 2 +- 5 files changed, 29 insertions(+), 24 deletions(-) create mode 100644 packages/@luke-ui/react/scripts/generate-docs.test.ts diff --git a/packages/@luke-ui/docs-tools/src/discover-exports.ts b/packages/@luke-ui/docs-tools/src/discover-exports.ts index 98b179d1..c47bed3f 100644 --- a/packages/@luke-ui/docs-tools/src/discover-exports.ts +++ b/packages/@luke-ui/docs-tools/src/discover-exports.ts @@ -20,11 +20,6 @@ export interface DiscoveredExport { } export interface DiscoverExportsOptions { - /** - * Export specifiers to classify as `asset` (no docs page). - * Defaults to {@link DEFAULT_ASSET_PATHS}, which is coupled to `@luke-ui/react`. - */ - assetPaths?: Iterable; /** * Export specifiers to classify as `barrel` (multi-export foundations). * Defaults to {@link DEFAULT_BARREL_PATHS}, which is coupled to `@luke-ui/react`. @@ -34,7 +29,9 @@ export interface DiscoverExportsOptions { } /** - * Default barrel paths, coupled to `@luke-ui/react`'s public API. + * Default barrel paths. Some of these (e.g. `./styles`) have only one runtime + * export, so barrel vs. component can't be told apart mechanically and this + * list has to be maintained by hand. * Other consumers of `discoverExports` should pass their own via {@link DiscoverExportsOptions.barrelPaths}. */ export const DEFAULT_BARREL_PATHS: ReadonlyArray = [ @@ -47,31 +44,21 @@ export const DEFAULT_BARREL_PATHS: ReadonlyArray = [ './utils', ]; -/** - * Default asset paths, coupled to `@luke-ui/react`'s public API. - * Other consumers of `discoverExports` should pass their own via {@link DiscoverExportsOptions.assetPaths}. - */ -export const DEFAULT_ASSET_PATHS: ReadonlyArray = [ - './stylesheet.css', - './spritesheet.svg', - './package.json', -]; - const LEADING_DOT_SLASH = /^\.\//; const SLASH = /\//g; const FILE_EXTENSION = /\.[^.]+$/; +const JS_TARGET = /\.js$/; export function discoverExports( exportsField: Record, options: DiscoverExportsOptions = {}, ): Array { const barrelPaths = new Set(options.barrelPaths ?? DEFAULT_BARREL_PATHS); - const assetPaths = new Set(options.assetPaths ?? DEFAULT_ASSET_PATHS); const result: Array = []; for (const [path, target] of Object.entries(exportsField)) { let shape: ExportShape; let tier: ExportTier; - if (assetPaths.has(path)) { + if (!JS_TARGET.test(target)) { shape = 'asset'; tier = 'n/a'; } else if (barrelPaths.has(path)) { diff --git a/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts b/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts index a6912a16..d31e865b 100644 --- a/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts +++ b/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts @@ -1,3 +1,9 @@ +/** + * `resolvePackageDocsCatalog` is the package interface catalog: the single place + * that resolves `package.json#exports` into classified, source-backed entries + * (tier, shape, description). Other tooling that needs export/tier/shape data + * should consume this rather than re-deriving it from `package.json` directly. + */ import { existsSync } from 'node:fs'; import type { DiscoveredExport, diff --git a/packages/@luke-ui/react/scripts/generate-docs.test.ts b/packages/@luke-ui/react/scripts/generate-docs.test.ts new file mode 100644 index 00000000..2d5bfd13 --- /dev/null +++ b/packages/@luke-ui/react/scripts/generate-docs.test.ts @@ -0,0 +1,12 @@ +import { DEFAULT_BARREL_PATHS } from '@luke-ui/docs-tools/discover-exports'; +import { describe, expect, it } from 'vite-plus/test'; +import packageJson from '../package.json' with { type: 'json' }; + +describe('generate-docs package interface conformance', () => { + it('keeps DEFAULT_BARREL_PATHS in sync with the real package exports', () => { + const realExportPaths = new Set(Object.keys(packageJson.exports)); + for (const barrelPath of DEFAULT_BARREL_PATHS) { + expect(realExportPaths.has(barrelPath)).toBe(true); + } + }); +}); diff --git a/packages/@luke-ui/react/vite.config.ts b/packages/@luke-ui/react/vite.config.ts index de5b9634..fb18fccd 100644 --- a/packages/@luke-ui/react/vite.config.ts +++ b/packages/@luke-ui/react/vite.config.ts @@ -10,6 +10,7 @@ import packageJson from './package.json' with { type: 'json' }; const workspaceRoot = fileURLToPath(new URL('../../../', import.meta.url)); const distDir = fileURLToPath(new URL('dist/', import.meta.url)); const preservedDistFiles = new Set(['spritesheet.svg', 'docs']); +const assetExports = ['./stylesheet.css', './spritesheet.svg']; async function cleanDistExceptPreservedFiles() { let entries: Array; @@ -36,7 +37,7 @@ export default defineConfig({ pack: { attw: { // Exclude static asset exports. CSS/SVG files do not need type definitions. - excludeEntrypoints: ['./stylesheet.css', './spritesheet.svg'], + excludeEntrypoints: assetExports, profile: 'esm-only', }, clean: false, @@ -48,10 +49,9 @@ export default defineConfig({ '*': ['src/*/index.tsx', 'src/*/index.ts', 'src/*/primitive/index.tsx'], }, exports: { - customExports: { - './spritesheet.svg': './dist/spritesheet.svg', - './stylesheet.css': './dist/stylesheet.css', - }, + customExports: Object.fromEntries( + assetExports.map((path) => [path, `./dist/${path.slice(2)}`]), + ), }, format: ['esm'], hooks: { diff --git a/packages/@luke-ui/react/vitest.config.ts b/packages/@luke-ui/react/vitest.config.ts index 9655a598..3ddc8b94 100644 --- a/packages/@luke-ui/react/vitest.config.ts +++ b/packages/@luke-ui/react/vitest.config.ts @@ -24,7 +24,7 @@ export default defineConfig({ test: { environment: 'node', exclude: ['**/node_modules/**', '**/*.browser.test.*'], - include: ['src/**/*.test.ts'], + include: ['src/**/*.test.ts', 'scripts/**/*.test.ts'], name: 'unit', }, }, -- 2.51.2