@julien-lin/universal-pwa-cli
v1.3.20
Published
CLI to transform any web project into a PWA
Maintainers
Readme
@julien-lin/universal-pwa-cli
Command-line interface for UniversalPWA - Transform any web project into a Progressive Web App (PWA) with one click.
Quick Start (No Installation Required)
npx @julien-lin/universal-pwa-cli initThis 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-configCette 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
- Arguments CLI (priorité la plus haute)
- Fichier de configuration
- 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 distWhy? 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/orbuild/exists with recent files (< 24h) - Local: Otherwise, defaults to
public/
- Production: If
- 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 initThe 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/orbuild/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-detectsdist/for React/Vite, otherwisepublic/)--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.jsonis 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 promptwindow.isPWAInstalled(): Checks if the app is already installedwindow.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 installablepwa-installed: Emitted after successful installationpwa-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 scanOutput:
- 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 8080Generated Files
After running npx @julien-lin/universal-pwa-cli init, the following files are generated:
manifest.json- PWA manifest filesw.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 lintLinks
- Repository: https://github.com/julien-lin/UniversalPWA
- Issues: https://github.com/julien-lin/UniversalPWA/issues
- Discussions: https://github.com/julien-lin/UniversalPWA/discussions
- Contributing: https://github.com/julien-lin/UniversalPWA/blob/main/CONTRIBUTING.md
- Releases: https://github.com/julien-lin/UniversalPWA/releases
- Sponsor: https://github.com/sponsors/julien-lin
- npm Package: https://www.npmjs.com/package/@julien-lin/universal-pwa-cli
