├── .gitignore ├── lib ├── templates │ ├── starter-pwa │ │ ├── _config.yml │ │ ├── build │ │ │ ├── styles │ │ │ │ └── style.css │ │ │ ├── fonts │ │ │ │ └── roboto │ │ │ │ │ ├── Roboto-Bold.woff │ │ │ │ │ ├── Roboto-Bold.woff2 │ │ │ │ │ ├── Roboto-Light.woff │ │ │ │ │ ├── Roboto-Thin.woff │ │ │ │ │ ├── Roboto-Thin.woff2 │ │ │ │ │ ├── Roboto-Light.woff2 │ │ │ │ │ ├── Roboto-Medium.woff │ │ │ │ │ ├── Roboto-Medium.woff2 │ │ │ │ │ ├── Roboto-Regular.woff │ │ │ │ │ └── Roboto-Regular.woff2 │ │ │ ├── images │ │ │ │ └── icons │ │ │ │ │ ├── icon-128x128.png │ │ │ │ │ ├── icon-144x144.png │ │ │ │ │ ├── icon-152x152.png │ │ │ │ │ ├── icon-192x192.png │ │ │ │ │ └── icon-256x256.png │ │ │ ├── scripts │ │ │ │ └── app.js │ │ │ ├── manifest.json │ │ │ ├── service-worker.js │ │ │ └── index.html │ │ ├── .gitignore │ │ ├── images │ │ │ └── template-progressive-web-app.png │ │ ├── server.js │ │ ├── package.json │ │ └── package-lock.json │ ├── react-pwa │ │ ├── .babelrc │ │ ├── build │ │ │ ├── favicon.ico │ │ │ ├── img │ │ │ │ ├── forge-144.png │ │ │ │ ├── forge-196.png │ │ │ │ ├── forge-384.png │ │ │ │ ├── forge-48.png │ │ │ │ └── forge-96.png │ │ │ ├── manifest.json │ │ │ ├── index.html │ │ │ ├── sw.js │ │ │ └── bundle.js │ │ ├── .gitignore │ │ ├── client │ │ │ ├── index.js │ │ │ ├── Header.jsx │ │ │ ├── app.scss │ │ │ └── App.jsx │ │ ├── server.js │ │ ├── webpack.config.js │ │ └── package.json │ └── beginners-guide │ │ ├── build │ │ ├── favicon.ico │ │ ├── forge-bg.jpg │ │ ├── theForge.png │ │ ├── img │ │ │ ├── forge-48.png │ │ │ ├── forge-96.png │ │ │ ├── forge-144.png │ │ │ ├── forge-196.png │ │ │ └── forge-384.png │ │ ├── chromeDevTools.png │ │ ├── manifest.json │ │ ├── styles.css │ │ ├── sw.js │ │ └── index.html │ │ ├── .gitignore │ │ ├── server.js │ │ ├── package.json │ │ └── package-lock.json ├── init │ ├── AWS │ │ ├── .ebextensions │ │ │ └── nodecommand.config │ │ └── .elasticbeanstalk │ │ │ └── config.yml │ └── Firebase │ │ └── firebase.json ├── commands.js ├── firebase.js ├── generator.js ├── aws.js └── inquirer.js ├── test ├── index.js └── js │ ├── aws-test.js │ ├── firebase-test.js │ └── generator-test.js ├── postinstall.js ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /lib/templates/react-pwa/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-2"] 3 | } 4 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | require('./js/aws-test'); 2 | require('./js/firebase-test'); 3 | require('./js/generator-test'); 4 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/favicon.ico -------------------------------------------------------------------------------- /lib/init/AWS/.ebextensions/nodecommand.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | aws:elasticbeanstalk:container:nodejs: 3 | NodeCommand: "npm start" -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/styles/style.css: -------------------------------------------------------------------------------- 1 | .card { 2 | margin: 15px; 3 | } 4 | 5 | .container { 6 | text-align: center; 7 | } 8 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/favicon.ico -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/img/forge-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/img/forge-144.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/img/forge-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/img/forge-196.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/img/forge-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/img/forge-384.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/img/forge-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/img/forge-48.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/img/forge-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/react-pwa/build/img/forge-96.png -------------------------------------------------------------------------------- /postinstall.js: -------------------------------------------------------------------------------- 1 | console.log('\nThank you for downloading the Forge! 🔥 🔥 🔥\n'); 2 | console.log('For help on getting started, run forge -h\n'); 3 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/forge-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/forge-bg.jpg -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/theForge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/theForge.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/img/forge-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/img/forge-48.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/img/forge-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/img/forge-96.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/chromeDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/chromeDevTools.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/img/forge-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/img/forge-144.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/img/forge-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/img/forge-196.png -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/img/forge-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/beginners-guide/build/img/forge-384.png -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Elastic Beanstalk Files 4 | .elasticbeanstalk/* 5 | !.elasticbeanstalk/*.cfg.yml 6 | !.elasticbeanstalk/*.global.yml 7 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Elastic Beanstalk Files 4 | .elasticbeanstalk/* 5 | !.elasticbeanstalk/*.cfg.yml 6 | !.elasticbeanstalk/*.global.yml 7 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /lib/templates/beginners-guide/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Elastic Beanstalk Files 4 | .elasticbeanstalk/* 5 | !.elasticbeanstalk/*.cfg.yml 6 | !.elasticbeanstalk/*.global.yml 7 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/fonts/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/build/fonts/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /lib/templates/starter-pwa/images/template-progressive-web-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/forgepwa/the_forge/HEAD/lib/templates/starter-pwa/images/template-progressive-web-app.png -------------------------------------------------------------------------------- /lib/templates/react-pwa/client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import App from './App.jsx'; 5 | import styles from "./app.scss"; 6 | 7 | render(, document.getElementById('root')); 8 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/client/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Header = () => ( 4 |
5 | forge logo 6 |

Welcome to the Forge

7 |
8 | ); 9 | 10 | export default Header; 11 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/client/app.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | #app-container { 6 | text-align: center; 7 | font-family: Arial, Helvetica, sans-serif; 8 | } 9 | 10 | .header-container { 11 | padding: 25px; 12 | background-color: maroon; 13 | color: white; 14 | } 15 | -------------------------------------------------------------------------------- /lib/commands.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | changeDir(dir) { 3 | try { 4 | process.chdir(`${dir}`); 5 | console.log(`Navigated to directory: ${process.cwd()}`); 6 | } catch (err) { 7 | throw new Error(`Could not navigate to directory.\nError: ${err}`); 8 | } 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /lib/init/Firebase/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "build", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { 11 | "source": "**", 12 | "destination": "/index.html" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const app = express(); 5 | 6 | const PORT = 8081; 7 | 8 | app.use(express.static(path.join(__dirname, 'build'))); 9 | 10 | app.get('/', (req, res) => res.sendFile('index.html')); 11 | 12 | app.listen(PORT, console.log(`listening on... ${PORT}`)); 13 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const app = express(); 5 | 6 | const PORT = 8081; 7 | 8 | app.use(express.static(path.join(__dirname, 'build'))); 9 | 10 | app.get('/', (req, res) => res.sendFile('index.html')); 11 | 12 | app.listen(PORT, console.log(`listening on... ${PORT}`)); 13 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | 4 | const app = express(); 5 | 6 | const PORT = 8081; 7 | 8 | app.use(express.static(path.join(__dirname, 'build'))); 9 | 10 | app.get('/', (req, res) => res.sendFile('index.html')); 11 | 12 | app.listen(PORT, console.log(`listening on... ${PORT}`)); 13 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/client/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Header from './Header.jsx'; 3 | 4 | class App extends Component { 5 | render() { 6 | return ( 7 |
8 |
9 |

Please enjoy the Forge!

