How to properly configure Module Federation in Quasar.js app

Viewed 51

My overall plan is to make a suite of Quasar(js) SPA's that have the ability to share components, stores(pinia) and routers between them via Webpacks Module Federation. I'm just starting out and I'm having issues sharing components. I have two basic Quasar SPAs using the quasar cli (Home and SPA1), both using Webpack and Pinia(new VueX). I can share a basic Vue component with no issues, however If I try to use a Quasar.js component inside of my component it fails.

For Example, below is my component located in the Home Spa called HomeButton. And it works, with no errors or warnings.

    <template>
        <button class="btn bg-blue" @click="basicButton"> Basic Button </button>
    </template>
    <script>
    export default {
    name: 'HomeButton',
    methods: {
        basicButton() {
            console.log("basic button");
        }
    }
    }
    </script>
However,  when I modify that component to use Quasars button component (like below). Then it fails to render on the page, and the console has two warning - 

 1. runtime-core.esm-bundler.js?f781:38 [Vue warn]: resolveComponent can only be used in render() or setup().
 2. runtime-core.esm-bundler.js?f781:38 [Vue warn]: Invalid VNode type: Symbol(Text) (symbol) 
  at <HomeButton> 
  at <QPage class="flex flex-center" > 
  at <IndexPage onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouterView> 
  at <QPageContainer> 
  at <QLayout view="lHh Lpr lFf" > 
  at <MainLayout onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > 
  at <RouterView> 
  at <App>.

Modified to use q-btn

    <template>
        <q-btn class="btn bg-blue" @click="basicButton"> Basic Button </q-btn>
    </template>
    <script>
    export default {
    name: 'HomeButton',
    methods: {
        basicButton() {
            console.log("basic button");
        }
    }
    }
    </script>

I can't seem to find much about those warnings, but I understand they are common when two versions of Vue are running simultaneously, but I don't think that is the case here because when I'm not using the quasar library it works fine. I'm sure it has to do with how I'm sharing my dependencies between the project, but I'm not sure what with lack of feedback provided. Below are my quasar configs for each SPA.

*** Please note that the configs below are boilerplate besides me adding the ModuleFederationPlugin to the chainWebpack method and setting the splitChunks options to false inside the build property - ctx.optimization.splitChunks = false;

