npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@ulu/vite-plugin-virtual-modules

v1.0.2

Published

This plugin allows the creation of virtual modules using normal ES modules that return dynamic modules

Downloads

9

Readme

@ulu/vite-plugin-virtual-modules

This plugin allows you to easily create virtual modules (modules whose contents are created at build time) using normal javascript modules (files).

import users from "./data/users.js?virtual-module";

// Example: [ { user }, { user }, ... } ]
console.log(users); 

// ... Use the data however

The virtual data is loaded by the module that was requested. (ie. "./data/users.js" in this example).

import { toJsonModule } from "@ulu/vite-plugin-virtual-modules";
import { getContent, contentUpdated } from "./service.js";

export default function() {
  return {
    async load() {
      try {
        return toJsonModule(await getContent("users"));
      } catch (error) {
        console.log(error);
      }
    }
  }
}

This was originally created to get data for SSG apps, in order to avoid having to bring any of the fetching code and dependencies into the bundle/app. Instead just providing a JSON module of the fetch results.

Features:

  • Use ES modules to create virtual modules (intuitive)
  • HMR Update when you modify a host/loader module
  • Watch other files for changes, to update virtual module
  • Access to queries

If you encounter bugs or have a feature request, feel free to open an issue on github.

Contents

Vite Setup

import { defineConfig } from "vite";
import virtualModules from "@ulu/vite-plugin-virtual-modules";

export default defineConfig({
  plugins: [
    virtualModules({
      // See options
    })
  ]
});

Usage

The example below just demonstrates returning JSON in an ES module but any form of ES module can be returned by the virtual module. In addition to the examples below, see "src/tests/" in this repo for more examples.

Say for example we were building a static site that displayed a list of users that are managed in a CMS. We want to fetch the data from the CMS via an endpoint and display only the results (JSON) in the page.

Below is an example of the vistual module file, it fetches users from the CMS and returns an ES module that exposes the JSON (in string form). When this is imported in the application only the resulting JSON module will exist and the fetching logic will be left behind as part of the build process.

  • The virtual module must provide default export function
  • The function can be async
  • The function must return either a string (the string version of ES module) or and object with 'code' and 'map' properties, which follow the Vite plugin transform API and could be used to provide the source map if needed.
  • toJsonModule() below is a helper method that exported from this library. It wraps the JSON in an ES module.
// fetch-users.js (mock code)

import { toJsonModule } from "@ulu/vite-plugin-virtual-modules";
import { getContent, contentUpdated } from "./some-service.js";

export default function({ reload, isServe }) {
  return {
    async load() {
      try {
        const result = await getContent("users");
        const users = await result.json();

        // Reload this virtual module when something changes
        // - Only if running dev server
        if (isServe) {
          contentUpdated(() => reload());
        }
        
        return toJsonModule(users);
      } catch (error) {
        console.log(error);
      }
    }
  }
}

Now to use this dynamic module we import the file above like normal in combination with the special ?virtual-module suffix.

// user-view.js

import users from "./fetch-users.js?virtual-module";

// JSON: [ { user }, { user }, ... } ]
console.log(users); 

// ... Use the data however

Note: The context object contains details about the module. See context below. This can be used to adjust output based on queries.

// dog-view.js

import dogs from "./fetch-animals.js?virtual-module&type=dog";

// JSON: [ { dog }, { dog } ]
console.log(dogs); 

// ... Use the data however
// fetch-animals.js

import { toJsonModule } from "@ulu/vite-plugin-virtual-modules";

export default function({ queries }) {
  return {
    load() {
      if (queries.type) {
        return toJsonModule(await animalsByType(queries.type));
      } else {
        return toJsonModule(await allAnimals());
      }
    }
  }
}

API

Importing Virtual Module

// The suffix "?virtual-module" is used to load the module as a virtual module
import testReload from "./path/to/file.js?virtual-module";
// Using queries
import testQuery from "./path/to/file.js?virtual-module&type=dog";

Virtual Module Structure

This is the module that creates the virtual module.

export default function({ 
  // Module ID (import path)
  id,
  // Any URL queries passed
  queries,
  // Boolean is the serve command
  isServe,
  // String command name (build, serve, etc)
  command,
  // The path to this file
  filePath,
  // The path that is used by node to import the module
  importPath,
  // Function that will reload this module (call load again) when called
  // - Use to udpate the module programmatically
  reload
}) {
  return {
    // Function that should return a string version of module to load
    // - Recieves an array of watchedFiles if watch is set
    // - Can be async
    load(watchedFiles) {
      // return  "export default..."
    },
    // Watch files option (anything valid to be passed to chokidar)
    // - By default cwd is this module's director 
    //   so everything is relative to this file
    watch: ["some/files/**/*.txt"],
    // Options to pass to Chokidar
    watchOptions: {},
    // Events that should trigger reload of module
    watchEvents: ["add", "unlink", "change", "unlinkDir", "addDir"]
  }
}

Plugin Options

Options that can be passed when adding this plugin to vite

import { defineConfig } from "vite";
import virtualModules from "@ulu/vite-plugin-virtual-modules";

export default defineConfig({
  plugins: [
    virtualModules({
      // Suffix on the end of imports (Regex)
      suffix: /\?virtual-module(&.*)*$/,
      // Events that trigger reload when watching
      // - Can be overridden by loader module
      watchEvents: ["add", "unlink", "change", "unlinkDir", "addDir"],
      // Options to be passed to Chokidar for watching 
      // - Can be overridden by loader module
      watchOptions: {}
    })
  ]
});

Change Log

Change Log