10 |
11 | ); 12 | } 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-pwa", 3 | "version": "1.0.0", 4 | "description": "PWA Example Template", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.16.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beginners-guide", 3 | "version": "1.0.0", 4 | "description": "A simple PWA tutorial.", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.16.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/init/AWS/.elasticbeanstalk/config.yml: -------------------------------------------------------------------------------- 1 | branch-defaults: 2 | default: 3 | environment: null 4 | group_suffix: null 5 | global: 6 | application_name: forge-app 7 | branch: null 8 | default_ec2_keyname: null 9 | default_platform: node.js 10 | default_region: us-west-2 11 | include_git_submodules: true 12 | instance_profile: null 13 | platform_name: null 14 | platform_version: null 15 | profile: eb-cli 16 | repository: null 17 | sc: null 18 | workspace_type: Application 19 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const config = { 4 | entry: './client/index.js', 5 | output: { 6 | path: path.join(__dirname, 'build'), 7 | filename: 'bundle.js', 8 | }, 9 | module: { 10 | rules: [ 11 | { test: /\.css$/, use: 'css-loader' }, 12 | { test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }, 13 | { test: /\.jsx$/, use: 'babel-loader', exclude: /node_modules/ }, 14 | { 15 | test: /\.scss$/, 16 | use: ['style-loader', 'css-loader', 'sass-loader'], 17 | }, 18 | ], 19 | }, 20 | }; 21 | 22 | module.exports = config; 23 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/scripts/app.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | window.onload = () => { 3 | const message = localStorage.getItem('message') || 'Your message will display here'; 4 | $('#message').html(message); 5 | $('#display').html(message); 6 | } 7 | 8 | $('#button').click(() => { 9 | console.log('click'); 10 | const message = $('#message').val(); 11 | console.log(message); 12 | $('#display').html(message); 13 | localStorage.setItem('message', message); 14 | }); 15 | 16 | if ('serviceWorker' in navigator) { 17 | navigator.serviceWorker 18 | .register('./service-worker.js') 19 | .then(function (reg) { reg.update(); console.log('Service Worker Registered'); }); 20 | } 21 | })(); 22 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Template PWA", 3 | "short_name": "Template PWA", 4 | "icons": [{ 5 | "src": "images/icons/icon-128x128.png", 6 | "sizes": "128x128", 7 | "type": "image/png" 8 | }, { 9 | "src": "images/icons/icon-144x144.png", 10 | "sizes": "144x144", 11 | "type": "image/png" 12 | }, { 13 | "src": "images/icons/icon-152x152.png", 14 | "sizes": "152x152", 15 | "type": "image/png" 16 | }, { 17 | "src": "images/icons/icon-192x192.png", 18 | "sizes": "192x192", 19 | "type": "image/png" 20 | }, { 21 | "src": "images/icons/icon-256x256.png", 22 | "sizes": "256x256", 23 | "type": "image/png" 24 | }], 25 | "start_url": "/index.html", 26 | "display": "standalone", 27 | "background_color": "#3E4EB8", 28 | "theme_color": "#2F3BA2" 29 | } 30 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Beginner-PWA", 3 | "name": "Beginner-PWA", 4 | "display": "standalone", 5 | "icons": [ 6 | { 7 | "src": "img/forge-48.png", 8 | "sizes": "48x48", 9 | "type": "image/png" 10 | }, 11 | { 12 | "src": "img/forge-96.png", 13 | "sizes": "96x96", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "img/forge-144.png", 18 | "sizes": "144x144", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "img/forge-196.png", 23 | "sizes": "196x196", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "img/forge-384.png", 28 | "sizes": "384x384", 29 | "type": "image/png" 30 | } 31 | ], 32 | "start_url": "index.html?launcher=true", 33 | "theme_color": "#a040a0", 34 | "background_color": "#EEEEEE" 35 | } 36 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "react-PWA", 3 | "name": "react-PWA", 4 | "display": "standalone", 5 | "icons": [ 6 | { 7 | "src": "img/forge-48.png", 8 | "sizes": "48x48", 9 | "type": "image/png" 10 | }, 11 | { 12 | "src": "img/forge-96.png", 13 | "sizes": "96x96", 14 | "type": "image/png" 15 | }, 16 | { 17 | "src": "img/forge-144.png", 18 | "sizes": "144x144", 19 | "type": "image/png" 20 | }, 21 | { 22 | "src": "img/forge-196.png", 23 | "sizes": "196x196", 24 | "type": "image/png" 25 | }, 26 | { 27 | "src": "img/forge-384.png", 28 | "sizes": "384x384", 29 | "type": "image/png" 30 | } 31 | ], 32 | "start_url": "../build/index.html?launcher=true", 33 | "theme_color": "#a040a0", 34 | "background_color": "#EEEEEE" 35 | } 36 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-pwa-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js", 9 | "start-dev": "nodemon server.js", 10 | "build": "webpack -p --watch" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "css-loader": "^0.28.11", 16 | "express": "^4.16.3", 17 | "node-sass": "^4.9.0", 18 | "react": "^16.3.2", 19 | "react-dom": "^16.3.2", 20 | "sass-loader": "^7.0.1", 21 | "style-loader": "^0.21.0", 22 | "webpack": "^4.8.3" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.26.0", 26 | "babel-loader": "^7.1.4", 27 | "babel-preset-env": "^1.6.1", 28 | "babel-preset-react": "^6.24.1", 29 | "nodemon": "^1.17.4", 30 | "webpack-cli": "^2.1.3", 31 | "webpack-dev-server": "^3.1.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/js/aws-test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const os = require('os'); 3 | const expect = require('expect'); 4 | const aws = require('../../lib/aws'); 5 | 6 | describe('AWS unit tests', () => { 7 | describe('#AWSLogin', () => { 8 | it('credentials are read from config file', async () => { 9 | const homedir = os.homedir(); 10 | const configPath = `${homedir}/.aws/config`; 11 | const contents = '[profile eb-cli]\naws_access_key_id = tester1\naws_secret_access_key = tester2\n'; 12 | fs.writeFileSync(configPath, contents, 'utf8'); 13 | const run = async () => aws.AWSLogin(); 14 | const answer = await run(); 15 | expect(answer).toEqual(contents); 16 | }); 17 | }); 18 | 19 | describe('#AWSLogout', () => { 20 | it('removes config file', () => { 21 | expect(aws.AWSLogout()).toEqual(true); 22 | }); 23 | 24 | it('will error when no config exists', () => { 25 | expect(aws.AWSLogout()).toEqual(false); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/js/firebase-test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect'); 2 | const Configstore = require('configstore'); 3 | const firebase = require('../../lib/firebase'); 4 | const pkg = require('../../package.json'); 5 | 6 | const conf = new Configstore(pkg.name); 7 | 8 | describe('Firebase unit tests', () => { 9 | describe('#FBLogin', () => { 10 | it('Firebase credentials are read from configstore', async () => { 11 | conf.set('firebase.token', 'testingtesting'); 12 | const run = async () => firebase.FBLogin(); 13 | const answer = await run(); 14 | expect(answer).toEqual('testingtesting'); 15 | }); 16 | }); 17 | 18 | describe('#getStoredFBToken', () => { 19 | it('Token is found in configstore', () => { 20 | expect(firebase.getStoredFBToken()).toEqual('testingtesting'); 21 | }); 22 | }); 23 | 24 | describe('#FBLogout', () => { 25 | it('removes configstore token', async () => { 26 | await firebase.FBLogout(); 27 | expect(firebase.getStoredFBToken()).toEqual(''); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: white; 3 | background-color: black; 4 | } 5 | 6 | .background-image { 7 | position: fixed; 8 | width: 100%; 9 | opacity: 0.55; 10 | z-index: -1; 11 | } 12 | @media only screen and (max-device-width: 767px) { 13 | .background-image { 14 | width: auto; 15 | left: -140%; 16 | } 17 | } 18 | @media only screen and (max-device-width: 1024px) { 19 | .background-image { 20 | height: 100%; 21 | width: auto; 22 | } 23 | } 24 | 25 | .logo { 26 | display: none; 27 | } 28 | 29 | @media only screen and (max-device-width: 767px) { 30 | .logo { 31 | display: block; 32 | width: 100%; 33 | margin-bottom: 25px; 34 | } 35 | 36 | .logo-bottom { 37 | display: none; 38 | } 39 | } 40 | 41 | .logo-bottom { 42 | margin-top: 25px; 43 | width: 100%; 44 | } 45 | 46 | .image { 47 | width: 100%; 48 | } 49 | 50 | .forge { 51 | color: orange; 52 | } 53 | 54 | .welcome { 55 | text-align: center; 56 | } 57 | 58 | .offline { 59 | margin-bottom: 1rem; 60 | } 61 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | the Forge React PWA 15 | 16 | 17 |
18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/sw.js: -------------------------------------------------------------------------------- 1 | var dataCacheName = 'template-pwa-react'; 2 | var cacheName = 'template-pwa-react'; 3 | var filesToCache = [ 4 | '/', 5 | './index.html', 6 | './bundle.js', 7 | "./sw.js", 8 | "./manifest.json", 9 | './img/forge-48.png', 10 | './img/forge-96.png', 11 | './img/forge-144.png', 12 | './img/forge-196.png', 13 | './img/forge-384.png', 14 | ]; 15 | 16 | self.addEventListener('install', (e) => { 17 | console.log('[ServiceWorker] Install'); 18 | e.waitUntil(caches.open(cacheName).then((cache) => { 19 | console.log('[ServiceWorker] Caching app shell'); 20 | return cache.addAll(filesToCache); 21 | })); 22 | }); 23 | 24 | self.addEventListener('activate', (e) => { 25 | console.log('[ServiceWorker] Activate'); 26 | e.waitUntil(caches.keys().then((keyList) => { 27 | return Promise.all(keyList.map((key) => { 28 | if (key !== cacheName && key !== dataCacheName) { 29 | console.log('[ServiceWorker] Removing old cache', key); 30 | return caches.delete(key); 31 | } 32 | })); 33 | })); 34 | return self.clients.claim(); 35 | }); 36 | 37 | self.addEventListener('fetch', (event) => { 38 | console.log('[Service Worker] Fetch', event.request.url); 39 | event.respondWith(fetch(event.request).catch(() => caches.match(event.request))); 40 | }); 41 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/sw.js: -------------------------------------------------------------------------------- 1 | const dataCacheName = 'beginners-guide'; 2 | const cacheName = 'beginners-guide'; 3 | const filesToCache = [ 4 | '/', 5 | './index.html', 6 | './sw.js', 7 | './manifest.json', 8 | './styles.css', 9 | './forge-bg.jpg', 10 | './img/forge-48.png', 11 | './img/forge-96.png', 12 | './img/forge-144.png', 13 | './img/forge-196.png', 14 | './img/forge-384.png', 15 | ]; 16 | 17 | self.addEventListener('install', (e) => { 18 | console.log('[ServiceWorker] Install'); 19 | e.waitUntil(caches.open(cacheName).then((cache) => { 20 | console.log('[ServiceWorker] Caching app shell'); 21 | return cache.addAll(filesToCache); 22 | })); 23 | }); 24 | 25 | self.addEventListener('activate', (e) => { 26 | console.log('[ServiceWorker] Activate'); 27 | e.waitUntil(caches.keys().then((keyList) => { 28 | return Promise.all(keyList.map((key) => { 29 | if (key !== cacheName && key !== dataCacheName) { 30 | console.log('[ServiceWorker] Removing old cache', key); 31 | return caches.delete(key); 32 | } 33 | })); 34 | })); 35 | return self.clients.claim(); 36 | }); 37 | 38 | self.addEventListener('fetch', (e) => { 39 | console.log('[Service Worker] Fetch', e.request.url); 40 | e.respondWith(fetch(e.request).catch(() => caches.match(e.request))); 41 | }); 42 | -------------------------------------------------------------------------------- /test/js/generator-test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const expect = require('expect'); 3 | const generator = require('../../lib/generator'); 4 | 5 | describe('Generator unit tests', () => { 6 | describe('#generateTemplate', () => { 7 | it('Generates the beginners-guide with local hosting', () => { 8 | console.log(process.cwd()); 9 | generator.generateTemplate('testing1', 'beginners-guide', 'Local'); 10 | const stats = fs.statSync(process.cwd()); 11 | process.chdir('..'); 12 | expect(stats.isDirectory()).toEqual(true); 13 | }); 14 | 15 | it('Generates the react-pwa with firebase hosting', () => { 16 | console.log(process.cwd()); 17 | generator.generateTemplate('testing2', 'react-pwa', 'Firebase'); 18 | const stats = fs.statSync(process.cwd()); 19 | const files = fs.readdirSync(process.cwd()).filter(file => file[0] !== '.'); 20 | process.chdir('..'); 21 | expect(files).toContain('firebase.json'); 22 | expect(stats.isDirectory()).toEqual(true); 23 | }); 24 | }); 25 | 26 | describe('#generateInits', () => { 27 | it('Generates AWS init files to testing1 directory', () => { 28 | console.log(process.cwd()); 29 | generator.generateInits('testing1'); 30 | process.chdir('../testing1'); 31 | const files = fs.readdirSync(process.cwd()); 32 | expect(files).toContain('.ebextensions'); 33 | expect(files).toContain('.elasticbeanstalk'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "the_forge", 3 | "version": "1.0.2", 4 | "description": "A quick and easy command line interface for generating and hosting a Progressive Web App", 5 | "main": "index.js", 6 | "dependencies": { 7 | "chalk": "^2.4.1", 8 | "clear": "^0.1.0", 9 | "clui": "^0.3.6", 10 | "command-line-args": "^5.0.2", 11 | "command-line-usage": "^5.0.5", 12 | "configstore": "^3.1.2", 13 | "figlet": "^1.2.0", 14 | "firebase-tools": "^3.19.0", 15 | "inquirer": "^6.0.0", 16 | "node-cmd": "^3.0.0", 17 | "opn": "^5.3.0" 18 | }, 19 | "devDependencies": { 20 | "expect": "^23.2.0", 21 | "mocha": "^5.2.0" 22 | }, 23 | "scripts": { 24 | "start": "node index.js", 25 | "test": "export NODE_ENV=test && mocha --timeout 8000 test/", 26 | "postinstall": "node postinstall.js" 27 | }, 28 | "bin": { 29 | "forge": "./index.js" 30 | }, 31 | "preferGlobal": true, 32 | "repository": { 33 | "type": "git", 34 | "url": "git+https://github.com/forgepwa/the_forge.git" 35 | }, 36 | "author": "Tiffany Yang, Christopher Washburn, Jeff Kang, Kyle Loftin", 37 | "license": "ISC", 38 | "bugs": { 39 | "url": "https://github.com/forgepwa/the_forge/issues" 40 | }, 41 | "homepage": "https://github.com/forgepwa/the_forge", 42 | "keywords": [ 43 | "pwa", 44 | "progressive web application", 45 | "firebase", 46 | "AWS", 47 | "generator", 48 | "react", 49 | "express", 50 | "node", 51 | "hosting", 52 | "deploy", 53 | "service worker", 54 | "deployment", 55 | "forge", 56 | "CLI", 57 | "mobile development", 58 | "mobile", 59 | "web development" 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/service-worker.js: -------------------------------------------------------------------------------- 1 | const dataCacheName = 'template-pwa'; 2 | const cacheName = 'template-pwa'; 3 | const filesToCache = [ 4 | '/', 5 | './fonts', 6 | './fonts/roboto', 7 | './fonts/roboto/Roboto-Bold.woff', 8 | './fonts/roboto/Roboto-Bold.woff2', 9 | './fonts/roboto/Roboto-Light.woff', 10 | './fonts/roboto/Roboto-Light.woff2', 11 | './fonts/roboto/Roboto-Medium.woff', 12 | './fonts/roboto/Roboto-Medium.woff2', 13 | './fonts/roboto/Roboto-Regular.woff', 14 | './fonts/roboto/Roboto-Regular.woff2', 15 | './fonts/roboto/Roboto-Thin.woff', 16 | './fonts/roboto/Roboto-Thin.woff2', 17 | './images', 18 | './images/icons', 19 | './images/icons/icon-128x128.png', 20 | './images/icons/icon-144x144.png', 21 | './images/icons/icon-152x152.png', 22 | './images/icons/icon-192x192.png', 23 | './images/icons/icon-256x256.png', 24 | './index.html', 25 | './manifest.json', 26 | './scripts', 27 | './scripts/app.js', 28 | './service-worker.js', 29 | './styles', 30 | './styles/style.css', 31 | ]; 32 | 33 | self.addEventListener('install', (e) => { 34 | console.log('[ServiceWorker] Install'); 35 | e.waitUntil(caches.open(cacheName).then((cache) => { 36 | console.log('[ServiceWorker] Caching app shell'); 37 | return cache.addAll(filesToCache); 38 | })); 39 | }); 40 | 41 | self.addEventListener('activate', (e) => { 42 | console.log('[ServiceWorker] Activate'); 43 | e.waitUntil(caches.keys().then((keyList) => { 44 | return Promise.all(keyList.map((key) => { 45 | if (key !== cacheName && key !== dataCacheName) { 46 | console.log('[ServiceWorker] Removing old cache', key); 47 | return caches.delete(key); 48 | } 49 | })); 50 | })); 51 | return self.clients.claim(); 52 | }); 53 | 54 | self.addEventListener('fetch', (event) => { 55 | console.log('[Service Worker] Fetch', event.request.url); 56 | event.respondWith(fetch(event.request).catch(() => (caches.match(event.request)))); 57 | }); 58 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Template PWA 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 |
30 |
31 |
32 |
33 |
34 | Your message 35 |

36 |

DISPLAY
37 |

38 |
39 |
40 | Enter your message 41 | 42 | 45 |
46 |
47 |
48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /lib/firebase.js: -------------------------------------------------------------------------------- 1 | const client = require('firebase-tools'); 2 | const pkg = require('../package.json'); 3 | const Configstore = require('configstore'); 4 | const opn = require('opn'); 5 | 6 | const conf = new Configstore(pkg.name); 7 | 8 | module.exports = { 9 | getStoredFBToken: () => conf.get('firebase.token'), 10 | 11 | async setFBToken() { 12 | return new Promise((resolve) => { 13 | console.log('Launching Firebase authentication in the browser...'); 14 | client.login.ci().then((user) => { 15 | conf.set('firebase.token', user.tokens.refresh_token); 16 | console.log('Successfully logged in. 🗝'); 17 | resolve(user.tokens.refresh_token); 18 | }).catch(() => { 19 | throw new Error('Unable to store token.'); 20 | }); 21 | }); 22 | }, 23 | 24 | async FBLogin() { 25 | try { 26 | // Run getStoredFBToken to check for token 27 | let token = this.getStoredFBToken(); 28 | // setFBToken based on token check 29 | if (!token) token = await this.setFBToken(); 30 | // assign process env FIREBASE_TOKEN to token 31 | process.env.FIREBASE_TOKEN = token; 32 | console.log('Authentication passed. 🔐\n'); 33 | return token; 34 | } catch (err) { 35 | throw err; 36 | } 37 | }, 38 | 39 | async FBLogout() { 40 | const token = this.getStoredFBToken(); 41 | await client.logout({ token }).then(() => { 42 | conf.set('firebase.token', ''); 43 | console.log('Logged out of Firebase. 👋 👋 👋\n'); 44 | return true; 45 | }).catch((err) => { 46 | throw new Error('Unable to logout.\nError:', err); 47 | }); 48 | }, 49 | 50 | deploy(firebaseName, projectDirName) { 51 | console.log('Forging 🔨, please wait...'); 52 | client.deploy({ 53 | project: firebaseName, 54 | token: process.env.FIREBASE_TOKEN, 55 | cwd: process.cwd() 56 | }).then(() => { 57 | console.log(`${projectDirName} has been deployed at: https://${firebaseName}.firebaseapp.com 😊`); 58 | opn(`https://${firebaseName}.firebaseapp.com`); 59 | process.exit(); 60 | }).catch((err) => { 61 | console.log(`Unable to deploy 😔. Please make sure that the firebase project name you entered (${firebaseName}) matches your firebase project name at https://console.firebase.google.com --- Error: ${err}`); 62 | }); 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /lib/generator.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const chalk = require('chalk'); 3 | const commands = require('./commands'); 4 | 5 | module.exports = { 6 | createDirectoryContents(templatePath, newProjectPath) { 7 | const CURR_DIR = process.cwd(); 8 | const filesToCreate = fs.readdirSync(templatePath); 9 | filesToCreate.forEach((file) => { 10 | const origFilePath = `${templatePath}/${file}`; 11 | // get stats about the current file 12 | const stats = fs.statSync(origFilePath); 13 | // Read/write files without encoding for images 14 | if (file.endsWith('.png') || file.endsWith('.jpg') || file.endsWith('.jpeg')) { 15 | const image = fs.readFileSync(origFilePath); 16 | const writePath = `${CURR_DIR}/${newProjectPath}/${file}`; 17 | fs.writeFileSync(writePath, image); 18 | } else if (stats.isFile()) { 19 | const contents = fs.readFileSync(origFilePath, 'utf8'); 20 | const writePath = `${CURR_DIR}/${newProjectPath}/${file}`; 21 | fs.writeFileSync(writePath, contents, 'utf8'); 22 | } else if (stats.isDirectory()) { 23 | fs.mkdirSync(`${CURR_DIR}/${newProjectPath}/${file}`); 24 | // recursive call 25 | this.createDirectoryContents(origFilePath, `${newProjectPath}/${file}`); 26 | } 27 | }); 28 | }, 29 | 30 | generateTemplate(projectName, projectChoice, hosting) { 31 | try { 32 | const CURR_DIR = process.cwd(); 33 | const templatePath = `${__dirname}/templates/${projectChoice}`; 34 | const initPath = `${__dirname}/init/${hosting}`; 35 | fs.mkdirSync(`${CURR_DIR}/${projectName}`); 36 | // creates the new template 37 | this.createDirectoryContents(templatePath, projectName); 38 | // adds init files to new template 39 | if (hosting !== 'Local') this.createDirectoryContents(initPath, projectName); 40 | console.log(chalk.blue(`Finished building ${projectName}`)); 41 | commands.changeDir(projectName); 42 | } catch (err) { 43 | throw new Error(`Could not generate template. Be sure to input a project name that does not already exist.\nError: ${err}`); 44 | } 45 | }, 46 | 47 | generateInits(projectName) { 48 | try { 49 | const initPath = `${__dirname}/init/AWS`; 50 | this.createDirectoryContents(initPath, projectName); 51 | console.log(chalk.blue(`Finished initializing ${projectName}`)); 52 | commands.changeDir(projectName); 53 | } catch (err) { 54 | throw new Error(`Could not create init files for this project.\nError: ${err}`); 55 | } 56 | }, 57 | } -------------------------------------------------------------------------------- /lib/aws.js: -------------------------------------------------------------------------------- 1 | const cmd = require('node-cmd'); 2 | const fs = require('fs'); 3 | const os = require('os'); 4 | const CLI = require('clui'); 5 | const inquirer = require('./inquirer'); 6 | 7 | const { Spinner } = CLI; 8 | const status = new Spinner('Forging 🔨, please wait...', ['🔥', '🔥', '🔥', '🔥', '💥', '💥', '💥', '💥', '⚡', '⚡', '⚡', '⚡', '🌋', '🌋', '🌋', '🌋']); 9 | 10 | module.exports = { 11 | async AWSLogin() { 12 | const homedir = os.homedir(); 13 | const configPath = `${homedir}/.aws/config`; 14 | try { 15 | // if fs.readFileSync Errors out then it catches 16 | const config = fs.readFileSync(configPath, 'utf8'); 17 | // if (!config.includes('profile eb-cli')) throw 'No eb profile'; 18 | console.log('Authentication passed. 🔐\n'); 19 | return config; 20 | } catch (err) { 21 | console.log('⚠️ Be sure to set up an AWS user in your account\'s IAM Management Console.\n'); 22 | const { ACCESS_KEY_ID, SECRET_ACCESS_KEY } = await inquirer.askKeysAWS(); 23 | // NOTE: Potential problem if user has multiple profiles 24 | const contents = `[profile eb-cli]\naws_access_key_id = ${ACCESS_KEY_ID}\naws_secret_access_key = ${SECRET_ACCESS_KEY}\n`; 25 | fs.writeFileSync(configPath, contents, 'utf8'); 26 | console.log('Authentication credentials set. 🔐\n'); 27 | return contents; 28 | } 29 | }, 30 | 31 | AWSLogout() { 32 | const homedir = os.homedir(); 33 | const configPath = `${homedir}/.aws/config`; 34 | try { 35 | fs.unlinkSync(configPath); 36 | console.log('\nLogged out of AWS. ✌️ ✌️ ✌️\n'); 37 | return true; 38 | } catch (err) { 39 | console.log('\nNo AWS Login credentials found.\n'); 40 | return false; 41 | } 42 | }, 43 | 44 | async createCLI(projectName) { 45 | status.message('Creating AWS project, this will take a few minutes...'); 46 | status.start(); 47 | try { 48 | cmd.get(`eb create ${projectName}`, (err) => { 49 | if (!err) { 50 | status.stop(); 51 | console.log('Deployed your project! Opening in your browser...'); 52 | cmd.get('eb open'); 53 | } else { 54 | status.stop(); 55 | throw err; 56 | } 57 | }); 58 | } catch (err) { 59 | throw new Error('Could not create AWS Application and Environment. You may need to logout with forge -o and reauthenticate your AWS credentials.'); 60 | } 61 | }, 62 | 63 | deploy() { 64 | status.message('Redeploying to AWS. This may take a few minutes...'); 65 | status.start(); 66 | try { 67 | cmd.get('eb deploy', (err) => { 68 | if (!err) { 69 | status.stop(); 70 | console.log('Redeployed your project! Opening in your browser...'); 71 | cmd.get('eb open'); 72 | } else { 73 | status.stop(); 74 | throw err; 75 | } 76 | }); 77 | } catch (err) { 78 | throw new Error('Could not deploy your project to AWS. You may need to logout with forge -o and reauthenticate your AWS credentials.\nError: ', err); 79 | } 80 | }, 81 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # the Forge 2 | "**Where code is smithed.**" 3 | 4 | Create *and* deploy Progressive Web Apps with one simple command. 5 | 6 |

7 | help screen 8 |

9 | 10 | ## Getting Started: 11 | Be sure to install the Forge with the global -g flag: 12 | ```sh 13 | npm install the_forge -g 14 | forge 15 | ``` 16 | When creating a project, you will be asked how you would like to host it. If you aren't already logged-in, you will be directed to log-in to Firebase or AWS.

17 | After authenticating, answer the questions to select what type of PWA you'd like to create and name your new project directory.

18 | Everything will be generated and deployed for you!
19 | 20 |

21 | forge 22 |

23 | 24 | ### Start smithing your PWA immediately. 25 | 26 | Your server and webpack files are pre-configured and ready to go but easy to modify to suit your needs as your app progresses. When you make changes you can run forge -r to redeploy.

27 | **A note about navigating directories**: the Forge scans for directories in your terminal's current working directory, so be sure to run the Forge from one directory above your targeted directory when using redeploy and init flags. 28 | 29 | ## How to create a new firebase project: 30 | Navigate to https://console.firebase.google.com and look for the **Add project** button -> 31 |

32 | new-project-firebase-console 33 |

34 | Follow the instructions to create the new project.

35 | After creating the project, copy its name (the bottom one) for use in the Forge CLI -> 36 |

37 | copy-project-name 38 |

39 | 40 | ## How to use the AWS deployment feature: 41 | Before you choose AWS as your deployment method, install the elastic beanstalk command line interface with homebrew. If installing with pip, be sure to configure the command path to work as **eb**.
42 | ### **AWS deployment requires these tools.** 43 | ```sh 44 | brew install awsebcli 45 | ``` 46 | 47 | ### Creating an AWS User Profile 48 | 1. Log in to your [AWS Management Console](https://aws.amazon.com/console/).
49 | 2. Click on Services and search for [IAM](https://console.aws.amazon.com/iam/home).
50 | 3. Click Users and then add a user.
51 | 4. Enter a name and select the "Programmatic access" box, then hit Next.
52 | 5. Select "Attach existing policies directly" and check the box for "AdministratorAccess", then hit Next again.
53 | 6. Click "Create User" and wait a few seconds.
54 | 7. You should now see your Access key ID and have the option to view the Secret access key, download these with the "Download .csv" button and have them ready for input into the Forge prompt. 55 | 56 | You are now ready to use the Forge to deploy a project to AWS! 👌 57 | 58 | Enjoy the Forge! 😊 59 | 60 | 61 | ## Issues / Feedback 62 | Submit issues [here](https://github.com/forgepwa/the_forge/issues). Tell us about any bugs you find or any features you'd like to suggest!
63 | [Website](https://forgepwa.com) | [Github](https://github.com/forgepwa/the_forge) | [NPM](https://npmjs.com/package/the_forge) 64 | 65 | ## Contributors 66 | [Tiffany Yang](https://github.com/tyang1) | [Christopher Washburn](https://github.com/SKChristopher) | [Jeff Kang](https://github.com/jkang215) | [Kyle Loftin](https://github.com/KALoftin) 67 | -------------------------------------------------------------------------------- /lib/inquirer.js: -------------------------------------------------------------------------------- 1 | const inquirer = require('inquirer'); 2 | const fs = require('fs'); 3 | 4 | module.exports = { 5 | askHosting: (redeploy) => { 6 | let CHOICES; 7 | if (!redeploy) CHOICES = ['AWS', 'Firebase', 'Local']; 8 | else CHOICES = ['AWS', 'Firebase']; 9 | const QUESTIONS = [ 10 | { 11 | name: 'hosting', 12 | type: 'list', 13 | message: 'How would you like to host your project?', 14 | choices: CHOICES, 15 | }, 16 | ]; 17 | return inquirer.prompt(QUESTIONS); 18 | }, 19 | askTemplateFB: () => { 20 | // Grab templates and filter out hidden files 21 | const CHOICES = fs.readdirSync(`${__dirname}/templates`).filter(file => file[0] !== '.'); 22 | const QUESTIONS = [ 23 | { 24 | name: 'firebaseName', 25 | type: 'input', 26 | message: 'What is the name of your Firebase project? (this must match the name on your firebase console)', 27 | validate(input) { 28 | if (/^([A-Za-z\-_\d])+$/.test(input)) return true; 29 | return 'Firebase project name may only include letters, numbers, underscores and hashes.'; 30 | }, 31 | }, 32 | { 33 | name: 'projectChoice', 34 | type: 'list', 35 | message: 'What project template would you like to generate?', 36 | choices: CHOICES, 37 | }, 38 | { 39 | name: 'projectName', 40 | type: 'input', 41 | message: 'Name your project:', 42 | validate(input) { 43 | if (fs.readdirSync(process.cwd()).indexOf(input) !== -1) return 'Project name must be unique.'; 44 | if (/^([A-Za-z\-_\d])+$/.test(input)) return true; 45 | return 'Project name may only include letters, numbers, underscores and hashes.'; 46 | }, 47 | }, 48 | ]; 49 | return inquirer.prompt(QUESTIONS); 50 | }, 51 | askTemplate: async () => { 52 | // Grab templates and filter out hidden files 53 | const CHOICES = fs.readdirSync(`${__dirname}/templates`).filter(file => file[0] !== '.'); 54 | const QUESTIONS = [ 55 | { 56 | name: 'projectChoice', 57 | type: 'list', 58 | message: 'What project template would you like to generate?', 59 | choices: CHOICES, 60 | }, 61 | { 62 | name: 'projectName', 63 | type: 'input', 64 | message: 'Name your project:', 65 | validate(input) { 66 | if (fs.readdirSync(process.cwd()).indexOf(input) !== -1) return 'Project name must be unique.'; 67 | if (/^([A-Za-z\-_\d])+$/.test(input) && input.length >= 4) return true; 68 | return 'Project name must be over 3 characters long and may only include letters, numbers, underscores and hashes.'; 69 | }, 70 | }, 71 | ]; 72 | return inquirer.prompt(QUESTIONS); 73 | }, 74 | askFolder: (message) => { 75 | // Grab templates and filter out hidden files, grab directories only 76 | const CHOICES = fs.readdirSync(process.cwd()).filter((file) => { 77 | if (file[0] === '.') return false; 78 | const stats = fs.statSync(file); 79 | return stats.isDirectory(); 80 | }); 81 | const QUESTIONS = [ 82 | { 83 | name: 'projectChoice', 84 | type: 'list', 85 | message, 86 | choices: CHOICES, 87 | }, 88 | ]; 89 | return inquirer.prompt(QUESTIONS); 90 | }, 91 | redeployFB: () => { 92 | // Grab templates and filter out hidden files, grab directories only 93 | const CHOICES = fs.readdirSync(process.cwd()).filter((file) => { 94 | if (file[0] === '.') return false; 95 | const stats = fs.statSync(file); 96 | return stats.isDirectory(); 97 | }); 98 | const QUESTIONS = [ 99 | { 100 | name: 'firebaseName', 101 | type: 'input', 102 | message: 'What is the name of your firebase project? (this must match the name on your firebase console)', 103 | validate(input) { 104 | if (/^([A-Za-z\-_\d])+$/.test(input)) return true; 105 | return 'Firebase project name may only include letters, numbers, underscores and hashes.'; 106 | }, 107 | }, 108 | { 109 | name: 'projectChoice', 110 | type: 'list', 111 | message: 'What project would you like to redeploy?', 112 | choices: CHOICES, 113 | }, 114 | ]; 115 | return inquirer.prompt(QUESTIONS); 116 | }, 117 | askKeysAWS: () => { 118 | const QUESTIONS = [ 119 | { 120 | name: 'ACCESS_KEY_ID', 121 | type: 'input', 122 | message: 'Please enter your AWS ACCESS_KEY_ID:', 123 | validate(input) { 124 | if (/^([A-Za-z\-_\d])+$/.test(input)) return true; 125 | return 'Access key may only include letters, numbers, underscores and hashes.'; 126 | }, 127 | }, 128 | { 129 | name: 'SECRET_ACCESS_KEY', 130 | type: 'input', 131 | message: 'Please enter your AWS SECRET_ACCESS_KEY:', 132 | }, 133 | ]; 134 | return inquirer.prompt(QUESTIONS); 135 | }, 136 | }; 137 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | Forge Beginners Guide 18 | 19 | 20 |
21 | 22 |
23 |
24 |

Welcome to the Forge beginner's guide!

25 |

26 |
27 |
28 | 29 |

What is a PWA? 🤷

30 |

The term Progressive Web Application (PWA) refers to a collection of paradigms and best practices for making a website or web application that functions similarly to a native mobile application.

31 |

All Progressive Web Apps are:

32 |
33 |
Reliable
34 |
- Capable of running and providing a UI without internet connection
35 |
- Cross-platform loading on different browsers and desktop/mobile platforms
36 |
Fast
37 |
- Available on a mobile home screen without downloading from an app service
38 |
- Loads rapidly and can cache assets to be served immediately
39 |
Engaging
40 |
- Fluid UI that looks and feels like a traditional mobile application
41 |
42 |

Progressive Web Apps need to have the following properties:

43 |
    44 |
  1. Served on HTTPS
  2. 45 |
  3. Mobile and desktop friendly
  4. 46 |
  5. All URL's load offline - Service workers cache all URL routes
  6. 47 |
  7. Have a web app manifest - JSON file with info for running on mobile platforms
  8. 48 |
  9. Load fast on bad connections - Initial download when the app starts up is small and fast
  10. 49 |
  11. Works across all major browsers
  12. 50 |
  13. Page transitions are snappy - Rendered as a single-page app with front end frameworks
  14. 51 |
52 |
53 | 71 |
72 |
73 |

Try Offline! 🤯

74 |

Try turning off your WiFi and refreshing the page. Notice that you can still navigate the site. This is because of the service workers in sw.js! If you deployed using AWS, you'll have to configure HTTPS routing and obtain an SSL certificate to enable this feature.

75 | Service Worker Cache Information 76 |
77 | 88 |

Thank you for using the Forge!

89 | the Forge logo 90 |
91 |
92 |
93 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const chalk = require('chalk'); 3 | const clear = require('clear'); 4 | const figlet = require('figlet'); 5 | const commandLineArgs = require('command-line-args'); 6 | const commandLineUsage = require('command-line-usage'); 7 | const inquirer = require('./lib/inquirer'); 8 | const firebase = require('./lib/firebase'); 9 | const aws = require('./lib/aws'); 10 | const generator = require('./lib/generator'); 11 | const commands = require('./lib/commands'); 12 | 13 | // Defines command line option flags 14 | const optionDefinitions = [ 15 | { name: 'help', alias: 'h', type: Boolean }, 16 | { name: 'init', alias: 'i', type: Boolean }, 17 | { name: 'redeploy', alias: 'r', type: Boolean }, 18 | { name: 'logout', alias: 'o', type: Boolean }, 19 | ]; 20 | const options = commandLineArgs(optionDefinitions); 21 | 22 | // Print welcome banner and welcome string 23 | const welcomeLogo = (welcomeString) => { 24 | clear(); 25 | console.log(chalk.red(figlet.textSync('\nthe Forge', { font: 'ANSI Shadow', horizontalLayout: 'full' }))); 26 | console.log(`Welcome to the Forge! ${welcomeString} 🔥 🔥 🔥\n`); 27 | }; 28 | 29 | if (options.help) { // Help flag entered, print help text 30 | welcomeLogo('This is the help prompt.'); 31 | const sections = [ 32 | { 33 | header: 'the Forge', 34 | raw: true, 35 | content: 'Run with {bold forge} to generate and host a Progressive Web App\n\nOr add one of the option flags below.', 36 | }, 37 | { 38 | header: 'Options', 39 | optionList: [ 40 | { 41 | name: 'help', 42 | alias: 'h', 43 | description: 'Print this usage guide.\n', 44 | }, 45 | { 46 | name: 'init', 47 | alias: 'i', 48 | description: `Launches the Forge's command line tool to deploy an existing project to AWS. ${chalk.yellow('NOTE')}: Project must have npm start script & user must have an AWS account.\n`, 49 | }, 50 | { 51 | name: 'redeploy', 52 | alias: 'r', 53 | description: 'Launches the Forge\'s command line tool to redeploy an existing project.\n', 54 | }, 55 | { 56 | name: 'logout', 57 | alias: 'o', 58 | description: 'Logs out of Firebase and AWS on this computer and clears any cached authentication tokens.\n', 59 | }, 60 | ], 61 | tableOptions: { 62 | columns: [ 63 | { 64 | name: 'option', 65 | noWrap: true, 66 | padding: { left: '🔥 ', right: '' }, 67 | width: 30, 68 | }, 69 | { 70 | name: 'description', 71 | width: 50, 72 | padding: { left: '', right: ' 🔥' }, 73 | }, 74 | ], 75 | }, 76 | }, 77 | { 78 | header: 'Templates', 79 | content: [ 80 | 'Here is a summary of the available templates you can generate with the Forge:', 81 | '', 82 | ' • {bold beginners-guide}: A very simple PWA that contains a basic webpage with HTML and CSS. Generate this template to read a handy guide through what PWA\'s are and how to navigate the codebase.\n', 83 | ' • {bold starter-pwa}: A more functional PWA that lets you store a message and cache it with service workers. Try going offline and refreshing this PWA to see service workers in action.\n', 84 | ' • {bold react-pwa}: A barebones React enabled PWA. This is for developers looking for a quick launching point on a React PWA. Contains a ready-to-go webpack for easy building and redeployment.\n', 85 | ], 86 | }, 87 | { 88 | content: 'Project home 🏡 : {underline https://forgepwa.com}\n\n Project repository: {underline https://github.com/forgepwa/the_forge}\n', 89 | }, 90 | ]; 91 | const usage = commandLineUsage(sections); 92 | console.log(usage); 93 | } else if (options.logout) { // Logout flag entered, initiate logout process 94 | try { 95 | aws.AWSLogout(); 96 | firebase.FBLogout(); 97 | } catch (err) { 98 | console.log(err); 99 | } 100 | } else if (options.redeploy) { // Redeploy flag entered, initiate redeployment 101 | welcomeLogo('Launching redeployment prompt.'); 102 | const run = async () => { 103 | const host = await inquirer.askHosting(true); 104 | if (host.hosting === 'Firebase') { 105 | await firebase.FBLogin(); 106 | console.log('Visit https://console.firebase.google.com to view your Firebase projects.\n'); 107 | const { firebaseName, projectChoice } = await inquirer.redeployFB(); 108 | console.log(chalk.blue(`Redeploying ${projectChoice}`)); 109 | commands.changeDir(projectChoice); 110 | firebase.deploy(firebaseName, projectChoice); 111 | console.log('*** Please refresh your loaded page to see your updates ***'); 112 | } else { // AWS redeploy 113 | await aws.AWSLogin(); 114 | const { projectChoice } = await inquirer.askFolder('What project would you like to redeploy?'); 115 | console.log(chalk.blue(`Redeploying ${projectChoice}`)); 116 | commands.changeDir(projectChoice); 117 | aws.deploy(); 118 | } 119 | }; 120 | try { 121 | run(); 122 | } catch (err) { 123 | console.log(err); 124 | } 125 | } else if (options.init) { // Init flag entered, Deploy an existing project 126 | welcomeLogo('Launching AWS deployment prompt.'); 127 | console.log('For successful deployment to AWS, please make sure your project has a npm start script set up.\n'); 128 | const run = async () => { 129 | const { projectChoice } = await inquirer.askFolder('What existing project would you like to deploy to AWS?'); 130 | await aws.AWSLogin(); 131 | await generator.generateInits(projectChoice); 132 | aws.createCLI(projectChoice); 133 | }; 134 | try { 135 | run(); 136 | } catch (err) { 137 | console.log(err); 138 | } 139 | } else { // No options, go to standard prompt 140 | welcomeLogo('Launching code generator and deployment prompt.'); 141 | const run = async () => { 142 | const host = await inquirer.askHosting(false); 143 | if (host.hosting === 'Firebase') { 144 | await firebase.FBLogin(); 145 | console.log('⚠️ Visit https://console.firebase.google.com to create a firebase project (essential to successful deployment).\n'); 146 | const { firebaseName, projectName, projectChoice } = await inquirer.askTemplateFB(); 147 | generator.generateTemplate(projectName, projectChoice, host.hosting); 148 | firebase.deploy(firebaseName, projectName); 149 | } else if (host.hosting === 'AWS') { 150 | await aws.AWSLogin(); 151 | const { projectName, projectChoice } = await inquirer.askTemplate(); 152 | generator.generateTemplate(projectName, projectChoice, host.hosting); 153 | await aws.createCLI(projectName); 154 | } else { // Local deployment 155 | const { projectName, projectChoice } = await inquirer.askTemplate(); 156 | generator.generateTemplate(projectName, projectChoice, host.hosting); 157 | console.log('Generated template for local hosting! 🔥'); 158 | console.log('To run on a localhost, navigate to the project we created for you in the terminal and run:\n'); 159 | console.log(chalk.green('npm install\n')); 160 | console.log('Then:\n'); 161 | console.log(chalk.green('npm start\n')); 162 | console.log('After your server starts up, you can go to http://localhost:8081 to see your PWA!\n'); 163 | console.log('If you\'d like to host the project we forged for you on an AWS account, run forge -i\n'); 164 | } 165 | }; 166 | try { 167 | run(); 168 | } catch (err) { 169 | console.log(err); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /lib/templates/starter-pwa/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-pwa", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.18.2", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 25 | "requires": { 26 | "bytes": "3.0.0", 27 | "content-type": "1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "1.1.2", 30 | "http-errors": "1.6.3", 31 | "iconv-lite": "0.4.19", 32 | "on-finished": "2.3.0", 33 | "qs": "6.5.1", 34 | "raw-body": "2.3.2", 35 | "type-is": "1.6.16" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.2", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 47 | }, 48 | "content-type": { 49 | "version": "1.0.4", 50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 52 | }, 53 | "cookie": { 54 | "version": "0.3.1", 55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 57 | }, 58 | "cookie-signature": { 59 | "version": "1.0.6", 60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 62 | }, 63 | "debug": { 64 | "version": "2.6.9", 65 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 66 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 67 | "requires": { 68 | "ms": "2.0.0" 69 | } 70 | }, 71 | "depd": { 72 | "version": "1.1.2", 73 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 74 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 75 | }, 76 | "destroy": { 77 | "version": "1.0.4", 78 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 79 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 80 | }, 81 | "ee-first": { 82 | "version": "1.1.1", 83 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 84 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 85 | }, 86 | "encodeurl": { 87 | "version": "1.0.2", 88 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 89 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 90 | }, 91 | "escape-html": { 92 | "version": "1.0.3", 93 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 94 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 95 | }, 96 | "etag": { 97 | "version": "1.8.1", 98 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 99 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 100 | }, 101 | "express": { 102 | "version": "4.16.3", 103 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 104 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 105 | "requires": { 106 | "accepts": "1.3.5", 107 | "array-flatten": "1.1.1", 108 | "body-parser": "1.18.2", 109 | "content-disposition": "0.5.2", 110 | "content-type": "1.0.4", 111 | "cookie": "0.3.1", 112 | "cookie-signature": "1.0.6", 113 | "debug": "2.6.9", 114 | "depd": "1.1.2", 115 | "encodeurl": "1.0.2", 116 | "escape-html": "1.0.3", 117 | "etag": "1.8.1", 118 | "finalhandler": "1.1.1", 119 | "fresh": "0.5.2", 120 | "merge-descriptors": "1.0.1", 121 | "methods": "1.1.2", 122 | "on-finished": "2.3.0", 123 | "parseurl": "1.3.2", 124 | "path-to-regexp": "0.1.7", 125 | "proxy-addr": "2.0.3", 126 | "qs": "6.5.1", 127 | "range-parser": "1.2.0", 128 | "safe-buffer": "5.1.1", 129 | "send": "0.16.2", 130 | "serve-static": "1.13.2", 131 | "setprototypeof": "1.1.0", 132 | "statuses": "1.4.0", 133 | "type-is": "1.6.16", 134 | "utils-merge": "1.0.1", 135 | "vary": "1.1.2" 136 | } 137 | }, 138 | "finalhandler": { 139 | "version": "1.1.1", 140 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 141 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 142 | "requires": { 143 | "debug": "2.6.9", 144 | "encodeurl": "1.0.2", 145 | "escape-html": "1.0.3", 146 | "on-finished": "2.3.0", 147 | "parseurl": "1.3.2", 148 | "statuses": "1.4.0", 149 | "unpipe": "1.0.0" 150 | } 151 | }, 152 | "forwarded": { 153 | "version": "0.1.2", 154 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 155 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 156 | }, 157 | "fresh": { 158 | "version": "0.5.2", 159 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 160 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 161 | }, 162 | "http-errors": { 163 | "version": "1.6.3", 164 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 165 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 166 | "requires": { 167 | "depd": "1.1.2", 168 | "inherits": "2.0.3", 169 | "setprototypeof": "1.1.0", 170 | "statuses": "1.4.0" 171 | } 172 | }, 173 | "iconv-lite": { 174 | "version": "0.4.19", 175 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 176 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 177 | }, 178 | "inherits": { 179 | "version": "2.0.3", 180 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 181 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 182 | }, 183 | "ipaddr.js": { 184 | "version": "1.6.0", 185 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 186 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 187 | }, 188 | "media-typer": { 189 | "version": "0.3.0", 190 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 191 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 192 | }, 193 | "merge-descriptors": { 194 | "version": "1.0.1", 195 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 196 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 197 | }, 198 | "methods": { 199 | "version": "1.1.2", 200 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 201 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 202 | }, 203 | "mime": { 204 | "version": "1.4.1", 205 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 206 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 207 | }, 208 | "mime-db": { 209 | "version": "1.33.0", 210 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 211 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 212 | }, 213 | "mime-types": { 214 | "version": "2.1.18", 215 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 216 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 217 | "requires": { 218 | "mime-db": "1.33.0" 219 | } 220 | }, 221 | "ms": { 222 | "version": "2.0.0", 223 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 224 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 225 | }, 226 | "negotiator": { 227 | "version": "0.6.1", 228 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 229 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 230 | }, 231 | "on-finished": { 232 | "version": "2.3.0", 233 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 234 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 235 | "requires": { 236 | "ee-first": "1.1.1" 237 | } 238 | }, 239 | "parseurl": { 240 | "version": "1.3.2", 241 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 242 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 243 | }, 244 | "path-to-regexp": { 245 | "version": "0.1.7", 246 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 247 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 248 | }, 249 | "proxy-addr": { 250 | "version": "2.0.3", 251 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 252 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 253 | "requires": { 254 | "forwarded": "0.1.2", 255 | "ipaddr.js": "1.6.0" 256 | } 257 | }, 258 | "qs": { 259 | "version": "6.5.1", 260 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 261 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 262 | }, 263 | "range-parser": { 264 | "version": "1.2.0", 265 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 266 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 267 | }, 268 | "raw-body": { 269 | "version": "2.3.2", 270 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 271 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 272 | "requires": { 273 | "bytes": "3.0.0", 274 | "http-errors": "1.6.2", 275 | "iconv-lite": "0.4.19", 276 | "unpipe": "1.0.0" 277 | }, 278 | "dependencies": { 279 | "depd": { 280 | "version": "1.1.1", 281 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 282 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 283 | }, 284 | "http-errors": { 285 | "version": "1.6.2", 286 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 287 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 288 | "requires": { 289 | "depd": "1.1.1", 290 | "inherits": "2.0.3", 291 | "setprototypeof": "1.0.3", 292 | "statuses": "1.4.0" 293 | } 294 | }, 295 | "setprototypeof": { 296 | "version": "1.0.3", 297 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 298 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 299 | } 300 | } 301 | }, 302 | "safe-buffer": { 303 | "version": "5.1.1", 304 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 305 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 306 | }, 307 | "send": { 308 | "version": "0.16.2", 309 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 310 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 311 | "requires": { 312 | "debug": "2.6.9", 313 | "depd": "1.1.2", 314 | "destroy": "1.0.4", 315 | "encodeurl": "1.0.2", 316 | "escape-html": "1.0.3", 317 | "etag": "1.8.1", 318 | "fresh": "0.5.2", 319 | "http-errors": "1.6.3", 320 | "mime": "1.4.1", 321 | "ms": "2.0.0", 322 | "on-finished": "2.3.0", 323 | "range-parser": "1.2.0", 324 | "statuses": "1.4.0" 325 | } 326 | }, 327 | "serve-static": { 328 | "version": "1.13.2", 329 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 330 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 331 | "requires": { 332 | "encodeurl": "1.0.2", 333 | "escape-html": "1.0.3", 334 | "parseurl": "1.3.2", 335 | "send": "0.16.2" 336 | } 337 | }, 338 | "setprototypeof": { 339 | "version": "1.1.0", 340 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 341 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 342 | }, 343 | "statuses": { 344 | "version": "1.4.0", 345 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 346 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 347 | }, 348 | "type-is": { 349 | "version": "1.6.16", 350 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 351 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 352 | "requires": { 353 | "media-typer": "0.3.0", 354 | "mime-types": "2.1.18" 355 | } 356 | }, 357 | "unpipe": { 358 | "version": "1.0.0", 359 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 360 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 361 | }, 362 | "utils-merge": { 363 | "version": "1.0.1", 364 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 365 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 366 | }, 367 | "vary": { 368 | "version": "1.1.2", 369 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 370 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /lib/templates/beginners-guide/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "beginners-guide", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.18.2", 23 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 24 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 25 | "requires": { 26 | "bytes": "3.0.0", 27 | "content-type": "1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "1.1.2", 30 | "http-errors": "1.6.3", 31 | "iconv-lite": "0.4.19", 32 | "on-finished": "2.3.0", 33 | "qs": "6.5.1", 34 | "raw-body": "2.3.2", 35 | "type-is": "1.6.16" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.0.0", 40 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 41 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.2", 45 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 46 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 47 | }, 48 | "content-type": { 49 | "version": "1.0.4", 50 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 51 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 52 | }, 53 | "cookie": { 54 | "version": "0.3.1", 55 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 56 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 57 | }, 58 | "cookie-signature": { 59 | "version": "1.0.6", 60 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 61 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 62 | }, 63 | "debug": { 64 | "version": "2.6.9", 65 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 66 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 67 | "requires": { 68 | "ms": "2.0.0" 69 | } 70 | }, 71 | "depd": { 72 | "version": "1.1.2", 73 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 74 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 75 | }, 76 | "destroy": { 77 | "version": "1.0.4", 78 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 79 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 80 | }, 81 | "ee-first": { 82 | "version": "1.1.1", 83 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 84 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 85 | }, 86 | "encodeurl": { 87 | "version": "1.0.2", 88 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 89 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 90 | }, 91 | "escape-html": { 92 | "version": "1.0.3", 93 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 94 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 95 | }, 96 | "etag": { 97 | "version": "1.8.1", 98 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 99 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 100 | }, 101 | "express": { 102 | "version": "4.16.3", 103 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 104 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 105 | "requires": { 106 | "accepts": "1.3.5", 107 | "array-flatten": "1.1.1", 108 | "body-parser": "1.18.2", 109 | "content-disposition": "0.5.2", 110 | "content-type": "1.0.4", 111 | "cookie": "0.3.1", 112 | "cookie-signature": "1.0.6", 113 | "debug": "2.6.9", 114 | "depd": "1.1.2", 115 | "encodeurl": "1.0.2", 116 | "escape-html": "1.0.3", 117 | "etag": "1.8.1", 118 | "finalhandler": "1.1.1", 119 | "fresh": "0.5.2", 120 | "merge-descriptors": "1.0.1", 121 | "methods": "1.1.2", 122 | "on-finished": "2.3.0", 123 | "parseurl": "1.3.2", 124 | "path-to-regexp": "0.1.7", 125 | "proxy-addr": "2.0.3", 126 | "qs": "6.5.1", 127 | "range-parser": "1.2.0", 128 | "safe-buffer": "5.1.1", 129 | "send": "0.16.2", 130 | "serve-static": "1.13.2", 131 | "setprototypeof": "1.1.0", 132 | "statuses": "1.4.0", 133 | "type-is": "1.6.16", 134 | "utils-merge": "1.0.1", 135 | "vary": "1.1.2" 136 | } 137 | }, 138 | "finalhandler": { 139 | "version": "1.1.1", 140 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 141 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 142 | "requires": { 143 | "debug": "2.6.9", 144 | "encodeurl": "1.0.2", 145 | "escape-html": "1.0.3", 146 | "on-finished": "2.3.0", 147 | "parseurl": "1.3.2", 148 | "statuses": "1.4.0", 149 | "unpipe": "1.0.0" 150 | } 151 | }, 152 | "forwarded": { 153 | "version": "0.1.2", 154 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 155 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 156 | }, 157 | "fresh": { 158 | "version": "0.5.2", 159 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 160 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 161 | }, 162 | "http-errors": { 163 | "version": "1.6.3", 164 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 165 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 166 | "requires": { 167 | "depd": "1.1.2", 168 | "inherits": "2.0.3", 169 | "setprototypeof": "1.1.0", 170 | "statuses": "1.4.0" 171 | } 172 | }, 173 | "iconv-lite": { 174 | "version": "0.4.19", 175 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 176 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 177 | }, 178 | "inherits": { 179 | "version": "2.0.3", 180 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 181 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 182 | }, 183 | "ipaddr.js": { 184 | "version": "1.6.0", 185 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 186 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 187 | }, 188 | "media-typer": { 189 | "version": "0.3.0", 190 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 191 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 192 | }, 193 | "merge-descriptors": { 194 | "version": "1.0.1", 195 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 196 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 197 | }, 198 | "methods": { 199 | "version": "1.1.2", 200 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 201 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 202 | }, 203 | "mime": { 204 | "version": "1.4.1", 205 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 206 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 207 | }, 208 | "mime-db": { 209 | "version": "1.33.0", 210 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 211 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 212 | }, 213 | "mime-types": { 214 | "version": "2.1.18", 215 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 216 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 217 | "requires": { 218 | "mime-db": "1.33.0" 219 | } 220 | }, 221 | "ms": { 222 | "version": "2.0.0", 223 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 224 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 225 | }, 226 | "negotiator": { 227 | "version": "0.6.1", 228 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 229 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 230 | }, 231 | "on-finished": { 232 | "version": "2.3.0", 233 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 234 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 235 | "requires": { 236 | "ee-first": "1.1.1" 237 | } 238 | }, 239 | "parseurl": { 240 | "version": "1.3.2", 241 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 242 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 243 | }, 244 | "path-to-regexp": { 245 | "version": "0.1.7", 246 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 247 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 248 | }, 249 | "proxy-addr": { 250 | "version": "2.0.3", 251 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 252 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 253 | "requires": { 254 | "forwarded": "0.1.2", 255 | "ipaddr.js": "1.6.0" 256 | } 257 | }, 258 | "qs": { 259 | "version": "6.5.1", 260 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 261 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 262 | }, 263 | "range-parser": { 264 | "version": "1.2.0", 265 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 266 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 267 | }, 268 | "raw-body": { 269 | "version": "2.3.2", 270 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 271 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 272 | "requires": { 273 | "bytes": "3.0.0", 274 | "http-errors": "1.6.2", 275 | "iconv-lite": "0.4.19", 276 | "unpipe": "1.0.0" 277 | }, 278 | "dependencies": { 279 | "depd": { 280 | "version": "1.1.1", 281 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 282 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 283 | }, 284 | "http-errors": { 285 | "version": "1.6.2", 286 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 287 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 288 | "requires": { 289 | "depd": "1.1.1", 290 | "inherits": "2.0.3", 291 | "setprototypeof": "1.0.3", 292 | "statuses": "1.4.0" 293 | } 294 | }, 295 | "setprototypeof": { 296 | "version": "1.0.3", 297 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 298 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 299 | } 300 | } 301 | }, 302 | "safe-buffer": { 303 | "version": "5.1.1", 304 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 305 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 306 | }, 307 | "send": { 308 | "version": "0.16.2", 309 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 310 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 311 | "requires": { 312 | "debug": "2.6.9", 313 | "depd": "1.1.2", 314 | "destroy": "1.0.4", 315 | "encodeurl": "1.0.2", 316 | "escape-html": "1.0.3", 317 | "etag": "1.8.1", 318 | "fresh": "0.5.2", 319 | "http-errors": "1.6.3", 320 | "mime": "1.4.1", 321 | "ms": "2.0.0", 322 | "on-finished": "2.3.0", 323 | "range-parser": "1.2.0", 324 | "statuses": "1.4.0" 325 | } 326 | }, 327 | "serve-static": { 328 | "version": "1.13.2", 329 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 330 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 331 | "requires": { 332 | "encodeurl": "1.0.2", 333 | "escape-html": "1.0.3", 334 | "parseurl": "1.3.2", 335 | "send": "0.16.2" 336 | } 337 | }, 338 | "setprototypeof": { 339 | "version": "1.1.0", 340 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 341 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 342 | }, 343 | "statuses": { 344 | "version": "1.4.0", 345 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 346 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 347 | }, 348 | "type-is": { 349 | "version": "1.6.16", 350 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 351 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 352 | "requires": { 353 | "media-typer": "0.3.0", 354 | "mime-types": "2.1.18" 355 | } 356 | }, 357 | "unpipe": { 358 | "version": "1.0.0", 359 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 360 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 361 | }, 362 | "utils-merge": { 363 | "version": "1.0.1", 364 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 365 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 366 | }, 367 | "vary": { 368 | "version": "1.1.2", 369 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 370 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /lib/templates/react-pwa/build/bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=21)}([function(e,t,n){"use strict";e.exports=n(20)},function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,n){"use strict";e.exports={}},function(e,t,n){"use strict";var r=function(e){};e.exports=function(e,t,n,o,a,i,l,u){if(r(t),!e){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[n,o,a,i,l,u],f=0;(c=new Error(t.replace(/%s/g,function(){return s[f++]}))).name="Invariant Violation"}throw c.framesToPop=1,c}}},function(e,t,n){"use strict"; 2 | /* 3 | object-assign 4 | (c) Sindre Sorhus 5 | @license MIT 6 | */var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,a=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,i,l=function(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),u=1;u=0&&s.splice(t,1)}function v(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),y(t,e.attrs),m(e,t),t}function y(e,t){Object.keys(t).forEach(function(n){e.setAttribute(n,t[n])})}function g(e,t){var n,r,o,a;if(t.transform&&e.css){if(!(a=t.transform(e.css)))return function(){};e.css=a}if(t.singleton){var i=c++;n=u||(u=v(t)),r=k.bind(null,n,i,!1),o=k.bind(null,n,i,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",y(t,e.attrs),m(e,t),t}(t),r=function(e,t,n){var r=n.css,o=n.sourceMap,a=void 0===t.convertToAbsoluteUrls&&o;(t.convertToAbsoluteUrls||a)&&(r=f(r));o&&(r+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */");var i=new Blob([r],{type:"text/css"}),l=e.href;e.href=URL.createObjectURL(i),l&&URL.revokeObjectURL(l)}.bind(null,n,t),o=function(){h(n),n.href&&URL.revokeObjectURL(n.href)}):(n=v(t),r=function(e,t){var n=t.css,r=t.media;r&&e.setAttribute("media",r);if(e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}.bind(null,n),o=function(){h(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else o()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=i()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return d(n,t),function(e){for(var r=[],o=0;othis.eventPool.length&&this.eventPool.push(e)}function Ee(e){e.eventPool=[],e.getPooled=ke,e.release=xe}i(we.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=l.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=l.thatReturnsTrue)},persist:function(){this.isPersistent=l.thatReturnsTrue},isPersistent:l.thatReturnsFalse,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;for(t=0;t=Pe),Re=String.fromCharCode(32),Ue={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["compositionend","keypress","textInput","paste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"blur compositionend keydown keypress keyup mousedown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"blur compositionstart keydown keypress keyup mousedown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"blur compositionupdate keydown keypress keyup mousedown".split(" ")}},Me=!1;function Ie(e,t){switch(e){case"keyup":return-1!==Te.indexOf(t.keyCode);case"keydown":return 229!==t.keyCode;case"keypress":case"mousedown":case"blur":return!0;default:return!1}}function Fe(e){return"object"==typeof(e=e.detail)&&"data"in e?e.data:null}var Le=!1;var De={eventTypes:Ue,extractEvents:function(e,t,n,r){var o=void 0,a=void 0;if(Se)e:{switch(e){case"compositionstart":o=Ue.compositionStart;break e;case"compositionend":o=Ue.compositionEnd;break e;case"compositionupdate":o=Ue.compositionUpdate;break e}o=void 0}else Le?Ie(e,n)&&(o=Ue.compositionEnd):"keydown"===e&&229===n.keyCode&&(o=Ue.compositionStart);return o?(Oe&&(Le||o!==Ue.compositionStart?o===Ue.compositionEnd&&Le&&(a=ve()):(he._root=r,he._startText=ye(),Le=!0)),o=_e.getPooled(o,t,n,r),a?o.data=a:null!==(a=Fe(n))&&(o.data=a),ee(o),a=o):a=null,(e=Ne?function(e,t){switch(e){case"compositionend":return Fe(t);case"keypress":return 32!==t.which?null:(Me=!0,Re);case"textInput":return(e=t.data)===Re&&Me?null:e;default:return null}}(e,n):function(e,t){if(Le)return"compositionend"===e||!Se&&Ie(e,t)?(e=ve(),he._root=null,he._startText=null,he._fallbackText=null,Le=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1