├── 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 | 
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 |
--------------------------------------------------------------------------------