diff --git a/.changeset/eager-lamps-occur.md b/.changeset/eager-lamps-occur.md new file mode 100644 index 000000000..4f589a0ff --- /dev/null +++ b/.changeset/eager-lamps-occur.md @@ -0,0 +1,5 @@ +--- +'@vapor-ui/core': minor +--- + +add data-slots for all components diff --git a/.claude/skills/add-composites-test-bed/SKILL.md b/.claude/skills/add-composites-test-bed/SKILL.md new file mode 100644 index 000000000..f2d080d21 --- /dev/null +++ b/.claude/skills/add-composites-test-bed/SKILL.md @@ -0,0 +1,149 @@ +--- +name: add-composites-test-bed +description: packages/composites의 대상 컴포넌트에 시각적 회귀용 `Test Bed` 스토리를 추가한다. +disable-model-invocation: true +--- + +# Add Composites Test Bed + +`packages/composites` 컴포넌트의 시각적 회귀 diff를 위한 정적 `Test Bed` 스토리 하나를 대상 `*.stories.tsx`에 추가한다. + +- 회귀 하네스 필터: `story.name`이 `'Test Bed'`로 시작 + `title`이 `'Composites/'`로 시작. (`packages/composites/__tests__/regressions.test.ts:12`) — `Test Bed Light`, `Test Bed Dark` 두 스토리 모두 매칭. +- 컴포넌트당 스토리 2개: **`TestBed_Light`** (라이트 모드) + **`TestBed_Dark`** (다크 모드). 동일한 회귀 조건 테이블을 두 appearance에서 각각 렌더한다. +- 테이블 구조: 회귀 조건별 컬럼(현재는 각 variant prop 하나) + 마지막 `render` 컬럼(조건 적용된 컴포넌트 렌더 결과). +- 테이블 렌더는 `packages/composites/src/utils/regressions.tsx`의 `Regression.Table` 편의 컴포넌트를 재사용한다. `conditions`(각 조건의 `key`·`label`·`values`·`format`), `render`(row + container → 컴포넌트) 2개 prop만 넘기면 카테시안 곱 rows·ColumnGroup·Header·Condition 셀·Render 셀이 자동 생성된다. 세밀한 커스텀이 필요하면 하위 컴파운드(`Regression.Root / ColumnGroup / ConditionColumn / RenderColumn / Header / Body / Row / Heading / Condition / Render`)를 직접 조립한다. Vapor `Table` 위에 얹혀 있고, 스타일은 `$css`로 붙는다. + +## Steps + +### 1. 대상 컴포넌트 확정 + +사용자가 지정하지 않았다면 `AskUserQuestion`으로 묻는다. 대상은 `packages/composites/src/components//` 하위. + +완료: 폴더 경로가 확정되어 있고, 해당 폴더에 `.tsx`와 `.stories.tsx`가 존재한다. + +### 2. Default 스토리 확인 + +`.stories.tsx`에서 `export const Default`를 찾는다. + +- 부재 시 즉시 종료. 사용자에게 "Default 스토리를 먼저 추가하라"라고 안내. +- 존재 시 `Default.args`와 `Default.render`를 완전히 읽고, 각 슬롯 자리에 넘긴 JSX 값과 args를 목록으로 적어 둔다. + +완료: 각 슬롯 이름 → JSX 값, 각 arg 이름 → 값의 매핑이 목록으로 정리돼 있다. + +### 3. 구현체 파악 + +`.tsx`를 읽고 컴포넌트의 모든 prop을 나열한다. `createSlots(...)` 호출 인자로 슬롯 이름을 확정한다. 필수 슬롯은 `SlotProps`의 두 번째 제네릭 인자로 식별한다. + +완료: 프로젝트 규약이 `packages/composites/**` 진입 시 `.claude/rules/composites.md`로 자동 로드됨을 확인했고, 컴포넌트의 모든 prop을 흠 없이 열거할 수 있다. + +### 4. Prop 분류 + +각 prop을 세 카테고리 중 정확히 하나로 배타 분류한다. + +- **variants** — 시각 표현을 바꾸는 prop. 값 도메인이 유한한 리터럴 유니온이거나 boolean. 예: `size: 'md' | 'lg' | 'xl'`, `disabled: boolean`. +- **slots** — `createSlots`로 선언된 슬롯. `children`이 슬롯 역할이면 슬롯. variants 셀에서 재사용할 defaultSlots 상수의 원본이 된다. +- **functional** — 제어 상태(`open`/`defaultOpen`/`onOpenChange`), `ref`, `container`, `keepMounted`, `ariaLabels`, `closeOnClick` 등. Test Bed는 다루지 않는다. + +각 variant prop의 값 리스트를 확정한다. 값 도메인이 무한하거나 임의 문자열이면 variants에서 제외한다. + +완료: 세 카테고리에 분배되지 않은 prop이 0개이고, 각 variant prop에 대응하는 값 배열이 리터럴 상수로 확정돼 있다. + +### 5. Variants 테이블 렌더 + +모든 variant prop 값의 **카테시안 곱**을 `Regression` 컴파운드로 렌더한다. 조건 컬럼은 각 variant prop 하나씩 + 마지막 `render` 컬럼. variants가 0개면 스토리 자체가 무의미 — 사용자에게 확인 요청 후 종료. + +- `conditions`는 `{ key, label, values, format }[]` 배열. `key`는 row 필드명, `label`은 헤더 텍스트, `values`는 그 조건의 후보 값 배열(카테시안 곱 재료), `format`은 셀 표시용 값 변환기(예: `(v) => \`description = ${v ? 'O' : 'X'}\``). Boolean/null 인코딩된 조건은 반드시 `format`으로 O/X·리터럴 등으로 변환한다. +- `Regression.Table`이 `values`들로부터 카테시안 곱 rows를 자동 생성한다. rows를 밖에서 만들 필요 없다. +- 마크업: ` } />` 한 줄로 rows·헤더·ColGroup·조건 컬럼·render 셀이 자동 생성된다. +- 슬롯은 파일 최상단 `defaultSlots` 상수에서 spread. functional prop(예: `defaultOpen`)은 `render` 함수 안에서 지정. +- 컬럼 폭·셀 스타일·wrapper overflow·portal reset은 `Regression.Table` 내부가 담당. 스토리에서 손대지 않는다. +- 컴포넌트별 우회(예: Dialog overlay 숨김)는 스토리 자체 ` + `size = ${v}`, + }, + { + key: 'hasDescription', + label: 'description', + values: [ + '유사도 검사 결과를 다운받으시겠어요? 결과는 현재 표에 적용된 필터 기준으로 다운로드 됩니다.', + null, + ], + format: (v) => `description = ${v ? 'O' : 'X'}`, + }, + { + key: 'hasScroll', + label: 'scroll', + values: [true, false], + format: (v) => `scroll = ${v ? 'O' : 'X'}`, + }, + { + key: 'footer', + label: 'footer', + values: ['none', 'action', 'action + assistive'] as const, + format: (v) => `footer = ${v}`, + }, + ]} + render={(row, container) => ( + 확인} + assistive={ + row.footer !== 'action + assistive' ? null : ( + 보조 + ) + } + size={row.size} + > + + {Array.from({ length: row.hasScroll ? 10 : 2 }, (_, index) => ( + + {index + 1}. 내용물입니다. + + ))} + + + )} + /> + + ); +}; + +export const TestBed_Light: Story = { + render: () => , +}; + +export const TestBed_Dark: Story = { + globals: { appearance: 'dark' }, + render: () => , +}; diff --git a/packages/composites/src/utils/regressions.tsx b/packages/composites/src/utils/regressions.tsx new file mode 100644 index 000000000..da66e259d --- /dev/null +++ b/packages/composites/src/utils/regressions.tsx @@ -0,0 +1,293 @@ +import type { ReactNode } from 'react'; +import { useState } from 'react'; + +import { Box, Table } from '@vapor-ui/core'; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Root + * -----------------------------------------------------------------------------------------------*/ + +const RegressionRoot = ({ children }: { children: ReactNode }) => { + return ( + <> + + + + {children} + + + + ); +}; + +const portalResetStyles = ` +.regression-cell [data-slots$=".Portal"] { + position: static !important; + display: contents !important; +} +.regression-cell [data-slots$=".Positioner"] { + position: static !important; + inset: auto !important; + top: auto !important; + left: auto !important; + right: auto !important; + bottom: auto !important; + transform: none !important; + max-width: 100% !important; + max-height: 100% !important; + box-sizing: border-box !important; +} +`; + +/* ----------------------------------------------------------------------------------------------- + * Regression.ColumnGroup + * -----------------------------------------------------------------------------------------------*/ + +const RegressionColumnGroup = ({ children }: { children: ReactNode }) => { + return {children}; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.ConditionColumn + * -----------------------------------------------------------------------------------------------*/ + +const RegressionConditionColumn = () => { + return ; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.RenderColumn + * -----------------------------------------------------------------------------------------------*/ + +const RegressionRenderColumn = () => { + return ; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Header + * -----------------------------------------------------------------------------------------------*/ + +const RegressionHeader = ({ children }: { children: ReactNode }) => { + return {children}; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Body + * -----------------------------------------------------------------------------------------------*/ + +const RegressionBody = ({ children }: { children: ReactNode }) => { + return {children}; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Row + * -----------------------------------------------------------------------------------------------*/ + +const RegressionRow = ({ children }: { children: ReactNode }) => { + return {children}; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Heading + * -----------------------------------------------------------------------------------------------*/ + +const RegressionHeading = ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Condition + * -----------------------------------------------------------------------------------------------*/ + +const RegressionCondition = ({ children }: { children: ReactNode }) => { + return ( + + {children} + + ); +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Render + * -----------------------------------------------------------------------------------------------*/ + +const RegressionRender = ({ + children: childrenProp, +}: { + children: ((container: HTMLElement | null) => ReactNode) | ReactNode; +}) => { + const [container, setContainer] = useState(null); + const children = typeof childrenProp === 'function' ? childrenProp(container) : childrenProp; + + return ( + + + {children} + + + ); +}; + +/* ----------------------------------------------------------------------------------------------- + * cartesianRows + * -----------------------------------------------------------------------------------------------*/ + +export type CartesianRows> = Array<{ + [K in keyof T]: T[K][number]; +}>; + +export const cartesianRows = >( + conditions: T, +): CartesianRows => { + const keys = Object.keys(conditions) as (keyof T)[]; + if (keys.length === 0) return [{}] as CartesianRows; + + return keys.reduce>>( + (acc, key) => + acc.flatMap((row) => conditions[key].map((value) => ({ ...row, [key]: value }))), + [{}], + ) as CartesianRows; +}; + +/* ----------------------------------------------------------------------------------------------- + * Regression.Table + * -----------------------------------------------------------------------------------------------*/ + +type RegressionCondition = { + key: Key; + label: ReactNode; + values: readonly Value[]; + format: (value: Value) => ReactNode; +}; + +type AnyRegressionCondition = RegressionCondition; + +type RegressionRow = { + [K in C[number]['key']]: Extract['values'][number]; +}; + +type RegressionTableProps = { + conditions: C; + render: (row: RegressionRow, container: HTMLElement | null) => ReactNode; +}; + +const RegressionTable = ({ + conditions, + render, +}: RegressionTableProps) => { + const conditionMap = Object.fromEntries( + conditions.map((condition) => [condition.key, condition.values]), + ); + + const rows = cartesianRows(conditionMap) as RegressionRow[]; + + return ( + + + {conditions.map((_, colIdx) => ( + + ))} + + + + + {conditions.map((condition, colIdx) => ( + {condition.label} + ))} + render + + + + {rows.map((row, rowIdx) => ( + + {conditions.map((condition, colIdx) => ( + + {condition.format(row[condition.key as keyof RegressionRow])} + + ))} + {(container) => render(row, container)} + + ))} + + + + + 총 {rows.length}개 케이스 + + + + + ); +}; + +/* -----------------------------------------------------------------------------------------------*/ + +export const Regression = { + Root: RegressionRoot, + ColumnGroup: RegressionColumnGroup, + ConditionColumn: RegressionConditionColumn, + RenderColumn: RegressionRenderColumn, + Header: RegressionHeader, + Body: RegressionBody, + Row: RegressionRow, + Heading: RegressionHeading, + Condition: RegressionCondition, + Render: RegressionRender, + Table: RegressionTable, +}; diff --git a/packages/composites/vitest.config.ts b/packages/composites/vitest.config.ts index c77c97e88..5f17a8518 100644 --- a/packages/composites/vitest.config.ts +++ b/packages/composites/vitest.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ noExternal: ['@vapor-ui/icons', '@vapor-ui/core'], }, test: { - setupFiles: ['./__tests__/setup-tests.ts'], + setupFiles: ['./vitest.setup.ts'], environment: 'happy-dom', exclude: ['node_modules', 'dist'], include: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}'], diff --git a/packages/composites/__tests__/setup-tests.ts b/packages/composites/vitest.setup.ts similarity index 100% rename from packages/composites/__tests__/setup-tests.ts rename to packages/composites/vitest.setup.ts diff --git a/packages/core/package.json b/packages/core/package.json index 3126789ea..d63858e33 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -61,7 +61,6 @@ "lint": "eslint ./src", "test": "vitest --run", "test:coverage": "vitest --coverage", - "test:regressions": "playwright test -c ./playwright.config.ts", "test:watch": "vitest", "typecheck": "tsc --noEmit" }, @@ -79,7 +78,6 @@ "rainbow-sprinkles": "^1.0.0" }, "devDependencies": { - "@playwright/test": "^1.62.1", "@repo/eslint-config": "workspace:*", "@repo/rolldown-config": "workspace:*", "@repo/typescript-config": "workspace:*", @@ -102,7 +100,7 @@ "react": "catalog:", "react-dom": "catalog:", "rimraf": "^6.1.3", - "rolldown": "^1.2.6", + "rolldown": "catalog:", "rolldown-plugin-dts": "^0.27.14", "rollup-plugin-node-externals": "^8.1.2", "typescript": "catalog:", diff --git a/packages/core/playwright.merge.ts b/packages/core/playwright.merge.ts deleted file mode 100644 index 3f411d7b2..000000000 --- a/packages/core/playwright.merge.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default { - reporter: [ - ['html', { outputFolder: './__tests__/report' }], - ['json', { outputFile: './__tests__/report/index.json' }], - ], -}; diff --git a/packages/core/rolldown.config.ts b/packages/core/rolldown.config.ts index 4f64620ce..8d967de0b 100644 --- a/packages/core/rolldown.config.ts +++ b/packages/core/rolldown.config.ts @@ -1,6 +1,7 @@ import { bundle, generateInputs } from '@repo/rolldown-config'; import { cleanLayerDeclaration, + dataSlots, depsExternal, dts, identifiers, @@ -40,19 +41,28 @@ const resolve = { extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], }; +const replaceFileName = (name: string, ext: string) => { + return `${name.replace(/\.css$/, '.css.vanilla')}.${ext}`; +}; + export default defineConfig([ // ESM Build bundle({ input: inputs, resolve, - plugins: [depsExternal(), vanillaExtractPlugin({ identifiers }), cleanLayerDeclaration()], + plugins: [ + depsExternal(), + dataSlots(), + vanillaExtractPlugin({ identifiers }), + cleanLayerDeclaration(), + ], output: { format: 'esm', strict: true, exports: 'named', // e.g., 'styles/tailwind-preset.css' -> 'styles/tailwind-preset.css.vanilla.js' - entryFileNames: ({ name }) => `${name.replace(/\.css$/, '.css.vanilla')}.mjs`, + entryFileNames: ({ name }) => replaceFileName(name, 'mjs'), assetFileNames, }, }), @@ -61,13 +71,18 @@ export default defineConfig([ bundle({ input: inputs, resolve, - plugins: [depsExternal(), vanillaExtractPlugin({ identifiers }), cleanLayerDeclaration()], + plugins: [ + depsExternal(), + dataSlots(), + vanillaExtractPlugin({ identifiers }), + cleanLayerDeclaration(), + ], output: { format: 'cjs', strict: true, exports: 'named', - entryFileNames: ({ name }) => `${name.replace(/\.css$/, '.css.vanilla')}.cjs`, + entryFileNames: ({ name }) => replaceFileName(name, 'cjs'), assetFileNames, }, }), @@ -78,7 +93,7 @@ export default defineConfig([ resolve, plugins: [depsExternal(), dts()], output: { - entryFileNames: ({ name }) => `${name.replace(/\.css\.d$/, '.css.vanilla.d')}.mts`, + entryFileNames: ({ name }) => replaceFileName(name, 'mts'), }, }), @@ -87,7 +102,7 @@ export default defineConfig([ resolve, plugins: [depsExternal(), dts()], output: { - entryFileNames: ({ name }) => `${name.replace(/\.css\.d$/, '.css.vanilla.d')}.cts`, + entryFileNames: ({ name }) => replaceFileName(name, 'cts'), }, }), ]); diff --git a/packages/core/src/components/pagination/pagination.tsx b/packages/core/src/components/pagination/pagination.tsx index d60962eed..374bac741 100644 --- a/packages/core/src/components/pagination/pagination.tsx +++ b/packages/core/src/components/pagination/pagination.tsx @@ -120,7 +120,7 @@ export const PaginationRootPrimitive = forwardRef{element}; }, ); -PaginationRootPrimitive.displayName = 'PaginationRootPrimitive.Root'; +PaginationRootPrimitive.displayName = 'Pagination.RootPrimitive'; /* ------------------------------------------------------------------------------------------------- * Pagination.ListPrimitive diff --git a/packages/core/src/components/sheet/sheet.tsx b/packages/core/src/components/sheet/sheet.tsx index f953c9d6b..376c6b7b2 100644 --- a/packages/core/src/components/sheet/sheet.tsx +++ b/packages/core/src/components/sheet/sheet.tsx @@ -120,21 +120,27 @@ SheetRoot.displayName = 'Sheet.Root'; * Sheet.Trigger * -----------------------------------------------------------------------------------------------*/ -export const SheetTrigger = Dialog.Trigger; +export const SheetTrigger = forwardRef((props, ref) => ( + +)); SheetTrigger.displayName = 'Sheet.Trigger'; /* ------------------------------------------------------------------------------------------------- * Sheet.Close * -----------------------------------------------------------------------------------------------*/ -export const SheetClose = Dialog.Close; +export const SheetClose = forwardRef((props, ref) => ( + +)); SheetClose.displayName = 'Sheet.Close'; /* ------------------------------------------------------------------------------------------------- * Sheet.OverlayPrimitive * -----------------------------------------------------------------------------------------------*/ -export const SheetOverlayPrimitive = Dialog.OverlayPrimitive; +export const SheetOverlayPrimitive = forwardRef( + (props, ref) => , +); SheetOverlayPrimitive.displayName = 'Sheet.OverlayPrimitive'; /* ------------------------------------------------------------------------------------------------- @@ -142,11 +148,7 @@ SheetOverlayPrimitive.displayName = 'Sheet.OverlayPrimitive'; * -----------------------------------------------------------------------------------------------*/ export const SheetPortalPrimitive = forwardRef( - (props, ref) => { - const componentProps = resolveStyles(props); - - return ; - }, + (props, ref) => , ); SheetPortalPrimitive.displayName = 'Sheet.PortalPrimitive'; @@ -388,14 +390,18 @@ SheetFooter.displayName = 'Sheet.Footer'; * Sheet.Title * -----------------------------------------------------------------------------------------------*/ -export const SheetTitle = Dialog.Title; +export const SheetTitle = forwardRef((props, ref) => ( + +)); SheetTitle.displayName = 'Sheet.Title'; /* ------------------------------------------------------------------------------------------------- * Sheet.Description * -----------------------------------------------------------------------------------------------*/ -export const SheetDescription = Dialog.Description; +export const SheetDescription = forwardRef( + (props, ref) => , +); SheetDescription.displayName = 'Sheet.Description'; /* -----------------------------------------------------------------------------------------------*/ diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts index 33826e7ac..250c45564 100644 --- a/packages/core/vitest.config.ts +++ b/packages/core/vitest.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ noExternal: ['@vapor-ui/icons'], }, test: { - setupFiles: ['./__tests__/setup-tests.ts'], + setupFiles: ['./vitest.setup.ts'], environment: 'happy-dom', exclude: ['node_modules', 'dist'], include: ['src/**/*.{test,spec}.{js,jsx,ts,tsx}'], diff --git a/packages/core/__tests__/setup-tests.ts b/packages/core/vitest.setup.ts similarity index 100% rename from packages/core/__tests__/setup-tests.ts rename to packages/core/vitest.setup.ts diff --git a/packages/rolldown-config/package.json b/packages/rolldown-config/package.json index 94f742a71..d1d97f2ef 100644 --- a/packages/rolldown-config/package.json +++ b/packages/rolldown-config/package.json @@ -20,15 +20,17 @@ "lint": "eslint ./src" }, "dependencies": { - "rolldown-plugin-dts": "^0.27.1", + "magic-string": "^1.2.3", + "rolldown-plugin-dts": "^0.27.14", "rollup-plugin-node-externals": "^8.1.2" }, "devDependencies": { + "@oxc-project/types": "catalog:", "@repo/eslint-config": "workspace:*", "@repo/typescript-config": "workspace:*", "eslint": "catalog:", "rimraf": "^6.1.3", - "rolldown": "^1.2.6", + "rolldown": "catalog:", "rollup": "^4.62.4", "tinyglobby": "^0.2.17", "typescript": "catalog:" diff --git a/packages/rolldown-config/src/plugins/data-slots.ts b/packages/rolldown-config/src/plugins/data-slots.ts new file mode 100644 index 000000000..dd670e899 --- /dev/null +++ b/packages/rolldown-config/src/plugins/data-slots.ts @@ -0,0 +1,310 @@ +import type { + ArrowFunctionExpression, + CallExpression, + JSXElement, + Node, + ObjectExpression, + ObjectProperty, + Program, + Statement, + VariableDeclarator, +} from '@oxc-project/types'; +import MagicString from 'magic-string'; +import path from 'node:path'; +import type { RolldownPlugin } from 'rolldown'; + +const SLOT_ATTR = 'data-slots'; +const PRIMITIVE_SUFFIX = 'Primitive'; +const DEFAULT_INCLUDE = /\/src\/components\/.+\.tsx$/; +// Filename variant has no `.displayName` marker; gate on component shape. +const COMPONENT_MARKER = /\b(forwardRef|useRenderElement)\b/; +const HOC_CALLEES = new Set(['forwardRef', 'memo']); +const NESTED_SCOPE = new Set([ + 'ArrowFunctionExpression', + 'FunctionExpression', + 'FunctionDeclaration', + 'ObjectMethod', + 'ClassMethod', + 'ClassPrivateMethod', +]); +const AST_META_KEYS = new Set([ + 'type', + 'start', + 'end', + 'range', + 'loc', + 'parent', + 'leadingComments', + 'trailingComments', + 'innerComments', + 'comments', + 'directives', + 'tokens', + 'extra', +]); + +interface Options { + include?: RegExp; +} + +export const dataSlots = ({ include = DEFAULT_INCLUDE }: Options = {}): RolldownPlugin => ({ + name: 'vapor-data-slots-filename', + transform: { + filter: { + id: include, + code: COMPONENT_MARKER, + }, + handler(code, id) { + const namespace = deriveNamespace(id); + if (!namespace) return null; + + const program = this.parse(code, { lang: 'tsx' }); + const declarators = collectDeclarators(program); + if (declarators.size === 0) return null; + + const magic = new MagicString(code); + let touched = false; + + for (const [name, declarator] of declarators) { + const slot = deriveSlot(namespace, name); + if (!slot) continue; + + const fn = resolveCallback(declarator); + if (!fn) continue; + + if (inject(fn, slot, magic)) touched = true; + } + + if (!touched) return null; + return { + code: magic.toString(), + map: magic.generateMap({ hires: true, source: id }), + }; + }, + }, +}); + +/* -----------------------------------------------------------------------------------------------*/ + +/** Extract namespace from an absolute tsx path. `radio-group.tsx` → `RadioGroup`. */ +function deriveNamespace(id: string): string | null { + const base = path.basename(id).replace(/\.tsx$/, ''); + return pascalize(base); +} + +/** kebab-case → PascalCase. Handles `-` and `_` boundaries. */ +function pascalize(input: string): string | null { + const segments = input.split(/[-_]+/).filter(Boolean); + if (segments.length === 0) return null; + return segments.map((seg) => seg[0].toUpperCase() + seg.slice(1)).join(''); +} + +/** Derive slot from `namespace` and top-level variable `name`. + * + * Convention: variable MUST start with `namespace`. `rest` is the tail after + * namespace. Trailing `Primitive` is stripped from `rest`. When `rest` is + * empty (standalone with variable name === namespace) the slot is just the + * namespace. Otherwise `${namespace}.${rest}`. + * + * Variables not starting with namespace are skipped — probably internal + * helpers, not public parts. + * + * Examples (namespace='Dialog'): + * DialogRoot → Dialog.Root + * DialogPortalPrimitive → Dialog.Portal + * Dialog → Dialog + * someHelper → null (skip) */ +function deriveSlot(namespace: string, name: string): string | null { + if (!name.startsWith(namespace)) return null; + + const rest = stripPrimitiveSuffix(name.slice(namespace.length)); + return rest ? `${namespace}.${rest}` : namespace; +} + +/** Strip trailing `Primitive` unless the string equals `Primitive`. */ +function stripPrimitiveSuffix(rest: string): string { + if (!rest.endsWith(PRIMITIVE_SUFFIX)) return rest; + if (rest === PRIMITIVE_SUFFIX) return rest; + return rest.slice(0, -PRIMITIVE_SUFFIX.length); +} + +/* -----------------------------------------------------------------------------------------------*/ + +function collectDeclarators(program: Program): Map { + const declarators = new Map(); + for (const item of program.body) { + const stmt = unwrapExport(item as Statement); + if (stmt.type === 'VariableDeclaration') { + for (const decl of stmt.declarations) { + if (decl.id.type === 'Identifier') declarators.set(decl.id.name, decl); + } + } + } + return declarators; +} + +function unwrapExport(item: Statement): Statement { + if (item.type === 'ExportNamedDeclaration' && item.declaration) { + return item.declaration as Statement; + } + return item; +} + +/* -----------------------------------------------------------------------------------------------*/ + +type Callback = ArrowFunctionExpression; + +function resolveCallback(decl: VariableDeclarator): Callback | null { + const init = decl.init; + if (!init) return null; + if (init.type === 'ArrowFunctionExpression') return init; + if (init.type !== 'CallExpression') return null; + + const callee = init.callee; + const calleeName = + callee.type === 'Identifier' + ? callee.name + : callee.type === 'MemberExpression' && + !callee.computed && + callee.property.type === 'Identifier' + ? callee.property.name + : null; + + if (!calleeName || !HOC_CALLEES.has(calleeName)) return null; + + const first = init.arguments[0]; + return first && first.type === 'ArrowFunctionExpression' ? first : null; +} + +/* -----------------------------------------------------------------------------------------------*/ + +type RefCarrier = { kind: 'jsx'; element: JSXElement } | { kind: 'render'; call: CallExpression }; + +function inject(fn: Callback, slot: string, magic: MagicString): boolean { + const carriers = collectRefCarriers(fn); + if (carriers.length !== 1) return false; + + const target = carriers[0]; + return target.kind === 'jsx' + ? injectJsxAttribute(target.element, slot, magic) + : injectIntoRenderElement(target.call, slot, magic); +} + +function collectRefCarriers(fn: Callback): RefCarrier[] { + const carriers: RefCarrier[] = []; + walkScope(fn.body, (node) => { + if (node.type === 'JSXElement') { + if (jsxHasRefAttribute(node)) carriers.push({ kind: 'jsx', element: node }); + return; + } + if ( + node.type === 'CallExpression' && + isUseRenderElementCall(node) && + renderOptionsHaveRef(node) + ) { + carriers.push({ kind: 'render', call: node }); + } + }); + return carriers; +} + +function isUseRenderElementCall(call: CallExpression): boolean { + return call.callee.type === 'Identifier' && call.callee.name === 'useRenderElement'; +} + +function jsxHasRefAttribute(element: JSXElement): boolean { + for (const attr of element.openingElement.attributes) { + if (attr.type !== 'JSXAttribute') continue; + if (attr.name.type === 'JSXIdentifier' && attr.name.name === 'ref') return true; + } + return false; +} + +function renderOptionsHaveRef(call: CallExpression): boolean { + const arg = call.arguments[0]; + if (!arg || arg.type !== 'ObjectExpression') return false; + for (const property of arg.properties) { + if (property.type !== 'Property' || property.computed) continue; + const key = property.key; + if (key.type === 'Identifier' && key.name === 'ref') return true; + if (key.type === 'Literal' && key.value === 'ref') return true; + } + return false; +} + +/* -----------------------------------------------------------------------------------------------*/ + +function injectJsxAttribute(element: JSXElement, slot: string, magic: MagicString): boolean { + magic.appendLeft(element.openingElement.name.end, ` ${SLOT_ATTR}="${slot}"`); + return true; +} + +/* -----------------------------------------------------------------------------------------------*/ + +function injectIntoRenderElement(call: CallExpression, slot: string, magic: MagicString): boolean { + const arg = call.arguments[0]; + if (!arg || arg.type !== 'ObjectExpression') return false; + + const propsProperty = findProperty(arg, 'props'); + if (!propsProperty) { + return insertAtObjectHead(arg, `props: { '${SLOT_ATTR}': '${slot}' }`, magic); + } + if (propsProperty.shorthand) return false; + + const value = propsProperty.value; + if (value.type === 'ObjectExpression') { + return insertAtObjectHead(value, `'${SLOT_ATTR}': '${slot}'`, magic); + } + + magic.appendLeft(value.start, `{ '${SLOT_ATTR}': '${slot}', ...(`); + magic.appendRight(value.end, `) }`); + return true; +} + +function findProperty(obj: ObjectExpression, name: string): ObjectProperty | null { + for (const property of obj.properties) { + if (property.type !== 'Property' || property.computed) continue; + const key = property.key; + if (key.type === 'Identifier' && key.name === name) return property; + if (key.type === 'Literal' && key.value === name) return property; + } + return null; +} + +function insertAtObjectHead(obj: ObjectExpression, entry: string, magic: MagicString): boolean { + const opener = obj.start + 1; + const suffix = obj.properties.length > 0 ? ',' : ' '; + magic.appendRight(opener, ` ${entry}${suffix} `); + return true; +} + +/* -----------------------------------------------------------------------------------------------*/ + +function walkScope(root: Node, visit: (node: Node) => void): void { + const stack: unknown[] = [root]; + while (stack.length > 0) { + const current = stack.pop(); + if (Array.isArray(current)) { + for (let i = current.length - 1; i >= 0; i--) stack.push(current[i]); + continue; + } + if (!current || typeof current !== 'object') continue; + const node = current as Node; + if (typeof node.type !== 'string') continue; + if (node !== root && NESTED_SCOPE.has(node.type)) continue; + + visit(node); + + if (node.type === 'JSXElement') { + const jsx = node as JSXElement; + if (jsx.openingElement) stack.push(jsx.openingElement); + if (!jsxHasRefAttribute(jsx)) stack.push(jsx.children); + continue; + } + + for (const key of Object.keys(node)) { + if (AST_META_KEYS.has(key)) continue; + stack.push((node as unknown as Record)[key]); + } + } +} diff --git a/packages/rolldown-config/src/plugins/index.ts b/packages/rolldown-config/src/plugins/index.ts index a35c818f1..cef6c9ea2 100644 --- a/packages/rolldown-config/src/plugins/index.ts +++ b/packages/rolldown-config/src/plugins/index.ts @@ -2,3 +2,4 @@ export { cleanLayerDeclaration } from './clean-css-layer.ts'; export { depsExternal } from './deps-external.ts'; export { dts } from './dts.ts'; export { identifiers } from './vanilla-extract.ts'; +export { dataSlots } from './data-slots.ts'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f794c76b8..083da16ac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,6 +6,9 @@ settings: catalogs: default: + '@oxc-project/types': + specifier: 0.147.0 + version: 0.147.0 '@types/react': specifier: ^19.2.18 version: 19.2.18 @@ -27,6 +30,9 @@ catalogs: react-dom: specifier: ^19.2.8 version: 19.2.8 + rolldown: + specifier: 1.2.6 + version: 1.2.6 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -181,6 +187,9 @@ importers: '@eslint/js': specifier: ^9.39.5 version: 9.39.5 + '@playwright/test': + specifier: ^1.62.1 + version: 1.62.1 '@repo/eslint-config': specifier: workspace:* version: link:../../packages/eslint-config @@ -214,6 +223,9 @@ importers: globals: specifier: ^16.5.0 version: 16.5.0 + http-server: + specifier: ^14.1.1 + version: 14.1.1 storybook: specifier: ^9.1.20 version: 9.1.20(@testing-library/dom@10.4.1)(prettier@3.9.6)(vite@7.3.6(@types/node@25.5.0)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0)) @@ -494,9 +506,6 @@ importers: '@figma/code-connect': specifier: ^2.0.0 version: 2.0.0 - '@playwright/test': - specifier: ^1.62.1 - version: 1.62.1 '@repo/eslint-config': specifier: workspace:^ version: link:../eslint-config @@ -603,9 +612,6 @@ importers: specifier: ^1.0.0 version: 1.0.0(@vanilla-extract/css@1.21.2)(@vanilla-extract/dynamic@2.1.5) devDependencies: - '@playwright/test': - specifier: ^1.62.1 - version: 1.62.1 '@repo/eslint-config': specifier: workspace:* version: link:../eslint-config @@ -673,7 +679,7 @@ importers: specifier: ^6.1.3 version: 6.1.3 rolldown: - specifier: ^1.2.6 + specifier: 'catalog:' version: 1.2.6 rolldown-plugin-dts: specifier: ^0.27.14 @@ -888,13 +894,19 @@ importers: packages/rolldown-config: dependencies: + magic-string: + specifier: ^1.2.3 + version: 1.2.3 rolldown-plugin-dts: - specifier: ^0.27.1 + specifier: ^0.27.14 version: 0.27.14(rolldown@1.2.6)(typescript@5.9.3) rollup-plugin-node-externals: specifier: ^8.1.2 version: 8.1.2(rollup@4.62.4) devDependencies: + '@oxc-project/types': + specifier: 'catalog:' + version: 0.147.0 '@repo/eslint-config': specifier: workspace:* version: link:../eslint-config @@ -908,7 +920,7 @@ importers: specifier: ^6.1.3 version: 6.1.3 rolldown: - specifier: ^1.2.6 + specifier: 'catalog:' version: 1.2.6 rollup: specifier: ^4.62.4 @@ -1872,8 +1884,8 @@ packages: resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} - '@csstools/color-helpers@6.1.1': - resolution: {integrity: sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==} + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} '@csstools/css-calc@2.1.4': @@ -1883,8 +1895,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-calc@3.3.0': - resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -1897,8 +1909,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@4.2.2': - resolution: {integrity: sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==} + '@csstools/css-color-parser@4.1.7': + resolution: {integrity: sha512-CmjJFQTFQx/U/xNJhSjCQ0ilpesPmNQ8+eOUeM/+kDOVW33qsIjeOXc27vrQDdWVkf83ZSWwtg7kXSUvKDJ8cQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -1916,8 +1928,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.12': - resolution: {integrity: sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==} + '@csstools/css-syntax-patches-for-csstree@1.1.5': + resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -2996,8 +3008,8 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.6.0': - resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -4517,8 +4529,8 @@ packages: '@ts-morph/common@0.29.0': resolution: {integrity: sha512-35oUmphHbJvQ/+UTwFNme/t2p3FoKiGJ5auTjjpNTop2dyREspirjMy82PLSC1pnDJ8ah1GU98hwpVt64YXQsg==} - '@tsconfig/node10@1.0.13': - resolution: {integrity: sha512-gcLdvR9HO1ZJBypsOGqaP6TFEzb6vIta0KSTLt9NAQ6pXQO3cRgSVyCN6pzYqI9DlJgY71XKO0dpDhCf08b3pg==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -5367,6 +5379,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -5452,6 +5467,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -5747,6 +5766,10 @@ packages: core-js-compat@3.45.1: resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + corser@2.0.1: + resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} + engines: {node: '>= 0.4.0'} + cosmiconfig@8.3.6: resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} engines: {node: '>=14'} @@ -6321,6 +6344,9 @@ packages: resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} engines: {node: '>= 0.8'} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + execa@0.4.0: resolution: {integrity: sha512-QPexBaNjeOjyiZ47q0FCukTO1kX3F+HMM0EWpnxXddcr3MZtElILMkz9Y38nmSZtp03+ZiSRMffrKWBPOIoSIg==} engines: {node: '>=0.12'} @@ -6428,6 +6454,15 @@ packages: resolution: {integrity: sha512-MLqjFn72Dvndqrkjy280HaIs4AV9Z6nxVRmNPO3TjbYcipg4hR7QX7tEYZYsVvaaZWZPGe6Mithluk2aPGlDOw==} engines: {node: '>=0.4.0'} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -6787,9 +6822,17 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + hsluv@0.1.0: resolution: {integrity: sha512-ERcanKLAszD2XN3Vh5r5Szkrv9q0oSTudmP0rkiKAGM/3NMc9FLmMZBB7TSqTaXJfSDBOreYTfjezCOYbRKqlw==} + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -6811,6 +6854,15 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + http-server@14.1.1: + resolution: {integrity: sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==} + engines: {node: '>=12'} + hasBin: true + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -7590,8 +7642,8 @@ packages: resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} engines: {node: 20 || >=22} - lru-cache@11.5.2: - resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} lru-cache@4.1.5: @@ -7612,6 +7664,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.2.3: + resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} @@ -7837,6 +7892,11 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -8072,6 +8132,10 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -8258,6 +8322,10 @@ packages: engines: {node: '>=20'} hasBin: true + portfinder@1.0.38: + resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} + engines: {node: '>= 10.12'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -8345,6 +8413,10 @@ packages: pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -8658,6 +8730,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -8687,6 +8762,9 @@ packages: scroll-into-view-if-needed@3.1.0: resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + secure-compare@3.0.1: + resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} + semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -8747,6 +8825,10 @@ packages: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + side-channel-map@1.0.1: resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} engines: {node: '>= 0.4'} @@ -8759,6 +8841,10 @@ packages: resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} engines: {node: '>= 0.4'} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -9040,11 +9126,11 @@ packages: resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tldts-core@7.4.11: - resolution: {integrity: sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==} + tldts-core@7.4.3: + resolution: {integrity: sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==} - tldts@7.4.11: - resolution: {integrity: sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==} + tldts@7.4.3: + resolution: {integrity: sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==} hasBin: true tmp@0.2.5: @@ -9066,8 +9152,8 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} - tough-cookie@6.0.2: - resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} tr46@0.0.3: @@ -9267,10 +9353,6 @@ packages: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} - undici@7.29.0: - resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} - engines: {node: '>=20.18.1'} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -9301,6 +9383,10 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + union@0.5.0: + resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} + engines: {node: '>= 0.8.0'} + unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} @@ -9361,6 +9447,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + url-join@4.0.1: + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} + url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} @@ -9576,6 +9665,11 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -9798,8 +9892,8 @@ snapshots: '@asamuzakjp/css-color@5.1.11': dependencies: '@asamuzakjp/generational-cache': 1.0.1 - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 optional: true @@ -10023,16 +10117,6 @@ snapshots: '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - optional: true - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.4)': - dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': dependencies: @@ -10690,7 +10774,7 @@ snapshots: '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.4) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -10698,7 +10782,7 @@ snapshots: '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -11580,7 +11664,7 @@ snapshots: '@csstools/color-helpers@5.1.0': {} - '@csstools/color-helpers@6.1.1': + '@csstools/color-helpers@6.0.2': optional: true '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': @@ -11588,7 +11672,7 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -11601,10 +11685,10 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@4.2.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 6.1.1 - '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 optional: true @@ -11618,7 +11702,7 @@ snapshots: '@csstools/css-tokenizer': 4.0.0 optional: true - '@csstools/css-syntax-patches-for-csstree@1.1.12(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 optional: true @@ -12549,7 +12633,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': @@ -12559,17 +12643,17 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 optional: true '@manypkg/find-root@1.1.0': @@ -13893,7 +13977,7 @@ snapshots: path-browserify: 1.0.1 tinyglobby: 0.2.17 - '@tsconfig/node10@1.0.13': + '@tsconfig/node10@1.0.12': optional: true '@tsconfig/node12@1.0.11': @@ -14479,6 +14563,26 @@ snapshots: - vite optional: true + '@vitest/browser@3.2.7(playwright@1.62.1)(vite@7.3.6(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testing-library/user-event': 14.6.6(@testing-library/dom@10.4.1) + '@vitest/mocker': 3.2.7(vite@7.3.6(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0)) + '@vitest/utils': 3.2.7 + magic-string: 0.30.21 + sirv: 3.0.2 + tinyrainbow: 2.0.0 + vitest: 3.2.7(@types/debug@4.1.12)(@types/node@22.20.1)(@vitest/browser@3.2.7)(happy-dom@20.11.2)(jiti@2.7.0)(jsdom@29.0.2(@noble/hashes@1.8.0))(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0) + ws: 8.21.3 + optionalDependencies: + playwright: 1.62.1 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + optional: true + '@vitest/browser@3.2.7(playwright@1.62.1)(vite@8.2.1(@types/node@22.19.21)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7)': dependencies: '@testing-library/dom': 10.4.1 @@ -14532,9 +14636,9 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.7(@types/debug@4.1.12)(@types/node@22.20.1)(@vitest/browser@3.2.7)(happy-dom@20.11.2)(jiti@2.7.0)(jsdom@29.0.2(@noble/hashes@1.8.0))(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0) + vitest: 3.2.7(@types/debug@4.1.12)(@types/node@22.19.21)(@vitest/browser@3.2.7)(happy-dom@20.9.0)(jiti@2.7.0)(jsdom@29.0.2(@noble/hashes@1.8.0))(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0) optionalDependencies: - '@vitest/browser': 3.2.7(playwright@1.62.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7) + '@vitest/browser': 3.2.7(playwright@1.62.1)(vite@8.2.1(@types/node@22.19.21)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7) transitivePeerDependencies: - supports-color @@ -14915,6 +15019,8 @@ snapshots: async-function@1.0.0: {} + async@3.2.6: {} + asynckit@0.4.0: {} autoprefixer@10.5.4(postcss@8.5.26): @@ -15049,6 +15155,10 @@ snapshots: baseline-browser-mapping@2.11.15: {} + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + better-opn@3.0.2: dependencies: open: 8.4.2 @@ -15321,6 +15431,8 @@ snapshots: dependencies: browserslist: 4.28.2 + corser@2.0.1: {} + cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 @@ -16100,6 +16212,8 @@ snapshots: '@types/node': 22.20.1 require-like: 0.1.2 + eventemitter3@4.0.7: {} + execa@0.4.0: dependencies: cross-spawn-async: 2.2.5 @@ -16237,6 +16351,8 @@ snapshots: flow-parser@0.291.0: {} + follow-redirects@1.16.0: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -16698,8 +16814,14 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + he@1.2.0: {} + hsluv@0.1.0: {} + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -16724,6 +16846,33 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.16.0 + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + http-server@14.1.1: + dependencies: + basic-auth: 2.0.1 + chalk: 4.1.2 + corser: 2.0.1 + he: 1.2.0 + html-encoding-sniffer: 3.0.0 + http-proxy: 1.18.1 + mime: 1.6.0 + minimist: 1.2.8 + opener: 1.5.2 + portfinder: 1.0.38 + secure-compare: 3.0.1 + union: 0.5.0 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + - supports-color + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -17470,19 +17619,19 @@ snapshots: '@asamuzakjp/css-color': 5.1.11 '@asamuzakjp/dom-selector': 7.1.1 '@bramus/specificity': 2.4.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.12(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) '@exodus/bytes': 1.15.1(@noble/hashes@1.8.0) css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@1.8.0) decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0) is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.2 + lru-cache: 11.5.1 parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.2 - undici: 7.29.0 + tough-cookie: 6.0.1 + undici: 7.28.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -17696,7 +17845,7 @@ snapshots: lru-cache@11.2.1: {} - lru-cache@11.5.2: + lru-cache@11.5.1: optional: true lru-cache@4.1.5: @@ -17716,7 +17865,11 @@ snapshots: magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/sourcemap-codec': 1.5.5 + + magic-string@1.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 magicast@0.3.5: dependencies: @@ -18216,6 +18369,8 @@ snapshots: dependencies: mime-db: 1.52.0 + mime@1.6.0: {} + mimic-fn@2.1.0: {} min-indent@1.0.1: {} @@ -18439,6 +18594,8 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + opener@1.5.2: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -18640,6 +18797,13 @@ snapshots: optionalDependencies: fsevents: 2.3.2 + portfinder@1.0.38: + dependencies: + async: 3.2.6 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + possible-typed-array-names@1.1.0: {} postcss-load-config@6.0.1(jiti@2.7.0)(postcss@8.5.26)(tsx@4.23.13)(yaml@2.9.0): @@ -18719,6 +18883,11 @@ snapshots: pure-rand@7.0.1: {} + qs@6.16.0: + dependencies: + es-define-property: 1.0.1 + side-channel: 1.1.1 + quansync@0.2.11: {} querystringify@2.2.0: {} @@ -19187,6 +19356,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -19220,6 +19391,8 @@ snapshots: dependencies: compute-scroll-into-view: 3.1.1 + secure-compare@3.0.1: {} + semver@5.7.2: {} semver@6.3.1: {} @@ -19330,6 +19503,11 @@ snapshots: es-errors: 1.3.0 object-inspect: 1.13.4 + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-map@1.0.1: dependencies: call-bound: 1.0.4 @@ -19353,6 +19531,14 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -19701,12 +19887,12 @@ snapshots: tinyspy@4.0.4: {} - tldts-core@7.4.11: + tldts-core@7.4.3: optional: true - tldts@7.4.11: + tldts@7.4.3: dependencies: - tldts-core: 7.4.11 + tldts-core: 7.4.3 optional: true tmp@0.2.5: {} @@ -19726,9 +19912,9 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 - tough-cookie@6.0.2: + tough-cookie@6.0.1: dependencies: - tldts: 7.4.11 + tldts: 7.4.3 optional: true tr46@0.0.3: {} @@ -19795,7 +19981,7 @@ snapshots: ts-node@10.9.2(@types/node@20.19.43)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.13 + '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 @@ -19948,9 +20134,6 @@ snapshots: undici@7.28.0: {} - undici@7.29.0: - optional: true - unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -19981,6 +20164,10 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + union@0.5.0: + dependencies: + qs: 6.16.0 + unist-util-is@6.0.0: dependencies: '@types/unist': 3.0.3 @@ -20077,6 +20264,8 @@ snapshots: dependencies: punycode: 2.3.1 + url-join@4.0.1: {} + url-parse@1.5.10: dependencies: querystringify: 2.2.0 @@ -20485,7 +20674,7 @@ snapshots: optionalDependencies: '@types/debug': 4.1.12 '@types/node': 22.20.1 - '@vitest/browser': 3.2.7(playwright@1.62.1)(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7) + '@vitest/browser': 3.2.7(playwright@1.62.1)(vite@7.3.6(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.33.0)(sass@1.102.0)(tsx@4.23.13)(yaml@2.9.0))(vitest@3.2.7) happy-dom: 20.11.2 jsdom: 29.0.2(@noble/hashes@1.8.0) transitivePeerDependencies: @@ -20525,6 +20714,10 @@ snapshots: webpack-virtual-modules@0.6.2: {} + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f048ca273..cb4f3caa1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,6 +13,8 @@ catalog: '@types/react-dom': ^19.2.5 typescript: ^5.9.3 eslint: ^9.39.5 + rolldown: 1.2.6 + '@oxc-project/types': 0.147.0 vite: ^7.3.6 vitest: ^3.2.7 '@vitest/coverage-v8': ^3.2.7 diff --git a/turbo.json b/turbo.json index 605f84a2e..60edb205a 100644 --- a/turbo.json +++ b/turbo.json @@ -2,6 +2,9 @@ "$schema": "https://turbo.build/schema.json", "ui": "tui", "tasks": { + "storybook#dev": { + "dependsOn": ["@vapor-ui/core#build"] + }, "website#dev": { "dependsOn": [ "@vapor-ui/core#build",