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

wxt-module-layers

v1.0.0

Published

Nuxt-like layers functionality for WXT browser extensions

Downloads

14

Readme

WXT Layers

Nuxt-like layers functionality for WXT browser extensions

Abstract

WXT Layers brings Nuxt's layer architecture to browser extensions, enabling you to organize your extension into isolated, reusable modules.

What are layers?

Layers are self-contained directories that mirror WXT's project structure. Each layer can have its own entry points, auto-imports, assets, and manifest modifications - just like a mini-extension.

Why use layers?

  • Feature isolation: Keep related code together (e.g., layers/analytics/, layers/auth/)
  • Code reusability: Share layers across projects or teams
  • Cleaner organization: Use core WXT structure for shared code, layers for specific features

How layers combine:

  • Entrypoints from all layers are registered alongside your src entrypoints
  • Auto-imports from layer folders can be added to the global import pool
  • Public assets are copied with organized paths (e.g., /analytics/icon.png)
  • Aliases let you reference layers easily (#analytics, #auth)
  • Manifest can be modified at build time from individual layers

Setup

Installation

npm install wxt-module-layers

Basic Configuration

Add the module to your wxt.config.ts:

import { defineConfig } from 'wxt'

export default defineConfig({
  modules: ['wxt-module-layers'],
})

Create Your First Layer

Layers use the same structure as WXT:

layers/
  analytics/
    entrypoints/
      content.ts        # Same as WXT: auto-discovered
    composables/
      useTracking.ts    # Auto-imported globally
    public/
      icons/
        tracker.png     # Copied to /analytics/icons/tracker.png

That's it! The module will:

  • ✅ Create an alias #analyticslayers/analytics
  • ✅ Register the content script as an entrypoint
  • ✅ Make useTracking() available everywhere via auto-import
  • ✅ Copy public files to /analytics/ in the build output

Layer Structure

Each layer mirrors WXT's project structure:

layers/my-layer/
  entrypoints/          # Same as WXT
    background.ts
    popup.html
    content.ts
  composables/          # Auto-imported (if enabled)
  components/           # Auto-imported (if enabled)
  hooks/                # Auto-imported (if enabled)
  services/             # Auto-imported (if enabled)
  stores/               # Auto-imported (if enabled)
  utils/                # Auto-imported (if enabled)
  public/               # Copied to /<layer-name>/ in build
  layer.config.ts       # Optional layer configuration
  index.ts              # Optional exports

Entrypoint discovery works exactly like WXT:

  • entrypoints/popup.html or entrypoints/popup/index.html
  • entrypoints/background.ts
  • entrypoints/content.ts or entrypoints/content/index.ts
  • Named entrypoints: entrypoints/content.twitter.ts

See Layer Options for manual entrypoint configuration.

Common Use Cases

Core Extension Logic

Use WXT's base structure for shared functionality:

entrypoints/          # Main extension entrypoints
  popup.html
  background.ts
composables/          # Shared composables
  useStorage.ts
  useSettings.ts
utils/                # Shared utilities
  logger.ts
  api.ts
public/               # Shared assets
  icon.png

Feature Layers

Isolate specific features in their own layers:

layers/
  analytics/
    entrypoints/
      content.ts      # Track page views
    services/
      tracker.ts
    public/
      icons/
  auth/
    entrypoints/
      background.ts   # Handle auth state
      popup.html      # Login UI
    composables/
      useAuth.ts
  theme-switcher/
    entrypoints/
      content.ts
    composables/
      useTheme.ts

Import between layers using aliases:

// layers/analytics/services/tracker.ts
import { useAuth } from '#auth'  // Access auth layer

Package Layers

Create reusable layers for common functionality across projects:

layers/
  error-reporting/    # Reusable Sentry integration
  feature-flags/      # Reusable feature flag system
  telemetry/          # Reusable analytics

Import Strategies

Different ways to import code in your extension:

Auto-imports (Global)

No import needed - available everywhere:

// layers/analytics/composables/useTracking.ts
export function useTracking () { /* ... */ }

// Anywhere:
const track = useTracking()  // Just works

Best for: Composables, hooks, utilities used frequently across the extension.

Source Imports (~/)

Import from your main src/ directory:

import { logger } from '~/utils/logger'
import type { User } from '~/types'

Best for: Core utilities, types, and shared code in your base WXT structure.

Layer Local (Relative)

Import from the same layer:

// Within layers/analytics/
import { TrackerService } from '../services/tracker'
import type { Event } from '../types'

Best for: Internal layer code that shouldn't be exposed outside.

Layer Global (Aliases)

Import from other layers:

import { AuthService } from '#auth'
import { trackEvent } from '#analytics'

Best for: Cross-layer dependencies, services, and types.

[!IMPORTANT] Restart your TypeScript server if layer aliases don't resolve:

  • VS Code: Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"
  • WebStorm: Shift + Shift → "Restart TypeScript Service"

Options

Module Options

Configure in wxt.config.ts:

export default defineConfig({
  modules: ['wxt-module-layers'],
  layers: {
    // Where to find layers (default: 'layers/*')
    sources: [
      'layers/*',           // All folders under /layers/
      'src/packages/*',     // All folders under /src/packages/
      'vendor/analytics',   // Single specific layer
    ],

    // Default alias template (default: true → '#{name}')
    layerAlias: '#{name}',  // #analytics, #auth, etc.

    // Default auto-import folders (default: false)
    autoImports: true,      // Enable default folders
    // autoImports: ['composables', 'utils'],  // Specific folders

    // Default public file prefix (default: '{name}')
    publicPrefix: '{name}',  // /analytics/, /auth/, etc.

    // Logging level (default: 'info')
    logLevel: 'debug',  // Use for troubleshooting
  }
})

[!TIP] Use logLevel: 'debug' to see exactly what the module discovers and registers

Layer Options

Most layers work fine with defaults. Configure when you need to:

  • Control background script execution order
  • Use non-standard folder structures
  • Modify the extension manifest
// layers/analytics/layer.config.ts
import { defineLayer } from 'wxt-module-layers'

export default defineLayer({
  // Control background script order (default: 50, lower = earlier)
  order: 10,

  // Manually specify entrypoints (bypasses auto-discovery)
  entrypoints: {
    'background': 'background/index.ts',
    'content.linkedin': 'content/linkedin.ts',
    'content.twitter': 'content/twitter.ts',
  },

  // Modify extension manifest
  manifest: (wxt, manifest) => {
    manifest.permissions?.push('storage', 'cookies')
    manifest.host_permissions?.push('*://*.example.com/*')
  },

  // Override module defaults (rarely needed)
  layerAlias: '@analytics',
  autoImports: ['composables', 'services'],
  publicPrefix: 'tracking',
})

Entrypoints

Default Folder Structure

Layer entrypoints work exactly like WXT - place files in entrypoints/:

layers/my-layer/
  entrypoints/
    background.ts       # Background script
    popup.html          # Popup UI
    content.ts          # Content script
    sidepanel.html      # Sidepanel

All standard WXT entrypoint patterns are supported.

Multiple Layer Backgrounds

If you have multiple layer backgrounds (and no src background), a virtual module automatically runs them in order:

layers/
  core/entrypoints/background.ts
  analytics/entrypoints/background.ts
// layers/core/entrypoints/background.ts
export default defineBackground(async () => {
  // run code (even async code)
  console.log('Core background ready')
  
  // pass data to the next background layer
  return { state: 123 }
})

Output:

✓ Created virtual entrypoint for 2 layer backgrounds

Execution order is controlled by the order property in layer config (lower runs first):

// layers/core/layer.config.ts
export default defineLayer({
  order: 10,  // Runs first (default is 50)
})

With Main Background

If you have a main entrypoints/background.ts, manually initialize layer backgrounds:

// entrypoints/background.ts
import coreBackground from '#core/entrypoints/background'
import analyticsBackground from '#analytics/entrypoints/background'

export default defineBackground(() => {
  coreBackground()
  analyticsBackground()

  // Your main logic
  console.log('Main background ready')
})

The module will warn you if manual initialization is needed.

Manual Entrypoints

Use manual configuration for non-standard structures:

layers/my-feature/
  background/
    index.ts
    services/
  panel/
    index.html
    services/
// layers/my-feature/layer.config.ts
export default defineLayer({
  entrypoints: {
    background: 'background/index.ts',
    sidepanel: 'panel/index.html',
  }
})

[!TIP] You can set default entrypoints in module options to configure all layers at once (missing entrypoints are simply skipped).

Named Content Scripts

Target different sites with separate content scripts:

export default defineLayer({
  entrypoints: {
    'content.linkedin': 'content/linkedin.ts',
    'content.twitter': 'content/twitter.ts',
    'content.github': 'content/github.ts',
  }
})

Each can specify its own match patterns:

// entrypoints/content.linkedin.ts
export default defineContentScript({
  matches: ['*://*.linkedin.com/*'],
  main () {
    // LinkedIn-specific logic
  }
})

Debugging

Enable Debug Logging

export default defineConfig({
  layers: {
    logLevel: 'debug',
  }
})

Output shows what's discovered:

[layers] Scanning 2 layer folders...
[layers] Processing layer: layers/analytics
[layers]   - alias: #analytics
[layers]   - entrypoint: content.ts (content)
[layers]   - auto-imports: composables, services

Common Issues

Aliases not resolving:

  • Restart TypeScript server
  • Verify layerAlias isn't false

Auto-imports not working:

  • Check autoImports is enabled
  • Verify WXT's imports aren't disabled

Entrypoints not found:

  • Use debug logging to see what's scanned
  • Check file naming matches WXT conventions
  • Check no duplicate entrypoint names
  • Consider manual entrypoints config