How CRXJS Vite Plugin Performs Manifest Version Validation During Build

The CRXJS Vite plugin validates the manifest version synchronously in the config hook by checking that manifest.manifest_version equals 3, immediately throwing an error if any other version is detected.

The CRXJS Vite plugin enforces strict Manifest V3 compliance for Chrome extensions by validating the manifest version before any build steps execute. This validation occurs in the config hook of the plugin, ensuring developers receive immediate feedback if they attempt to use deprecated Manifest V2 formats. Understanding this validation mechanism helps prevent build failures and ensures compatibility with modern Chrome extension standards.

Where Manifest Version Validation Occurs

The validation logic resides in packages/vite-plugin/src/node/plugin-manifest.ts inside the crx:manifest-init plugin. According to the CRXJS source code, the plugin registers a config hook—the earliest Vite lifecycle hook available to plugins—to perform this check.

When Vite initializes, the plugin calls getOptions(config) to retrieve the user-provided manifest object or manifest factory function. This acquisition happens synchronously during Vite's configuration resolution phase, ensuring the build halts before any file processing begins if validation fails.

The Validation Implementation

The validation follows a strict three-step process:

  1. Manifest acquisition – The plugin retrieves the manifest via getOptions(config), which handles both static objects and dynamic functions.

  2. Version comparison – It reads manifest.manifest_version and compares it against the supported integer 3.

  3. Error throwing – If the version is not exactly 3, the plugin throws an error with the message:

    
    CRXJS does not support Manifest vX, please use Manifest v3
    

This check runs synchronously, meaning Vite never proceeds to the build phase with an unsupported manifest. The source code includes a comment // TODO: build this out into full validation plugin, indicating that while comprehensive schema validation may arrive in future versions, the version gate serves as the critical safeguard today.

Code Examples

Valid Manifest V3 Configuration

This configuration passes validation and allows the build to proceed:

// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: {
        manifest_version: 3,          // ✅ Required version
        name: 'My Extension',
        version: '1.0.0',
        action: {
          default_popup: 'index.html',
        },
      },
    }),
  ],
});

Invalid Manifest V2 Error Handling

Attempting to use Manifest V2 triggers an immediate build failure:

// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: {
        manifest_version: 2,          // ❌ Unsupported version
        name: 'Old Extension',
        version: '0.1.0',
        browser_action: {
          default_popup: 'index.html',
        },
      },
    }),
  ],
});
$ vite build
Error: CRXJS does not support Manifest v2, please use Manifest v3
    at config (packages/vite-plugin/src/node/plugin-manifest.ts:...)

Dynamic Manifest Functions

The validation also works when providing a function that generates the manifest at runtime:

// vite.config.ts
import { defineConfig } from 'vite';
import { crx } from '@crxjs/vite-plugin';

export default defineConfig({
  plugins: [
    crx({
      manifest: (env) => ({
        manifest_version: env.mode === 'production' ? 3 : 3, // Must return 3
        name: 'Dynamic Manifest',
        version: '1.0.0',
      }),
    }),
  ],
});

Even with dynamic generation, the config hook executes the function and validates the returned object's manifest_version before Vite continues.

Why Early Validation Matters

Performing manifest version validation in the config hook provides three critical benefits:

  • Safety – Prevents accidental bundling of MV2 extensions, which are deprecated and unsupported by the CRXJS toolchain.
  • Early feedback – Developers see the error immediately when running vite or vite build, rather than discovering issues during browser loading or distribution packaging.
  • Future-proofing – The synchronous check establishes a foundation for the TODO-commented "full validation plugin" that may add schema validation for permissions, host permissions, and other Manifest V3 requirements.

Summary

  • CRXJS validates manifest version in the config hook located in packages/vite-plugin/src/node/plugin-manifest.ts before Vite processes any source files.
  • Only manifest_version: 3 is supported; any other integer value triggers an immediate fatal error.
  • The validation uses getOptions(config) to retrieve the manifest, then checks manifest.manifest_version against the hardcoded value 3.
  • Error messages explicitly state: "CRXJS does not support Manifest vX, please use Manifest v3".
  • Dynamic manifest functions are executed and validated during the config resolution phase, ensuring runtime-generated manifests still comply with V3 requirements.

Frequently Asked Questions

What happens if I use Manifest V2 with CRXJS?

The build fails immediately during Vite's configuration resolution phase. The plugin throws an error stating "CRXJS does not support Manifest v2, please use Manifest v3" and exits without processing any source files.

Can I use a function to generate the manifest dynamically?

Yes, CRXJS supports passing a function to the manifest option in the plugin configuration. The function receives the Vite environment object and must return a manifest object with manifest_version: 3. The validation occurs after the function executes but before the build begins.

Where is the manifest version validation code located?

The validation logic is implemented in the config hook inside packages/vite-plugin/src/node/plugin-manifest.ts. This file imports the manifest via getOptions from packages/vite-plugin/src/node/plugin-optionsProvider.ts and performs the version check against the ManifestV3 type definition found in packages/vite-plugin/src/node/manifest.ts.

Is Manifest V3 validation the only check performed?

Currently, only the manifest_version integer is strictly validated. However, the source code contains a TODO comment indicating plans to expand the plugin-manifest.ts logic into a comprehensive validation plugin that may verify permissions, host permissions, and other Manifest V3 schema requirements in future releases.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →