Search code examples
typescriptvue.jswebpackes6-modules

Build VueJS 3 application as a single ES Module bundle?


I have a Vue3 project which I configured with typescript and a main.ts entry file with one single default export.

import { App, createApp } from "vue";
import { createIntl } from "vue-intl";

import Application from "./App.vue";
import { AppProps, Settings } from "types";

let appRef = {} as App<Element>;

const AppLifecycle = {
  mount: (container: HTMLElement, appProps: AppProps, settings: Settings) => {
    const { themeUrl, userPreferences } = settings;
    const { language } = userPreferences;

    appRef = createApp(Application, { ...appProps, themeUrl });
    appRef.use(
      createIntl({
        locale: language,
        defaultLocale: "en",
        messages: messages[language],
      })
    );
    appRef.mount(container);
  },
  unmount: (_: HTMLElement) => {
    appRef.unmount();
  },
};

export default AppLifecycle;

I'd like to build this as a single ES Module bundle in order to integrate it inside a private platform which has this requirements:

the app’s bundle must be a JavaScript ES Module;

the default export of the app must be an object to handle the app’s lifecycle (the AppLifecycle object above)

From a boilerplate project (written in React + Typescript) they're using the following webpack configuration:

const path = require("path");

module.exports = {
  mode: "production",
  entry: "./src/index.tsx",
  experiments: {
    outputModule: true,
  },
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
    library: {
      type: "module",
    },
  },
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: "css-loader",
      },
      {
        test: /\.tsx?$/,
        use: "ts-loader",
        exclude: /node_modules/,
      },
    ],
  },
};

From what I've understood Vue3 comes using webpack4 under the hood and the configuration can be tuned, till a certain degree, using a webpack chain inside vue.config.js. Moreover, the vue-cli can be used to specify a target (for instance --target lib) but I don't think ES modules are supported this way. I've made an attempt using the following configuration but I don't know if this is the right way.

module.exports = {
  chainWebpack: (config) => {
    config.optimization.set("splitChunks", false);
    config.plugins.delete("prefetch");
    config.plugins.delete("preload");
  },
  css: {
    extract: false,
  },
  filenameHashing: false,
};

I didn't find any detailed resources on how to build specifically a single ES Module with a single typescript entry file using Vue3 so I'm asking here. Thanks in advance.


Solution

  • I've solved this by upgrading vue-cli to version 5 which among the various changes takes into account Webpack 5 that handles the ES module generation https://next.cli.vuejs.org/migrations/migrate-from-v4.html#webpack-5

    I've changed the vue.config.js file to be compliant with the boilerplate one I've posted. Like the following:

    module.exports = {
      configureWebpack: {
        entry: "./src/main.ts",
        experiments: {
          outputModule: true,
        },
        optimization: {
          splitChunks: false,
        },
        output: {
          library: {
            type: "module",
          },
        },
      },
      chainWebpack: (config) => {
        config.plugins.delete("prefetch");
        config.plugins.delete("preload");
      },
      css: {
        extract: false,
      },
      filenameHashing: false,
    };
    

    I prefer this solution instead of using Vite as Estus Flask suggested since I don't know the tool and I prefer sticking to Webpack being the same used by the target platform.