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

@julien-lin/universal-pwa-cli

v1.3.20

Published

CLI to transform any web project into a PWA

Readme

@julien-lin/universal-pwa-cli

GitHub Sponsors npm version

Command-line interface for UniversalPWA - Transform any web project into a Progressive Web App (PWA) with one click.

🇫🇷 Documentation en français

Quick Start (No Installation Required)

npx @julien-lin/universal-pwa-cli init

This command will:

  • Guide you through an interactive setup
  • Generate all PWA assets (icons, manifest, service worker)
  • Inject meta tags into your HTML files

No global installation needed — npx is the recommended way to run the CLI.

Configuration

UniversalPWA peut être configuré via un fichier de configuration pour éviter de passer toutes les options en ligne de commande.

Génération automatique

npx @julien-lin/universal-pwa-cli generate-config

Cette commande génère un fichier universal-pwa.config.ts (ou .js, .json, .yaml) basé sur votre projet.

Formats supportés

  • universal-pwa.config.ts (TypeScript - recommandé)
  • universal-pwa.config.js (JavaScript)
  • universal-pwa.config.json (JSON)
  • universal-pwa.config.yaml (YAML)

Priorité de configuration

  1. Arguments CLI (priorité la plus haute)
  2. Fichier de configuration
  3. Valeurs par défaut (priorité la plus basse)

Documentation complète

Voir CONFIGURATION.md pour la documentation complète avec tous les exemples.

Usage

init Command

Initialize a PWA in your project.

⚠️ Important: Workflow for Production Builds

For projects using build tools (React, Vite, Vue, etc.), always build first, then initialize the PWA:

# 1. Build your project first (generates assets with hashes)
npm run build
# or
pnpm build
# or
yarn build

# 2. Then initialize PWA
# In interactive mode, select "Production" when prompted
# The CLI will auto-detect dist/ directory and suggest it
npx @julien-lin/universal-pwa-cli init

# Or explicitly specify output directory
npx @julien-lin/universal-pwa-cli init --output-dir dist

Why? The service worker needs to precache all your built assets (JS/CSS with hashes). If you initialize before building, the service worker won't know about the hashed filenames.

Environment Detection:

  • The CLI automatically detects your environment:
    • Production: If dist/ or build/ exists with recent files (< 24h)
    • Local: Otherwise, defaults to public/
  • Detection indicators are displayed during interactive prompts
  • You can override the detection by explicitly choosing Local or Production

Interactive Mode (Recommended)

Simply run without arguments to launch interactive prompts:

npx @julien-lin/universal-pwa-cli init

The CLI will guide you through a 2-phase workflow:

Phase 1: Environment Selection

  • Choose between Local (development) or Production (build)
  • The CLI automatically detects your environment based on the presence of dist/ or build/ directories
  • Displays detection indicators (e.g., "dist/ directory exists with 15 built files")

Phase 2: Application Configuration

  • App name (auto-detected from package.json)
  • Short name (max 12 characters, auto-generated from app name)
  • Icon source path (auto-detected from common locations)
  • Theme and background colors (suggested based on detected framework)
  • Icon generation options

All prompts include smart defaults, validation, and contextual suggestions!

Command Line Mode

npx @julien-lin/universal-pwa-cli init [options]

