@julien-lin/universal-pwa-cli
TypeScript icon, indicating that this package has built-in type declarations

1.3.20 • Public • Published

@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