├── .gitignore ├── README.md ├── build ├── data.json ├── images │ └── screenshot.png ├── index.html ├── scripts │ ├── core.js │ ├── core.js.map │ ├── home.js │ └── home.js.map ├── sitemap.xml └── styles │ ├── core.css │ ├── core.css.map │ ├── home.css │ └── home.css.map ├── gulpfile.js ├── package.json └── source ├── data ├── .gitkeep └── index.js ├── images ├── .gitkeep └── screenshot.png ├── scripts ├── .gitkeep ├── core.js ├── home.js └── tools │ └── log.js ├── statics └── .gitkeep ├── styles ├── .gitkeep ├── core.less ├── home.less └── tools │ └── reset.less └── templates ├── index.html └── layouts └── basic.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules 4 | npm-debug.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yet another front-end build process # 2 | 3 | THIS PROJECT IS DEPRECATED! TRY THIS INSTEAD: https://github.com/koljakutschera/laststaticsitegenerator 4 | 5 | Version: 1.2.4 6 | 7 | Simple but powerful static site generator, starter or boilerplate. 8 | 9 | Impression: 10 | 11 | ![Templating with data](source/images/screenshot.png) 12 | 13 | ## Features ## 14 | 15 | * Templating (Layouts, blocks, includes, macros, more) 16 | * Template data layer (Access data in template or via ajax: global, per page, from all pages) 17 | * Template helpers(moment.js, accounting.js) 18 | * Modular less-stylesheets (Hot-reloading)(Autoprefixed)(Sourcemaps) 19 | * Modular javascript (Sourcemaps) 20 | * Auto image-optimization 21 | * Sitemap-generator 22 | * Autoreload 23 | * Tunnel(expose localhost to public URL) 24 | * Proxy 25 | * PageSpeed Insights reporting 26 | * Remove unused CSS 27 | 28 | 29 | ## Installation ## 30 | 31 | If you have not already installed nodejs and npm: [https://nodejs.org/](https://nodejs.org/ "https://nodejs.org/") 32 | 33 | If you have not already installed git: [https://git-scm.com](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git "https://git-scm.com/book/en/v2/Getting-Started-Installing-Git") (Optional) 34 | 35 | Install gulp-cli via Terminal. 36 | 37 | ``` 38 | npm install --g gulp-cli 39 | ``` 40 | 41 | 42 | ## Setup ## 43 | 44 | Clone this repository via Terminal. Or just click on the download-button in the right corner... 45 | 46 | ``` 47 | git clone https://github.com/koljakutschera/yafbp && cd yafbp 48 | ``` 49 | 50 | Install dependencies via Terminal(Maybe sudo is needed). 51 | 52 | ``` 53 | npm install 54 | ``` 55 | 56 | 57 | ## Configuration ## 58 | 59 | You can configure the process-settings in /gulpfile.js: 60 | 61 | Example: Default configuration 62 | 63 | ```javascript 64 | var config = { 65 | source_dir : './source/', 66 | build_dir : './build/', 67 | statics_dir : 'statics/', 68 | templates_dir : 'templates/', 69 | data_dir : 'data/', 70 | styles_dir : 'styles/', 71 | scripts_dir : 'scripts/', 72 | images_dir : 'images/', 73 | connect_port : 8080, 74 | proxy_port : 9090, 75 | proxyAuth : '', 76 | autoprefix : ["> 1%","last 4 versions"] 77 | } 78 | ``` 79 | 80 | 81 | ## Usage ## 82 | 83 | In the project-directory start the development process via Terminal: 84 | 85 | > Hint: All commands starting with -- can be combined. 86 | 87 | ``` 88 | gulp 89 | ``` 90 | 91 | Or with minification for styles, scripts, images and html. 92 | 93 | ``` 94 | gulp --production 95 | ``` 96 | 97 | Or with removing all unused styles. 98 | 99 | ``` 100 | gulp --purify 101 | ``` 102 | 103 | Or with exposing localhost to public URL(See public development covered in the next section) 104 | 105 | ``` 106 | gulp --tunnel 107 | ``` 108 | 109 | Or with [PageSpeed-Insights](https://developers.google.com/speed/pagespeed/insights/) reports for all pages(could take a few minutes...) 110 | 111 | ``` 112 | gulp --production --tunnel --psi 113 | ``` 114 | 115 | 116 | ## Development ## 117 | 118 | ### Local development ### 119 | 120 | When the process is running open the following url in your browser: 121 | 122 | ``` 123 | http://localhost:8080 124 | ``` 125 | 126 | ### Public development ### 127 | 128 | When using --tunnel flag you can also access your project via temporary public URL. 129 | With this you can for example check your site with [browserstack](https://www.browserstack.com/) or send the link to your customer for live-testing. 130 | 131 | > Hint: On every process start you get a new temporary public URL. You can see it in terminal logs. 132 | 133 | Example: See terminal logs for public URL 134 | ``` 135 | Exposing localhost:8080 via public URL: https://temorary123.eu.ngrok.io 136 | ``` 137 | 138 | 139 | ## Templating ## 140 | 141 | In **source/templates** [Nunjucks](https://mozilla.github.io/nunjucks/templating.html "Nunjucks") is used for templating with all its features like block inheritance(layouts), autoescaping, macros and more. 142 | Every(except for files starting with an _) .html file on the root-level of the directory is compiled to the build/ directory. Templates on the root-level are your "pages". 143 | Files in sub-directories are ignored, but can be included in root-templates via Nunjucks [template-inheritance-system](https://mozilla.github.io/nunjucks/templating.html#template-inheritance "Nunjucks template-inheritance"). 144 | 145 | > Info: The default directory structure is only a recommendation - feel free to delete everything and start from scratch. 146 | 147 | > Hint: Files starting with an _ will not compile. Use this for drafts etc. 148 | 149 | Example: Simple layout 150 | 151 | ```html 152 | 153 | 154 | 155 | 156 | {{ page.title }} 157 | {% block meta %} 158 | {% endblock %} 159 | 160 | {% block styles %} 161 | {% endblock %} 162 | 163 | 164 | {% block body %} 165 | {% endblock %} 166 | 167 | {% block scripts %} 168 | {% endblock %} 169 | 170 | 171 | ``` 172 | 173 | Example: Simple page 174 | 175 | ```html 176 | --- 177 | title: Home 178 | --- 179 | 180 | {% extends "layouts/basic.html" %} 181 | 182 | {% block meta %} 183 | 184 | {% endblock %} 185 | 186 | {% block styles %} 187 | 188 | {% endblock %} 189 | 190 | {% block body %} 191 |

