Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/vapor-condition-and-composables.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 5 additions & 1 deletion docs/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>sideEffects</code>-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 <code>vael-ui</code> anstelle von <code>vael-ui/vapor</code> vorschlägt – eine bekannte TypeScript-Einschränkung, wenn ein Paket denselben Namen von zwei Einstiegspunkten exportiert – legen Sie eine <code>vapor</code>-Exportbedingung fest, anstatt aus dem Unterpfad zu importieren. \nJeder <code>Import … von „vael-ui“</code> 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.",
Expand Down
4 changes: 4 additions & 0 deletions docs/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<code>vael-ui/vapor</code>). By default the resolver imports from the VDOM build; pass <code>variant: 'vapor'</code> 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 <code>vael-ui</code> instead of <code>vael-ui/vapor</code> — a known TypeScript limitation when a package exports the same name from two entry points — set a <code>vapor</code> export condition instead of importing from the subpath. Every <code>import … from 'vael-ui'</code> 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 <code>sideEffects</code> entry to also import a component's stylesheet.",
"bundlersTitle": "Bundlers",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<code>vael-ui/vapor</code>). \nDe forma predeterminada, el solucionador importa desde la compilación VDOM; \npase <code>variant: 'vapor'</code> 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 <code>vael-ui</code> en lugar de <code>vael-ui/vapor</code> (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 <code>vapor</code> en lugar de importar desde la ruta secundaria. \nCada <code>importación... desde 'vael-ui'</code> 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 <código>",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<code>vael-ui/vapor</code>). \nPar défaut, le résolveur importe à partir de la version VDOM ; \ntransmettez plutôt <code>variant: 'vapor'</code> 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 <code>vael-ui</code> au lieu de <code>vael-ui/vapor</code> — 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 <code>vapor</code> au lieu d'importer à partir du sous-chemin. \nChaque <code>importation… depuis 'vael-ui'</code> 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 <code>",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<code>vael-ui/vapor</code>). \nPer impostazione predefinita il risolutore importa dalla build VDOM; \npassa invece <code>variant: 'vapor'</code> 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 <code>vael-ui</code> invece di <code>vael-ui/vapor</code> — una limitazione nota di TypeScript quando un pacchetto esporta lo stesso nome da due punti di ingresso — imposta una condizione di esportazione <code>vapor</code> invece di importare dal sottopercorso. \nOgni <code>import … from 'vael-ui'</code> 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 <codice>",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@
"setupTitle": "設定",
"title": "自動インポート",
"vaporIntro": "vael-ui は、同じソースから 2 つのビルド、つまり通常の VDOM ビルドと Vapor ビルド (<code>vael-ui/vapor</code>) を出荷します。\nデフォルトでは、リゾルバーは VDOM ビルドからインポートします。\n代わりに <code>variant: 'vapor'</code> を渡して、Vapor ビルドから解決します。\nアプリが実際にレンダリングするものを選択してください。1 つのアプリ内で 2 つを混合することはサポートされていません。",
"vaporTitle": "蒸気"
"vaporTitle": "蒸気",
"oneImportIntro": "エディターの自動インポートで <code>vael-ui/vapor</code> ではなく <code>vael-ui</code> が推奨され続ける場合 (パッケージが 2 つのエントリ ポイントから同じ名前をエクスポートする場合の TypeScript の既知の制限)、サブパスからインポートする代わりに <code>vapor</code> エクスポート条件を設定します。\nすべての <code>import … from 'vael-ui'</code> は、コンポーネントとコンポーザブル同様にプロジェクト全体の Vapor ビルドに解決され、自動インポートが間違って選択するものは何も残りません。",
"oneImportNote": "Vite 条件は実際に何をロードするかを制御します。 \nTS 条件は、それに一致する型を維持するだけです。\n一方を設定せずに他方を設定すると、エラーではなく、正しい型と間違ったビルドが読み込まれた、またはその逆という、サイレントな不一致が発生します。",
"oneImportNoteTitle": "両方の設定を同期させます",
"oneImportTitle": "1 つの共有インポート、/vapor サブパスなし"
},
"nuxt": {
"excludeIntro": "特定のコンポーネント名をスキップします。独自の <code> を登録する場合に便利です。",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -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 (<code>vael-ui/vapor</code>). \nPor padrão, o resolvedor importa da compilação VDOM; \npasse <code>variant: 'vapor'</code> 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 <code>vael-ui</code> em vez de <code>vael-ui/vapor</code> — 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 <code>vapor</code> em vez de importar do subcaminho. \nCada <code>import … from 'vael-ui'</code> é 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 <código>",
Expand Down
6 changes: 5 additions & 1 deletion docs/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@
"setupTitle": "设置",
"title": "自动导入",
"vaporIntro": "vael-ui 提供了来自同一来源的两个版本:常规 VDOM 版本和 Vapor 版本 (<code>vael-ui/vapor</code>)。\n默认情况下,解析器从 VDOM 构建导入;\n传递 <code>variant: 'vapor'</code> 从 Vapor 构建中解析。\n选择您的应用程序实际渲染的任何一个 - 不支持将两者混合在一个应用程序中。",
"vaporTitle": "汽"
"vaporTitle": "汽",
"oneImportIntro": "如果编辑器的自动导入功能不断建议使用 <code>vael-ui</code> 而不是 <code>vael-ui/vapor</code>(当包从两个入口点导出相同名称时,这是一种已知的 TypeScript 限制),请设置 <code>vapor</code> 导出条件,而不是从子路径导入。\n然后,每次从“vael-ui”导入…都会解析为项目范围内的 Vapor 构建,组件和可组合项都是如此,并且自动导入不会再出现任何错误。",
"oneImportNote": "Vite 条件控制实际加载的内容; \nTS 条件只保留与其匹配的类型。\n设置一个而不设置另一个,你会得到一个无声的不匹配——正确的类型加载了错误的构建,反之亦然——而不是错误。",
"oneImportNoteTitle": "保持两个设置同步",
"oneImportTitle": "一个共享导入,无 /vapor 子路径"
},
"nuxt": {
"excludeIntro": "跳过特定的组件名称 - 如果您想注册自己的 <code>,这很有用",
Expand Down
24 changes: 24 additions & 0 deletions docs/src/pages/guides/AutoImport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ export default {
<p v-html="t('autoImport.vaporIntro')" />
<CodeBlock code="VaelUiResolver({ variant: 'vapor' })" />

<h2 id="one-import">{{ t('autoImport.oneImportTitle') }}</h2>
<p v-html="t('autoImport.oneImportIntro')" />
<CodeBlock
lang="typescript"
code="// vite.config.ts
export default {
resolve: { conditions: ['vapor'] },
}"
/>
<CodeBlock
lang="json"
code='// tsconfig.json
{
"compilerOptions": {
"customConditions": ["vapor"]
}
}'
/>
<Message variant="warning" :title="t('autoImport.oneImportNoteTitle')">
{{ t('autoImport.oneImportNote') }}
</Message>

<h2 id="css">{{ t('autoImport.cssTitle') }}</h2>
<p v-html="t('autoImport.cssIntro')" />

Expand All @@ -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'
Expand All @@ -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') },
])
Expand Down
16 changes: 13 additions & 3 deletions packages/scripts/emit-vapor-types.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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))`,
)
36 changes: 35 additions & 1 deletion packages/scripts/generate-vapor.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))`,
)
}

Expand Down
Loading