├── src ├── fonts │ └── not-empty ├── images │ └── not-empty ├── stylesheets │ ├── hello_world.css │ └── style.css ├── scripts │ ├── hello_world.js │ └── app.js └── index.html ├── .gitignore ├── server.js ├── package.json ├── Makefile └── readme.md /src/fonts/not-empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/images/not-empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /src/stylesheets/hello_world.css: -------------------------------------------------------------------------------- 1 | .hello-world { 2 | color: #d77; 3 | } 4 | -------------------------------------------------------------------------------- /src/scripts/hello_world.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | var HelloWorld = React.createClass({ 4 | render() { 5 | return

Hello, world!

6 | } 7 | }) 8 | 9 | export default HelloWorld 10 | -------------------------------------------------------------------------------- /src/scripts/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * You can require() modules, use and ES2015. 3 | */ 4 | 5 | import React from 'react' 6 | import ReactDOM from 'react-dom' 7 | import HelloWorld from './hello_world' 8 | 9 | ReactDOM.render( , document.getElementById('app') ) 10 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var express = require('express') 3 | 4 | var args = process.argv.slice(2) 5 | var env = args[0] 6 | var staticfiles = path.resolve(env === 'dev' ? '.' : 'dist') 7 | 8 | var app = express() 9 | app.use(express.static(staticfiles)) 10 | app.get('/', (req, res, next) => { 11 | res.sendFile(path.resolve('dist/index.html')) 12 | }) 13 | app.listen(8080, function() { 14 | console.log('Server started at 8080') 15 | }) 16 | -------------------------------------------------------------------------------- /src/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is pure CSS. 3 | * 4 | * "@import" will cause browser to make requests. Don't worry, it's only for 5 | * keeping the development tasks faster. All CSS files will be bundled in your 6 | * production app. 7 | * 8 | * You don't need to write any vendor prefix, assuming you're using a modern 9 | * browser during development. Your production CSS will be autoprefixed. 10 | * */ 11 | 12 | @import './hello_world.css'; 13 | 14 | body { 15 | font: normal 400 100%/1.6 helvetica, arial, sans-serif; 16 | } 17 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Project Title 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | {{#if LIVE_RELOAD}} 14 | 22 | {{/if}} 23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project-name", 3 | "version": "0.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "react": "^15.1.0", 14 | "react-dom": "^15.1.0" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^6.3.6", 18 | "babel-preset-es2015": "^6.9.0", 19 | "babel-preset-react": "^6.5.0", 20 | "babelify": "^7.3.0", 21 | "browserify": "^13.0.1", 22 | "cssnano-cli": "^1.0.4", 23 | "express": "^4.13.4", 24 | "handlebars": "^4.0.5", 25 | "html-minifier": "^2.1.3", 26 | "livereload": "git+https://github.com/scriptype/node-livereload.git", 27 | "nodemon": "^1.10.0", 28 | "postcss-cli": "^2.5.2", 29 | "postcss-import": "^8.1.2", 30 | "uglifyjs": "^2.4.10", 31 | "watchify": "^3.7.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN = ./node_modules/.bin 2 | DIST = ./dist 3 | SRC = ./src 4 | 5 | HTML_INPUT = $(SRC)/index.html 6 | HTML_OUTPUT = $(DIST)/index.html 7 | 8 | CSS_INPUT_DIR = $(SRC)/stylesheets 9 | CSS_INPUT = $(CSS_INPUT_DIR)/style.css 10 | CSS_OUTPUT_MIN_DIR = $(DIST) 11 | CSS_OUTPUT_MIN = $(CSS_OUTPUT_MIN_DIR)/style.min.css 12 | 13 | JS_INPUT_DIR = $(SRC)/scripts 14 | JS_INPUT = $(JS_INPUT_DIR)/app.js 15 | JS_OUTPUT = $(DIST)/all.js 16 | JS_OUTPUT_MIN = $(DIST)/all.min.js 17 | 18 | PROD_CSS_PATH = style.min.css 19 | PROD_JS_PATH = all.min.js 20 | 21 | all: clean copy_static html js 22 | @echo "Finished $@. `date`" 23 | @make watch & \ 24 | $(BIN)/nodemon \ 25 | --ignore $(SRC) \ 26 | --ignore $(DIST) \ 27 | server.js dev 28 | 29 | _release: clean copy_static post_html post_css post_js 30 | @echo "Finished $@. `date`" & node server.js prod 31 | 32 | release: 33 | @NODE_ENV=production make _release 34 | 35 | clean: 36 | @echo "Cleaning..." 37 | @rm -rf $(DIST) 38 | @mkdir $(DIST) 39 | 40 | watch: 41 | @make watch_js & $(BIN)/livereload "$(DIST), $(CSS_INPUT_DIR)" 42 | 43 | html: 44 | @make replace_path \ 45 | SCRIPT_FILE=$(JS_OUTPUT) \ 46 | STYLE_FILE=$(CSS_INPUT) \ 47 | LIVE_RELOAD='true' 48 | 49 | post_html: 50 | @make replace_path \ 51 | SCRIPT_FILE=$(PROD_JS_PATH) \ 52 | STYLE_FILE=$(PROD_CSS_PATH) \ 53 | LIVE_RELOAD='false' 54 | @echo "Minifying markup..." 55 | @$(BIN)/html-minifier \ 56 | --collapse-whitespace \ 57 | --remove-attribute-quotes \ 58 | --remove-comments \ 59 | --remove-empty-attributes \ 60 | --remove-redundant-attributes \ 61 | --output $(DIST)/tmp.index.html \ 62 | $(HTML_OUTPUT) 63 | @mv $(DIST)/tmp.index.html $(HTML_OUTPUT) 64 | 65 | post_css: 66 | @echo "PostCSS..." 67 | @$(BIN)/postcss \ 68 | --use autoprefixer \ 69 | --use postcss-import \ 70 | --local-plugins \ 71 | --output $(DIST)/tmp.style.css \ 72 | $(CSS_INPUT) 73 | @echo "Minifying stylesheets..." 74 | @$(BIN)/cssnano $(DIST)/tmp.style.css $(CSS_OUTPUT_MIN) 75 | @rm $(DIST)/tmp.style.css 76 | 77 | js: 78 | @echo "Browserify..." 79 | @$(BIN)/browserify \ 80 | --delay=100 \ 81 | --verbose \ 82 | --transform [ babelify --presets [ es2015 react ] ] \ 83 | --outfile $(JS_OUTPUT) \ 84 | --debug \ 85 | $(JS_INPUT) 86 | 87 | watch_js: 88 | @echo "Started watching JS..." 89 | @$(BIN)/watchify \ 90 | --delay=100 \ 91 | --verbose \ 92 | --transform [ babelify --presets [ es2015 react ] ] \ 93 | --outfile $(JS_OUTPUT) \ 94 | --debug \ 95 | $(JS_INPUT) 96 | 97 | post_js: js 98 | @echo "Minifying scripts..." 99 | @$(BIN)/uglifyjs $(JS_OUTPUT) \ 100 | --mangle \ 101 | --compress \ 102 | --output $(JS_OUTPUT_MIN) 103 | @rm $(JS_OUTPUT) 104 | 105 | copy_static: 106 | @echo "Copying static files..." 107 | @mkdir -p $(DIST)/fonts 108 | @mkdir -p $(DIST)/images 109 | @cp -r $(SRC)/fonts/ $(DIST)/fonts 110 | @cp -r $(SRC)/images/ $(DIST)/images 111 | 112 | replace_path: 113 | @echo "Updating markup..." 114 | @$(BIN)/handlebars $(HTML_INPUT) -f $(DIST)/tmp.index.hbs.js 115 | @node -p " \ 116 | var Handlebars = require('handlebars'); \ 117 | var template = require('./$(DIST)/tmp.index.hbs.js'); \ 118 | Handlebars.templates['index.html']({ \ 119 | SCRIPT_FILE: '$(SCRIPT_FILE)', \ 120 | STYLE_FILE: '$(STYLE_FILE)', \ 121 | LIVE_RELOAD: $(LIVE_RELOAD) \ 122 | }) \ 123 | " > $(HTML_OUTPUT) 124 | @rm $(DIST)/tmp.index.hbs.js 125 | 126 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Screenshot of Makefile](https://cdn.pbrd.co/images/1C4FPlj3.png) 2 | 3 | # Makefile for the Front End 4 | 5 | I've used this Makefile for a fast development environment for a side-project 6 | that I no longer continue. 7 | 8 | You can clone this repository to kickstart a frontend project. Required folder 9 | structure is set up for Makefile to work. 10 | 11 | #### TL;DR 12 | 13 | - Use [static, non-preprocessed CSS](https://css-tricks.com/dont-use-css-preprocessors/) with [livereload](https://github.com/napcs/node-livereload) to give lightning-fast 14 | prototyping abilities. 15 | 16 | - Use [watchify](https://github.com/substack/watchify) + [babelify](https://github.com/babel/babelify) to compile JSX and enable ES2015. Also auto-refresh 17 | the browser. 18 | 19 | - Minify HTML ([html-minifier](https://github.com/kangax/html-minifier)), CSS ([cssnano](https://github.com/ben-eb/cssnano)), JS ([uglifyjs](https://github.com/mishoo/UglifyJS2)) for production. 20 | 21 | - Use PostCSS plugins ([import](https://github.com/postcss/postcss-import), [autoprefixer](https://github.com/postcss/autoprefixer)) for production CSS. 22 | 23 | 24 | ## Why Make? 25 | 26 | I'm using Make to manage ops-side of my front-end projects because it's simple. 27 | In Gulp, Grunt or Webpack or any other tool, you have to comprehend the complexity 28 | of programmatic usage and learn how some 3rd party abstractions work. 29 | 30 | When, instead, Make is used for the same tasks, it's much easier for anyone to 31 | contribute, fix or make any kind of change on the build script (at least for me). 32 | It's not asynchronous, it doesn't give you abstractions to "make your job easier". 33 | It's flat. You read a Makefile in sequential order, with no indirection. 34 | 35 | ## Setup 36 | 37 | You need `Node` and `npm` installed on your machine which is capable of running 38 | Makefiles (i.e: not Windows as far as I know). 39 | 40 | ``` 41 | # Clone the repository 42 | git clone git@github.com:scriptype/Makefile-for-the-Front-End.git 43 | 44 | # Go to project directory 45 | cd Makefile-for-the-Front-End 46 | 47 | # Install dependencies 48 | npm i 49 | ``` 50 | 51 | Besides modules used in Makefile, `react` and `react-dom` for JS and 52 | `express` for dev-server will also be installed. They are not mandatory. 53 | 54 | ## Run 55 | 56 | #### Development 57 | 58 | ```sh 59 | make 60 | ``` 61 | 62 | When finished, head over to `localhost:8080` 63 | 64 | #### Production 65 | 66 | ```sh 67 | make release 68 | ``` 69 | 70 | When finished, head over to `localhost:8080` 71 | 72 | ## Notes 73 | 74 | - It's far from being perfect (and it's not intended to be). 75 | 76 | - `Express` dependency is for `server.js`. You can remove it and handle 77 | dev-server however you like. 78 | 79 | - Handlebars is used in `index.html` to dynamically manipulate the path of JS 80 | and CSS files according to environment. It also calls livereload script in dev mode. 81 | 82 | - No CSS pre-processor was used. CSS files are completely static and source files 83 | are directly used in dev mode. You get an experience similar to prototyping in 84 | browser thanks to livereload. 85 | 86 | - When releasing the project: 87 | 88 | - CSS will be autoprefixed and `@imports` will be concatenated into a single file. 89 | 90 | - JS modules will be babelified with `react` and `es2015` presets. 91 | 92 | - HTML, JS and CSS outputs will be minified. 93 | 94 | ## Contribution 95 | 96 | Please don't hesitate to open issues and making pull requests. When doing that, 97 | consider that this boilerplate is aimed to have minimum complexity. e.g: Implementing 98 | redux is out of scope in this project. 99 | 100 | ## Licence 101 | ``` 102 | MIT License 103 | 104 | Copyright (c) 2016 Mustafa Enes Ertarhanacı 105 | 106 | Permission is hereby granted, free of charge, to any person obtaining a copy 107 | of this software and associated documentation files (the "Software"), to deal 108 | in the Software without restriction, including without limitation the rights 109 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 110 | copies of the Software, and to permit persons to whom the Software is 111 | furnished to do so, subject to the following conditions: 112 | 113 | The above copyright notice and this permission notice shall be included in all 114 | copies or substantial portions of the Software. 115 | 116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 117 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 118 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 119 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 120 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 121 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 122 | SOFTWARE. 123 | ``` 124 | --------------------------------------------------------------------------------