Closed mrcego closed 1 year ago
Hi @mrcego, as far as I remember, the only missing part I could not find a solution for in Vite was the extra style support for Tailwind CSS and SCSS, I was unable to find an equivalent Vite solution.
I'm not so familiar with Vite, but my impression was that Vite plugins ecosystem doesn't have the needed solution, with that being said, it would mean that one might need to develop a new Vite plugin.
Can you share your Vite configuration?
Hi @mrcego, as far as I remember, the only missing part I could not find a solution for in Vite was the extra style support for Tailwind CSS and SCSS, I was unable to find an equivalent Vite solution.
I'm not so familiar with Vite, but my impression was that Vite plugins ecosystem doesn't have the needed solution, with that being said, it would mean that one might need to develop a new Vite plugin.
Can you share your Vite configuration?
I don't know if what I will say help in some manner, but using Tailwind with Vite was never a problem for CE. In fact, we are working with Tailwind to develop new CEs as microfrontends.
What do you mean when you say "the extra style support for Tailwind CSS and SCSS", could you elaborate for better understanding? Thx in advance.
As you probably know, one of the big advantages of CE is the isolated shadow DOM. The plugin can dynamically add the style tag of each component into the shadow DOM of the CE, and with webpack "extra style support for Tailwind CSS and SCSS" we can inject the needed tailwind CSS in to the root component, which makes it available to all child components. Because it is all inside the shadow DOM, there are no CSS leaks to microfront host.
Here is what I mean with "extra style support"
{
test: /\.(css|scss)$/,
oneOf: [
{
resourceQuery: /raw/,
use: [
'to-string-loader',
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {
sassOptions: {
indentedSyntax: false, // Use the SCSS syntax
},
},
},
],
},
{
use: [
'style-loader',
'css-loader',
'postcss-loader',
{
loader: 'sass-loader',
options: {
sassOptions: {
indentedSyntax: false, // Use the SCSS syntax
},
},
},
],
},
],
},
Does it make sense? What are your use case requirements?
If you have any equivalent solution that could work with Vite that would be awesome.
@EranGrin
We are working with microfrontends using a monorepo with pnpm workspaces. It contains two folders: package
and playground
. package
includes whole core app in regular Vue components, and App.vue is only the CE "container" (App.ce.vue); here, Vite is set up in library mode. playground
is like client side project, but it works for build CE element into an unique JS file with index.html
entry
vite.config.ts from package
import { fileURLToPath, URL } from 'node:url'
import { resolve } from 'path'
import type { UserConfig } from 'vite'
import Vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import dts from 'vite-plugin-dts'
import pkg from './package.json'
process.env.VITE_APP_VERSION = pkg.version
if (process.env.NODE_ENV === 'production') process.env.VITE_APP_BUILD_EPOCH = new Date().getTime().toString()
// https://vitejs.dev/config/
const config: UserConfig = {
resolve: {
alias: {
'~': fileURLToPath(new URL('./src', import.meta.url)),
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
build: {
lib: {
entry: resolve(__dirname, './src/index.ts'),
name: 'vue-colegium-comunicaciones-ce',
fileName: 'index',
formats: ['es'],
},
rollupOptions: {
// Externalize deps that shouldn't be bundled into the library.
input: resolve(__dirname, './src/index.ts'),
output: { globals: { vue: 'Vue' }, exports: 'named', format: 'es' },
},
sourcemap: true,
target: 'esnext',
minify: 'esbuild',
},
plugins: [
dts({
insertTypesEntry: true,
}),
Vue(),
// https://github.com/antfu/unplugin-auto-import
AutoImport({
imports: [
'vue',
'@vueuse/core',
{
'@vueuse/integrations/useAxios': ['useAxios'],
axios: [
['default', 'axios'], // import { default as axios } from 'axios',
],
},
'vue/macros',
],
dts: './auto-imports.d.ts',
dirs: ['src/composables', 'src/plugins', 'src/utils'],
eslintrc: {
enabled: true,
globalsPropValue: true,
},
vueTemplate: true,
}),
// https://github.com/antfu/unplugin-vue-components
Components({
dirs: ['src/components', 'src/views'],
extensions: ['vue'],
dts: './components.d.ts',
}),
],
}
export default config
vite.config.ts config from playground
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
define: {
__VUE_OPTIONS_API__: 'false',
__VUE_PROD_DEVTOOLS__: 'false',
},
build: {
rollupOptions: {
input: ['index.html'],
output: {
entryFileNames: `assets/index.js`,
chunkFileNames: `assets/index.js`,
assetFileNames: `assets/index.css`,
},
},
sourcemap: true,
},
server: {
port: 5174,
},
plugins: [
vue({
template: {
compilerOptions: {
isCustomElement: (tagName) => tagName.includes('-'),
},
},
}),
],
})
These configs work flawless, compilation respects shadow DOM when it is embbeded and it doesn't leak CSS outside. Are you agree if trying your workaround with these configs? Thx in advance.
@mrcego so if understand you correctly, you have a vite configuration that gives you support for CE without the "vue-web-component-wrapper" plugin ? Does your solution support vue plugins such as 'vue-router' and 'vuex'?
@EranGrin Sure. The tricky here is the style mapping, it works but need some improvements because sometimes styles overlap another ones when using tailwind. You could adapt to your workaround and test.
export const defineCustomElementWrapped = (component, { plugins = [] } = {}) =>
defineCustomElement({
styles: component.styles.map((style) => {
return style.replace(':root', ':host').replace('body {', '#app {')
}),
props: component.props,
setup(props) {
const app = createApp({})
// install plugins
plugins.forEach(app.use)
app.use(VueTippy, TippyConfig)
const inst = getCurrentInstance() as ComponentInternalInstance & AppContext
Object.assign(inst!.appContext, app._context)
Object.assign(inst!.provides, app._context.provides)
return () =>
h(component, {
...props,
})
},
})
And main.ts:
import VueCEComponent from './views/VueCEComponent.ce.vue'
export const CE = defineCustomElementWrapped(VueCEComponent, {
plugins: [router, pinia],
})
customElements.define(tagname, CE)
@mrcego thank you for your input, I started to work on POC for vite app support with TS, It would probably be ready in a few days
@mrcego thank you for your input, I started to work on POC for vite app support with TS, It would probably be ready in a few days
Nice! I'm in support mode if you need test or some code collab 🤜🏽🤛🏽
Nice! I'm in support mode if you need test or some code collab 🤜🏽🤛🏽
If you have time for testing, you can do so with this branch. https://github.com/EranGrin/vue-web-component-wrapper/tree/feature/vite-support I added a simple demo vite implementation example, would be nice to see how can it work with your pnpm design. I also added some types to support TS, but they're pretty basic.
Could you check #3, please? I'm playing and testing, it works well, my intention is convert vite demo in a scaffold and check if package needs edge cases improvements.
Nice... sure, I can do some checks in the coming week, thanks for the PR. one edge case I found is that the root component must have a wrapper element in the template same as in vue2
<template><div></div></template>
so.... it might be interesting to check anything that can break the component style tag injection
@baiwusanyu-c comes with this gem inspired by this solution. Thanks for your hard work, Bai, it's awesome!
vue-web-component-wrapper latest version is now supporting Vite, @mrcego Thank You!
Hey @EranGrin! Thanks for your efforts trying to workaround missing completed support for CE.
Question about Vite support: What is missing? I have an almost exactly configuration of a custom defineCustomElement composable and it works decently. I could to help testing it.
Thanks in advance.