CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vite-plugin-pwa

Zero-config PWA plugin for Vite that enables offline functionality, service workers, and web app manifest generation.

Pending
Overview
Eval results
Files

pwa-info-assets.mddocs/

PWA Information and Assets

Virtual modules providing access to PWA information, generated assets, and metadata for integrations and custom implementations.

Capabilities

PWA Information Module

Access to PWA configuration and runtime information.

// Virtual module: virtual:pwa-info
/**
 * PWA information object containing web manifest and service worker data
 * Available only when PWA is enabled and not in SSR builds
 */
const pwaInfo: PwaInfo | undefined;

interface PwaInfo {
  /** Whether PWA is running in development environment */
  pwaInDevEnvironment: boolean;
  /** Web manifest information - always present when pwaInfo is available */
  webManifest: {
    /** URL to the web manifest file */
    href: string;
    /** Whether to use credentials for manifest requests */
    useCredentials: boolean;
    /** Complete link tag for the web manifest */
    linkTag: string;
  };
  /** Service worker registration information - conditionally present */
  registerSW?: {
    /** @deprecated Use mode instead */
    inline: boolean;
    /** Registration mode - inline script or external script */
    mode: 'inline' | 'script' | 'script-defer';
    /** Path for inline service worker registration script */
    inlinePath: string;
    /** Path for external service worker registration script */
    registerPath: string;
    /** Service worker scope */
    scope: string;
    /** Service worker type */
    type: 'classic' | 'module';
    /** Complete script tag for service worker registration */
    scriptTag?: string;
  };
}

Usage Example:

import { pwaInfo } from "virtual:pwa-info";

// Use in framework integrations or custom implementations
if (pwaInfo) {
  console.log("PWA is enabled");
  console.log("Manifest URL:", pwaInfo.webManifest.href);
  
  // Add manifest link to head
  document.head.insertAdjacentHTML("beforeend", pwaInfo.webManifest.linkTag);
  
  // Handle service worker registration
  if (pwaInfo.registerSW && pwaInfo.registerSW.scriptTag) {
    document.head.insertAdjacentHTML("beforeend", pwaInfo.registerSW.scriptTag);
  }
} else {
  console.log("PWA is disabled or in SSR mode");
}

// Framework integration example
function usePWAInfo() {
  return {
    isPWAEnabled: !!pwaInfo,
    isDevEnvironment: pwaInfo?.pwaInDevEnvironment ?? false,
    manifestUrl: pwaInfo?.webManifest.href,
    hasServiceWorker: !!pwaInfo?.registerSW,
  };
}

PWA Assets Head Module

Access to generated PWA assets for HTML head injection.

// Virtual module: virtual:pwa-assets/head
/**
 * PWA assets head configuration with links and theme color
 * Available when PWA assets generation is enabled
 */
const pwaAssetsHead: PWAAssetsHead;

interface PWAAssetsHead {
  /** Array of head link elements for PWA assets */
  links: PWAAssetHeadLink[];
  /** Theme color meta tag configuration */
  themeColor?: ColorSchemeMeta;
}

interface PWAAssetHeadLink {
  /** Optional link ID for targeting */
  id?: string;
  /** Link relationship type */
  rel: 'apple-touch-startup-image' | 'apple-touch-icon' | 'icon';
  /** Asset URL */
  href: string;
  /** Media query for responsive assets */
  media?: string;
  /** Icon sizes specification */
  sizes?: string;
  /** MIME type of the asset */
  type?: string;
}

interface ColorSchemeMeta {
  /** Meta tag name attribute */
  name: string;
  /** Meta tag content attribute */
  content: string;
}

Usage Example:

import { pwaAssetsHead } from "virtual:pwa-assets/head";

