diff --git a/.changeset/vapor-condition-and-composables.md b/.changeset/vapor-condition-and-composables.md new file mode 100644 index 0000000..704cacd --- /dev/null +++ b/.changeset/vapor-condition-and-composables.md @@ -0,0 +1,5 @@ +--- +'vael-ui': patch +--- + +`vael-ui/vapor` now also re-exports composables and utilities, not just components. This also enables a `vapor` export condition on the package root — set it in your bundler and `tsconfig.json` to use `vael-ui` everywhere with no `/vapor` subpath needed. See the [Auto Import guide](https://vael-ui.dev/docs/guides/auto-import#one-import) for setup; see the [0.2.0 release notes](https://github.com/Mini-Sylar/vael-ui/releases/tag/vael-ui%400.2.0) for the original Vapor/resolver docs. diff --git a/docs/src/locales/de.json b/docs/src/locales/de.json index 7c9353f..cf56e33 100644 --- a/docs/src/locales/de.json +++ b/docs/src/locales/de.json @@ -283,7 +283,11 @@ "cssTitle": "CSS", "cssIntro": "Hier ist keine zusätzliche Verkabelung nötig. Jede vael-ui-Komponente importiert bereits intern ihr eigenes CSS, sodass das Auflösen des Komponenten-Imports auch deren Styles kostenlos mitbringt — anders als bei den meisten unplugin-vue-components-Resolvern, die einen separaten sideEffects-Eintrag benötigen, um auch das Stylesheet einer Komponente zu importieren.", "bundlersTitle": "Bundler", - "bundlersIntro": "unplugin-vue-components unterstützt Vite, Webpack, Rollup, esbuild und Rspack. Der Resolver selbst ist nur eine einfache Funktion — er funktioniert unabhängig davon, welchen Sie verwenden, identisch." + "bundlersIntro": "unplugin-vue-components unterstützt Vite, Webpack, Rollup, esbuild und Rspack. Der Resolver selbst ist nur eine einfache Funktion — er funktioniert unabhängig davon, welchen Sie verwenden, identisch.", + "oneImportIntro": "Wenn der automatische Import Ihres Editors weiterhin vael-ui anstelle von vael-ui/vapor vorschlägt – eine bekannte TypeScript-Einschränkung, wenn ein Paket denselben Namen von zwei Einstiegspunkten exportiert – legen Sie eine vapor-Exportbedingung fest, anstatt aus dem Unterpfad zu importieren. \nJeder Import … von „vael-ui“ wird dann projektweit in den Vapor-Build aufgelöst, sowohl für Komponenten als auch für Composables, und es gibt nichts mehr, was der automatische Import falsch auswählen könnte.", + "oneImportNote": "Die Vite-Bedingung steuert, was tatsächlich geladen wird. \nDie TS-Bedingung behält einfach die Typen bei, die ihr entsprechen. \nWenn Sie das eine ohne das andere festlegen, erhalten Sie eine stille Nichtübereinstimmung – richtige Typen mit dem falschen geladenen Build oder umgekehrt – und keinen Fehler.", + "oneImportNoteTitle": "Halten Sie beide Einstellungen synchron", + "oneImportTitle": "Ein gemeinsamer Import, kein /vapor-Unterpfad" }, "notFound": { "body": "Die gesuchte Seite existiert nicht oder wurde verschoben.", diff --git a/docs/src/locales/en.json b/docs/src/locales/en.json index d2457d4..144467a 100644 --- a/docs/src/locales/en.json +++ b/docs/src/locales/en.json @@ -291,6 +291,10 @@ "setupOutro": "That's it — write the tag, the import gets inserted at build time:", "vaporTitle": "Vapor", "vaporIntro": "vael-ui ships two builds from the same source: the regular VDOM build and a Vapor build (vael-ui/vapor). By default the resolver imports from the VDOM build; pass variant: 'vapor' to resolve from the Vapor build instead. Pick whichever your app actually renders with — mixing the two in one app isn't supported.", + "oneImportTitle": "One shared import, no /vapor subpath", + "oneImportIntro": "If your editor's auto-import keeps suggesting vael-ui instead of vael-ui/vapor — a known TypeScript limitation when a package exports the same name from two entry points — set a vapor export condition instead of importing from the subpath. Every import … from 'vael-ui' then resolves to the Vapor build project-wide, components and composables alike, and there's nothing left for auto-import to pick wrong.", + "oneImportNoteTitle": "Keep both settings in sync", + "oneImportNote": "The Vite condition controls what actually loads; the TS condition just keeps types matching it. Set one without the other and you'll get a silent mismatch — correct types with the wrong build loaded, or vice versa — not an error.", "cssTitle": "CSS", "cssIntro": "No extra wiring needed here. Every vael-ui component already imports its own CSS internally, so resolving the component import pulls its styles in for free — unlike most unplugin-vue-components resolvers, which need a separate sideEffects entry to also import a component's stylesheet.", "bundlersTitle": "Bundlers", diff --git a/docs/src/locales/es.json b/docs/src/locales/es.json index 222b907..2edcea4 100644 --- a/docs/src/locales/es.json +++ b/docs/src/locales/es.json @@ -312,7 +312,11 @@ "setupTitle": "Configuración", "title": "Importación automática", "vaporIntro": "vael-ui envía dos compilaciones de la misma fuente: la compilación VDOM normal y una compilación Vapor (vael-ui/vapor). \nDe forma predeterminada, el solucionador importa desde la compilación VDOM; \npase variant: 'vapor' para resolverlo desde la compilación de Vapor. \nElija la opción con la que realmente se procesa su aplicación; no se admite mezclar las dos en una sola aplicación.", - "vaporTitle": "Vapor" + "vaporTitle": "Vapor", + "oneImportIntro": "Si la importación automática de su editor sigue sugiriendo vael-ui en lugar de vael-ui/vapor (una limitación conocida de TypeScript cuando un paquete exporta el mismo nombre desde dos puntos de entrada), establezca una condición de exportación vapor en lugar de importar desde la ruta secundaria. \nCada importación... desde 'vael-ui' luego se resuelve en todo el proyecto de compilación de Vapor, componentes y elementos componibles por igual, y no queda nada para que la importación automática seleccione incorrectamente.", + "oneImportNote": "La condición Vite controla lo que realmente se carga; \nla condición TS simplemente mantiene los tipos que la coinciden. \nConfigure uno sin el otro y obtendrá una discrepancia silenciosa (tipos correctos con la compilación incorrecta cargada, o viceversa), no un error.", + "oneImportNoteTitle": "Mantenga ambas configuraciones sincronizadas", + "oneImportTitle": "Una importación compartida, sin subruta /vapor" }, "nuxt": { "excludeIntro": "Omita nombres de componentes específicos: útil si desea registrar su propio ", diff --git a/docs/src/locales/fr.json b/docs/src/locales/fr.json index 46e0cba..85c05e1 100644 --- a/docs/src/locales/fr.json +++ b/docs/src/locales/fr.json @@ -312,7 +312,11 @@ "setupTitle": "Installation", "title": "Importation automatique", "vaporIntro": "vael-ui fournit deux builds à partir de la même source : la build VDOM standard et une build Vapor (vael-ui/vapor). \nPar défaut, le résolveur importe à partir de la version VDOM ; \ntransmettez plutôt variant: 'vapor' pour résoudre à partir de la version Vapor. \nChoisissez celui avec lequel votre application s'affiche réellement : le mélange des deux dans une seule application n'est pas pris en charge.", - "vaporTitle": "Vapeur" + "vaporTitle": "Vapeur", + "oneImportIntro": "Si l'importation automatique de votre éditeur continue de suggérer vael-ui au lieu de vael-ui/vapor — une limitation TypeScript connue lorsqu'un package exporte le même nom à partir de deux points d'entrée — définissez une condition d'exportation vapor au lieu d'importer à partir du sous-chemin. \nChaque importation… depuis 'vael-ui' est ensuite résolue dans l'ensemble du projet de construction Vapor, composants et composables, et il ne reste plus rien pour que l'importation automatique puisse se tromper.", + "oneImportNote": "La condition Vite contrôle ce qui se charge réellement ; \nla condition TS conserve simplement les types qui lui correspondent. \nDéfinissez l’un sans l’autre et vous obtiendrez une incompatibilité silencieuse – types corrects avec la mauvaise version chargée, ou vice versa – et non une erreur.", + "oneImportNoteTitle": "Gardez les deux paramètres synchronisés", + "oneImportTitle": "Une importation partagée, pas de sous-chemin /vapor" }, "nuxt": { "excludeIntro": "Ignorer les noms de composants spécifiques - utile si vous souhaitez enregistrer votre propre ", diff --git a/docs/src/locales/it.json b/docs/src/locales/it.json index 21896c0..419e3be 100644 --- a/docs/src/locales/it.json +++ b/docs/src/locales/it.json @@ -312,7 +312,11 @@ "setupTitle": "Impostare", "title": "Importazione automatica", "vaporIntro": "vael-ui fornisce due build dalla stessa fonte: la build VDOM normale e una build Vapor (vael-ui/vapor). \nPer impostazione predefinita il risolutore importa dalla build VDOM; \npassa invece variant: 'vapor' per risolverlo dalla build Vapor. \nScegli quello con cui viene effettivamente eseguito il rendering della tua app: la combinazione delle due app in una non è supportata.", - "vaporTitle": "Vapore" + "vaporTitle": "Vapore", + "oneImportIntro": "Se l'importazione automatica del tuo editor continua a suggerire vael-ui invece di vael-ui/vapor — una limitazione nota di TypeScript quando un pacchetto esporta lo stesso nome da due punti di ingresso — imposta una condizione di esportazione vapor invece di importare dal sottopercorso. \nOgni import … from 'vael-ui' si risolve quindi nella build Vapor a livello di progetto, componenti e componenti componibili allo stesso modo, e non c'è più nulla che l'importazione automatica possa scegliere in modo sbagliato.", + "oneImportNote": "La condizione Vite controlla cosa viene effettivamente caricato; \nla condizione TS mantiene semplicemente i tipi corrispondenti. \nImpostane uno senza l'altro e otterrai una mancata corrispondenza silenziosa (tipi corretti con la build sbagliata caricata o viceversa) non un errore.", + "oneImportNoteTitle": "Mantieni entrambe le impostazioni sincronizzate", + "oneImportTitle": "Un'importazione condivisa, nessun sottopercorso /vapor" }, "nuxt": { "excludeIntro": "Salta nomi di componenti specifici: utile se desideri registrare il tuo ", diff --git a/docs/src/locales/ja.json b/docs/src/locales/ja.json index e90fffb..0bef4f1 100644 --- a/docs/src/locales/ja.json +++ b/docs/src/locales/ja.json @@ -312,7 +312,11 @@ "setupTitle": "設定", "title": "自動インポート", "vaporIntro": "vael-ui は、同じソースから 2 つのビルド、つまり通常の VDOM ビルドと Vapor ビルド (vael-ui/vapor) を出荷します。\nデフォルトでは、リゾルバーは VDOM ビルドからインポートします。\n代わりに variant: 'vapor' を渡して、Vapor ビルドから解決します。\nアプリが実際にレンダリングするものを選択してください。1 つのアプリ内で 2 つを混合することはサポートされていません。", - "vaporTitle": "蒸気" + "vaporTitle": "蒸気", + "oneImportIntro": "エディターの自動インポートで vael-ui/vapor ではなく vael-ui が推奨され続ける場合 (パッケージが 2 つのエントリ ポイントから同じ名前をエクスポートする場合の TypeScript の既知の制限)、サブパスからインポートする代わりに vapor エクスポート条件を設定します。\nすべての import … from 'vael-ui' は、コンポーネントとコンポーザブル同様にプロジェクト全体の Vapor ビルドに解決され、自動インポートが間違って選択するものは何も残りません。", + "oneImportNote": "Vite 条件は実際に何をロードするかを制御します。 \nTS 条件は、それに一致する型を維持するだけです。\n一方を設定せずに他方を設定すると、エラーではなく、正しい型と間違ったビルドが読み込まれた、またはその逆という、サイレントな不一致が発生します。", + "oneImportNoteTitle": "両方の設定を同期させます", + "oneImportTitle": "1 つの共有インポート、/vapor サブパスなし" }, "nuxt": { "excludeIntro": "特定のコンポーネント名をスキップします。独自の を登録する場合に便利です。", diff --git a/docs/src/locales/pt.json b/docs/src/locales/pt.json index b686cc3..53b6182 100644 --- a/docs/src/locales/pt.json +++ b/docs/src/locales/pt.json @@ -312,7 +312,11 @@ "setupTitle": "Configurar", "title": "Importação automática", "vaporIntro": "vael-ui envia duas compilações da mesma fonte: a compilação VDOM regular e uma compilação Vapor (vael-ui/vapor). \nPor padrão, o resolvedor importa da compilação VDOM; \npasse variant: 'vapor' para resolver a partir da compilação do Vapor. \nEscolha o que seu aplicativo realmente renderiza - não há suporte para misturar os dois em um aplicativo.", - "vaporTitle": "Vapor" + "vaporTitle": "Vapor", + "oneImportIntro": "Se a importação automática do seu editor continuar sugerindo vael-ui em vez de vael-ui/vapor — uma limitação conhecida do TypeScript quando um pacote exporta o mesmo nome de dois pontos de entrada — defina uma condição de exportação vapor em vez de importar do subcaminho. \nCada import … from 'vael-ui' é então resolvida para a compilação do Vapor em todo o projeto, componentes e elementos que podem ser compostos, e não há mais nada para a importação automática escolher errado.", + "oneImportNote": "A condição Vite controla o que realmente carrega; \na condição TS apenas mantém os tipos correspondentes. \nDefina um sem o outro e você obterá uma incompatibilidade silenciosa - tipos corretos com a compilação errada carregada ou vice-versa - e não um erro.", + "oneImportNoteTitle": "Mantenha as duas configurações sincronizadas", + "oneImportTitle": "Uma importação compartilhada, sem subcaminho /vapor" }, "nuxt": { "excludeIntro": "Ignore nomes de componentes específicos — útil se você deseja registrar seu próprio ", diff --git a/docs/src/locales/zh.json b/docs/src/locales/zh.json index 6c5deb8..e964b02 100644 --- a/docs/src/locales/zh.json +++ b/docs/src/locales/zh.json @@ -312,7 +312,11 @@ "setupTitle": "设置", "title": "自动导入", "vaporIntro": "vael-ui 提供了来自同一来源的两个版本:常规 VDOM 版本和 Vapor 版本 (vael-ui/vapor)。\n默认情况下,解析器从 VDOM 构建导入;\n传递 variant: 'vapor' 从 Vapor 构建中解析。\n选择您的应用程序实际渲染的任何一个 - 不支持将两者混合在一个应用程序中。", - "vaporTitle": "汽" + "vaporTitle": "汽", + "oneImportIntro": "如果编辑器的自动导入功能不断建议使用 vael-ui 而不是 vael-ui/vapor(当包从两个入口点导出相同名称时,这是一种已知的 TypeScript 限制),请设置 vapor 导出条件,而不是从子路径导入。\n然后,每次从“vael-ui”导入…都会解析为项目范围内的 Vapor 构建,组件和可组合项都是如此,并且自动导入不会再出现任何错误。", + "oneImportNote": "Vite 条件控制实际加载的内容; \nTS 条件只保留与其匹配的类型。\n设置一个而不设置另一个,你会得到一个无声的不匹配——正确的类型加载了错误的构建,反之亦然——而不是错误。", + "oneImportNoteTitle": "保持两个设置同步", + "oneImportTitle": "一个共享导入,无 /vapor 子路径" }, "nuxt": { "excludeIntro": "跳过特定的组件名称 - 如果您想注册自己的 ,这很有用", diff --git a/docs/src/pages/guides/AutoImport.vue b/docs/src/pages/guides/AutoImport.vue index 64040ee..09633f7 100644 --- a/docs/src/pages/guides/AutoImport.vue +++ b/docs/src/pages/guides/AutoImport.vue @@ -34,6 +34,28 @@ export default {

+

{{ t('autoImport.oneImportTitle') }}

+

+ + + + {{ t('autoImport.oneImportNote') }} + +

{{ t('autoImport.cssTitle') }}

@@ -46,6 +68,7 @@ export default { import { computed } from 'vue' import { useHead } from '@unhead/vue' import { useI18n } from 'vue-i18n' +import { Message } from 'vael-ui' import CodeBlock from '../../components/CodeBlock.vue' import GuideLayout from '../../components/GuideLayout.vue' import { useBreadcrumbSchema } from '../../composables/useBreadcrumbSchema' @@ -61,6 +84,7 @@ useBreadcrumbSchema(() => [ const tocLinks = computed(() => [ { id: 'setup', label: t('autoImport.setupTitle') }, { id: 'vapor', label: t('autoImport.vaporTitle') }, + { id: 'one-import', label: t('autoImport.oneImportTitle') }, { id: 'css', label: t('autoImport.cssTitle') }, { id: 'bundlers', label: t('autoImport.bundlersTitle') }, ]) diff --git a/packages/scripts/emit-vapor-types.mjs b/packages/scripts/emit-vapor-types.mjs index c04c64d..9d2f774 100644 --- a/packages/scripts/emit-vapor-types.mjs +++ b/packages/scripts/emit-vapor-types.mjs @@ -31,10 +31,20 @@ const outPath = join(__dirname, '../ui/dist/vapor/index.d.ts') const lines = [ `export { ${componentNames.join(', ')}, vTooltipVapor as vTooltip, vScrollMaskVapor as vScrollMask } from '../index'`, ] -const directiveTypeNames = ['TooltipDirectiveOptions', 'TooltipDirectiveValue'] -const allTypeNames = [...typeNames, ...directiveTypeNames] +const allTypeNames = typeNames if (allTypeNames.length > 0) lines.push(`export type { ${allTypeNames.join(', ')} } from '../index'`) + +// Reuse the composable re-export lines generate-vapor.mjs already computed, pointed at ../index instead. +const composableValueLine = barrel.match(/^export \{ (?!default as|vTooltipVapor)[^}]+\} from 'vael-ui'$/m) +const composableTypeLine = barrel.match(/^export type \{[^}]+\} from 'vael-ui'$/m) +let composableCount = 0 +if (composableValueLine) { + lines.push(composableValueLine[0].replace(/from 'vael-ui'$/, `from '../index'`)) + composableCount = composableValueLine[0].split(',').length +} +if (composableTypeLine) lines.push(composableTypeLine[0].replace(/from 'vael-ui'$/, `from '../index'`)) + writeFileSync(outPath, lines.join('\n') + '\n') console.log( - `generated dist/vapor/index.d.ts (${componentNames.length} component(s), ${allTypeNames.length} type(s))`, + `generated dist/vapor/index.d.ts (${componentNames.length} component(s), ${allTypeNames.length} type(s), ${composableCount} composable(s)/utilit(y/ies))`, ) diff --git a/packages/scripts/generate-vapor.mjs b/packages/scripts/generate-vapor.mjs index a8e695c..95db8f1 100644 --- a/packages/scripts/generate-vapor.mjs +++ b/packages/scripts/generate-vapor.mjs @@ -49,6 +49,31 @@ function collectPublicExports(indexSource) { return names } +// Excluded: vdom-unsafe originals already replaced by the aliased vTooltipVapor/vScrollMaskVapor line below. +const DIRECTIVE_ORIGINALS_EXCLUDED = new Set(['vTooltip', 'vScrollMask']) + +function collectNonComponentExports(indexSource) { + const values = new Set() + const types = new Set() + const re = /export\s+(type\s+)?\{([^}]+)\}\s+from\s+'([^']+)'/g + let match + while ((match = re.exec(indexSource))) { + const [, typeOnly, namedClause, specifier] = match + if (specifier.endsWith('.vue')) continue // component export, handled separately + for (const raw of namedClause.split(',')) { + const trimmed = raw.trim() + if (!trimmed) continue + const isTypeMember = typeOnly || trimmed.startsWith('type ') + const name = trimmed.replace(/^type\s+/, '') + const asMatch = name.match(/\bas\s+(\S+)/) + const exported = asMatch ? asMatch[1] : name + if (DIRECTIVE_ORIGINALS_EXCLUDED.has(exported)) continue + ;(isTypeMember ? types : values).add(exported) + } + } + return { values: [...values], types: [...types] } +} + // Finds sibling .vue imports (not composables or third-party). `fromId` is // the importing module's own id (e.g. 'Message/Message' or 'Select') so // './' and '../' specifiers resolve against its real directory — a @@ -186,9 +211,18 @@ function main() { ] }) barrelLines.push(`export { vTooltipVapor as vTooltip, vScrollMaskVapor as vScrollMask } from 'vael-ui'`) + + const { values: composableValues, types: composableTypes } = collectNonComponentExports(indexSource) + if (composableValues.length > 0) { + barrelLines.push(`export { ${composableValues.join(', ')} } from 'vael-ui'`) + } + if (composableTypes.length > 0) { + barrelLines.push(`export type { ${composableTypes.join(', ')} } from 'vael-ui'`) + } + writeFileSync(join(OUT_DIR, 'index.ts'), barrelLines.join('\n') + '\n') console.log( - `\ngenerated src/generated/index.ts (${COMPONENTS.length} public component(s), ${toGenerate.size} file(s) total)`, + `\ngenerated src/generated/index.ts (${COMPONENTS.length} public component(s), ${toGenerate.size} file(s) total, ${composableValues.length} composable(s)/utilit(y/ies))`, ) } diff --git a/packages/ui/package.json b/packages/ui/package.json index 1229a2f..f7c78e3 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -30,7 +30,14 @@ "**/*.css" ], "exports": { - ".": "./dist/index.js", + ".": { + "vapor": { + "types": "./dist/vapor/index.d.ts", + "default": "./dist/vapor/index.js" + }, + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, "./vapor": { "types": "./dist/vapor/index.d.ts", "default": "./dist/vapor/index.js"