Welcome to: {{ global.siteUrl }}

192 |

You are here: {{ page.title }}({{pages.index.path}})

193 | {% endblock %} 194 | 195 | {% block scripts %} 196 | 197 | {% endblock %} 198 | ``` 199 | 200 | Result: 201 | 202 | ```html 203 | 204 | 205 | 206 | 207 | Home 208 | 209 | 210 | 211 | 212 | 213 |

Welcome to: http://www.change-to-your-domain.de

214 |

You are here: Home(index.html)

215 | 216 | 217 | 218 | 219 | ``` 220 | 221 | See [https://mozilla.github.io/nunjucks/](https://mozilla.github.io/nunjucks/) for more. 222 | 223 | ### Template helpers ### 224 | 225 | You can use the following included helpers in your nunjucks templates and macros: 226 | * [moment.js](http://momentjs.com/) - Parse, validate, manipulate, and display dates. 227 | * [accounting.js](http://openexchangerates.github.io/accounting.js/) - number, money and currency formatting 228 | 229 | > Hint: You can add your own helpers to the global data object covered in the next section. 230 | 231 | 232 | Example: Formating a date-string with moment.js 233 | 234 | ```html 235 | {{moment("2017-09-20").locale('de').format('LL')}} 236 | ``` 237 | 238 | Result: Showing formatted German date 239 | 240 | ```html 241 | 20. September 2017 242 | ``` 243 | 244 | 245 | Example: Formating money with accounting.js 246 | 247 | ```html 248 | {{ accounting.formatMoney(4999.99, "€", 2, ".", ",") }} 249 | ``` 250 | 251 | Result: Showing formatted money 252 | 253 | ```html 254 | €4.999,99 255 | ``` 256 | 257 | 258 | ## Template data layer ## 259 | 260 | ### Global data ### 261 | 262 | In **data/index.js** you can define your own data-sets and export it to all your templates. 263 | Think about generating navigation from json or exposing data from a csv-files that is read via nodejs. 264 | Global data is accessible in templates via "global" variable. 265 | The data is also written to build/data.json for ajax-access. 266 | 267 | > Info: The default directory structure in data/ is only a recommendation - feel free to delete everything except for data/index.js and start from scratch. 268 | 269 | > Help: There is a helper-function in data/index.js to import node-modules un-cached. Use this if you always want fresh data on every file-change. 270 | 271 | > Hint: You can dump the global data with: {{ global | dump }} 272 | 273 | Example: Export site-url to all templates. 274 | 275 | ```javascript 276 | var global = { 277 | siteUrl : 'http://www.change-to-yourdomain.de' // Required for sitemap.xml - do not delete! 278 | }; 279 | 280 | // Expose data to templates 281 | module.exports = global; 282 | ``` 283 | 284 | Example: Access site-url in page: 285 | 286 | ```html 287 |

Welcome to: {{ global.siteUrl }}

288 | ``` 289 | 290 | Result: 291 | 292 | ```html 293 |

Welcome to: http://www.change-to-yourdomain.de

294 | ``` 295 | 296 | 297 | ### Per page data ### 298 | 299 | You can also provide data directly to a page via **YAML-Front-Matter** at the top of root-templates. 300 | This data is accessible in the template via "page" variable. 301 | 302 | > Hint: You can dump the data of a page with: {{ page | dump }} 303 | 304 | Example: YAML-Front-Matter at the top of a page: 305 | 306 | ``` 307 | --- 308 | title: Home 309 | --- 310 | ``` 311 | 312 | Example: Access YAML-Front-Matter from page in layout template: 313 | 314 | ```html 315 | {{ page.title }} 316 | ``` 317 | 318 | Result: 319 | 320 | ```html 321 | Home 322 | ``` 323 | 324 | ### All pages data ### 325 | 326 | All the data you have provided to all root-templates via YAML-Front-Matter is also accessible in all other templates via "pages"-variable. 327 | The data is also written to build/data.json for ajax-access. 328 | You can access data from another page by the name of the template without the .html extension. 329 | 330 | > Info: This gives you almost all possibilities you want from a static-page-generator. 331 | 332 | > Hint: You can dump the data of all pages with: {{ pages | dump }} 333 | 334 | Example: Access data of another page in a page: 335 | 336 | ```html 337 |

Every little page knows the path of the index-page: {{ pages.index.path }}

338 | ``` 339 | 340 | Result: 341 | 342 | ```html 343 |

Every little page knows the title of the index-page: index.html

344 | ``` 345 | 346 | 347 | ## Sitemap-generator ## 348 | 349 | For our SEOs all .html files on the root-level of **source/templates** are indexed to the sitemap.xml-file in the build/ directory. Files starting with an _ are excluded. 350 | 351 | 352 | ## Modular less-stylesheets ## 353 | 354 | In **source/styles** every(except for files starting with an _) .less file on the root-level of the directory is compiled to the build/styles/ directory. 355 | Files in sub-directories are ignored, but can be imported in root-stylesheets via less-imports. 356 | 357 | For example and depending on your needs you can use one core.less file and import all stylesheets in it for single-page-apps. Or you can use the core.less file for shared styles and multiple other files for per-page styles in a multi-page setup. 358 | 359 | > Hot: Styles are hot-reloaded(See file-changes without reloading the browser); 360 | 361 | > Info: The default directory structure is only a recommendation - feel free to delete everything and start from scratch. 362 | 363 | > Hint: You can also pass options to less-imports. [http://lesscss.org/](http://lesscss.org/features/#import-options "import-options") 364 | 365 | Example: Importing another stylesheet. 366 | 367 | ```less 368 | @import "tools/reset.less"; 369 | ``` 370 | 371 | See [http://lesscss.org/](http://lesscss.org/) for more. 372 | 373 | 374 | ## CSS-Autoprefixer ## 375 | 376 | When writing less/css browser-prefixes are automatically added. You can configure browser-versions in /gulpfile.js 377 | 378 | 379 | ## Modular javascript ## 380 | 381 | In **source/scripts** every(except for files starting with an _) .js-file on the root-level of the directory is compiled to the build/scripts/ directory. 382 | Files in sub-directories are ignored, but can be included in root-scripts via a special comment/directive. 383 | 384 | Depending on your needs you can use one core.js file and require all libs and scripts in it for single-page-apps. Or you can use the core.js file for shared libraries and multiple other files for per-page scripts in a multi-page setup. 385 | 386 | > Info: The default directory structure is only a recommendation - feel free to delete everything and start from scratch. 387 | 388 | > Hint: Script including works recursively (files can include files that can include files, and so on) 389 | 390 | Example: Importing another script. 391 | 392 | ```javascript 393 | //=require tools/log.js 394 | ``` 395 | 396 | See [https://www.npmjs.com/package/gulp-include](https://www.npmjs.com/package/gulp-include) for more. 397 | 398 | ## Auto image-optimization ## 399 | 400 | In **source/images** every .jpg, .png, .gif and .svg file is compiled/optimized to the build/images/ directory. 401 | 402 | > The default directory structure is only a recommendation - feel free to delete everything and start from scratch. 403 | 404 | 405 | ## Static Files ## 406 | 407 | In **source/statics** you can put fonts, media etc. Every file and directory is copied to the build/statics/ directory. 408 | 409 | 410 | ## Proxy ## 411 | 412 | The proxy is listening on port: 9090. Use it for example if you cant access an api-endpoint via ajax because of Cross-Origin Resource Sharing (CORS). 413 | 414 | > Hint: You can add http-authentication to the proxy in /gulpfile.js 415 | 416 | Example: Proxy usage with jQuery: 417 | 418 | ```javascript 419 | var proxy = 'http://localhost:9090/'; 420 | var endpoint = 'www.echojs.com/'; // change this to your api-endpoint. 421 | 422 | $.getJSON(proxy + endpoint, function( data ) { 423 | console.log(data) 424 | }); 425 | ``` 426 | 427 | ## Auto-reload ## 428 | 429 | When the build process is started a file-watcher watches for file-changes and auto-reloads the browser every time you change a file. Sometimes on first start you have to do a manual reload because the process is faster than the browser. 430 | 431 | 432 | ## Where do I start? ## 433 | 434 | Just look at the contents of the source/ directory. 435 | There are just a few example files that should be self-documenting. 436 | 437 | If you want a more advanced example checkout: [yafbp-example-blog](https://github.com/koljakutschera/yafbp-example-blog) 438 | 439 | To start from scratch - You can delete everything in source/images/, source/scripts/, source/styles/, source/templates/ directories. In the data/ directory only /index.js is required. 440 | For scripts and styles remember that files on the root-level act as entry-points. 441 | 442 | Feel free to configure anything in /gulpfile.js to your needs. 443 | 444 | ## Contribution ## 445 | 446 | Please don't! Except for bugs. Iam not interested in extending this project with more crazy stuff to increase its complexity. 447 | If you want babel, react or your shiny whatever JS-Framework just fork this repo and put it in the source directory :P 448 | 449 | 450 | ## Thank you ## 451 | 452 | Thank you to all funky developers listed in /package.json. 453 | 454 | 455 | ## History ## 456 | 457 | * V1.2.4: Write external source map files 458 | * V1.2.3: Remove unused CSS with --purify command. See readme -> Usage 459 | * V1.2.2: Optimized watchers for slow file-systems 460 | * V1.2.1: Configuration added. See readme.md -> Configuration 461 | * V1.2.0: Added PageSpeed Insights reporting feature. See readme.md -> Usage 462 | * V1.1.0: Added Tunnel(expose localhost to public URL) feature. See readme.md 463 | * V1.0.2: Global- and pages data objects are now nunjucks-globals so that they can be used in macros too. Cache optimized. 464 | * V1.0.1: Added Template helpers(moment.js, accounting.js). See readme.md 465 | * V1.0.0: Dropped SVGInjector for simplicity, Data-layer extended with global, page, pages variables, Added sitemap-generator, Simplify examples, Using _ in filenames for drafts, removed build/ from .gitignore 466 | 467 | 468 | ## Troubleshooting ## 469 | 470 | Linux: The system has a limit to how many files can be watched by a user. The following command increases the maximum amount of watches a user can have: 471 | 472 | ``` 473 | echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p 474 | ``` 475 | -------------------------------------------------------------------------------- /build/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "global": { 3 | "siteUrl": "http://www.change-to-your-domain.de" 4 | }, 5 | "pages": { 6 | "index": { 7 | "title": "Home", 8 | "path": "index.html" 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /build/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/build/images/screenshot.png -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Welcome to: http://www.change-to-your-domain.de

17 |

You are here: Home(index.html)

18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /build/scripts/core.js: -------------------------------------------------------------------------------- 1 | // Just an example 2 | console.log('tools/logs.js logging...'); 3 | //# sourceMappingURL=core.js.map 4 | -------------------------------------------------------------------------------- /build/scripts/core.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["tools/log.js"],"names":[],"mappings":"AAAA;AACA","file":"core.js","sourcesContent":["// Just an example\nconsole.log('tools/logs.js logging...');"]} -------------------------------------------------------------------------------- /build/scripts/home.js: -------------------------------------------------------------------------------- 1 | // Just an example 2 | console.log('home.js logging...'); 3 | //# sourceMappingURL=home.js.map 4 | -------------------------------------------------------------------------------- /build/scripts/home.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"names":[],"mappings":"","sources":["home.js"],"sourcesContent":["// Just an example\nconsole.log('home.js logging...');"],"file":"home.js"} -------------------------------------------------------------------------------- /build/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://www.change-to-your-domain.de/ 5 | 2017-04-19T11:13:45.000Z 6 | 7 | -------------------------------------------------------------------------------- /build/styles/core.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fafafa; 3 | } 4 | 5 | /*# sourceMappingURL=core.css.map */ 6 | -------------------------------------------------------------------------------- /build/styles/core.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["tools/reset.less","../../"],"names":[],"mappings":"AACA;EACC,oBAAA;CCAA","file":"core.css","sourcesContent":["// Just an example...\nbody {\n\tbackground: #fafafa;\n}","body {\n background: #fafafa;\n}\n"]} -------------------------------------------------------------------------------- /build/styles/home.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 32px; 3 | color: #222; 4 | } 5 | 6 | /*# sourceMappingURL=home.css.map */ 7 | -------------------------------------------------------------------------------- /build/styles/home.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["home.less","../../"],"names":[],"mappings":"AACA;EACI,gBAAA;EACA,YAAA;CCAH","file":"home.css","sourcesContent":["// Just an example...\nh1 {\n font-size: 32px;\n color: #222;\n}","h1 {\n font-size: 32px;\n color: #222;\n}\n"]} -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var util = require('gulp-util'); 3 | var plumber = require('gulp-plumber'); 4 | var less = require('gulp-less'); 5 | var include = require("gulp-include"); 6 | var path = require('path'); 7 | var watch = require('gulp-watch'); 8 | var gulpNunjucks = require('gulp-nunjucks'); 9 | var nunjucks = require('nunjucks'); 10 | var moment = require('moment'); 11 | var imagemin = require('gulp-imagemin'); 12 | var pngquant = require('imagemin-pngquant'); 13 | var connect = require('gulp-connect'); 14 | var cssnano = require('gulp-cssnano'); 15 | var uglify = require('gulp-uglify'); 16 | var htmlmin = require('gulp-htmlmin'); 17 | var sourcemaps = require('gulp-sourcemaps'); 18 | var del = require('del'); 19 | var open = require('gulp-open'); 20 | var corsAnywhere = require('cors-anywhere'); 21 | var LessPluginAutoPrefix = require('less-plugin-autoprefix'); 22 | var gulpData = require('gulp-data'); 23 | var fm = require('front-matter'); 24 | var _ = require('underscore'); 25 | var fs = require('fs'); 26 | var sitemap = require('gulp-sitemap'); 27 | var accounting = require('accounting'); 28 | var ngrok = require('ngrok'); 29 | var psi = require('psi'); 30 | var purify = require('gulp-purifycss'); 31 | 32 | // Configuration - Change to your needs... 33 | var config = { 34 | source_dir : './source/', 35 | build_dir : './build/', 36 | statics_dir : 'statics/', 37 | templates_dir : 'templates/', 38 | data_dir : 'data/', 39 | styles_dir : 'styles/', 40 | scripts_dir : 'scripts/', 41 | images_dir : 'images/', 42 | connect_port : 8080, 43 | proxy_port : 9090, 44 | proxyAuth : '', // 'user:passw' 45 | autoprefix : ["> 1%","last 4 versions"], // https://github.com/ai/browserslist#queries 46 | } 47 | 48 | config.production = !!util.env.production; // do not change! 49 | config.tunnel = !!util.env.tunnel; // do not change! 50 | config.psi = !!util.env.psi; // do not change! 51 | config.purify = !!util.env.purify; // do not change! 52 | 53 | // Configure autoprefixing 54 | var autoprefix = new LessPluginAutoPrefix({browsers: config.autoprefix}); 55 | 56 | // We will use our own nunjucks environment... 57 | var nunjucksEnv = nunjucks.configure(config.source_dir + config.templates_dir, { 58 | noCache : true, 59 | }); 60 | 61 | // ... because we want to add globals etc. 62 | nunjucksEnv.addGlobal('moment', moment); 63 | nunjucksEnv.addGlobal('accounting', accounting) 64 | 65 | // This is where the data for all pages gets indexed 66 | var dataPages = {}; 67 | 68 | // Handle gulp errors 69 | function handleError (err) { 70 | 71 | console.log(err); 72 | this.emit('end'); 73 | } 74 | 75 | // Require modules without caching them 76 | function requireUncached(module){ 77 | delete require.cache[require.resolve(module)] 78 | return require(module) 79 | } 80 | 81 | // Are we in production or development? 82 | console.log('Production: ' + !!util.env.production); 83 | 84 | // GULP TASKS 85 | 86 | // Clean build directory 87 | gulp.task('clean', function () { 88 | return del.sync([ 89 | config.build_dir + '**/*', 90 | ]); 91 | }); 92 | 93 | // Statics 94 | gulp.task('statics',function(){ 95 | gulp.src([config.source_dir + config.statics_dir + '**/*']) 96 | .pipe(plumber({ 97 | handleError: handleError 98 | })) 99 | .pipe(gulp.dest(config.build_dir + config.statics_dir)) 100 | .pipe(connect.reload()); 101 | }); 102 | 103 | // Templates 104 | gulp.task('templates',function(){ 105 | gulp.src([config.source_dir + config.templates_dir + '[^_]*.html']) 106 | .pipe(plumber({ 107 | handleError: handleError 108 | })) 109 | .pipe(gulpData(function(file) { 110 | 111 | var data = {}; 112 | var fileName = file.relative; 113 | var fileContent = fm(String(file.contents)); 114 | //file.contents = new Buffer(fileContent.body); 115 | 116 | // delete page-obj from pages-data if template gets deleted/renamed 117 | // todo: check performance 118 | _.each(dataPages, function (value, key, list) { 119 | 120 | try { 121 | fs.statSync(config.source_dir + config.templates_dir + key +'.html'); 122 | } catch(err) { 123 | 124 | delete dataPages[key]; 125 | } 126 | }); 127 | 128 | // cut off .html from filename for pages-data obj 129 | if (fileName.indexOf('.html') >= 0) { 130 | fileName = fileName.slice(0, -5); 131 | } 132 | 133 | // adding page-data to pages-data... 134 | dataPages[fileName] = fileContent.attributes; 135 | dataPages[fileName].path = file.relative; 136 | 137 | // collecting data... 138 | data.global = requireUncached(config.source_dir + config.data_dir + 'index.js'); 139 | data.pages = dataPages; 140 | data.page = fileContent.attributes; 141 | data.page.path = file.relative; 142 | 143 | // siteUrl is used by sitemap-generator 144 | siteUrl = data.global.siteUrl; 145 | // global- and pages data added to nunjucks-environment = accessible in macros too 146 | nunjucksEnv.addGlobal('global', data.global); 147 | nunjucksEnv.addGlobal('pages', data.pages) 148 | 149 | // write global and pages to json-file for ajax access 150 | // todo: check performance 151 | try { 152 | fs.writeFileSync(config.build_dir + 'data.json', JSON.stringify({ 153 | global : data.global, 154 | pages : data.pages 155 | }, null, 2) , 'utf-8'); 156 | } catch(err) { 157 | 158 | console.log(config.build_dir + 'data.json not writable') 159 | } 160 | 161 | // only page-data is directly returned to template 162 | return { 163 | page : data.page 164 | }; 165 | })) 166 | .pipe(gulpNunjucks.compile(null, { 167 | env : nunjucksEnv 168 | })) 169 | .pipe(plumber({ 170 | handleError: handleError 171 | })) 172 | .pipe(config.production ? htmlmin({ 173 | collapseWhitespace: true 174 | }) : util.noop()) 175 | .pipe(gulp.dest(config.build_dir)) 176 | .pipe(connect.reload()); 177 | }); 178 | 179 | // Sitemap 180 | gulp.task('sitemap', function () { 181 | gulp.src([config.source_dir + config.templates_dir + '[^_]*.html'], { 182 | read: false 183 | }) 184 | .pipe(sitemap({ 185 | siteUrl: require(config.source_dir + config.data_dir + 'index.js').siteUrl 186 | })) 187 | .pipe(gulp.dest(config.build_dir)); 188 | }); 189 | 190 | // Styles 191 | gulp.task('styles',function(){ 192 | gulp.src([config.source_dir + config.styles_dir + '[^_]*.{less,css}']) 193 | .pipe(plumber({ 194 | handleError: handleError 195 | })) 196 | .pipe(config.production ? util.noop() : sourcemaps.init()) 197 | .pipe(less({ 198 | plugins: [autoprefix] 199 | })) 200 | .pipe(config.production ? util.noop() : sourcemaps.write('')) 201 | .pipe(plumber({ 202 | handleError: handleError 203 | })) 204 | .pipe(config.purify ? purify([config.source_dir + config.scripts_dir + '[^_]*.js', config.source_dir + config.templates_dir + '[^_]*.html']) : util.noop()) 205 | .pipe(config.production ? cssnano() : util.noop()) 206 | .pipe(gulp.dest(config.build_dir + config.styles_dir)) 207 | .pipe(connect.reload()); 208 | }); 209 | 210 | // Scripts 211 | gulp.task('scripts',function(){ 212 | gulp.src([config.source_dir + config.scripts_dir + '[^_]*.js']) 213 | .pipe(plumber({ 214 | handleError: handleError 215 | })) 216 | .pipe(config.production ? util.noop() : sourcemaps.init()) 217 | .pipe(include()) 218 | .pipe(config.production ? util.noop() : sourcemaps.write('')) 219 | .pipe(plumber({ 220 | handleError: handleError 221 | })) 222 | .pipe(config.production ? uglify() : util.noop()) 223 | .pipe(gulp.dest(config.build_dir + config.scripts_dir)) 224 | .pipe(connect.reload()); 225 | }); 226 | 227 | // Images 228 | gulp.task('images',function(){ 229 | gulp.src([config.source_dir + config.images_dir + '**/*.{jpg,png,gif,svg}']) 230 | .pipe(plumber({ 231 | handleError: handleError 232 | })) 233 | .pipe(config.production ? imagemin({ 234 | progressive: true, 235 | svgoPlugins: [{removeViewBox: false}], 236 | use: [pngquant()] 237 | }) : util.noop()) 238 | .pipe(gulp.dest(config.build_dir + config.images_dir)) 239 | .pipe(connect.reload()); 240 | }); 241 | 242 | // Connect 243 | gulp.task('connect', function() { 244 | 245 | var httpProxyOptions = {}; 246 | 247 | if(config.proxyAuth && config.proxyAuth.length > 0){ 248 | 249 | httpProxyOptions.auth = config.proxyAuth; 250 | } 251 | 252 | corsAnywhere.createServer({ 253 | originWhitelist: [], 254 | httpProxyOptions : httpProxyOptions 255 | }).listen(config.proxy_port, 'localhost', function() { 256 | console.log('Running CORS Anywhere on ' + 'localhost' + ':' + config.proxy_port); 257 | }); 258 | 259 | connect.server({ 260 | root: config.build_dir, 261 | livereload: true, 262 | port: config.connect_port 263 | }); 264 | }); 265 | 266 | // Openuri 267 | gulp.task('openuri', function(){ 268 | gulp.src(config.build_dir + '*',{ 269 | read: false 270 | }).pipe(open({uri: 'http://localhost:' + config.connect_port})); 271 | }); 272 | 273 | gulp.task('tunnel', function(cb) { 274 | return ngrok.connect({ 275 | proto: 'http', // http|tcp|tls 276 | addr: config.connect_port, // port or network address 277 | //auth: 'yafbp:yafbp', // http basic authentication for tunnel 278 | //subdomain: 'alex', // reserved tunnel name https://alex.ngrok.io 279 | //authtoken: '12345', // your authtoken from ngrok.com 280 | //region: 'eu' // one of ngrok regions (us, eu, au, ap), defaults to us 281 | }, function (err, url) { 282 | site = url; 283 | console.log('Exposing localhost:'+ config.connect_port +' via public URL: ' + site); 284 | 285 | cb(); 286 | 287 | if(config.psi){ 288 | 289 | _.each(dataPages, function (value, key, list) { 290 | 291 | console.log('Awaiting psi-report for: ' + url + '/' + key +'.html'); 292 | 293 | psi.output(url + '/' + key +'.html',{ 294 | nokey: 'true', // or use key: ‘YOUR_API_KEY’ 295 | strategy: 'desktop', 296 | }).then(() => { 297 | 298 | psi.output(url + '/' + key +'.html',{ 299 | nokey: 'true', // or use key: ‘YOUR_API_KEY’ 300 | strategy: 'mobile', 301 | }); 302 | }); 303 | }); 304 | } 305 | }); 306 | }); 307 | 308 | // Default task 309 | gulp.task( 310 | 'default', 311 | ['clean','templates','styles','scripts','images','statics','connect','sitemap'], 312 | function(){ 313 | 314 | // WATCHERS 315 | watch([config.source_dir + config.templates_dir + '**/*.html', config.source_dir + config.data_dir + '*.{js,json}'], { 316 | read: false, 317 | readDelay: 100 318 | }, function() { 319 | 320 | gulp.start('templates'); 321 | }); 322 | 323 | watch([config.source_dir + config.templates_dir + '**/*.html'], { 324 | read: false, 325 | readDelay: 100 326 | }, function() { 327 | 328 | gulp.start('sitemap'); 329 | }); 330 | 331 | watch(config.source_dir + config.styles_dir + '**/*.{less,css}', { 332 | read: false, 333 | readDelay: 100 334 | }, function() { 335 | 336 | gulp.start('styles'); 337 | }); 338 | 339 | watch(config.source_dir + config.scripts_dir + '**/*.js', { 340 | read: false, 341 | readDelay: 100 342 | }, function() { 343 | 344 | gulp.start('scripts'); 345 | }); 346 | 347 | watch(config.source_dir + config.images_dir + '**/*.{jpg,png,gif,svg}', { 348 | read: false, 349 | readDelay: 100 350 | }, function() { 351 | 352 | gulp.start('images'); 353 | }); 354 | 355 | watch(config.source_dir + config.statics_dir + '**/*', { 356 | read: false, 357 | readDelay: 100 358 | }, function() { 359 | 360 | gulp.start('statics'); 361 | }); 362 | 363 | // Workaround for indexing all pages data on first run(only) 364 | setTimeout(function () { 365 | 366 | console.log('Running templates again to index initial data.'); 367 | 368 | gulp.start('templates'); // templates must be triggert twice because not all pages are indexed in dataPages in the first run 369 | 370 | setTimeout(function () { 371 | 372 | gulp.start('openuri'); 373 | 374 | if(config.tunnel){ 375 | 376 | gulp.start('tunnel'); 377 | } 378 | }, 1000); 379 | }, 1000); 380 | } 381 | ); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yet-another-frontend-build-process", 3 | "version": "1.2.4", 4 | "description": "yet-another-frontend-build-process.", 5 | "author": { 6 | "name": "Kolja Kutschera", 7 | "url": "koljakutschera.de" 8 | }, 9 | "url": "http://koljakutschera.de", 10 | "license": "MIT License", 11 | "keywords": [ 12 | "frontend", 13 | "build", 14 | "process", 15 | "yafbp" 16 | ], 17 | "devDependencies": { 18 | "accounting": "^0.4.1", 19 | "cors-anywhere": "^0.4.0", 20 | "del": "^2.2.2", 21 | "front-matter": "^2.1.0", 22 | "gulp": "^3.9.1", 23 | "gulp-connect": "^2.3.1", 24 | "gulp-cssnano": "^2.1.0", 25 | "gulp-data": "^1.2.1", 26 | "gulp-htmlmin": "^1.3.0", 27 | "gulp-imagemin": "^2.4.0", 28 | "gulp-include": "^2.1.0", 29 | "gulp-less": "^3.1.0", 30 | "gulp-nunjucks": "^2.2.0", 31 | "gulp-open": "^1.0.0", 32 | "gulp-plumber": "^1.0.0", 33 | "gulp-purifycss": "^0.2.0", 34 | "gulp-sitemap": "^4.2.0", 35 | "gulp-sourcemaps": "^1.6.0", 36 | "gulp-uglify": "^1.5.1", 37 | "gulp-util": "^3.0.7", 38 | "gulp-watch": "^4.3.5", 39 | "imagemin-pngquant": "^4.2.0", 40 | "less-plugin-autoprefix": "^1.5.1", 41 | "lodash": "^4.16.1", 42 | "moment": "^2.15.1", 43 | "ngrok": "^2.2.3", 44 | "nunjucks": "^2.5.2", 45 | "psi": "^2.0.4", 46 | "underscore": "^1.8.3" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/data/.gitkeep -------------------------------------------------------------------------------- /source/data/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Helper function that requires a module uncached. 3 | * 4 | * Why: 5 | * Node's module system automatically caches modules. 6 | * This is bad when watching for file-changes, 7 | * because we always get the cached(old) version of the module. 8 | * This function always loads the uncached version for us. Use this instead of require('MODULE_NAME') if you want to see changes in the file on every reload 9 | * 10 | * Example usage: 11 | * var myData = requireUncached('./myData.js'); 12 | * 13 | * @param {module} module 14 | * @returns undefined 15 | */ 16 | function requireUncached(module){ 17 | delete require.cache[require.resolve(module)] 18 | return require(module) 19 | } 20 | 21 | // Global template-data 22 | var global = { 23 | siteUrl : 'http://www.change-to-your-domain.de' // Required for sitemap.xml - do not delete! 24 | // Add your data here... 25 | }; 26 | 27 | // Expose data to templates 28 | module.exports = global; -------------------------------------------------------------------------------- /source/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/images/.gitkeep -------------------------------------------------------------------------------- /source/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/images/screenshot.png -------------------------------------------------------------------------------- /source/scripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/scripts/.gitkeep -------------------------------------------------------------------------------- /source/scripts/core.js: -------------------------------------------------------------------------------- 1 | //=require tools/log.js -------------------------------------------------------------------------------- /source/scripts/home.js: -------------------------------------------------------------------------------- 1 | // Just an example 2 | console.log('home.js logging...'); -------------------------------------------------------------------------------- /source/scripts/tools/log.js: -------------------------------------------------------------------------------- 1 | // Just an example 2 | console.log('tools/logs.js logging...'); -------------------------------------------------------------------------------- /source/statics/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/statics/.gitkeep -------------------------------------------------------------------------------- /source/styles/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brokolja/yafbp/ab2a1e01fd526b78ddade191b349d3facda66087/source/styles/.gitkeep -------------------------------------------------------------------------------- /source/styles/core.less: -------------------------------------------------------------------------------- 1 | // Just an example... 2 | @import "tools/reset.less"; -------------------------------------------------------------------------------- /source/styles/home.less: -------------------------------------------------------------------------------- 1 | // Just an example... 2 | h1 { 3 | font-size: 32px; 4 | color: #222; 5 | } -------------------------------------------------------------------------------- /source/styles/tools/reset.less: -------------------------------------------------------------------------------- 1 | // Just an example... 2 | body { 3 | background: #fafafa; 4 | } -------------------------------------------------------------------------------- /source/templates/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: Home 3 | --- 4 | 5 | {% extends "layouts/basic.html" %} 6 | 7 | {% block meta %} 8 | 9 | {% endblock %} 10 | 11 | {% block styles %} 12 | 13 | {% endblock %} 14 | 15 | {% block body %} 16 |

Welcome to: {{ global.siteUrl }}

17 |

You are here: {{ page.title }}({{pages.index.path}})

18 | {% endblock %} 19 | 20 | {% block scripts %} 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /source/templates/layouts/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ page.title }} 6 | {% block meta %} 7 | {% endblock %} 8 | 9 | {% block styles %} 10 | {% endblock %} 11 | 12 | 13 | {% block body %} 14 | {% endblock %} 15 | 16 | {% block scripts %} 17 | {% endblock %} 18 | 19 | --------------------------------------------------------------------------------