pwagenerator

🤖 PWA Generator is a static site generator that builds and deploys Progressive Web Apps to Netlify

15
2
PHP

PWA Generator

About

PWA Generator is a static site generator that builds static websites based on the project configuration specified in projects.json. It can generate everything needed for a website to functions a a progressive web app, e.g. it functions offline and it is installable. It can also deploy it to Netlify.

Usage

List all projects configured for building in projects.json

./pwagenerator.php projects

Build, deploy, and/or generate icons for a project using project configuration in projects.json

./pwagenerator.php [project] [option]
options:
	-v verbose
	-b build
	-i generate icons
	-d deploy

For example:

./pwagenerator exampleproject.com -b

To create a new project, add it to projects.json, then build it.

Options

  • Verbose (-v): Provides a more verbose output for the other option selected (-b, -i, or -d),
  • Build (-b): Builds the project. During this process, PWA Generator generates all files needed for the project to function. See Build Process for more information.
  • Generate Icons (-i): Generates icons for the project. During this process, PWA Generator sets up a config array and generates a set of favicons from the img/favicon.svg file in the project directory using real-favicon.
  • Deploy (-d): Deploys the project. During this process, PWA Generator increments the project version and deploys the project directory to Netlify using the netlify_id set in projects.json and netlify-cli.

File Structure

files Directory

The files directory contains files that are copied into the project when it is built.

Fonts in the fonts directory will only be copied over if they are listed in the fonts object in projects.json, e.g.:

"fonts": {
	"heading": "Roboto Slab",
	"body": "Roboto"
}

Files in the opt directory will only be copied over if they are listed in the opt_files object in projects.json, e.g.:

"opt_files": [
	"opt/google_auth.js"
]

inc Directory

The inc directory contains files that are used to execute project tasks.

  • class-build.php contains functionality for building, deploying, and generating favicons.
  • class-cli.php contains functionality for command line operations.
  • class-projects.php contains functionality for getting projects from projects.json.
  • class-text.php contains functionality for formatting text.

templates and template-parts Directories

The templates and template-parts directories contain files that are used for building pages.

Project File Structure