Home Spa quasar.config

    /* eslint-env node */
    
    /*
     * This file runs in a Node context (it's NOT transpiled by Babel), so use only
     * the ES6 features that are supported by your Node version. https://node.green/
     */
    
    // Configuration for your app
    // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js
    
    
    const ESLintPlugin = require('eslint-webpack-plugin')
    
    
    const { configure } = require('quasar/wrappers');
    const ModuleFederationPlugin = require("webpack").container.ModuleFederationPlugin;
    
    module.exports = configure(function (ctx) {
      return {
        // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts
        supportTS: false,
    
        // https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature
        // preFetch: true,
    
        // app boot file (/src/boot)
        // --> boot files are part of "main.js"
        // https://v2.quasar.dev/quasar-cli-webpack/boot-files
        boot: [
          
          
        ],
    
        // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
        css: [
          'app.scss'
        ],
    
        // https://github.com/quasarframework/quasar/tree/dev/extras
        extras: [
          // 'ionicons-v4',
          // 'mdi-v5',
          // 'fontawesome-v6',
          // 'eva-icons',
          // 'themify',
          // 'line-awesome',
          // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
    
          'roboto-font', // optional, you are not bound to it
          'material-icons', // optional, you are not bound to it
        ],
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-build
        build: {
          vueRouterMode: 'hash', // available values: 'hash', 'history'
    
          // transpile: false,
          // publicPath: '/',
    
          // Add dependencies for transpiling with Babel (Array of string/regex)
          // (from node_modules, which are by default not transpiled).
          // Applies only if "transpile" is set to true.
          // transpileDependencies: [],
    
          // rtl: true, // https://quasar.dev/options/rtl-support
          // preloadChunks: true,
          // showProgress: false,
          // gzip: true,
          // analyze: true,
    
          // Options below are automatically set depending on the env, set them if you want to override
          // extractCSS: false,
    
          // https://v2.quasar.dev/quasar-cli-webpack/handling-webpack
          // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
          
          chainWebpack (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js', 'vue' ] }])
              .use(ModuleFederationPlugin, [{
                name: 'home',
                filename: "remoteEntry.js",
                exposes: {
                  "./HomeHeader" : "./src/components/HomeHeader.vue",
                  "./HomeButton" : "./src/components/HomeButton.vue"
                },
                shared: {
                  "quasar": {
                    eager: true,
                    singleton: true,
                  } ,
                  "vue": {
                    eager: true,
                    singleton: true,
                  } ,
                  "@quasar/extras": {
                    eager: true,
                    singleton: true,
                  },
                  "core-js": {
                    eager: true,
                    singleton: true,
                  },
                  "pinia": {
                    eager: true,
                    singleton: true,
                  },
                  "vue-router": {
                    eager: true,
                    singleton: true,
                  },
                },
              }])
          },
          extendWebpack(ctx){
            ctx.optimization.splitChunks = false;
          }
          
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-devServer
        devServer: {
          server: {
            type: 'http'
          },
          port: 8080,
          open: true // opens browser window automatically
        },
    
        // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
        framework: {
          config: {},
    
          // iconSet: 'material-icons', // Quasar icon set
          // lang: 'en-US', // Quasar language pack
    
          // For special cases outside of where the auto-import strategy can have an impact
          // (like functional components as one of the examples),
          // you can manually specify Quasar components/directives to be available everywhere:
          //
          // components: [],
          // directives: [],
    
          // Quasar plugins
          plugins: []
        },
    
        // animations: 'all', // --- includes all animations
        // https://quasar.dev/options/animations
        animations: [],
    
        // https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/configuring-ssr
        ssr: {
          pwa: false,
    
          // manualStoreHydration: true,
          // manualPostHydrationTrigger: true,
    
          prodPort: 3000, // The default port that the production server should use
                          // (gets superseded if process.env.PORT is specified at runtime)
    
          maxAge: 1000 * 60 * 60 * 24 * 30,
            // Tell browser when a file from the server should expire from cache (in ms)
    
          
          chainWebpackWebserver (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          middlewares: [
            ctx.prod ? 'compression' : '',
            'render' // keep this as last one
          ]
        },
    
        // https://v2.quasar.dev/quasar-cli-webpack/developing-pwa/configuring-pwa
        pwa: {
          workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
          workboxOptions: {}, // only for GenerateSW
    
          // for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
          // if using workbox in InjectManifest mode
          
          chainWebpackCustomSW (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          manifest: {
            name: `Quasar App`,
            short_name: `Quasar App`,
            description: `A Quasar Project`,
            display: 'standalone',
            orientation: 'portrait',
            background_color: '#ffffff',
            theme_color: '#027be3',
            icons: [
              {
                src: 'icons/icon-128x128.png',
                sizes: '128x128',
                type: 'image/png'
              },
              {
                src: 'icons/icon-192x192.png',
                sizes: '192x192',
                type: 'image/png'
              },
              {
                src: 'icons/icon-256x256.png',
                sizes: '256x256',
                type: 'image/png'
              },
              {
                src: 'icons/icon-384x384.png',
                sizes: '384x384',
                type: 'image/png'
              },
              {
                src: 'icons/icon-512x512.png',
                sizes: '512x512',
                type: 'image/png'
              }
            ]
          }
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-cordova-apps/configuring-cordova
        cordova: {
          // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-capacitor-apps/configuring-capacitor
        capacitor: {
          hideSplashscreen: true
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-electron-apps/configuring-electron
        electron: {
          bundler: 'packager', // 'packager' or 'builder'
    
          packager: {
            // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
    
            // OS X / Mac App Store
            // appBundleId: '',
            // appCategoryType: '',
            // osxSign: '',
            // protocol: 'myapp://path',
    
            // Windows only
            // win32metadata: { ... }
          },
    
          builder: {
            // https://www.electron.build/configuration/configuration
    
            appId: 'quasar-home'
          },
    
          // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
          
          chainWebpackMain (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          
          chainWebpackPreload (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
        }
      }
    });

and here is SPA-1 config


    /* eslint-env node */
    
    /*
     * This file runs in a Node context (it's NOT transpiled by Babel), so use only
     * the ES6 features that are supported by your Node version. https://node.green/
     */
    
    // Configuration for your app
    // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js
    
    
    const ESLintPlugin = require('eslint-webpack-plugin')
    const { ModuleFederationPlugin } = require('webpack').container;
    
    
    
    
    const { configure } = require('quasar/wrappers');
    
    module.exports = configure(function (ctx) {
      return {
        // https://v2.quasar.dev/quasar-cli-webpack/supporting-ts
        supportTS: false,
    
        // https://v2.quasar.dev/quasar-cli-webpack/prefetch-feature
        // preFetch: true,
    
        // app boot file (/src/boot)
        // --> boot files are part of "main.js"
        // https://v2.quasar.dev/quasar-cli-webpack/boot-files
        boot: [
          
          
        ],
    
        // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-css
        css: [
          'app.scss'
        ],
    
        // https://github.com/quasarframework/quasar/tree/dev/extras
        extras: [
          // 'ionicons-v4',
          // 'mdi-v5',
          // 'fontawesome-v6',
          // 'eva-icons',
          // 'themify',
          // 'line-awesome',
          // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
    
          'roboto-font', // optional, you are not bound to it
          'material-icons', // optional, you are not bound to it
        ],
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-build
        build: {
          vueRouterMode: 'hash', // available values: 'hash', 'history'
    
          // transpile: false,
          // publicPath: '/',
    
          // Add dependencies for transpiling with Babel (Array of string/regex)
          // (from node_modules, which are by default not transpiled).
          // Applies only if "transpile" is set to true.
          // transpileDependencies: [],
    
          // rtl: true, // https://quasar.dev/options/rtl-support
          // preloadChunks: true,
          // showProgress: false,
          // gzip: true,
          // analyze: true,
    
          // Options below are automatically set depending on the env, set them if you want to override
          // extractCSS: false,
    
          // https://v2.quasar.dev/quasar-cli-webpack/handling-webpack
          // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
          
          chainWebpack (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js', 'vue' ] }])
              .use(ModuleFederationPlugin, [{
                name: "spa1",
                filename: "remoteEntry.js",
                remotes: {
                  home: "home@http://localhost:8080/remoteEntry.js",
                },
              }])
          }
    
          
          
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-devServer
        devServer: {
          server: {
            type: 'http'
          },
          port: 8081,
          open: true // opens browser window automatically
        },
    
        // https://v2.quasar.dev/quasar-cli-webpack/quasar-config-js#Property%3A-framework
        framework: {
          config: {},
    
          // iconSet: 'material-icons', // Quasar icon set
          // lang: 'en-US', // Quasar language pack
    
          // For special cases outside of where the auto-import strategy can have an impact
          // (like functional components as one of the examples),
          // you can manually specify Quasar components/directives to be available everywhere:
          //
          // components: [],
          // directives: [],
    
          // Quasar plugins
          plugins: []
        },
    
        // animations: 'all', // --- includes all animations
        // https://quasar.dev/options/animations
        animations: [],
    
        // https://v2.quasar.dev/quasar-cli-webpack/developing-ssr/configuring-ssr
        ssr: {
          pwa: false,
    
          // manualStoreHydration: true,
          // manualPostHydrationTrigger: true,
    
          prodPort: 3000, // The default port that the production server should use
                          // (gets superseded if process.env.PORT is specified at runtime)
    
          maxAge: 1000 * 60 * 60 * 24 * 30,
            // Tell browser when a file from the server should expire from cache (in ms)
    
          
          chainWebpackWebserver (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          middlewares: [
            ctx.prod ? 'compression' : '',
            'render' // keep this as last one
          ]
        },
    
        // https://v2.quasar.dev/quasar-cli-webpack/developing-pwa/configuring-pwa
        pwa: {
          workboxPluginMode: 'GenerateSW', // 'GenerateSW' or 'InjectManifest'
          workboxOptions: {}, // only for GenerateSW
    
          // for the custom service worker ONLY (/src-pwa/custom-service-worker.[js|ts])
          // if using workbox in InjectManifest mode
          
          chainWebpackCustomSW (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          manifest: {
            name: `Quasar App`,
            short_name: `Quasar App`,
            description: `A Quasar Project`,
            display: 'standalone',
            orientation: 'portrait',
            background_color: '#ffffff',
            theme_color: '#027be3',
            icons: [
              {
                src: 'icons/icon-128x128.png',
                sizes: '128x128',
                type: 'image/png'
              },
              {
                src: 'icons/icon-192x192.png',
                sizes: '192x192',
                type: 'image/png'
              },
              {
                src: 'icons/icon-256x256.png',
                sizes: '256x256',
                type: 'image/png'
              },
              {
                src: 'icons/icon-384x384.png',
                sizes: '384x384',
                type: 'image/png'
              },
              {
                src: 'icons/icon-512x512.png',
                sizes: '512x512',
                type: 'image/png'
              }
            ]
          }
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-cordova-apps/configuring-cordova
        cordova: {
          // noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-capacitor-apps/configuring-capacitor
        capacitor: {
          hideSplashscreen: true
        },
    
        // Full list of options: https://v2.quasar.dev/quasar-cli-webpack/developing-electron-apps/configuring-electron
        electron: {
          bundler: 'packager', // 'packager' or 'builder'
    
          packager: {
            // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
    
            // OS X / Mac App Store
            // appBundleId: '',
            // appCategoryType: '',
            // osxSign: '',
            // protocol: 'myapp://path',
    
            // Windows only
            // win32metadata: { ... }
          },
    
          builder: {
            // https://www.electron.build/configuration/configuration
    
            appId: 'qusar-spa1'
          },
    
          // "chain" is a webpack-chain object https://github.com/neutrinojs/webpack-chain
          
          chainWebpackMain (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
    
          
          chainWebpackPreload (chain) {
            chain.plugin('eslint-webpack-plugin')
              .use(ESLintPlugin, [{ extensions: [ 'js' ] }])
          },
          
        }
      }
    });

0 Answers
Related