├── .gitignore ├── .travis.yml ├── README.md ├── package.json ├── rollup.config.js ├── src ├── index.css └── index.js └── static ├── 404.html ├── icons ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png └── favicon.png ├── index.html ├── manifest.json └── sw.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | static/index.js 3 | yarn.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "7" 4 | 5 | script: npm run build 6 | 7 | deploy: 8 | fqdn: hyperapp-pwa.deployable.site 9 | local_dir: static 10 | provider: pages 11 | skip_cleanup: true 12 | github_token: $GITHUB_TOKEN 13 | on: 14 | branch: master 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hyperapp-pwa 2 | > A minimalist _progressive web app_ compliant template for hyperapp projects 3 | 4 | Once you have cloned or forked this project, use the following tasks from the project root: 5 | 6 | ``` 7 | npm install // Install all dependencies 8 | npm start // Serve app on localhost and watch source files 9 | ``` 10 | 11 | This will build the application and serve it on http://localhost:8080 12 | 13 | **DEMO:** https://hyperapp-pwa.deployable.site 14 | 15 | ## Features of this template 16 | 17 | - Very minimal config files, developer and client side dependencies 18 | - Developer task that rebuilds app when source files change using [rollup-watch](https://www.npmjs.com/package/rollup-watch) 19 | - Reloads the browser when source files change using [rollup-plugin-livereload](https://www.npmjs.com/package/rollup-plugin-livereload) 20 | - Bundle scripts supporting `ES6` and `JSX` transforms using [buble](https://www.npmjs.com/package/buble) 21 | - Preprocessing and concatenating stylesheets using using [rollup-plugin-postcss](https://www.npmjs.com/package/rollup-plugin-postcss) 22 | - Local static file server supporting HTML5 fallback using [rollup-plugin-server](https://www.npmjs.com/package/rollup-plugin-server) 23 | - Frontend application state management and routing using [hyperapp](https://www.npmjs.com/package/hyperapp) 24 | - PWA compliant resources; service worker, manifest and icons passing [lighthouse](https://github.com/GoogleChrome/lighthouse) 25 | - Static deploys to `gh-pages` when merged to master using [TravisCI](https://travis-ci.org/) 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "hyperapp-pwa", 4 | "description": "A ready-to-deploy progressive web app starter", 5 | "author": "Luke Jackson (lukejacksonn@gmail.com)", 6 | "license": "MIT", 7 | "main": "src/index.js", 8 | "scripts": { 9 | "build": "rollup -c", 10 | "start": "rollup -c -w" 11 | }, 12 | "devDependencies": { 13 | "postcss-nested": "^2.1.2", 14 | "rollup": "^0.49.2", 15 | "rollup-plugin-buble": "^0.15.0", 16 | "rollup-plugin-commonjs": "^8.2.0", 17 | "rollup-plugin-livereload": "^0.6.0", 18 | "rollup-plugin-node-resolve": "^3.0.0", 19 | "rollup-plugin-postcss": "^0.5.4", 20 | "rollup-plugin-serve": "^0.4.2", 21 | "rollup-plugin-uglify": "^2.0.1" 22 | }, 23 | "dependencies": { 24 | "hyperapp": "^1.0.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import buble from 'rollup-plugin-buble' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import resolve from 'rollup-plugin-node-resolve' 4 | import uglify from 'rollup-plugin-uglify' 5 | import serve from 'rollup-plugin-serve' 6 | import livereload from 'rollup-plugin-livereload' 7 | import postcss from 'rollup-plugin-postcss' 8 | import nested from 'postcss-nested' 9 | 10 | const prod = !process.env.ROLLUP_WATCH 11 | const dev = !!process.env.ROLLUP_WATCH 12 | 13 | export default { 14 | input: 'src/index.js', 15 | output: { 16 | file: 'static/index.js', 17 | sourcemap: dev ? 'inline' : false, 18 | format: 'iife', 19 | intro: 20 | !dev && 21 | ` 22 | history.replaceState(null, null, sessionStorage.redirect) 23 | delete sessionStorage.redirect 24 | if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js') 25 | `, 26 | }, 27 | plugins: [ 28 | postcss({ plugins: [nested()] }), 29 | resolve({ jsnext: true }), 30 | buble({ jsx: 'h' }), 31 | commonjs(), 32 | buble(), 33 | prod && uglify(), 34 | dev && livereload('static'), 35 | dev && 36 | serve({ 37 | contentBase: ['static'], 38 | historyApiFallback: true, 39 | port: 8080, 40 | }), 41 | ], 42 | } 43 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: calc(8px + 2vmin); 3 | font-family: sans-serif; 4 | background: white; 5 | } 6 | 7 | body, 8 | body * { 9 | display: block; 10 | background: none; 11 | box-sizing: border-box; 12 | margin: 0; 13 | border: 0; 14 | padding: 0; 15 | outline: 0; 16 | } 17 | 18 | body, 19 | noscript { 20 | height: 100vh; 21 | display: flex; 22 | align-items: center; 23 | justify-content: center; 24 | } 25 | 26 | div { 27 | display: flex; 28 | > * + * { 29 | margin-left: 0.38rem; 30 | } 31 | } 32 | 33 | noscript { 34 | width: 100vw; 35 | align-items: center; 36 | justify-content: center; 37 | font-weight: bold; 38 | } 39 | 40 | button { 41 | padding: 0.62rem 1rem; 42 | font-size: 0.62rem; 43 | border: 1px solid #000; 44 | font-weight: bold; 45 | cursor: pointer; 46 | touch-action: manipulation; 47 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 48 | } 49 | 50 | h1 { 51 | margin: auto; 52 | font-size: 8rem; 53 | cursor: pointer; 54 | text-align: center; 55 | } 56 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { app, h } from 'hyperapp' 2 | import './index.css' 3 | 4 | const state = { 5 | count: 0, 6 | } 7 | 8 | const actions = { 9 | reset: () => ({ count: 0 }), 10 | sum: data => ({ count }) => ({ count: count + data }), 11 | } 12 | 13 | const view = (state, actions) => ( 14 |
15 |