// Inject PWA assets into HTML head
function injectPWAAssets() {
  const head = document.head;
  
  // Inject icon and splash screen links
  pwaAssetsHead.links.forEach((link) => {
    const linkElement = document.createElement("link");
    linkElement.rel = link.rel;
    linkElement.href = link.href;
    
    if (link.id) linkElement.id = link.id;
    if (link.sizes) linkElement.setAttribute("sizes", link.sizes);
    if (link.type) linkElement.type = link.type;
    if (link.media) linkElement.media = link.media;
    
    head.appendChild(linkElement);
  });
  
  // Inject theme color meta tag
  if (pwaAssetsHead.themeColor) {
    const metaElement = document.createElement("meta");
    metaElement.name = pwaAssetsHead.themeColor.name;
    metaElement.content = pwaAssetsHead.themeColor.content;
    head.appendChild(metaElement);
  }
}

// Framework integration example (React)
function PWAHeadAssets() {
  return (
    <>
      {pwaAssetsHead.links.map((link, index) => (
        <link
          key={link.id || index}
          rel={link.rel}
          href={link.href}
          {...(link.sizes && { sizes: link.sizes })}
          {...(link.type && { type: link.type })}
          {...(link.media && { media: link.media })}
        />
      ))}
      {pwaAssetsHead.themeColor && (
        <meta
          name={pwaAssetsHead.themeColor.name}
          content={pwaAssetsHead.themeColor.content}
        />
      )}
    </>
  );
}

PWA Assets Icons Module

Access to generated PWA icon assets organized by type and purpose.

// Virtual module: virtual:pwa-assets/icons
/**
 * PWA assets icons collection organized by type
 * Available when PWA assets generation is enabled
 */
const pwaAssetsIcons: PWAAssetsIcons;

interface PWAAssetsIcons {
  /** Favicon icons for browser tabs */
  favicon: Record<string, PWAAssetIcon<FaviconLink>>;
  /** Transparent background icons */
  transparent: Record<string, PWAAssetIcon<HtmlLink>>;
  /** Maskable icons for adaptive icon support */
  maskable: Record<string, PWAAssetIcon<HtmlLink>>;
  /** Apple-specific touch icons */
  apple: Record<string, PWAAssetIcon<HtmlLink>>;
  /** Apple splash screen images */
  appleSplashScreen: Record<string, PWAAssetIcon<AppleSplashScreenLink>>;
}

type PWAAssetIcon<T extends HtmlLink> = Omit<IconAsset<T>, 'buffer'>;

interface IconAsset<T extends HtmlLink> {
  /** Asset name/identifier */
  name: string;
  /** Asset file path */
  src: string;
  /** Icon dimensions */
  size: { width: number; height: number };
  /** HTML link configuration */
  htmlLink: T;
}

interface HtmlLink {
  rel: string;
  href: string;
  sizes?: string;
  type?: string;
}

interface FaviconLink extends HtmlLink {
  rel: 'icon';
}

interface AppleSplashScreenLink extends HtmlLink {
  rel: 'apple-touch-startup-image';
  media: string;
}

Usage Example:

import { pwaAssetsIcons } from "virtual:pwa-assets/icons";

// Access different icon types
function logIconAssets() {
  console.log("Favicon icons:", Object.keys(pwaAssetsIcons.favicon));
  console.log("Transparent icons:", Object.keys(pwaAssetsIcons.transparent));
  console.log("Maskable icons:", Object.keys(pwaAssetsIcons.maskable));
  console.log("Apple icons:", Object.keys(pwaAssetsIcons.apple));
  console.log("Apple splash screens:", Object.keys(pwaAssetsIcons.appleSplashScreen));
}

// Create manifest icons from generated assets
function createManifestIcons() {
  const icons = [];
  
  // Add transparent icons to manifest
  Object.values(pwaAssetsIcons.transparent).forEach((icon) => {
    icons.push({
      src: icon.src,
      sizes: `${icon.size.width}x${icon.size.height}`,
      type: icon.htmlLink.type || "image/png",
      purpose: "any",
    });
  });
  
  // Add maskable icons to manifest
  Object.values(pwaAssetsIcons.maskable).forEach((icon) => {
    icons.push({
      src: icon.src,
      sizes: `${icon.size.width}x${icon.size.height}`,
      type: icon.htmlLink.type || "image/png",
      purpose: "maskable",
    });
  });
  
  return icons;
}

