/meteor-vite

⚡ Replace Meteor's bundler with Vite for blazing fast build-times

Primary LanguageTypeScriptMIT LicenseMIT

meteor-vite

Use Vite in your Meteor app! ⚡️

Key features

Significantly speeds up Meteor's client-side build process. Uses the Vite dev server under-the-hood, leading to blazing fast hot-module-replacement during development.

  • Minimal migration steps for existing Meteor projects
  • Handles lazy-loaded Meteor packages & automatic code splitting for dynamic imports
  • Bring in all your favourite Vite plugins and use them together with your Meteor project.
  • Simplifies a lot of the setup for things like CSS preprocessors and UI frameworks.

Starter templates

Installation

# Install meteor-vite and Vite v4
meteor npm i -D vite@4 
meteor npm i meteor-vite

# Then add the Vite-Bundler package to your Meteor project.
# If you're using the Meteor v3 beta, use `jorgenvatle:vite-bundler@2.0.0-beta.8` 
meteor add jorgenvatle:vite-bundler

You can also install any vite plugin, for example @vitejs/plugin-vue:

meteor npm i -D @vitejs/plugin-vue

Make sure to have an import client entry (meteor.mainModule.client) in your package.json:

{
  "name": "my-app",
  "private": true,
  "scripts": {
    "dev": "meteor run",
    "build": "meteor build ../output/vue --directory"
  },
  "dependencies": {
    // ...
  },
  "meteor": {
    "mainModule": {
      "client": "imports/entrypoint/meteor.ts",
      "server": "server/main.ts"
    },
  }
}

You can leave your Meteor client entry file empty, but it's necessary to enable Meteor import mode. In the example above, we can create an empty imports/entrypoint/meteor.ts file.

Create a Vite configuration file (vite.config.js) in your project root. As we don't use a standard Vite index.html file, we need to specify an entry point (different from the Meteor one):

Example with Vue

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { meteor } from 'meteor-vite/plugin';

export default defineConfig({
    plugins: [
        meteor({
          clientEntry: 'imports/entrypoint/vite.ts',
        }),
        vue(),
    ],
    // Other vite options here...
})

You can then write your code from the vite.ts entry point and it will be handled by Vite! ⚡️

Configuration

Vite Config

// vite.config.ts
import { meteor } from 'meteor-vite/plugin';

export default defineConfig({
    plugins: [
        meteor({
          /**
           * Path to the client entrypoint for your app.
           * This becomes your new Vite-powered mainModule for Meteor.
           */
          clientEntry: 'imports/entrypoint/vite.ts',

          /**
           * Configures runtime validation of Meteor package stubs generated by Vite. 
           * See Features section in readme for more info
           * (optional)
           */
          stubValidation: {
            /**
             * list of packages to ignore export validation for.
             */
            ignorePackages: ["ostrio:cookies"],

            /**
             * Will only emit warnings in the console instead of throwing an exception that may prevent the client app
             * from loading.
             */
            warnOnly: true,

            /**
             * Set to true to completely disable stub validation. Any of the above options will be ignored.
             * This is discuraged as `warnOnly` should give you an important heads up if something might be wrong with Meteor-Vite
             */
            disabled: false,
          }
        })
    ],
})

Meteor plugin settings

There are a couple extra advanced settings you can change through your package.json file under meteor.vite. In most cases, you won't need to add anything here.

// package.json
{
  "name": "my-app"
  // ...
  
  "meteor": {
    "mainModule": { /* ... */ },
    
    // Additional configuration for Meteor-Vite (optional)
    "vite": {
      // If your Vite config file lives in another directory (e.g. private/vite.config.ts), specify its path here
      "configFile": "",
      
      // Replace or remove Meteor packages when bundling Vite for production.
      // This may be useful for a small subset of compiler-plugin packages that interfere with Vite's build process.
      //
      // This is only used during the Vite bundling step. The packages included in your final production 
      // bundle remains unaffected.
      "replacePackages": [
        // Removes standard-minifier packages while building with Vite. (default)
        { "startsWith": "standard-minifier", "replaceWith": "" },

        // Replace refapp:meteor-typescript with the official typescript package. (default)
        { "startsWith": "refapp:meteor-typescript", replaceWith: "typescript" },
      ]
    }
  }
}

Features in-depth

Lazy Loaded Meteor Packages

Meteor-Vite will automatically detect lazy loaded Meteor packages and import them into your Meteor client's entrypoint. This is necessary to ensure that the Vite bundler has access to your Meteor packages.

The imported files can safely be committed to your project repository. If you remove the associated package in the future, simply remove the import statement.

Our detection for these packages is fairly primitive, so it's best to keep the imports in the Meteor client entrypoint as specified in the meteor.mainModule.client field of your package.json file.

{
  "meteor": {
    "mainModule": {
      "client": "imports/entrypoint/meteor.ts", // Lazy loaded packages checked for and added to this file.
      "server": "server/main.ts"
    }
  }
}

Stub validation

Runtime validation at the client is performed for Meteor packages that are compiled by Vite. This is done to avoid a situation where Meteor-Vite incorrectly exports undefined values from a Meteor Package. Which can lead to silently broken Meteor packages.

The validation is done simply through verifying that package exports do not have a typeof value of undefined. If you do have a package that intentionally has undefined exports, you can disable the warning message for this package by excluding it in your Meteor settings.json file;

// vite.config.ts
export default defineConfig({
  plugins: [
      meteor({
        stubValidation: {
            ignorePackages: ["ostrio:cookies"]
        }
      })
  ]
})

A note about the Meteor mainModule

Code written to or imported by your Meteor client's mainModule.client will not be processed by Vite, however, it will still by loaded by the Meteor client. So if you have a use case where you have some code that you don't want Vite to process, but still want in your client bundle, this would be the place to put that.

But do be careful with this - any code that's imported by both your Vite config's clientEntry and your Meteor mainModule.client may lead to the code being included twice in your final production bundle.

Package Details

The Vite integration comes with two dependencies that work together to enable compatibility between Meteor and Vite.

Roadmap

  • Development mode
  • Build
  • Importing non-core Meteor packages
  • Lazy meteor packages
  • Reify support
    • Named exports
    • Default exports
    • Re-exports (named + wildcard)
    • Re-exports via intermediary variable
  • Meteor v3 support
    • Migrate bundler from Fibers to Async/Await
    • Update Meteor bundle parser to support new format introduced in v3.
  • Code-splitting/Dynamic imports
  • Migrate intermediary production-build transpile step from Babel to esbuild.
  • SSR (not tested)
  • Starter/demo templates