{"id":13433810,"url":"https://github.com/lukeed/pwa","last_synced_at":"2025-10-09T18:23:37.533Z","repository":{"id":33935269,"uuid":"138141173","full_name":"lukeed/pwa","owner":"lukeed","description":"(WIP) Universal PWA Builder","archived":false,"fork":false,"pushed_at":"2020-12-23T04:52:54.000Z","size":586,"stargazers_count":3137,"open_issues_count":19,"forks_count":101,"subscribers_count":60,"default_branch":"master","last_synced_at":"2025-10-02T00:35:28.179Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://pwa.cafe","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lukeed.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-21T08:21:51.000Z","updated_at":"2025-10-01T07:34:09.000Z","dependencies_parsed_at":"2022-09-05T11:01:35.504Z","dependency_job_id":null,"html_url":"https://github.com/lukeed/pwa","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/lukeed/pwa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpwa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpwa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpwa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpwa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lukeed","download_url":"https://codeload.github.com/lukeed/pwa/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukeed%2Fpwa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278354551,"owners_count":25973378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T02:01:36.832Z","updated_at":"2025-10-09T18:23:37.516Z","avatar_url":"https://github.com/lukeed.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","integrations","*.js","📦 Legacy \u0026 Inactive Projects"],"sub_categories":["cli tools","Node"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"logo.png\" alt=\"PWA\" height=\"200\" /\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/@pwa/cli\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/npm/v/@pwa/cli\" alt=\"version\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://travis-ci.org/lukeed/pwa\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/travis/lukeed/pwa\" alt=\"travis\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/@pwa/cli\"\u003e\n    \u003cimg src=\"https://badgen.now.sh/npm/dm/@pwa/cli\" alt=\"downloads\" /\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\n---\n\n\u003cp align=\"center\"\u003e\u003cstrong\u003eWORK IN PROGRESS\u003c/strong\u003e\u003c/p\u003e\n\n---\n\n## Features\n\n* **Framework Agnostic**\u003cbr\u003e\n  _Build with your preferred framework or with none at all!\u003cbr\u003eOfficial presets for Preact, React, Vue, and Svelte._\n\n* **Plug 'n Play**\u003cbr\u003e\n  _Don't worry about configuration, unless you want to.\u003cbr\u003ePresets and plugins are automatically applied. Just install and go!_\n\n* **Fully Extensible**\u003cbr\u003e\n  _Includes a plugin system that allows for easy, fine-grain control of your configuration... when needed._\n\n* **Feature Rich**\u003cbr\u003e\n  _Supports Babel, Bublé, Browserslist, TypeScript, PostCSS, ESLint, Prettier, and Service Workers out of the box!_\n\n* **Instant Prototyping**\u003cbr\u003e\n  _Quickly scaffold new projects with your preferred view library and toolkit.\u003cbr\u003eKick it off with a perfect Lighthouse score!_\n\n* **Static Site Generator**\u003cbr\u003e\n  _Export your routes as \"pre-rendered\" HTML.\u003cbr\u003eGreat for SEO and works on any static hosting service._\n\n\n## Installation\n\nPWA is split up into two main components ([`core`](/packages/core) and [`cli`](/packages/cli)) in addition to its list of [presets](#presets) and [plugins](#plugins).\n\n\u003e While most will opt for the CLI, the `core` module handles all configuration and can be used as a standalone module.\n\nPlease refer to each package for installation, API, and Usage information.\n\n#### Quick Start\n\n```sh\n# Install globally\n$ npm install --global @pwa/cli\n# OR\n$ yarn global add @pwa/cli\n\n# Display CLI's help text\n$ pwa --help\n\n# Generate new project\n$ pwa init\n```\n\n\u003e **Note:** The `global` modifiers are only required for _global_ command-line usage!\u003cbr\u003e\nLocal `devDependency` installation will also work, but then `pwa` usage is limited to the project.\n\n\n## Concepts\n\n\u003e Please read about [Progressive Web Apps](https://developers.google.com/web/progressive-web-apps/) if the term is unfamiliar to you.\n\n### Presets\n\nPresets are collections of [plugins](#plugins) that are tailored for a particular framework.\n\nWhile there may be \"official\" presets, this **does not** mean that PWA can only support these candidates! The current options are:\n\n* [`@pwa/preset-preact`](/packages/preset-preact)\n* [`@pwa/preset-react`](/packages/preset-react)\n* [`@pwa/preset-svelte`](/packages/preset-svelte)\n* [`@pwa/preset-vue`](/packages/preset-vue)\n\nThese packages are auto-loaded during PWA's initialization and are applied _first_, before any [Plugins](#plugins) or [custom configuration](#customizing). This means that you always have the option to override a value or setting shipped within the Preset.\n\n### Plugins\n\nPlugins are (typically) individual features or chunks of configuration that are encapsulated for easy/automatic application within your build process.\n\nWhile there may be \"official\" plugins, this **does not** mean that PWA can only support these functionalities! The current plugins include:\n\n* [`@pwa/plugin-buble`](/packages/plugin-buble)\n* [`@pwa/plugin-brotli`](/packages/plugin-brotli)\n* [`@pwa/plugin-critters`](/packages/plugin-critters)\n* [`@pwa/plugin-eslint`](/packages/plugin-eslint)\n* [`@pwa/plugin-gzip`](/packages/plugin-gzip)\n* [`@pwa/plugin-imagemin`](/packages/plugin-imagemin)\n* [`@pwa/plugin-offline`](/packages/plugin-offline)\n* [`@pwa/plugin-prettier`](/packages/plugin-prettier)\n* [`@pwa/plugin-sw-precache`](/packages/plugin-sw-precache)\n* [`@pwa/plugin-sw-workbox`](/packages/plugin-sw-workbox)\n* [`@pwa/plugin-typescript`](/packages/plugin-typescript)\n* [`@pwa/plugin-zopfli`](/packages/plugin-zopfli)\n\nThese packages are auto-loaded during PWA's initialization and are applied _second_, after any [Presets](#presets) and before [custom configuration](#customizing). This allows Plugins to override settings from Presets.\n\nPlugins may (sometimes) expose a new [key](#config-keys) on the config tree and then reference this value later in composition. This allows the end-user to change the Plugin's settings before running the build.\n\n\u003e Please see [`@pwa/plugin-critters`](https://github.com/lukeed/pwa/blob/master/packages/plugin-critters/index.js) for an example of this practice.\n\n## Commands\n\n\u003e This section applies to [`@pwa/cli`](/packages/cli) specifically.\n\n### Build\n\n\u003e Build your application for production\n\n```\n$ pwa build --help\n\n  Description\n    Build production assets\n\n  Usage\n    $ pwa build [src] [options]\n\n  Options\n    --analyze     Launch interactive Analyzer to inspect production bundle(s)\n    -o, --dest    Path to output directory  (default build)\n    -h, --help    Displays this message\n```\n\n\n### Export\n\n\u003e Export routes' HTML for static hosting\n\nInstead of `--routes`, you may define a `routes` array within [`pwa.config.js`](#customizing) config file.\n\nIf no routes are defined in either location, PWA will traverse your `\"@pages\"`-aliased directory (default: `src/pages/**`) and attempt to infer URL patterns from the file structure.\n\nIn the event that no files exist within that directory, PWA will show a warning but still scrape the index (`\"/\"`) route.\n\n```\n$ pwa export --help\n\n  Description\n    Export pre-rendered pages\n\n  Usage\n    $ pwa export [src] [options]\n\n  Options\n    -o, --dest        Path to output directory  (default build)\n    -w, --wait        Time (ms) to wait before scraping each route  (default 0)\n    -r, --routes      Comma-delimited list of routes to export\n    -i, --insecure    Launch Chrome Headless without sandbox\n    -h, --help        Displays this message\n```\n\n\u003e **Important:** Using `export` requires a local version of Chrome installed! See [`chrome-launcher`](https://www.npmjs.com/package/chrome-launcher).\u003cbr\u003eAdditionally, the `--insecure` flag launches Chrome without sandboxing. See [here](https://developers.google.com/web/updates/2017/04/headless-chrome#faq) and [here](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-fails-due-to-sandbox-issues) for help.\n\n\n### Watch\n\n\u003e Develop within a live-reload server\n\nWithin your [`pwa.config.js`](#customizing)'s `webpack` config, any/all [`devServer`](https://webpack.js.org/configuration/dev-server/) options are passed to Webpack Dev Server.\n\n```\n$ pwa watch --help\n\n  Description\n    Start development server\n\n  Usage\n    $ pwa watch [src] [options]\n\n  Options\n    -H, --host     A hostname on which to start the application  (default localhost)\n    -p, --port     A port number on which to start the application  (default 8080)\n    -q, --quiet    Disable logging to terminal, including errors and warnings\n    --https        Run the application over HTTP/2 with HTTPS\n    --key          Path to custom SSL certificate key\n    --cert         Path to custom SSL certificate\n    --cacert       Path to custom CA certificate override\n    -h, --help     Displays this message\n```\n\n### Build vs Export\n\nExport can be thought of as \"Build 2.0\" \u0026mdash; it spins up a [Headless Chrome browser](https://www.npmjs.com/package/chrome-launcher) and programmatically scrapes your routes.\n\nThis is ideal for SEO, PWA behavior, and all-around performance purposes, as your content will exist on the page _before_ the JavaScript application is downloaded, parsed, boots, and (finally) renders the content.\n\nThe generated HTML pages will be placed in your `build` directory. A `/login` route will be exported as `build/login/index.html` \u0026mdash; this makes it compatible with even the \"dumbest\" of static hosting services!\n\n\u003e **Note:** Running `export` will automatically run `build` before scraping.\n\n\n## Configuration\n\n### Overview\n\nAll configuration within the PWA tree is ***mutable***! [Presets](#presets), [Plugins](#plugins), and your [custom config](#customizing) file write into the same object(s). This is great for composability and extensibility, but _be warned_ that your custom config _may_ break the build if you're not careful.\n\n\u003e :bulb: Official presets \u0026 plugins are controlled releases and are ensured to play nicely with one another.\n\nThe config object(s) for your project are assembled in this sequence:\n\n1) **Presets:** All non-`webpack` config keys\n2) **Plugins:** All non-`webpack` config keys\n3) **Custom:** All non-`webpack` config keys\n4) **Presets:** The `webpack` config key, if any\n5) **Plugins:** The `webpack` config key, if any\n6) **Custom:** The `webpack` config key, if any\n\nBecause the final config object is passed to Webpack, internally, the `webpack` key always runs last as it composes \u0026 moves everything into its relevant loaders, plugins, etc.\n\n\u003e **Important:** When defining a [custom `webpack` key](#webpack) it **must always be a function**!\n\n\n### Mutations\n\nEvery [config key](#config-keys) can be defined or mutated in the same way!\n\nAny non-`Function` key will overwrite the existing value. This allows _strong_ opinions and/or allows a [Plugin]() to define a new config key and reference it later on.\n\nAny [`Function` key](#functions) will receive the existing, _matching_ config-value for direct mutation. This is for fine-grain control over the existing config.\n\n```js\n// defaults:\nexports.hello = { foo:1, bar:2 };\nexports.world = ['How', 'are', 'you?'];\n\n// preset/plugin/custom:\nexports.hello = function (config) {\n  config.bar = 42;\n  config.baz = [7, 8, 9];\n}\nexports.world = ['I', 'am', 'fine'];\nexports.HOWDY = 'PARTNER!';\n\n// result:\nexports.hello = {\n  foo: 1,\n  bar: 42,\n  baz: [7, 8, 9]\n}\nexports.world = ['I', 'am', 'fine'];\nexports.HOWDY = 'PARTNER!';\n```\n\n### Functions\n\nAny config key that is a function will have the signature of `(config, env, opts)`.\n\n#### config\nType: `Mixed`\n\nThis will be the _existing_ value for the current key. It will typically be an Object, but not always.\n\nIt will also be `undefined` if/when defining a new config key \u0026mdash; if you know that to be the case, you shouldn't be using a Function~!\n\n#### env\nType: `Object`\n\nWill be the _environmental_ values for this command.\u003cbr\u003e\nThis is passed from [`@pwa/core`](https://github.com/lukeed/pwa/tree/master/packages/core#coresrc-opts)'s options.\n\nThe `env.cwd`, `env.src`, `env.dest`, `env.log`, `env.production` and `env.webpack` keys are always defined.\u003cbr\u003eAnything else is contextual information for the current command being run.\n\n#### opts\nType: `Object`\n\nDirect access to configuraton keys, ***except*** `webpack`.\n\nAs an example, this can be used within a [Plugin](#plugins) for gaining insight or gaining access to other packages' settings.\n\nThe default [config keys](#config-keys) (except `webpack`) will always be present here.\n\n\n### Config Keys\n\nThe following keys are defined by default within every PWA instance. You may [mutate](#mutations) or [compose](#functions) with them accordingly.\n\n#### `babel`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L1-L20)\n\nYour Babel config object.\n\n#### `css`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L22-L28)\n\nCore CSS behavior \u0026mdash; see [`css-loader`](https://github.com/webpack-contrib/css-loader#options) for options.\n\n#### `html`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L30-L41)\n\nYour HTML plugin configuration \u0026mdash; see [`html-webpack-plugin`](https://github.com/jantimon/html-webpack-plugin#options) for options.\n\n#### `less`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L51)\n\nAny `less-loader` options \u0026mdash; see [`less-loader`](https://webpack.js.org/loaders/less-loader/#options) for documentation.\n\n\u003e **Note:** This is the entire loader config; you may need to include the `lessOptions` nested object.\n\n#### `postcss`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L45-L47)\n\nYour PostCSS config \u0026mdash; you may also use any config file/method that [`postcss-loader`](https://github.com/postcss/postcss-loader) accepts.\n\n\u003e **Important:** The `postcss.plugins` key cannot be a function!\n\n#### `sass`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L53)\n\nAny `sass-loader` options \u0026mdash; see [`sass-loader`](https://webpack.js.org/loaders/sass-loader/#options) for documentation.\n\nThis object will be used for _both_ `.scss` and `.sass` file extensions.\u003cbr\u003eThe `.sass` extension will automatically enforce the `indentedSyntax` option.\n\n\u003e **Note:** This is the entire loader config; you may need to include the `sassOptions` nested object.\n\n#### `stylus`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L52)\n\nAny `stylus-loader` options \u0026mdash; see [`stylus-loader`](https://www.npmjs.com/package/stylus-loader) for documentation.\n\n#### `terser`\nType: `Object`\u003cbr\u003e\nDefault: [Link](https://github.com/lukeed/pwa/blob/master/packages/core/config/index.js#L57-L70)\n\nThe options for [Terser Plugin](https://github.com/webpack-contrib/terser-webpack-plugin#options).\n\n\u003e **Note:** Expecting UglifyJS? It's no longer maintained!\u003cbr\u003eThe Terser configuration is nearly identical – simply rename `uglifyOptions` to `terserOptions` :+1:\n\n#### `webpack`\nType: `Function`\n\nThe main handler for ***all*** of PWA!\u003cbr\u003e\nWhen you define a [custom](#customizing) `webpack`, you are not overriding this function. Instead, you are manipulating Webpack's config immediately before PWA executes the build.\n\n### Browserslist\n\nThe preferred method for customizing your browser targets is thru the `browserslist` key within your `package.json` file.\n\n\u003e **Note:** When creating a new project with `pwa init`, our recommended config is automatically added for you!\n\nYou may choose to change the default values, or use [any configuration method that Browserslist accepts](https://github.com/browserslist/browserslist#queries).\n\nThe resulting array of browser targets will be automatically applied to Autoprefixer, Babel, Bublé, PostCSS, Stylelint, ...etc.\n\n\n### Customizing\n\n[Presets](#presets) and [Plugins](#plugins) are just encapsulated config mutations \u0026mdash; that's it!\n\nNow, if you want to _further_ customize your PWA build, beyond what your installed Presets \u0026 Plugins are giving you, then you can create a `pwa.config.js` in your project's root directory.\n\n\u003e **Note:** Your new `pwa.config.js` file should sit alongside your `package.json` :thumbsup:\n\nWith this file, you may [mutate](#mutations) or [compose](#functions) _any_ of the [config keys](#config-keys) that either PWA or its [Plugins](#plugins) exposes to you.\n\nHere is an example custom config file:\n\n```js\n// pwa.config.js\nconst OfflinePlugin = require('offline-plugin');\n\n// Mutate \"@pwa/plugin-eslint\" config\nexports.eslint = function (config) {\n  config.formatter = require('eslint-friendly-formatter');\n};\n\n// Add new PostCSS Plugin\nexports.postcss = function (config) {\n  config.plugins.push(\n    require('postcss-flexbugs-fixes')\n  );\n};\n\n// Export these pages during \"pwa export\" command\nexports.routes = ['/login', '/register', '/articles/hello-world'];\n\n// Update Webpack config; ENV-dependent\nexports.webpack = function (config, env) {\n  let { production, webpack } = env;\n\n  if (production) {\n    config.plugins.push(\n      new OfflinePlugin(),\n      new webpack.DefinePlugin({\n        MY_API: JSON.stringify('https://api.example.com')\n      })\n    );\n  } else {\n    config.devServer.https = true;\n    config.plugins.push(\n      new webpack.DefinePlugin({\n        MY_API: JSON.stringify('http://staging.example.com')\n      })\n    );\n  }\n};\n```\n\n\n## Credits\n\nA **huge** thank-you to [Jimmy Moon](https://github.com/ragingwind) for donating the `@pwa` organization on npm! :raised_hands: Aside from being the _perfect_ name, we wouldn't be able to have automatic preset/plugin resolution without a namespace!\n\n**Incredible thanks to the giants whose shoulders this project stands on~!** :heart:\n\nPWA was originally conceived in [2016](https://github.com/lukeed/pwa/commit/8b1c671134a5e8f64081fa2afafebcdd3f392583) but at that time, it wasn't yet possible to build it with the feature set I had in mind. Since then, an amazing amount of work has been done on [Webpack](https://webpack.js.org/) and its ecosystem, which now makes the project goals feasible.\n\nThere's no question that PWA takes inspiration from popular CLI applications, like [Preact CLI](https://github.com/developit/preact-cli), [Vue CLI](https://cli.vuejs.org/), and [Create React App](https://github.com/facebook/create-react-app). They _most definitely_ paved the way. I've used, learned from, and refined my wishlist over years while using these tools. Despite their greatness, I still found a need for a universal, framework-agnostic PWA builder that could unify all these great libraries.\n\n## License\n\nMIT © [Luke Edwards](https://lukeed.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukeed%2Fpwa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukeed%2Fpwa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukeed%2Fpwa/lists"}