EranGrin / vue-web-component-wrapper

vue3 - web component wrapper plugin
https://erangrin.github.io/vue-web-component-wrapper/
50 stars 5 forks source link

Vite Support #1

Closed mrcego closed 1 year ago

mrcego commented 1 year ago

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.

EranGrin commented 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?

mrcego commented 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?

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.

EranGrin commented 1 year ago

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.

mrcego commented 1 year ago

@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.

EranGrin commented 1 year ago

@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'?

mrcego commented 1 year ago

@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)
EranGrin commented 1 year ago

@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 commented 1 year ago

@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 🤜🏽🤛🏽

EranGrin commented 1 year ago

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.

mrcego commented 1 year ago

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.

EranGrin commented 1 year ago

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

mrcego commented 1 year ago

@baiwusanyu-c comes with this gem inspired by this solution. Thanks for your hard work, Bai, it's awesome!

EranGrin commented 1 year ago

vue-web-component-wrapper latest version is now supporting Vite, @mrcego Thank You!