or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client-api.mdcontent-scripts.mdfile-writer-system.mdindex.mdmanifest-definition.mdplugin-configuration.md
tile.json

manifest-definition.mddocs/

Manifest Definition

Type-safe Chrome Extension Manifest V3 definition utilities with TypeScript support, file path validation, and dynamic resource management. Provides comprehensive manifest schema with intelligent defaults and validation.

Capabilities

Define Manifest

Helper function for defining Chrome extension manifests with full TypeScript type safety and file path validation.

/**
 * Define a Chrome extension manifest with TypeScript support
 * @param manifest - Manifest configuration object, promise, or function
 * @returns ManifestV3Export for use with crx plugin
 */
function defineManifest<T extends string = string>(
  manifest: ManifestV3Options<T>
): ManifestV3Export;

type ManifestV3Export = ManifestV3 | Promise<ManifestV3> | ManifestV3Fn;
type ManifestV3Fn = (env: ConfigEnv) => ManifestV3 | Promise<ManifestV3>;
type ManifestV3Options<T extends string> = ManifestOptions<T> | Promise<ManifestOptions<T>> | ManifestV3Define<T>;
type ManifestV3Define<T extends string> = (env: ConfigEnv) => ManifestOptions<T> | Promise<ManifestOptions<T>>;

Usage Examples:

import { defineManifest } from "@crxjs/vite-plugin";

// Static manifest
const manifest = defineManifest({
  manifest_version: 3,
  name: "My Extension",
  version: "1.0.0",
  background: {
    service_worker: "src/background.ts",
  },
  content_scripts: [
    {
      matches: ["https://example.com/*"],
      js: ["src/content.ts"],
      css: ["src/content.css"],
    },
  ],
  action: {
    default_popup: "src/popup.html",
    default_icon: {
      16: "icons/icon-16.png",
      32: "icons/icon-32.png",
    },
  },
});

// Dynamic manifest with environment-based configuration
const manifest = defineManifest((env) => ({
  manifest_version: 3,
  name: env.mode === "development" ? "My Extension (Dev)" : "My Extension",
  version: "1.0.0",
  background: {
    service_worker: "src/background.ts",
  },
  permissions: env.mode === "development" 
    ? ["storage", "activeTab", "debugger"] 
    : ["storage", "activeTab"],
}));

// Async manifest loading
const manifest = defineManifest(async () => {
  const config = await import("./extension-config.json");
  return {
    manifest_version: 3,
    name: config.name,
    version: config.version,
    background: {
      service_worker: "src/background.ts",
    },
  };
});

File Path Validation

Type-safe file path validation ensures paths are relative to the Vite project root and properly formatted.

type ManifestFilePath<T extends string> =
  T extends `${Code}${string}`
    ? never
    : T extends `${string}.${infer Ext}`
      ? Ext extends ''
        ? never
        : T
      : never;

type Code = '.' | '/' | '\\';

interface ManifestIcons<T extends string> {
  [size: number]: ManifestFilePath<T>;
}

File Path Requirements:

  • Must be relative to Vite project root (where vite.config.js is located)
  • Cannot start with ./, /, or \\
  • Must have a file extension
  • Format: "subdir/file.ext" (correct) vs "./subdir/file.ext" (incorrect)

Examples:

// ✅ Correct file paths
const goodManifest = defineManifest({
  background: {
    service_worker: "src/background.ts", // ✅
  },
  action: {
    default_popup: "src/popup.html",     // ✅
    default_icon: {
      16: "icons/icon-16.png",           // ✅
    },
  },
  content_scripts: [{
    js: ["src/content.ts"],              // ✅
    css: ["styles/content.css"],         // ✅
  }],
});

// ❌ TypeScript will prevent these invalid paths
const badManifest = defineManifest({
  background: {
    service_worker: "./src/background.ts", // ❌ starts with ./
  },
  action: {
    default_popup: "/src/popup.html",      // ❌ starts with /
  },
});

Define Dynamic Resource

Helper for defining dynamic web-accessible resources with match patterns for content scripts.