// Framework integration example (Vue)
function usePWAIcons() {
  return {
    favicon: computed(() => pwaAssetsIcons.favicon),
    transparent: computed(() => pwaAssetsIcons.transparent),
    maskable: computed(() => pwaAssetsIcons.maskable),
    apple: computed(() => pwaAssetsIcons.apple),
    appleSplashScreen: computed(() => pwaAssetsIcons.appleSplashScreen),
    
    // Helper to get icon by size
    getIconBySize: (type: keyof PWAAssetsIcons, size: string) => {
      const icons = pwaAssetsIcons[type];
      return Object.values(icons).find(
        (icon) => `${icon.size.width}x${icon.size.height}` === size
      );
    },
    
    // Helper to create HTML links
    createHtmlLinks: (type: keyof PWAAssetsIcons) => {
      return Object.values(pwaAssetsIcons[type]).map((icon) => icon.htmlLink);
    },
  };
}

Integration Patterns

Server-Side Rendering (SSR)

Handle PWA info and assets in SSR environments:

// Check availability before use
function getSSRPWAInfo() {
  try {
    const info = pwaInfo;
    return info || null;
  } catch {
    // Module not available in SSR
    return null;
  }
}

// Conditional asset injection
function injectPWAAssetsSSR() {
  try {
    const assets = pwaAssetsHead;
    return assets.links.map(link => 
      `<link rel="${link.rel}" href="${link.href}"${
        link.sizes ? ` sizes="${link.sizes}"` : ''
      }${link.type ? ` type="${link.type}"` : ''}${
        link.media ? ` media="${link.media}"` : ''
      }>`
    ).join('\n');
  } catch {
    return '';
  }
}

Meta Framework Integration

Integration patterns for popular meta-frameworks:

// Next.js Head component integration
import Head from "next/head";
import { pwaAssetsHead } from "virtual:pwa-assets/head";

function PWAHead() {
  return (
    <Head>
      {pwaAssetsHead.links.map((link, index) => (
        <link
          key={index}
          rel={link.rel}
          href={link.href}
          sizes={link.sizes}
          type={link.type}
          media={link.media}
        />
      ))}
      {pwaAssetsHead.themeColor && (
        <meta
          name={pwaAssetsHead.themeColor.name}
          content={pwaAssetsHead.themeColor.content}
        />
      )}
    </Head>
  );
}

// Nuxt.js useHead integration
import { useHead } from "@vueuse/head";
import { pwaAssetsHead } from "virtual:pwa-assets/head";

function usePWAHead() {
  useHead({
    link: pwaAssetsHead.links,
    meta: pwaAssetsHead.themeColor ? [pwaAssetsHead.themeColor] : [],
  });
}

Dynamic Asset Loading

Load PWA assets dynamically based on conditions:

import { pwaAssetsIcons } from "virtual:pwa-assets/icons";

async function loadOptimalIcon(preferredSize = "192x192") {
  // Try to find icon with preferred size
  const transparent = Object.values(pwaAssetsIcons.transparent);
  let icon = transparent.find(
    (icon) => `${icon.size.width}x${icon.size.height}` === preferredSize
  );
  
  // Fall back to first available icon
  if (!icon && transparent.length > 0) {
    icon = transparent[0];
  }
  
  if (icon) {
    // Preload the icon
    const link = document.createElement("link");
    link.rel = "preload";
    link.as = "image";
    link.href = icon.src;
    document.head.appendChild(link);
    
    return icon;
  }
  
  return null;
}

Install with Tessl CLI

npx tessl i tessl/npm-vite-plugin-pwa

docs

index.md

plugin-configuration.md

pwa-info-assets.md

virtual-modules.md

tile.json