Accudio / async-alpine

Async Alpine brings advanced code splitting and lazy-loading to Alpine.js components!
https://async-alpine.dev
Apache License 2.0
152 stars 12 forks source link

Vite build fails for module importing with AsyncAlpine.data() #27

Closed jgauna closed 1 year ago

jgauna commented 1 year ago

Hi! I've been trying to build a project using AsyncAlpine + Vite for the last two days with no luck. I'm writing because I have no idea how to accomplish this.

This is my folder structure:

This is my vite.config.js:

//import {VitePWA} from 'vite-plugin-pwa';
import {defineConfig} from 'vite';
import {compression} from 'vite-plugin-compression2';
import {splitVendorChunkPlugin} from 'vite';
import compress from 'compression';
import {VitePluginFonts} from 'vite-plugin-fonts';
import VitePluginBrowserSync from 'vite-plugin-browser-sync';
import PathEnv from 'vite-plugin-patch-env';
import mockData from 'vite-plugin-mock-data';
import VitePluginHtmlEnv from 'vite-plugin-html-env';
// Icons
import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver';
import AutoImport from 'unplugin-auto-import/vite';

export default defineConfig({
  root: './',
  base: './',
  build: {
    emptyOutDir: true
  },
  publicDir: 'public',
  css: {
    preprocessorOptions: {
      scss: {
        includePaths: ['./assets/css']
      }
    }
  },
  plugins: [
    splitVendorChunkPlugin(),
    VitePluginFonts({
      google: {
        families: ['Josefin Sans']
      }
    }),
    VitePluginHtmlEnv({
      compiler: true
    }),
    mockData({
      mockAssetsDir: './mockAssets'
    }),
    PathEnv(),
    compression(),
    AutoImport({
      resolvers: [
        IconsResolver({
          prefix: false,
          extension: 'raw',
          enabledCollections: ['carbon', 'mdi']
        })
      ]
    }),
    Icons({
      autoInstall: true,
      compiler: 'raw',
      scale: 1.2, // Scale of icons against 1em
      defaultStyle: '', // Style apply to icons
      defaultClass: '' // Class names apply to icons
    }),
    VitePluginBrowserSync({
      bs: {
        reloadDelay: 500,
        notify: false,
        ghostMode: false
      }
    }) /*,
    VitePWA({
      registerType: 'autoUpdate',
      devOptions: {
        enabled: true
      }
    })*/
  ]
});

This is my main.js:

import './assets/css/main.scss';
import AsyncAlpine from 'async-alpine';
import Alpine from 'alpinejs';
import focus from '@alpinejs/focus';
import persist from '@alpinejs/persist';
import moment from '@victoryoalli/alpinejs-moment';
import timeout from '@victoryoalli/alpinejs-timeout';
import * as HashRouter from '@techexp/hash-router';
import {util} from 'prettier';

document.addEventListener('alpine:initialized', () => {
  console.log('Alpine Inited');
});

//window.Alpine = Alpine;
// Start Async Alpine
AsyncAlpine.init(Alpine)
  .data('homeModule', () => import('./assets/js/components/Home/home.js'))
  .data('toastModule', () => import('./assets/js/components/Toast/toast.js'))
  .data('shopInfo', () => import('./assets/js/components/ShopInfo/shopInfo.js'))
  .data('orderHeader', () => import('./assets/js/components/Operations/OrderHeader/orderHeader.js'))
  .data('orderTabs', () => import('./assets/js/components/Operations/OrderTabs/orderTabs.js'))
  .data('operationModule', () => import('./assets/js/components/Operations/operation.js'))
  .data('orderList', () => import('./assets/js/components/Operations/OrderList/order.js'))
  .data('orderGroup', () => import('./assets/js/components/Operations/OrderGroup/orderGroup.js'))
  .start();
// Start Alpine
Alpine.plugin(focus);
Alpine.plugin(persist);
Alpine.plugin(timeout);
Alpine.plugin(moment);
Alpine.start();

This is my index.html:

<!DOCTYPE html>
<html lang="en" data-theme="fika" class="overflow-y-hidden">
    <head>
        <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin/>
        <link rel="preload" as="style" onload="this.rel='stylesheet'" href="https://fonts.googleapis.com/css2?family=Josefin Sans&display=swap"/>
        <meta charset="UTF-8"/>
        <link rel="icon" type="image/x-icon" href="./assets/favicon.67af206a.ico"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <meta http-equiv="cache-control" content="no-cache"/>
        <meta http-equiv="expires" content="0"/>
        <meta http-equiv="pragma" content="no-cache"/>
        <title>FikaCore</title>
        <script type="module" crossorigin src="./assets/index.f178bb58.js"></script>
        <link rel="modulepreload" crossorigin href="./assets/vendor.88f6f930.js"/>
        <link rel="stylesheet" href="./assets/index.62293656.css"/>
    </head>
    <body>
        <div id="app" x-data>
            <div id="homeModule" ax-load x-data="homeModule()" x-html="view" x-ignore></div>
        </div>
    </body>
    <script>console.log('===== En modo DESARRROLLO =====');</script>
