originjs / vite-plugin-federation

Module Federation for vite & rollup
Other
2.39k stars 241 forks source link

The above dynamic import cannot be analyzed by Vite. #518

Closed goldyfruit closed 1 year ago

goldyfruit commented 1 year ago

I'm using vite-plugin-federation to load remote components (I'm not very familiar with Vite/VueJS).

Versions

Reproduction

Basically this works:

defineAsyncComponent(() => import('ovosSkillPersonalOpenvoiceos/MainComponent'));

But this doesn't:

const componentName = 'ovosSkillPersonalOpenvoiceos/MainComponent';
defineAsyncComponent(() => import(componentName));

I tried this as well but without success:

const componentName = 'SkillPersonalOpenvoiceos/MainComponent';
defineAsyncComponent(() => import('ovos' + componentName));

Vite returns this:

The above dynamic import cannot be analyzed by Vite.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

I tried the /* @vite-ignore */ which removes the warning but doesn't load the component. I read as was the rollup limitations but is there any "dirty" workaround to make this work?

I'm following as well this thread https://github.com/vitejs/vite/issues/14102#issuecomment-1745780646

Thanks :+1:

goldyfruit commented 1 year ago

Here is some more code:

JSON payload from a HTTP service

{
   "skills":[
      {
         "ovos-skill-personal.OpenVoiceOS":{
            "url":"http://192.168.1.227:4173/assets/remoteEntry.js",
            "name":"skillOvosPersonal"
         }
      }
   ]
}

Service to retrieve data from the an API

// src/services/SkillsService.ts'
import axios from 'axios';

export async function getSkillList(endpoint: string) {
  const { data } = await axios.get(endpoint);
  const skills: any = {};
  data.skills.forEach((skill: any) => {
    for (const key in skill) {
      skills[skill[key].name] = skill[key].url;
    }
  });
  return skills;
}
// vite.config.mts
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import federation from '@originjs/vite-plugin-federation';
import { fileURLToPath, URL } from 'node:url';
import { quasar, transformAssetUrls } from '@quasar/vite-plugin';
import { getSkillList } from './src/services/SkillsService';

const env = loadEnv('all', process.cwd());

export default defineConfig({
  plugins: [
    vue({
      template: { transformAssetUrls },
    }),
    quasar(),
    federation({
      name: 'ovos-gui',
      remotes: await getSkillList(env.VITE_SKILL_LIST_ENDPOINT || 'http://localhost:8040/skills'),
      shared: {
        vue: {},
        pinia: {},
        ovosStore: {
          packagePath: './src/store/ovos.ts',
        },
        websocketStore: {
          packagePath: './src/store/websocket.ts',
        },
      },
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),
    },
  },
});

Component where I'm trying to load the remote components

// components/ExternalComponent.vue
<script setup lang="ts">
import { defineAsyncComponent, onMounted, ref } from 'vue';
import { getSkillList } from '@/services/SkillsService';

const data = ref();
const skills = ref({});

onMounted(async () => {
  data.value = await getSkillList(
    import.meta.env.VITE_SKILL_LIST_ENDPOINT || 'http://localhost:8040/skills'
  );

  for (const key in data.value) {
    const componentName = `${key}/MainComponent`;
    skills.value[key] = defineAsyncComponent(
      () => import(componentName)
    );
  }
});
</script>

<template>
  <Suspense>
    <div v-for="(value, name) in skills" :key="name">
      <component :is="value"></component>
    </div>
  </Suspense>
</template>
goldyfruit commented 1 year ago

Looks very similar to https://github.com/originjs/vite-plugin-federation/issues/401

goldyfruit commented 1 year ago

Trying to get around this issue but still not able... I tried to follow rollup requirements but for some reason it still not working.

Based on the rollup documentation/limitations:

image

I renamed my remote component from ovosSkillPersonalOpenvoiceos/MainComponent to ./ovosSkillPersonalOpenvoiceos/MainComponent.vue but for some reason it tries to load the component from the host itself (localhost) and not from the remote (http://192.168.1.227:4173/assets/remoteEntry.js)...

This works:

defineAsyncComponent(() => import('./ovosSkillPersonalOpenvoiceos/MainComponent.vue'));

This doesn't work:

const componentName = 'ovosSkillPersonalOpenvoiceos/MainComponent';
defineAsyncComponent(() => import(`./${componentName}.vue`));

Error: image

goldyfruit commented 1 year ago

Any suggestion on this?

goldyfruit commented 1 year ago

I went with Webpack and https://github.com/MichaelDurfey/mf-dynamic-remote-component library.

goldyfruit commented 1 year ago

Here is the current code I'm using.

<script setup lang="ts">
import { RemoteComponent } from 'mf-dynamic-remote-component';
import { defineAsyncComponent } from 'vue';

const remoteConfig = {
  path: 'http://localhost:3001/remoteEntry.js',
  scope: 'app_expose',
  module: './RemoteSkill.vue',
};

const RemoteSkill = defineAsyncComponent(() => RemoteComponent(remoteConfig));
</script>

<template>
      <component :is="RemoteSkill"></component>
</template>

I hope it will help.

Mentat-Radnor commented 8 months ago

I have this problem too. I use vue 3 + Vite. const loadLocaleMessages = async () => { const files = import.meta.glob('./locales/*.json'); const locales = {}; await Promise.all(Object.keys(files).map(async (path) => { const localeName: string = path.match(/([A-Za-z0-9-_]+)\.json$/i)![1]; const module = await import(path); locales[localeName] = module.default; })); return locales; };

goldyfruit commented 8 months ago

I went with webpack https://github.com/module-federation/module-federation-examples/tree/master/quasar-cli-vue3-webpack-javascript