/**
 * Define dynamic web-accessible resources for content scripts
 * @param options - Match patterns and URL configuration
 * @returns WebAccessibleResourceByMatch configuration
 */
function defineDynamicResource(options: {
  matches?: string[];
  use_dynamic_url?: boolean;
}): WebAccessibleResourceByMatch;

interface WebAccessibleResourceByMatch {
  matches: string[];
  resources: string[];
  use_dynamic_url?: boolean;
}

const DYNAMIC_RESOURCE = '<dynamic_resource>' as const;

Usage Examples:

import { defineManifest, defineDynamicResource } from "@crxjs/vite-plugin";

const manifest = defineManifest({
  manifest_version: 3,
  name: "My Extension",
  version: "1.0.0",
  web_accessible_resources: [
    // Define dynamic resources for content scripts
    defineDynamicResource({
      matches: ["https://example.com/*", "https://test.com/*"],
      use_dynamic_url: true,
    }),
    // Manual resource definition
    {
      matches: ["https://api.example.com/*"],
      resources: ["assets/api-styles.css"],
    },
  ],
});

Dynamic Resource Features:

  • Auto-generation: Automatically includes content script resources
  • Match Patterns: Restricts resource access to specific origins
  • Dynamic URLs: Enables dynamic URL generation for enhanced security
  • Default Matches: Defaults to ['http://*/*', 'https://*/*'] if not specified

Manifest V3 Schema

Complete Chrome Extension Manifest V3 interface with all standard fields and Chrome-specific extensions.

interface ManifestV3 {
  // Required fields
  manifest_version: number;
  name: string;
  version: string;

  // Recommended fields
  default_locale?: string;
  description?: string;
  icons?: chrome.runtime.ManifestIcons;

  // Extension functionality
  action?: chrome.runtime.ManifestAction;
  background?: ChromeManifestBackground | FirefoxManifestBackground;
  content_scripts?: ContentScriptDefinition[];
  web_accessible_resources?: (WebAccessibleResourceByMatch | WebAccessibleResourceById)[];
  permissions?: string[];
  optional_permissions?: string[];
  host_permissions?: string[];

  // Additional Chrome extension fields (50+ more available)
  options_page?: string;
  devtools_page?: string;
  content_security_policy?: {
    extension_pages?: string;
    sandbox?: string;
  };
  // ... and many more
}

interface ChromeManifestBackground {
  service_worker: string;
  type?: 'module' | string; // If service worker uses ES modules
}

interface FirefoxManifestBackground {
  scripts: string[];
  persistent?: false;
}

interface ContentScriptDefinition {
  matches?: string[];
  exclude_matches?: string[];
  js?: string[];
  css?: string[];
  run_at?: 'document_start' | 'document_end' | 'document_idle';
  all_frames?: boolean;
  match_about_blank?: boolean;
  include_globs?: string[];
  exclude_globs?: string[];
}

Advanced Manifest Patterns

Environment-based Configuration:

import { defineConfig } from "vite";
import { crx, defineManifest } from "@crxjs/vite-plugin";

const manifest = defineManifest(({ mode }) => ({
  manifest_version: 3,
  name: mode === "development" ? "My Extension (Dev)" : "My Extension",
  version: "1.0.0",
  permissions: [
    "storage",
    "activeTab",
    ...(mode === "development" ? ["debugger"] : []),
  ],
  content_security_policy: {
    extension_pages: mode === "development"
      ? "script-src 'self' 'unsafe-eval'; object-src 'self'"
      : "script-src 'self'; object-src 'self'",
  },
}));

Multi-browser Support:

const manifest = defineManifest({
  manifest_version: 3,
  name: "Cross-browser Extension",
  version: "1.0.0",
  background: {
    service_worker: "src/background.ts",
    // Firefox compatibility
    scripts: ["src/background.ts"],
    persistent: false,
  } as any, // Type assertion for mixed browser support
});

export default defineConfig({
  plugins: [
    crx({
      manifest,
      browser: process.env.TARGET_BROWSER === 'firefox' ? 'firefox' : 'chrome',
    }),
  ],
});