</html>

When I run vite build && vite preview I got:

Captura de pantalla -2023-02-16 18-48-19

And I think this problem is related to the dist structure:

Captura de pantalla -2023-02-16 18-50-01

Since every module is in assets/js/components the dist index.html couldn't find the module because it's not the same folder structure as dev mode. Any thoughts?

Regards

Accudio commented 1 year ago

Hi @jgauna!

Could you please provide an example repo with this issue, including all the files you've sent and also including the components?

It is likely not the dist structure, Vite rewrites that structure to be flat and your main.js likely had the correct URLs. I'll be able to dig into it and see why it may not be working with a test repo though.

Thanks!

jgauna commented 1 year ago

Hey @Accudio thanks for the answer :)

If anyone is struggling with this, you should know that as @Accudio says, Vite creates a flat dist folder with all of the chunks from your project, so you can't have the same folder structure as in dev mode. The solution is to use Resolve.alias in order to resolve the module's paths from your project. So, in your vite.config.js you should have something like this (read the code's comment) πŸ‘‡

//import {VitePWA} from 'vite-plugin-pwa';
import {defineConfig} from 'vite';
import {compression} from 'vite-plugin-compression2';
import {splitVendorChunkPlugin} from 'vite';
import compress from 'compression';
import {VitePluginFonts} from 'vite-plugin-fonts';
import VitePluginBrowserSync from 'vite-plugin-browser-sync';
import PathEnv from 'vite-plugin-patch-env';
import mockData from 'vite-plugin-mock-data';
import VitePluginHtmlEnv from 'vite-plugin-html-env';
import path from 'path';
// Icons
import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver';
import AutoImport from 'unplugin-auto-import/vite';

export default defineConfig({
  root: './',
  base: './',
  publicDir: 'public',
  // Here's the fix for loading modules when building for prod πŸ‘‡πŸ‘‡πŸ‘‡
  resolve: {
    alias: {
      '@home': path.resolve(__dirname, './js/components/Home')
    }
  },
  build: {
    emptyOutDir: true,
    rollupOptions: {
      output: {
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: ({name}) => {
          if (/\.(gif|jpe?g|png|svg)$/.test(name ?? '')) {
            return 'assets/img/[name]-[hash][extname]';
          }
          if (/\.css$/.test(name ?? '')) {
            return 'assets/css/[name]-[hash][extname]';
          }
          return 'assets/[name]-[hash][extname]';
        }
      }
    }
  },
  css: {
    preprocessorOptions: {
      scss: {
        includePaths: ['./assets/css']
      }
    }
  },
  plugins: [
    splitVendorChunkPlugin(),
    VitePluginFonts({
      google: {
        families: ['Josefin Sans']
      }
    }),
    VitePluginHtmlEnv({
      compiler: true
    }),
    mockData({
      mockAssetsDir: './mockAssets'
    }),
    PathEnv(),
    compression(),
    AutoImport({
      resolvers: [
        IconsResolver({
          prefix: false,
          extension: 'raw',
          enabledCollections: ['carbon', 'mdi']
        })
      ]
    }),
    Icons({
      autoInstall: true,
      compiler: 'raw',
      scale: 1.2, // Scale of icons against 1em
      defaultStyle: '', // Style apply to icons
      defaultClass: '' // Class names apply to icons
    }),
    VitePluginBrowserSync({
      bs: {
        reloadDelay: 500,
        notify: false,
        ghostMode: false
      }
    }) 
  ]
});

And your main.js

AsyncAlpine.init(Alpine)
  // Same as alias key in vite.config.js πŸ‘‡
  .data('homeModule', () => import('@home/home.js')) 
  .start()

And if you want to make it more awesome you could use https://github.com/Subwaytime/vite-aliases which creates all aliases automatically according to your project structure.

jgauna commented 1 year ago

@Accudio maybe you can update your example so it's well documented. Btw, fantastic work, I love this lib :)

Accudio commented 1 year ago

@jgauna Glad you worked it out and thank you!

I've added a comment to the Vite example with a link to your solution above. Since it doesn't affect the base Vite implementation without configuration that seems like a good solution to me. If it ends up being a more common issue (I don't really use Vite so am not sure) then I'll make a page in the docs.

Thanks!