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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@indev42/zwidget

v0.1.4

Published

A vite plugin for creating Zoho widget artifacts.

Readme

zwidget

A Vite plugin for creating Zoho widget build artifacts.

Usage

import { defineConfig } from "vite";
import { zwidget } from "@indev42/zwidget";

export default defineConfig({
  plugins: [zwidget()],
});

Run vite build with a valid plugin-manifest.json at the Vite project root. The plugin writes widget.zip to Vite's resolved build.outDir.

Artifact Layout

widget.zip
  plugin-manifest.json
  app/
    index.html
    assets/

plugin-manifest.json is parsed to verify that it contains valid JSON, then copied into the zip using its original bytes. Vite build output remains unchanged on disk and is mapped under app/ only inside widget.zip.

Behavior

  • Runs only during vite build.
  • Defaults Vite base to "./" when no base is configured.
  • Rejects explicit non-relative Vite base values.
  • Supports standard client app builds only, not SSR or library builds.
  • Includes all files emitted to build.outDir, including dotfiles, except widget.zip itself.
  • Sorts archive entries and normalizes zip timestamps for deterministic output.
  • Warns when widget.zip approaches observed Zoho entry-count or file-size rejection ranges.

Zoho Size Limits

Zoho uploads have been observed to reject widget archives near 300 zip entries, even when the compressed size is accepted. Zoho has also been observed to reject individual uncompressed files over 5 MB.

If zwidget warns about the entry count or file size, adjust your Vite build so that widget.zip stays below both limits:

  • Keep the archive below roughly 250 entries.
  • Keep each emitted file below 5 MB uncompressed.
  • Avoid fully inlining dynamic imports when the resulting JavaScript file exceeds 5 MB.

For many apps, the right fix is bounded manual chunking: group small dynamic chunks into a few larger chunks, but keep each output file under Zoho's per-file limit.

To decide how to chunk your app:

  • Build once without inlineDynamicImports.
  • Inspect widget.zip for total entry count and uncompressed file sizes.
  • Identify dependencies or app features that create many small chunks, then group those related modules into named chunks.
  • Avoid grouping everything into one vendor chunk unless that chunk stays under 5 MB uncompressed.
  • Rebuild and repeat until the archive has a comfortable entry-count margin and every emitted file is below 5 MB.

The exact groups depend on your app. Start with packages or feature areas that account for the most emitted files, then split any group that grows too large.

import { defineConfig } from "vite";
import { zwidget } from "@indev42/zwidget";

const chunkGroups = [
  {
    name: "vendor-core",
    matches: ["/node_modules/package-a/", "/node_modules/package-b/"],
  },
  {
    name: "vendor-feature",
    matches: ["/node_modules/package-c/", "/src/feature/"],
  },
];

export default defineConfig({
  plugins: [zwidget()],
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          const normalizedId = id.split("\\").join("/");
          const group = chunkGroups.find(({ matches }) =>
            matches.some((match) => normalizedId.includes(match)),
          );

          return group?.name;
        },
      },
    },
  },
});

Replace the placeholder package and source paths with the modules that dominate your own build output. If a manual chunk exceeds 5 MB, split that chunk further or reduce what the app imports.

Release Process

This repo uses Changesets for versioning and GitHub Actions for CI/CD.

Create a changeset for user-facing changes:

bun run changeset

When changesets are merged to main, the Release workflow creates or updates a version PR. Merge that version PR, then create a GitHub Release with a tag matching the package version to publish to npm.

The npm publish job uses Trusted Publishing/OIDC. Configure npm's trusted publisher for this package with workflow filename release.yml and ensure package.json repository.url exactly matches the GitHub repository.