diff --git a/.sync/log/7eab4bcb78f17b9eed365d617aa3ef3874c21bac.md b/.sync/log/7eab4bcb78f17b9eed365d617aa3ef3874c21bac.md new file mode 100644 index 000000000..6ec7a5574 --- /dev/null +++ b/.sync/log/7eab4bcb78f17b9eed365d617aa3ef3874c21bac.md @@ -0,0 +1,59 @@ +# Port: perf(types): import cross-component types from source, not the barrel (#6646) + +**Upstream:** `7eab4bcb78f17b9eed365d617aa3ef3874c21bac` (nuxt/ui) +**Decision:** port + +## Upstream change +A perf refactor: stop importing cross-component types from the `../types` +**barrel** (`src/runtime/types/index.ts`, which `export *`s every component + +every `types/*` submodule). One barrel import eagerly pulls the whole library +into a consumer's type graph. Instead, import each type directly from its +**source** — the owning component `.vue` (e.g. `ButtonProps` from +`./Button.vue`) or a specific `../types/*` module (e.g. `Messages` from +`../types/locale`). Adds an eslint `no-restricted-imports` guard so the barrel +can't creep back in, with `useComponentProps.ts` exempted (it legitimately +needs the whole barrel via `import type * as ComponentTypes`). + +## b24ui port — adapted, not 1:1 +b24ui's `types/index.ts` barrel has the **same** shape (re-exports every +`components/*.vue` + `types/{content,editor,form,input,icons,locale,prose,color-mode,utils}`), +so the same refactor applies — but the per-type → source mapping had to be +resolved against **b24ui's own** exports because the component/type set +diverges from upstream: + +- b24ui has **no `Icon.vue`**: upstream maps `IconProps` → `./Icon.vue`; b24ui + components use `IconComponent`, which resolves to `../types/icons`. +- b24ui has its own component set (no `AuthForm`/`BlogPost`/…, plus extras like + `Advice`, `Countdown`, the `Sidebar*`/`Navbar*` families). +- b24ui maintains a **curated, smaller locale set** (§2) — 20 locale files + touched here vs upstream's 62. Existing locales are edited normally + (`Messages` import retargeted to `../types/locale`); no new locales added. + +Rather than copy upstream's per-file import lines, I built a name→source index +from b24ui's barrel sources and rewrote every consumer's +`import type { … } from '<../types barrel>'` into per-source grouped imports +with the correct relative specifier for the consumer's depth. Verified: **0** +unresolved type names, **0** ambiguous names (no type exported by >1 source). + +### Scope +- **119** consumer files rewritten (`src/runtime/{components,composables,utils, + dictionary,locale,vue/...}`), `+`eslint.config.mjs. `120` files total, + +284/−120 lines (single barrel lines split into per-source lines). +- Only remaining barrel import: `composables/useComponentProps.ts` + (`import type * as ComponentTypes from '../types'`) — intentional, and the + eslint rule turns `no-restricted-imports` **off** for that file, mirroring + upstream. +- eslint rule appended via the existing `.append(...)` chain (b24ui uses the + `bitrix24-ui/` plugin namespace, not upstream's `nuxt-ui/`). Rule `files` + globs and restricted paths (`../types`, `../../types`) mirror upstream. + b24ui's only depth-3 barrel importers live under `src/runtime/vue/…` (not + `components/**`/`composables/**`), so — exactly as upstream — they are + rewritten but not eslint-guarded. + +## Tests +Types-only change, no runtime/markup change → no snapshot churn. Full suite +unchanged (225 files, 5143 passed / 6 skipped). + +## Verify (CI=true) +`dev:prepare` · `lint` (new rule active, green) · `typecheck` (all 119 rewrites +resolve) · `test` · `build` — all green. diff --git a/.sync/nuxt-ui.json b/.sync/nuxt-ui.json index d2975ac45..f1f46b8c2 100644 --- a/.sync/nuxt-ui.json +++ b/.sync/nuxt-ui.json @@ -2,7 +2,7 @@ "upstream": "nuxt/ui", "branch": "v4", "sync_enabled": false, - "cursor": "88baabcf42b115b7da1a65e5f71092a37b5c9bf3", + "cursor": "7eab4bcb78f17b9eed365d617aa3ef3874c21bac", "_cursor_note": "cursor = last upstream commit ported into b24ui (oldest-first, manual cadence). sync_enabled stays false until Phase 2 (porter workflow #67 + CLAUDE_CODE_OAUTH_TOKEN) is wired and trusted. `processed` is maintained per port from now on (backfilled #68-#72 on 2026-06-09).", "stats": { "queue_depth": 0, @@ -773,10 +773,16 @@ "summary": "fix(ContentNavigation): key items by identity to prevent leading icon flash (#6640) — NO-OP: upstream keys the navigation v-for by `${index}-${link.path}` in src/runtime/components/content/ContentNavigation.vue (+spec). b24ui has no ContentNavigation component (git ls-files grep contentnavigation => none). Nothing to port. Bookkeeping only" }, "88baabcf42b115b7da1a65e5f71092a37b5c9bf3": { + "pr": 225, + "b24ui_sha": "007602bf", + "decision": "port", + "summary": "fix(Separator): forward fall-through attributes to root (#6641) — Separator.vue: add defineOptions({ inheritAttrs: false }) + change root binding v-bind=rootProps -> v-bind={...rootProps, ...$attrs} so fall-through attrs land on the actual root. Direct 1:1 (b24ui matched pre-change; useForwardProps rootProps + reka root unchanged; no ui/icon/color renames). +attr-forwarding spec mirroring #218 (d3b5f1d3) (targets [data-slot=root] since Separator is multi-root). 2 snapshots updated (Separator + -vue): the existing 'with color success' renderEach case passes `color` which is NOT a SeparatorProps member = a fall-through attr; pre-fix multi-root dropped it, post-fix $attrs forwards it to root -> snapshot now carries color=air-primary-success (the fix demonstrating itself)" + }, + "7eab4bcb78f17b9eed365d617aa3ef3874c21bac": { "pr": null, "b24ui_sha": "pending-merge", "decision": "port", - "summary": "fix(Separator): forward fall-through attributes to root (#6641) — Separator.vue: add defineOptions({ inheritAttrs: false }) + change root binding v-bind=rootProps -> v-bind={...rootProps, ...$attrs} so fall-through attrs land on the actual root. Direct 1:1 (b24ui matched pre-change; useForwardProps rootProps + reka root unchanged; no ui/icon/color renames). +attr-forwarding spec mirroring #218 (d3b5f1d3) (targets [data-slot=root] since Separator is multi-root). 2 snapshots updated (Separator + -vue): the existing 'with color success' renderEach case passes `color` which is NOT a SeparatorProps member = a fall-through attr; pre-fix multi-root dropped it, post-fix $attrs forwards it to root -> snapshot now carries color=air-primary-success (the fix demonstrating itself)" + "summary": "perf(types): import cross-component types from source, not the barrel (#6646) — replace `import type {...} from '../types'` barrel imports with per-source imports (owning component .vue, e.g. ButtonProps from ./Button.vue; or specific ../types/* module, e.g. Messages from ../types/locale) across 119 consumer files in src/runtime/{components,composables,utils,dictionary,locale,vue/...}; +eslint no-restricted-imports guard on ../types & ../../types for components/**/*.vue + composables/**/*.ts, with useComponentProps.ts exempted (keeps `import type * as ComponentTypes from '../types'`). Adapted, not 1:1: resolved each type->source against b24ui's OWN exports (IconComponent->../types/icons since b24ui has no Icon.vue; own component set; curated 20 locale files vs upstream 62 per §2). 0 unresolved / 0 ambiguous names. Rule appended via existing .append chain (bitrix24-ui/ namespace). Types-only, no snapshot churn. 120 files, +284/-120" } } } diff --git a/eslint.config.mjs b/eslint.config.mjs index ef17e8dc8..000034c63 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -180,4 +180,19 @@ export default createConfigForNuxt({ rules: { 'bitrix24-ui/no-bare-prop-refs': 'error' } +}).append({ + files: ['src/runtime/components/**/*.vue', 'src/runtime/composables/**/*.ts'], + rules: { + 'no-restricted-imports': ['error', { + paths: [ + { name: '../types', message: 'Import cross-component types from their source file (e.g. \'./Button.vue\') or a specific \'../types/*\' module, not the \'../types\' barrel: it re-exports every component, so one import eagerly loads the whole library into a consumer\'s type graph.' }, + { name: '../../types', message: 'Import cross-component types from their source file (e.g. \'./Button.vue\') or a specific \'../../types/*\' module, not the \'../../types\' barrel: it re-exports every component, so one import eagerly loads the whole library into a consumer\'s type graph.' } + ] + }] + } +}, { + files: ['src/runtime/composables/useComponentProps.ts'], + rules: { + 'no-restricted-imports': 'off' + } }) diff --git a/src/runtime/components/Accordion.vue b/src/runtime/components/Accordion.vue index 828f0aa98..23998a28d 100644 --- a/src/runtime/components/Accordion.vue +++ b/src/runtime/components/Accordion.vue @@ -4,7 +4,7 @@ import type { AccordionRootProps, AccordionRootEmits } from 'reka-ui' import type { VNode } from 'vue' import type { AppConfig } from '@nuxt/schema' import theme from '#build/b24ui/accordion' -import type { IconComponent } from '../types' +import type { IconComponent } from '../types/icons' import type { DynamicSlots, GetItemKeys } from '../types/utils' import type { ComponentConfig } from '../types/tv' diff --git a/src/runtime/components/Advice.vue b/src/runtime/components/Advice.vue index 95915b80c..59f411a7f 100644 --- a/src/runtime/components/Advice.vue +++ b/src/runtime/components/Advice.vue @@ -3,7 +3,7 @@ import type { VNode } from 'vue' import type { AppConfig } from '@nuxt/schema' import theme from '#build/b24ui/advice' import type { UseComponentIconsProps } from '../composables/useComponentIcons' -import type { AvatarProps } from '../types' +import type { AvatarProps } from './Avatar.vue' import type { ComponentConfig } from '../types/tv' type Advice = ComponentConfig diff --git a/src/runtime/components/Alert.vue b/src/runtime/components/Alert.vue index 8e98ac64e..901714255 100644 --- a/src/runtime/components/Alert.vue +++ b/src/runtime/components/Alert.vue @@ -2,7 +2,10 @@ import type { VNode } from 'vue' import type { AppConfig } from '@nuxt/schema' import theme from '#build/b24ui/alert' -import type { AvatarProps, ButtonProps, IconComponent, LinkPropsKeys } from '../types' +import type { IconComponent } from '../types/icons' +import type { AvatarProps } from './Avatar.vue' +import type { ButtonProps } from './Button.vue' +import type { LinkPropsKeys } from './Link.vue' import type { ComponentConfig } from '../types/tv' type Alert = ComponentConfig diff --git a/src/runtime/components/App.vue b/src/runtime/components/App.vue index f065eb3b1..516229d0e 100644 --- a/src/runtime/components/App.vue +++ b/src/runtime/components/App.vue @@ -1,7 +1,8 @@