├── Procfile ├── src ├── views │ ├── error.hbs │ ├── index.hbs │ ├── template.hbs │ └── partials │ │ └── intro.hbs └── routes │ ├── api │ └── locales.js │ └── index.js ├── static ├── css │ ├── styles.scss │ ├── _mixins.scss │ └── _base.scss └── js │ ├── templates │ └── helpers.js │ └── app.js ├── .travis.yml ├── config.json ├── test └── specs │ └── sample.test.js ├── locales └── en-us.json ├── .gitignore ├── .prettierrc ├── app.json ├── Makefile ├── LICENSE ├── package.json ├── web.js └── README.md /Procfile: -------------------------------------------------------------------------------- 1 | web: node web.js 2 | -------------------------------------------------------------------------------- /src/views/error.hbs: -------------------------------------------------------------------------------- 1 |

{{status}}: {{message}}

2 | -------------------------------------------------------------------------------- /src/views/index.hbs: -------------------------------------------------------------------------------- 1 |

{{__ title}}

2 | 3 | {{> intro}} 4 | -------------------------------------------------------------------------------- /static/css/styles.scss: -------------------------------------------------------------------------------- 1 | @import 'mixins'; 2 | @import 'base'; 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '12' 4 | after_success: 5 | - node --version 6 | - npm --version 7 | - npm test 8 | -------------------------------------------------------------------------------- /src/routes/api/locales.js: -------------------------------------------------------------------------------- 1 | module.exports = router => { 2 | router.get('/', (req, res) => { 3 | res.json(req.getCatalog()); 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "site_name": "Node.js Starter Kit", 3 | "description": "This repo is a starter kit for working with Node.js, Handlebars, Sass, and Babel." 4 | } 5 | -------------------------------------------------------------------------------- /test/specs/sample.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | describe('Sample Tests', () => { 4 | 'use strict'; 5 | 6 | it('Testing basic Math.', () => { 7 | assert.equal(1 + 1, 2); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /locales/en-us.json: -------------------------------------------------------------------------------- 1 | { 2 | "Node.js Starter Kit": "Node.js Starter Kit", 3 | "This repo is a starter kit for working with Node.js, Handlebars, Sass, and Babel.": "This repo is a starter kit for working with Node.js, Handlebars, Sass, and Babel." 4 | } -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | module.exports = router => { 2 | router.get('/', (req, res) => { 3 | res.render('index', { 4 | description: res.locals.config.description, 5 | title: res.locals.config.site_name 6 | }); 7 | }); 8 | }; 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | static/css/styles.css 4 | static/build.min.js 5 | static/build.min.js.map 6 | static/js/templates/partials.min.js 7 | static/js/templates/views.min.js 8 | 9 | coverage/ 10 | 11 | package-lock.json 12 | 13 | npm-debug.log 14 | 15 | .env 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "singleQuote": true, 4 | "tabWidth": 4, 5 | "trailingComma": "none", 6 | "overrides": [ 7 | { 8 | "files": ["*.json", ".*rc", "*.yaml"], 9 | "options": { 10 | "tabWidth": 2 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Node.js Starter Kit", 3 | "description": "This repo is a starter kit for working with Node.js, Handlebars, Sass, and Babel.", 4 | "repository": "https://github.com/neogeek/nodejs-starter-kit", 5 | "keywords": ["node", "babel", "express", "handlebars", "mocha", "sass"] 6 | } 7 | -------------------------------------------------------------------------------- /static/css/_mixins.scss: -------------------------------------------------------------------------------- 1 | $prefixes: '-webkit-', '-moz-', '-ms-', '-o-', ''; 2 | 3 | @mixin prefix($key, $value) { 4 | @each $prefix in $prefixes { 5 | #{$prefix}#{unquote($key)}: #{unquote($value)}; 6 | } 7 | } 8 | 9 | @mixin prefix-value($key, $value) { 10 | @each $prefix in $prefixes { 11 | #{unquote($key)}: #{$prefix}#{unquote($value)}; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /static/css/_base.scss: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | @include prefix('box-sizing', 'border-box'); 5 | } 6 | 7 | body { 8 | margin: 1rem; 9 | font-family: 'Helvetica', 'Arial', sans-serif; 10 | font-size: 1em; 11 | line-height: 1.3; 12 | } 13 | 14 | h1 { 15 | font-weight: normal; 16 | line-height: 1.3; 17 | } 18 | 19 | a { 20 | color: #00f; 21 | } 22 | 23 | p { 24 | margin: 1rem 0; 25 | } 26 | -------------------------------------------------------------------------------- /static/js/templates/helpers.js: -------------------------------------------------------------------------------- 1 | module.exports = Handlebars => { 2 | const helpers = { 3 | ifCond: (a, b, options) => { 4 | let renderer = options.fn; 5 | 6 | if (a !== b) { 7 | renderer = options.inverse; 8 | } 9 | 10 | return renderer(); 11 | } 12 | }; 13 | 14 | Object.keys(helpers).forEach(key => { 15 | Handlebars.registerHelper(key, helpers[key]); 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /src/views/template.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{#if page_title}}{{page_title}} — {{/if}}{{config.site_name}} 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | {{{body}}} 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/views/partials/intro.hbs: -------------------------------------------------------------------------------- 1 | {{#if description}} 2 | 3 |

{{__ description}}

4 | 5 | {{else}} 6 | 7 |

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna 8 | aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. 9 | Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur 10 | sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

11 | 12 | {{/if}} 13 | 14 | {{#ifCond true true}} 15 | 16 |

Handlebars helpers successfully loaded.

17 | 18 | {{/ifCond}} 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN=node_modules/.bin 2 | 3 | start: 4 | $(BIN)/concurrently --kill-others --timestamp-format "HH:mm:ss" --prefix "[{index}] {time}" "make serve" "make watch" 5 | 6 | serve: 7 | $(BIN)/nodemon web.js --ignore static/ --ignore test/ 8 | 9 | build: 10 | mkdir -p static/js/templates 11 | $(BIN)/handlebars src/views/ -f static/js/templates/views.min.js -e hbs -c handlebars 12 | $(BIN)/handlebars src/views/partials/ -f static/js/templates/partials.min.js -p -e hbs -c handlebars 13 | $(BIN)/spire-of-babel static/js/app.js --bundle --sourcemap > static/build.min.js 14 | $(BIN)/node-sass static/css/styles.scss static/css/styles.css 15 | 16 | watch: 17 | make build 18 | $(BIN)/onchange 'src/views/**/*.hbs' 'static/css/**/*.scss' 'static/js/**/*.js' -e 'static/**/*.min.*' -- make build 19 | -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | const Handlebars = require('handlebars'); 2 | 3 | require('./templates/helpers.js')(Handlebars); 4 | require('./templates/views.min.js'); 5 | require('./templates/partials.min.js'); 6 | 7 | let locales = {}; 8 | 9 | Handlebars.registerHelper('__', key => { 10 | if (locales[key]) { 11 | return locales[key]; 12 | } 13 | 14 | return key; 15 | }); 16 | 17 | const renderView = (view, data) => { 18 | document.querySelector('.contents').innerHTML = Handlebars.templates[view]( 19 | data 20 | ); 21 | }; 22 | 23 | fetch('/api/locales') 24 | .then(response => response.json()) 25 | .then(data => { 26 | locales = data; 27 | 28 | renderView('index', { 29 | description: 30 | 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.', 31 | title: 'Hello, world.' 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Scott Doxey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@neogeek/nodejs-starter-kit", 3 | "version": "0.0.1", 4 | "description": "This repo is a starter kit for working with Node.js, Handlebars, Sass, and Babel.", 5 | "engines": { 6 | "node": ">=12.0" 7 | }, 8 | "main": "web.js", 9 | "license": "MIT", 10 | "dependencies": { 11 | "body-parser": "1.19.0", 12 | "compression": "1.7.4", 13 | "cors": "2.8.5", 14 | "express": "4.17.1", 15 | "express-enrouten": "1.3.0", 16 | "express-hbs": "2.3.4", 17 | "express-session": "1.17.1", 18 | "handlebars": "4.7.6", 19 | "i18n": "0.13.2", 20 | "node-sass": "4.14.1", 21 | "spire-of-babel": "2.0.1" 22 | }, 23 | "devDependencies": { 24 | "codecov": "3.7.2", 25 | "concurrently": "5.3.0", 26 | "jest": "26.4.2", 27 | "nodemon": "2.0.4", 28 | "onchange": "7.0.2" 29 | }, 30 | "scripts": { 31 | "postinstall": "make build", 32 | "start": "node web.js", 33 | "coverage": "jest --coverage --verbose=true", 34 | "test": "jest --verbose=true" 35 | }, 36 | "keywords": [ 37 | "node", 38 | "babel", 39 | "express", 40 | "handlebars", 41 | "mocha", 42 | "sass" 43 | ], 44 | "repository": { 45 | "type": "git", 46 | "url": "git://github.com/neogeek/nodejs-starter-kit.git" 47 | }, 48 | "private": true 49 | } 50 | -------------------------------------------------------------------------------- /web.js: -------------------------------------------------------------------------------- 1 | const config = require('./config.json'); 2 | 3 | const express = require('express'); 4 | const app = express(); 5 | 6 | const cors = require('cors'); 7 | const session = require('express-session'); 8 | const bodyParser = require('body-parser'); 9 | const compression = require('compression'); 10 | 11 | const enrouten = require('express-enrouten'); 12 | 13 | const hbs = require('express-hbs'); 14 | 15 | require('./static/js/templates/helpers.js')(hbs); 16 | 17 | const i18n = require('i18n'); 18 | 19 | i18n.configure({ 20 | defaultLocale: 'en-us', 21 | directory: `${__dirname}/locales`, 22 | indent: ' ', 23 | locales: ['en-us'] 24 | }); 25 | 26 | app.use(i18n.init); 27 | 28 | app.disable('x-powered-by'); 29 | 30 | app.use(cors()); 31 | 32 | app.use( 33 | session({ 34 | resave: true, 35 | saveUninitialized: true, 36 | secret: process.env.SECRET || 'secret' 37 | }) 38 | ); 39 | 40 | app.use(bodyParser.urlencoded({ extended: true })); 41 | app.use(bodyParser.json()); 42 | 43 | app.use(compression()); 44 | 45 | app.use(express.static(`${__dirname}/static`)); 46 | 47 | app.use((req, res, next) => { 48 | res.locals.config = config; 49 | res.locals.layout = 'template'; 50 | res.locals.url = `${req.protocol}://${req.get('host')}${req.originalUrl}`; 51 | 52 | next(); 53 | }); 54 | 55 | app.use(enrouten({ directory: 'src/routes' })); 56 | 57 | app.use((err, req, res, next) => { 58 | res.status(err.status || '500'); 59 | res.render('error', { 60 | message: err.message, 61 | status: err.status || '500' 62 | }); 63 | }); 64 | 65 | app.use((req, res) => { 66 | res.status('404'); 67 | res.render('error', { 68 | message: 'Page Not Found', 69 | status: '404' 70 | }); 71 | }); 72 | 73 | app.engine( 74 | 'hbs', 75 | hbs.express4({ 76 | onCompile: (exhbs, source) => 77 | exhbs.handlebars.compile(source, { preventIndent: true }), 78 | partialsDir: `${__dirname}/src/views/partials` 79 | }) 80 | ); 81 | 82 | app.set('view engine', 'hbs'); 83 | app.set('views', `${__dirname}/src/views`); 84 | 85 | app.listen(process.env.PORT || '5000'); 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node.js Starter Kit 2 | 3 | > This repo is a starter kit for working with [Node.js](http://nodejs.org), [Handlebars](http://handlebarsjs.com) (both server and client side), [Sass](http://sass-lang.com) (via [node-sass](https://github.com/sass/node-sass)), and [Babel](https://babeljs.io/) (via [spire-of-babel](https://github.com/neogeek/spire-of-babel)). 4 | 5 | [![Build Status](https://travis-ci.org/neogeek/nodejs-starter-kit.svg?branch=master)](https://travis-ci.org/neogeek/nodejs-starter-kit) 6 | 7 | ## Install to Heroku 8 | 9 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fneogeek%2Fnodejs-starter-kit) 10 | 11 | ## Getting Started 12 | 13 | Run the following command to download the repo and unpack it into `nodejs-starter-kit-master/`. 14 | 15 | ```bash 16 | $ curl -L https://github.com/neogeek/nodejs-starter-kit/archive/master.tar.gz | tar -xz 17 | ``` 18 | 19 | All client-side files are located in `/static`. 20 | 21 | Both client and server-side Handlebars templates are stored in `/src/views`. 22 | 23 | ## Building 24 | 25 | The following command will install all Node.js dependencies. Once all dependencies are installed it will run `make build` compiling Handlebars and Sass files. 26 | 27 | ```bash 28 | $ npm install 29 | ``` 30 | 31 | To manually compile Handlebars and Sass files run: 32 | 33 | ```bash 34 | $ make build 35 | ``` 36 | 37 | To watch for changes to any Handlebars or Sass files run: 38 | 39 | ```bash 40 | $ make watch 41 | ``` 42 | 43 | ## Testing 44 | 45 | Tests are powered by [mocha](http://mochajs.org/). To run all tests use this command: 46 | 47 | ```bash 48 | $ npm test 49 | ``` 50 | 51 | A `.travis.yml` configuration file is included to support automated testing through [Travis CI](https://travis-ci.org). 52 | 53 | ## Server 54 | 55 | If you have the [Heroku Toolbelt](https://toolbelt.heroku.com) installed you can start the sample applications with this command: 56 | 57 | ```bash 58 | $ foreman start 59 | ``` 60 | 61 | If not, then the application can also be run using this command: 62 | 63 | ```bash 64 | $ make serve 65 | ``` 66 | 67 | Once the application is running it can be accessed at . 68 | 69 | ## Bash Alias 70 | 71 | Add the following to your `~/.bash_profile` and restart terminal. You will now be able to create a new project using the starter kit by typing `create-nodejs-starter-kittest` (replacing _test_ with your new project name). 72 | 73 | ```bash 74 | create-nodejs-starter-kit() { 75 | 76 | curl -L https://github.com/neogeek/nodejs-starter-kit/archive/master.tar.gz | tar -xz 77 | mkdir -p "${1}" 78 | mv nodejs-starter-kit-master/{*,.[^.]*} "${1}" 79 | rm -d nodejs-starter-kit-master 80 | cd "${1}" || exit 81 | npm install 82 | 83 | } 84 | ``` 85 | 86 | ## Demo 87 | 88 | A video covering the initial setup process and basic Handlebars usage is available at Vimeo. 89 | 90 | [![](http://i.vimeocdn.com/video/484145719_1280.jpg)](http://vimeo.com/neogeek/nodejs-starter-kit-demo) 91 | --------------------------------------------------------------------------------