Options:

  • -p, --project-path <path> : Project path (default: current directory)
  • -n, --name <name> : Application name
  • -s, --short-name <shortName> : Short name (max 12 characters)
  • -i, --icon-source <path> : Source image for icons
  • -t, --theme-color <color> : Theme color (hex, e.g., #2c3e50)
  • -b, --background-color <color> : Background color (hex)
  • --skip-icons : Skip icon generation
  • --skip-service-worker : Skip service worker generation
  • --skip-injection : Skip meta-tags injection
  • -o, --output-dir <dir> : Output directory (auto-detects dist/ for React/Vite, otherwise public/)
  • --base-path <path> : Base path for deployment (e.g., /app/, /api/pwa/)

Examples:

# For production build (React/Vite)
npm run build
npx @julien-lin/universal-pwa-cli init --output-dir dist --icon-source ./logo.png

# For development or static sites
npx @julien-lin/universal-pwa-cli init \
  --name "My Application" \
  --short-name "MyApp" \
  --icon-source ./logo.png \
  --theme-color "#2c3e50"

# For deployment under a subpath
npx @julien-lin/universal-pwa-cli init \
  --name "CreativeHub" \
  --output-dir public \
  --base-path "/creativehub/"

# For API-based PWA
npx @julien-lin/universal-pwa-cli init \
  --name "PWA API" \
  --output-dir dist \
  --base-path "/api/pwa/"

Deployment Under a Subpath

If your PWA is deployed under a subpath (e.g., behind a reverse proxy or on a shared domain), use the --base-path option to ensure all resources are properly scoped.

When to Use --base-path

  • Reverse Proxy/Load Balancer: App served at /app/ instead of /
  • Multiple PWAs on Same Domain: Each PWA has its own path
  • Shared Hosting: PWA is in a subdirectory like /pwa/ or /myapp/
  • API-Mounted PWA: Served from /api/v1/pwa/

How It Works

When you specify --base-path /app/:

  • Manifest link becomes: <link rel="manifest" href="/app/manifest.json">
  • Service Worker registered at: /app/sw.js
  • All resources are scoped to the /app/ path

This ensures:

  • ✅ Manifest is found at the correct path
  • ✅ Service Worker operates in the correct scope
  • ✅ No conflicts with other apps on the same domain

Examples

Symfony Project - Deployed under /creative-hub/ path:

npm run build
npx @julien-lin/universal-pwa-cli init \
  --name "Creative Hub" \
  --output-dir public \
  --base-path "/creative-hub/"

Next.js with Custom Base Path:

pnpm build
npx @julien-lin/universal-pwa-cli init \
  --output-dir .next \
  --base-path "/dashboard/"

Static Site on Shared Hosting - Deployed at example.com/apps/myapp/:

npx @julien-lin/universal-pwa-cli init \
  --name "My App" \
  --output-dir dist \
  --base-path "/apps/myapp/"

Important Notes:

  • Base path must start with / and ideally end with /
  • The base path is used for both manifest and service worker registration
  • Ensure your web server is configured to serve the PWA files from the specified base path
  • Test that https://yourdomain/basePath/manifest.json is accessible

PWA Install Button

The CLI automatically injects a PWA install handler into your HTML. To display an install button in your application, use the exposed global functions:

Available Global Functions

  • window.installPWA() : Triggers the install prompt
  • window.isPWAInstalled() : Checks if the app is already installed
  • window.isPWAInstallable() : Checks if the app is installable

Vanilla JavaScript Example

// Check if installable and show a button
if (window.isPWAInstallable && window.isPWAInstallable()) {
  const installButton = document.createElement("button");
  installButton.textContent = "Install App";
  installButton.onclick = () => {
    window.installPWA().catch(console.error);
  };
  document.body.appendChild(installButton);
}

React Example

import { useState, useEffect } from "react";

function InstallButton() {
  const [isInstallable, setIsInstallable] = useState(false);
  const [isInstalled, setIsInstalled] = useState(false);

  useEffect(() => {
    // Check initial state
    if (window.isPWAInstalled) {
      setIsInstalled(window.isPWAInstalled());
    }
    if (window.isPWAInstallable) {
      setIsInstallable(window.isPWAInstallable());
    }

    // Listen to custom events
    const handleInstallable = () => setIsInstallable(true);
    const handleInstalled = () => {
      setIsInstalled(true);
      setIsInstallable(false);
    };

    window.addEventListener("pwa-installable", handleInstallable);
    window.addEventListener("pwa-installed", handleInstalled);

    return () => {
      window.removeEventListener("pwa-installable", handleInstallable);
      window.removeEventListener("pwa-installed", handleInstalled);
    };
  }, []);

  if (isInstalled || !isInstallable) {
    return null;
  }

  return <button onClick={() => window.installPWA?.()}>Install App</button>;
}

Custom Events

The injected script emits custom events you can listen to:

  • pwa-installable : Emitted when the app becomes installable
  • pwa-installed : Emitted after successful installation
  • pwa-install-choice : Emitted with user's choice ({ detail: { outcome: 'accepted' | 'dismissed' } })

scan Command

Scan a project and detect framework, architecture, and assets.

npx @julien-lin/universal-pwa-cli scan [options]

Options:

  • -p, --project-path <path> : Project path (default: current directory)

Example:

npx @julien-lin/universal-pwa-cli scan

Output:

  • Detected framework (React, Vue, WordPress, etc.)
  • Architecture (SPA, SSR, static)
  • Build tool
  • Assets found (JS, CSS, images, fonts)

preview Command

Preview the PWA configuration of a project.

npx @julien-lin/universal-pwa-cli preview [options]

Options:

  • -p, --project-path <path> : Project path (default: current directory)
  • --port <port> : Server port (default: 3000)
  • --open : Open in browser

Example:

npx @julien-lin/universal-pwa-cli preview --port 8080

Generated Files

After running npx @julien-lin/universal-pwa-cli init, the following files are generated:

  • manifest.json - PWA manifest file
  • sw.js - Service Worker (Workbox)
  • sw-src.js - Service Worker source (for customization)
  • icon-*.png - PWA icons in multiple sizes (72x72 to 512x512)
  • apple-touch-icon.png - Apple Touch Icon (180x180)
  • splash-*.png - Splash screens for iOS

Meta tags are automatically injected into your HTML files.

Programmatic API

You can also use the CLI as a module:

import { initCommand } from "@julien-lin/universal-pwa-cli";

const result = await initCommand({
  projectPath: "./my-project",
  name: "My App",
  iconSource: "./icon.png",
});

💝 Sponsoring

If UniversalPWA is useful to you, please consider sponsoring the project to help maintain and improve it.

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Tests
pnpm test

# Lint
pnpm lint

Links