{state.count}

16 |
17 | 18 | 19 | 20 |
21 |
22 | ) 23 | 24 | app(state, actions, view, document.body) 25 | -------------------------------------------------------------------------------- /static/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /static/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejacksonn/hyperapp-pwa/f34bce95750728b187d8f343f7784f1669c76e3a/static/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /static/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejacksonn/hyperapp-pwa/f34bce95750728b187d8f343f7784f1669c76e3a/static/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /static/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejacksonn/hyperapp-pwa/f34bce95750728b187d8f343f7784f1669c76e3a/static/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /static/icons/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejacksonn/hyperapp-pwa/f34bce95750728b187d8f343f7784f1669c76e3a/static/icons/favicon.png -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | hyperapp-pwa 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "hyperapp-pwa", 3 | "name": "Hyperapp progressive web app template", 4 | "description": "A minimalist progressive web app compliant template for hyperapp projects", 5 | "display": "fullscreen", 6 | "scope": "/", 7 | "start_url": "/", 8 | "theme_color": "white", 9 | "background_color": "white", 10 | "orientation": "portrait", 11 | "dir": "ltr", 12 | "lang": "en", 13 | "icons": [{ 14 | "src": "/icons/apple-touch-icon.png", 15 | "sizes": "180x180", 16 | "type": "image/png" 17 | },{ 18 | "src": "/icons/android-chrome-192x192.png", 19 | "sizes": "192x192", 20 | "type": "image/png" 21 | },{ 22 | "src": "/icons/android-chrome-512x512.png", 23 | "sizes": "512x512", 24 | "type": "image/png" 25 | }] 26 | } 27 | -------------------------------------------------------------------------------- /static/sw.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', function(event) { 2 | event.waitUntil( 3 | caches.open('hyperapp-pwa') 4 | .then(cache => cache.addAll(['/index.js'])) 5 | ) 6 | }) 7 | 8 | self.addEventListener('fetch', function(event) { 9 | event.respondWith( 10 | caches.match(event.request) 11 | .then(response => response 12 | ? response 13 | : fetch(event.request) 14 | ) 15 | ) 16 | }) 17 | --------------------------------------------------------------------------------