When a project is built for the first time, several files and directories are created in the project directory.

  • index.php: Will not get overwritten by future builds and should contain any markup and functionality that are specific to the project.
  • js/main.ts: Will not get overwritten by future builds and should contain any functionality that is specific to the project.
  • scss/style.scss: Will not get overwritten by future builds and should contain any styles that are specific to the project.
  • _redirects: Netlify redirects file. Can contain redirect and rewrite rules.
  • .gitignore: Git ignore file. Contains a list of files and folders that Git should not track.
  • manifest.json: Web app manifest file. Contains data that tells the browser about the progressive web app and how it should behave when installed.
  • package.json: npm project data file. Contains project data for the npm package manager.
  • package-lock.json: npm dependency version file. Contains dependency versions for packages installed by the npm package manager. It is automatically generated by npm when a package is installed.
  • robots.txt: Robots file. Contains a list of rules to help search engines determine which pages they should and shouldn’t crawl.
  • sitemap.xml: Sitemap file. Contains a list of pages to help search engines with content discovery and thereby improves search engine optimization (SEO).
  • sw.js: Service worker file. Contains functionality which caches assets and allows for offline browsing and a downloadable “app-like” experience.
  • tsconfig.json: Webpack TypeScript compilation configuration file.
  • webpack.config.js: Webpack configuation file for generating the bundle.[hash].js from the js/*.ts and scss/*.scss files.

fonts Directory

The fonts directory contains font files that are copied over from files/fonts during the build process. Any changes made in this directory will get overwritten by future builds.

img Directory

The img directory contains images and files that are generated via the -i command. A favicon.svg should be placed in this directory prior to running that command.

This directory should also contain:

  • Any images included in the cache_files object in projects.json. If included in cache_files and in the nav.image object in projects.json, the image would be used in the top header nav bar. If included in cache_files and in the header.image object in projects.json, the image would be used in the main header.
  • A share.jpg image used for social media sharing.
  • A featured image for any posts included in posts.json, which should be named the URL of the post with a file extension of .jpg.
  • Any images included in the screenshots object in projects.json.

js Directory

The js directory contains .ts files that are copied over from files/js during the build process, and a bundle.[hash].js file that is generated from these .ts files and files in the scss directory.

This directory also contains a main.ts file, which will be generated as a blank file on the initial build. It will not get overwritten by future builds and should contain any functionality specific to the project.

This directory can also optionally contain a main.js, if an uncompiled file is needed. If this is needed, this file should also be included in the cache_files object in projects.json.

scss Directory

The scss directory contains .scss files that are copied over from files/scss during the build process.

This directory also contains a style.scss file, which will be generated on the initial build as a file containing only a :root {} declaration containing the fonts and colors from the fonts and colors objects in projects.json. It will not get overwritten by future builds and should contain any styles specific to the project.

Build Process

  1. Gets the project configuration from projects.json depending on the project passed in via the command line.
  2. Constructs several arrays that are used later in the build process.
    • The $this->project_data['files']['compile'] array is used to compile files into the project directory.
      • Pages are added to $this->project_data['files']['compile']
    • The $this->project_data['files']['copy'] array is used to copy files into the project directory.
      • Files in files/scss and files/js are added to $this->project_data['files']['copy'].
      • Files in files/opt are only added to $this->project_data['files']['copy'] if they are also listed in the opt_files object in projects.json, e.g.:
        "opt_files": [
        	"opt/google_auth.js"
        ]
        
      • Font files for the heading font, body font, and Fontawesome are added to $this->project_data['files']['copy'].
    • The $this->project_data['files']['cache'] array is used to add files to the sw.js file for caching by the service worker.
      • Files used to generate pages, data.json (used to generate additional pages to create an app-like search experience) and posts.json (used to generate blog posts), are added to $this->project_data['files']['cache'].
      • Files listed in the cache_files object in projects.json are added to $this->project_data['files']['cache'], e.g.:
        "cache_files": [
        	"img/logo_header.svg",
        	"img/logo_nav.svg",
        	"img/shortcut.png"
        ]
        
      • Files in files/opt are only added to $this->project_data['files']['cache'] if they are also listed in the opt_files object in projects.json, e.g.:
        "opt_files": [
        	"opt/google_auth.js"
        ]
        
      • Pages are added to $this->project_data['files']['cache']
        • If the page is a post (e.g. extracted from posts.json), an image is also added to $this->project_data['files']['cache'], using the page URL and the .jpg file extension.
      • Font files for the heading font, body font, and Fontawesome are added to $this->project_data['files']['cache']
  3. Adds pages $this->project_data['sitemap']['urls'] for generating the sitemap.xml file later on.
  4. Creates and populates the project directory if it doesn’t already exist:
    • package.json file
    • index.php file
    • scss directory and scss/style.scss with a :root {} declaration containing the fonts and colors from the fonts and colors objects in projects.json, e.g.
    "fonts": {
    	"heading": "Roboto Slab",
    	"body": "Roboto"
    },
    "colors": {
    	"main": {
    		"normal": "92D7DA",
    		"dark": "489094"
    	},
    	"accent": {
    		"normal": "FA96C0",
    		"dark": "B34F78"
    	}
    }
    
    • js directory and js/main.ts
    • opt directory
    • .gitignore file with node_modules
    • sw.js service worker file
  5. Copies files listed in $this->project_data['files']['copy'] array into project directory.
  6. Generates a bundle.[hash].js file from the js/*.ts and scss/*.scss files provided using Webpack via webpack-cli, then uses the php command with the project, page, and project_data passed in as arguments to compile each file in $this->project_data['files']['compile'] into a minified .html file.
  7. Generates the manifest.json file. The manifest.json file is copied into the project directory, then modified according to values in projects.json (categories, screenshots, shortcuts, android_app_id, and apple_app_id).
  8. Replaces the following patterns in files in $this->project_data['files']['compile'] and $this->project_data['files']['copy']:
    • ***FILES***: Files from $this->project_data['files']['cache']
    • ***URLS***: URLs from $this->project_data['redirects'] and $this->project_data['sitemap']['urls']
    • ***REDIRECT_URLS***: URLs from $this->project_data['files']['compile'] and $this->project_data['redirects']
    • ***URL***: The project URL from $this->project_data['url']
    • ***TITLE***: The project title from $this->project_data['title']
    • ***DESCRIPTION***: The project description from $this->project_data['description']
    • ***VERSION***: The project version from $this->project_data['version'] (extracted from sw.js)
    • ***DATE***: date(‘Y-m-d’)

Fonts

These are the fonts available for using in the fonts object in projects.json, e.g.:

"fonts": {
	"heading": "Roboto Slab",
	"body": "Roboto"
}

Serif

Sans serif

Misc

Example Projects

The following open source projects have been built using PWA Generator. Check out the website, the GitHub repo, and the project configuration for projects.json below.

nicolefurlan.com / https://github.com/nikkifurls/nicolefurlan.com

{
	"url": "nicolefurlan.com",
	"title": "Nicole Furlan",
	"description": "Software engineer on a pursuit of equality, compassion, and justice for all.",
	"keywords": "nicole furlan, nicole pitts",
	"categories": [],
	"netlify_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
	"gtm_id": "G-XXXXXXXXXX",
	"fbpixel_id": null,
	"repixel_id": null,
	"google_ads": false,
	"amazon_ads": false,
	"other_ads": false,
	"google_api": {
		"google_api_client_id": null,
		"google_api_client_key": null,
		"google_api_key": null,
		"google_api_scope": null,
		"google_api_url": null,
		"google_api_callback": null
	},
	"fonts": {
		"heading": "Playfair Display",
		"body": "Raleway"
	},
	"fontawesome": true,
	"android_app_id": {
		"free": null,
		"paid": null
	},
	"apple_app_id": {
		"free": null,
		"paid": null
	},
	"screenshots": [
		"img/screenshot.png"
	],
	"shortcuts": null,
	"links": [],
	"nav": {
		"image": "img/logo_nav.svg"
	},
	"header": {
		"title": "Hi! I'm Nicole",
		"description": "I'm a Software Engineer working on building a better world for us all",
		"image": "img/background.webp",
		"image_credit": "Photo by <a href=\"https://pbase.com/tclout/root\" alt=\"Thomas Cloutier Photography\">Thomas Cloutier Photography</a>"
	},
	"social": {
		"mailto": "[email protected]",
		"facebook": null,
		"twitter": null,
		"github": "nikkifurls",
		"patreon": null,
		"paypal": "donate?hosted_button_id=M7MMF3EWQTLKG",
		"portfolio": null,
		"yelp": null,
		"tripadvisor": null,
		"custom": [
			{
				"url": null,
				"label": null,
				"text": null,
				"link": null
			}
		]
	},
	"author": {
		"name": "Nicole Furlan",
		"twitter": "nicolemfurlan"
	},
	"redirects": [
		{
			"from": "/donate",
			"to": "https://www.paypal.com/donate?hosted_button_id=M7MMF3EWQTLKG"
		}
	],
	"pages": [],
	"cache_files": [
		"img/background.webp",
		"img/logo_nav.svg"
	],
	"opt_files": []
}

dogsafefoods.com / https://github.com/nikkifurls/nicolefurlan.com

{
	"url": "dogsafefoods.com",
	"title": "Dog Safe Foods",
	"description": "Sharing food with your dog? Make sure it's safe first",
	"keywords": "dog safe foods, dog safe human food, dog safe human foods, safe for dogs to eat, can dogs eat, can my dog eat, can dogs have, can my dog have, good human food for dogs, food dog can eat",
	"categories": [
		"pets",
		"food"
	],
	"netlify_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
	"gtm_id": "G-XXXXXXXXXX",
	"fbpixel_id": "xxxxxxxxxxxxxxx",
	"repixel_id": "xxxxxxxxxxxxxxxxxxxxxxxx",
	"google_ads": true,
	"amazon_ads": false,
	"other_ads": false,
	"google_api": {
		"google_api_client_id": null,
		"google_api_client_key": null,
		"google_api_key": null,
		"google_api_scope": null,
		"google_api_url": null,
		"google_api_callback": null
	},
	"fonts": {
		"heading": "Roboto Slab",
		"body": "Roboto"
	},
	"fontawesome": true,
	"android_app_id": {
		"free": "com.dog_safe_foods.twa",
		"paid": "com.dogsafefoods.twa"
	},
	"apple_app_id": {
		"free": null,
		"paid": null
	},
	"screenshots": [
		"img/screenshot1.png",
		"img/screenshot2.png",
		"img/screenshot3.png"
	],
	"shortcuts": [
		{
			"name": "View Safe Foods",
			"description": "View all foods that are safe for dogs to eat",
			"url": "./safe",
			"icons": [
				{
					"src": "img/icon-thumbs-up.png",
					"sizes": "512x512"
				}
			]
		},
		{
			"name": "View Unsafe Foods",
			"description": "View all foods that are not safe for dogs to eat",
			"url": "./unsafe",
			"icons": [
				{
					"src": "img/icon-thumbs-down.png",
					"sizes": "512x512"
				}
			]
		}
	],
	"links": [],
	"nav": {
		"image": "img/logo_nav.svg"
	},
	"header": {
		"title": null,
		"description": null,
		"image": "img/logo_header.svg",
		"image_credit": null
	},
	"social": {
		"mailto": "[email protected]",
		"facebook": null,
		"twitter": "dogsafefoods",
		"github": "nikkifurls/dogsafefoods",
		"patreon": null,
		"paypal": "donate?hosted_button_id=M7MMF3EWQTLKG",
		"portfolio": "nicolefurlan.com",
		"yelp": null,
		"tripadvisor": null,
		"custom": [
			{
				"url": "https://catsafefoods.com",
				"label": "Cat Safe Foods",
				"text": null,
				"link": "<i class='fas fa-cat'></i>"
			}
		]
	},
	"author": {
		"name": "Nicole Furlan",
		"twitter": "nicolemfurlan"
	},
	"redirects": [
		{
			"from": "/healthy",
			"to": "/safe 301!"
		},
		{
			"from": "/unhealthy",
			"to": "/unsafe 301!"
		},
		{
			"from": "/healthy.html",
			"to": "/safe 301!"
		},
		{
			"from": "/unhealthy.html",
			"to": "/unsafe 301!"
		},
		{
			"from": "/* food=:food",
			"to": "/:food 301!"
		}
	],
	"pages": {
		"*": {
			"url": "",
			"title": "",
			"description": "Sharing ***TITLE*** with your dog? Make sure it's safe first",
			"keywords": "can dogs eat ***TITLE***, can dogs have ***TITLE***, dogs ***TITLE***, dogs and ***TITLE***, ***TITLE*** safe for dogs, ***TITLE*** bad for dogs, ***TITLE*** ok for dogs, is it safe for dogs to eat ***TITLE***, is it safe for dogs to have ***TITLE***"
		},
		"safe": {
			"url": "safe",
			"title": "Safe Foods for Dogs",
			"description": "Sharing food with your dog? Make sure it's on this list of safe foods for dogs first",
			"keywords": "healthy human food for dogs, safe human food for dogs, human food that dogs can eat, good human food for dogs"
		},
		"unsafe": {
			"url": "unsafe",
			"title": "Unsafe Foods for Dogs",
			"description": "Sharing food with your dog? Make sure it's not on this list of unsafe foods for dogs first",
			"keywords": "unhealthy human food for dogs, food not to feed dogs, bad food for dogs, bad human food for dogs"
		}
	},
	"cache_files": [
		"img/logo_header.svg",
		"img/logo_nav.svg",
		"img/icon-thumbs-up.png",
		"img/icon-thumbs-down.png",
		"ads.txt",
		"sellers.json"
	],
	"opt_files": []
}

Contributions

Contributions are welcome! If you discover an issue, or have an idea for an improvement, feel free to create an issue.

Get in Touch