├── .gitignore ├── app ├── templates │ ├── gitattributes │ ├── views │ │ ├── modules │ │ │ ├── _footer.pug │ │ │ └── _header.pug │ │ ├── data │ │ │ ├── _index.yaml │ │ │ └── _index.json │ │ ├── helpers │ │ │ └── _language.pug │ │ ├── _index.pug │ │ └── layouts │ │ │ └── _default.pug │ ├── app │ │ ├── robots.txt │ │ ├── favicon.ico │ │ └── .htaccess │ ├── gitignore │ ├── eslintignore │ ├── gulp │ │ ├── tasks │ │ │ ├── _default.js │ │ │ ├── _clean.js │ │ │ ├── _watch.js │ │ │ ├── _browserSync.js │ │ │ ├── _modernizr.js │ │ │ ├── _serve.js │ │ │ ├── _rev.js │ │ │ ├── _deploy.js │ │ │ ├── _copy.js │ │ │ ├── _images.js │ │ │ ├── _styles.js │ │ │ ├── _build.js │ │ │ ├── _scripts.js │ │ │ └── _templates.js │ │ ├── utils │ │ │ ├── _buildHelper.js │ │ │ └── _handleError.js │ │ └── _config.js │ ├── scripts │ │ ├── external │ │ │ └── jquery.js │ │ ├── modules │ │ │ └── _utils.js │ │ └── _main.js │ ├── babelrc │ ├── env │ ├── env.example │ ├── _gulpfile.js │ ├── eslintrc │ ├── editorconfig │ ├── styles │ │ └── _main.scss │ ├── _readme.md │ └── _package.json └── index.js ├── .travis.yml ├── .jshintrc ├── LICENSE ├── package.json ├── readme.md └── test └── generator-spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /app/templates/gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /app/templates/views/modules/_footer.pug: -------------------------------------------------------------------------------- 1 | footer 2 | -------------------------------------------------------------------------------- /app/templates/views/modules/_header.pug: -------------------------------------------------------------------------------- 1 | header 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | - "4" -------------------------------------------------------------------------------- /app/templates/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/templates/gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .tmp 3 | .sass-cache 4 | bower_components 5 | .env 6 | -------------------------------------------------------------------------------- /app/templates/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightingbeetle/generator-lb/HEAD/app/templates/app/favicon.ico -------------------------------------------------------------------------------- /app/templates/eslintignore: -------------------------------------------------------------------------------- 1 | **/*.js 2 | !app/scripts/*.js 3 | !app/scripts/modules/**/*.js 4 | !app/scripts/external/**/*.js -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_default.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp-help')(require('gulp')); 2 | 3 | gulp.task('default', false, ['help']); -------------------------------------------------------------------------------- /app/templates/scripts/external/jquery.js: -------------------------------------------------------------------------------- 1 | import $ from 'jquery'; 2 | 3 | // Globalize jQuery, $ 4 | window.jQuery = $; 5 | window.$ = $; 6 | 7 | export default $; 8 | -------------------------------------------------------------------------------- /app/templates/views/data/_index.yaml: -------------------------------------------------------------------------------- 1 | index: 2 | <% if (includeMultiLanguage) { %>greeting: 3 | en: "Good luck!" 4 | de: "Viel Glück!" 5 | sk: "Veľa štastia!"<% } %> 6 | footer: "Lighting Beetle @ 2016" -------------------------------------------------------------------------------- /app/templates/views/helpers/_language.pug: -------------------------------------------------------------------------------- 1 | - function l(text) 2 | if text[language] 3 | - return text[language] 4 | else if text[primaryLanguage] 5 | - return text[primaryLanguage] 6 | else 7 | - return text -------------------------------------------------------------------------------- /app/templates/babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }], 4 | ["es2016"] 5 | ], 6 | "plugins": [ 7 | "transform-object-rest-spread", 8 | "transform-class-properties" 9 | ] 10 | } -------------------------------------------------------------------------------- /app/templates/scripts/modules/_utils.js: -------------------------------------------------------------------------------- 1 | export function sayHello(name = '<%= projectNameSlug %>') { // eslint-disable-line import/prefer-default-export 2 | console.log(`${name} says hello!`); // eslint-disable-line no-console 3 | } 4 | -------------------------------------------------------------------------------- /app/templates/env: -------------------------------------------------------------------------------- 1 | FTP_DEV_HOSTNAME= 2 | FTP_DEV_USER= 3 | FTP_DEV_DEST= 4 | 5 | FTP_PROD_HOSTNAME= 6 | FTP_PROD_USER= 7 | FTP_PROD_DEST= 8 | 9 | UGLIFYJS= 10 | MINIFYCSS= 11 | CACHEBUST= 12 | OPTIMIZEIMAGES= 13 | LINTJS= 14 | SOURCEMAPS= -------------------------------------------------------------------------------- /app/templates/env.example: -------------------------------------------------------------------------------- 1 | FTP_DEV_HOSTNAME= 2 | FTP_DEV_USER= 3 | FTP_DEV_DEST= 4 | 5 | FTP_PROD_HOSTNAME= 6 | FTP_PROD_USER= 7 | FTP_PROD_DEST= 8 | 9 | UGLIFYJS= 10 | MINIFYCSS= 11 | CACHEBUST= 12 | OPTIMIZEIMAGES= 13 | LINTJS= 14 | SOURCEMAPS= -------------------------------------------------------------------------------- /app/templates/views/data/_index.json: -------------------------------------------------------------------------------- 1 | { 2 | "index" : { 3 | <% if (includeMultiLanguage) { %>"greeting": { 4 | "en": "Good luck!", 5 | "de": "Viel Glück!", 6 | "sk": "Veľa štastia!" 7 | },<% } %> 8 | "footer" : "Lighting Beetle @ 2015" 9 | } 10 | } -------------------------------------------------------------------------------- /app/templates/_gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var requireDir = require('require-dir'); 4 | 5 | // handle enviromental variables in .env file 6 | require('dotenv').config({silent: true}); 7 | 8 | // Require all tasks in gulp/tasks, including subfolders 9 | requireDir('./gulp/tasks', { recurse: true }); -------------------------------------------------------------------------------- /app/templates/gulp/utils/_buildHelper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var build = false; 4 | 5 | var isBuild = function() { 6 | return build; 7 | }; 8 | 9 | var setBuild = function(arg) { 10 | build = arg; 11 | }; 12 | 13 | module.exports = { 14 | isBuild : isBuild, 15 | setBuild : setBuild 16 | }; -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var del = require('del'); 5 | 6 | var config = require('./../config.js'); 7 | 8 | // Cleaning task 9 | 10 | gulp.task('clean', 'Delete `dist/` and `temp/` folders', del.bind(null, config.clean)); -------------------------------------------------------------------------------- /app/templates/views/_index.pug: -------------------------------------------------------------------------------- 1 | extends layouts/_default 2 | 3 | block content 4 | // Sample intro 5 | .sample-intro 6 | h1 Please <%= projectName %> come for G. 7 | h2 Hodd luck! 8 | <% if (includeMultiLanguage) { %>span 9 | !=l(index.greeting)<% } %> 10 | p= index.footer 11 | -------------------------------------------------------------------------------- /app/templates/eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | 4 | "globals": { 5 | "$": true, 6 | "jQuery": true, 7 | "Modernizr": true, 8 | "window": true, 9 | }, 10 | 11 | "env": { 12 | "browser": true, 13 | }, 14 | 15 | "rules": { 16 | "no-param-reassign": 0, 17 | }, 18 | 19 | "parserOptions": { 20 | "ecmaFeatures": { 21 | experimentalObjectRestSpread: true, 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "jquery" : true 22 | } 23 | -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_watch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | 5 | var config = require('./../config.js'); 6 | var reload = require('./browserSync.js').reload; 7 | 8 | // Watch source files 9 | 10 | gulp.task('watch', 'Watch source files', function () { 11 | gulp.watch(config.watch.styles, ['styles']); 12 | gulp.watch(config.watch.pug, ['templates', reload]); 13 | gulp.watch(config.watch.scripts, ['scripts', reload]); 14 | }); 15 | -------------------------------------------------------------------------------- /app/templates/editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_browserSync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var browserSync = require('browser-sync'); 5 | 6 | var config = require('./../config.js'); 7 | 8 | // Serve project with livereload 9 | 10 | gulp.task('browser-sync', false, function() { 11 | browserSync(config.browserSync.dev); 12 | }); 13 | 14 | // Serve dist of project 15 | 16 | gulp.task('browser-sync:dist', false, function() { 17 | browserSync(config.browserSync.dist); 18 | }); 19 | 20 | module.exports.reload = browserSync.reload; -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_modernizr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var modernizr = require('gulp-modernizr'); 5 | 6 | var config = require('./../config.js'); 7 | var build = require('./../utils/buildHelper.js'); 8 | 9 | // Lean Modernizr build 10 | 11 | gulp.task('modernizr', 'Create modernizr lean build', function () { 12 | var dest = build.isBuild() ? config.modernizr.destBuild : config.modernizr.dest; 13 | 14 | return gulp.src(config.modernizr.src) 15 | .pipe(modernizr(config.modernizr.cfg)) 16 | .pipe(gulp.dest(dest)); 17 | }); -------------------------------------------------------------------------------- /app/templates/styles/_main.scss: -------------------------------------------------------------------------------- 1 | <% if (includeBootstrap) { -%> 2 | $icon-font-path: "../../node_modules/bootstrap-sass/assets/fonts/bootstrap/"; 3 | 4 | @import "bootstrap-sass/assets/stylesheets/_bootstrap"; 5 | <% } -%> 6 | <% if (includeFoundation) { -%> 7 | @import './../../node_modules/foundation-sites/scss/foundation'; 8 | 9 | @include foundation-everything; 10 | <% } -%> 11 | 12 | .sample-intro { 13 | padding-top: 60px; 14 | font-family: 'Helvetica Neue', Helvetica; 15 | text-align: center; 16 | 17 | h1, 18 | h2, 19 | p { 20 | color: rgb(246, 117, 50); 21 | } 22 | 23 | a { border-bottom-color: #f7854a; } 24 | } 25 | -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_serve.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var runSequence = require('run-sequence'); 5 | 6 | // Serve project with watching and livereload 7 | 8 | gulp.task('serve', 'Serve project with livereload and file watching',function (cb) { 9 | runSequence( 10 | ['styles', 'templates', 'scripts'], 11 | <% if (includeModernizr) { %>'modernizr',<% } %> 12 | 'browser-sync', 13 | 'watch', 14 | cb 15 | ); 16 | }); 17 | 18 | gulp.task('serve:dist', 'Bulid preview', function (cb) { 19 | runSequence( 20 | 'build', 21 | 'browser-sync:dist', 22 | cb 23 | ); 24 | }); -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_rev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | 5 | var rev = require('gulp-rev'); 6 | var revReplace = require('gulp-rev-replace'); 7 | 8 | var config = require('./../config.js'); 9 | 10 | gulp.task('rev:files', function(){ 11 | return gulp.src(config.rev.srcFiles) 12 | .pipe(rev()) 13 | .pipe(gulp.dest(config.rev.dest)) 14 | .pipe(rev.manifest()) 15 | .pipe(gulp.dest(config.rev.dest)); 16 | }); 17 | 18 | gulp.task('rev', 'Added hashes to files and rewrite file paths in HTML (Cache busting)', ['rev:files'], function(){ 19 | return gulp.src(config.rev.srcHtml) 20 | .pipe(revReplace({manifest: gulp.src(config.rev.manifestPath)})) 21 | .pipe(gulp.dest(config.rev.dest)); 22 | }); -------------------------------------------------------------------------------- /app/templates/gulp/utils/_handleError.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var notify = require('gulp-notify'); 4 | var gutil = require('gulp-util'); 5 | var argv = require('yargs').argv; 6 | 7 | var build = require('./buildHelper.js'); 8 | 9 | 10 | module.exports = function() { 11 | var args = Array.prototype.slice.call(arguments); 12 | // Send error to notification center with gulp-notify 13 | notify.onError({ 14 | title: '<%%= error.plugin %>', 15 | message: '<%%= error.message %>' 16 | }).apply(this, args); 17 | 18 | // End process in build task 19 | // Use gulp build --force to override 20 | if (build.isBuild() && !argv.force) { 21 | gutil.log(gutil.colors.red('There was an error in building process!')); 22 | process.exit(1); 23 | } 24 | 25 | // Keep gulp from hanging on this task 26 | this.emit('end'); 27 | }; 28 | -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_deploy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | 5 | var rsync = require('gulp-rsync'); 6 | var plumber = require('gulp-plumber'); 7 | 8 | var config = require('./../config.js'); 9 | var handleError = require('./../utils/handleError.js'); 10 | 11 | // Deploying via rsync/sftp 12 | // Credentials are stored in .env file 13 | 14 | // TODO plumber not working with this 15 | 16 | gulp.task('deploy', 'Deploy to development enviroment (specified in `.env`)', function() { 17 | return gulp.src(config.deploy.src) 18 | .pipe(plumber(handleError)) 19 | .pipe(rsync(config.deploy.dev)); 20 | }); 21 | 22 | gulp.task('deploy:prod', 'Deploy to production enviroment (specified in `.env`)', function() { 23 | return gulp.src(config.deploy.src) 24 | .pipe(plumber(handleError)) 25 | .pipe(rsync(config.deploy.dist)); 26 | }); -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_copy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | 5 | var config = require('./../config.js'); 6 | 7 | // copy font to dist folder 8 | gulp.task('fonts', 'Copy fonts to `dist/`', function () { 9 | return gulp.src(config.copyFonts.src) 10 | .pipe(gulp.dest(config.copyFonts.dest)); 11 | }); 12 | 13 | // copy icons to dist folder 14 | gulp.task('icons', 'Copy icons to `dist/`', function () { 15 | return gulp.src(config.copyIcons.src) 16 | .pipe(gulp.dest(config.copyIcons.dest)); 17 | }); 18 | 19 | // copy extras in app/ directory 20 | gulp.task('extras', 'Copy extras in `app/` root to `dist/`', function () { 21 | return gulp.src(config.copyExtras.src, config.copyExtras.cfg) 22 | .pipe(gulp.dest(config.copyExtras.dest)); 23 | }); 24 | 25 | gulp.task('copy', 'Copy fonts and extras to `dist/` folder', ['fonts', 'extras', 'icons']); -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_images.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var imagemin = require('gulp-imagemin'); 5 | var cache = require('gulp-cache'); 6 | var gulpif = require('gulp-if'); 7 | 8 | var config = require('./../config.js'); 9 | 10 | // Clear imagemin cache 11 | 12 | gulp.task('clearCache', 'Clear Imagemin cache', function (done) { 13 | return cache.clearAll(done); 14 | }); 15 | 16 | // Copy SVG to dist 17 | 18 | gulp.task('copySvg', 'Copy SVGs to `dist/`', function () { 19 | return gulp.src(config.images.srcSVG) 20 | .pipe(gulp.dest(config.images.dest)); 21 | }); 22 | 23 | // Optimize images 24 | 25 | gulp.task('images', 'Run Imagemin optimalizations and copy to `dist/`', ['copySvg'], function () { 26 | return gulp.src(config.images.src) 27 | .pipe(gulpif(config.optimizeImages, cache(imagemin(config.images.cfg)))) 28 | .pipe(gulp.dest(config.images.dest)); 29 | }); -------------------------------------------------------------------------------- /app/templates/views/layouts/_default.pug: -------------------------------------------------------------------------------- 1 | <% if (includeMultiLanguage) { %>include ../helpers/_language<% } %> 2 | 3 | doctype html 4 | 5 | 6 | 7 | 8 | 9 | 10 | head 11 | meta(charset="utf-8") 12 | title <%= projectName %> 13 | meta(name="description", content="") 14 | meta(name="viewport", content="width=device-width, initial-scale=1") 15 | link(rel="shortcut icon", href="/favicon.ico") 16 | 17 | link(rel="stylesheet", href="/styles/main.css") 18 | 19 | <% if (includeModernizr) { %> 20 | 21 | script(src="/scripts/plugins/modernizr.js") 22 | 23 | <% } %> 24 | 25 | body 26 | 27 | include ../modules/_header 28 | 29 | main 30 | block content 31 | 32 | include ../modules/_footer 33 | 34 | script(src="/scripts/main.js") 35 | 36 | | 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-lb", 3 | "version": "4.0.1", 4 | "description": "Lighting Beetle static website/templates generator", 5 | "license": "WTFPL", 6 | "repository": "https://github.com/lightingbeetle/generator-lb", 7 | "author": "Lighting Beetle", 8 | "main": "app/index.js", 9 | "engines": { 10 | "node": ">=0.10.0", 11 | "npm": ">=1.2.10" 12 | }, 13 | "files": [ 14 | "app" 15 | ], 16 | "keywords": [ 17 | "yeoman-generator", 18 | "web", 19 | "app", 20 | "front-end", 21 | "h5bp", 22 | "modernizr", 23 | "jquery", 24 | "gulp", 25 | "jade", 26 | "sass", 27 | "bootstrap", 28 | "foundation", 29 | "lb" 30 | ], 31 | "dependencies": { 32 | "chalk": "~1.1.1", 33 | "gulp-frep": "~0.1.3", 34 | "gulp-if": "~2.0.0", 35 | "insight": "~0.8.1", 36 | "mkdirp": "~0.5.0", 37 | "underscore.string": "~3.3.4", 38 | "yeoman-generator": "~0.24.1", 39 | "yosay": "~1.2.0" 40 | }, 41 | "peerDependencies": { 42 | "yo": ">=1.2.0" 43 | }, 44 | "devDependencies": { 45 | "tap-spec": "~4.1.1", 46 | "tape": "~4.6.2", 47 | "yeoman-assert": "~2.2.1", 48 | "yeoman-test": "~1.5.1" 49 | }, 50 | "scripts": { 51 | "test": "node test/generator-spec.js | ./node_modules/.bin/tap-spec", 52 | "start": "node app/index.js" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/templates/_readme.md: -------------------------------------------------------------------------------- 1 | # <%= projectName %> 2 | 3 | > This is description 4 | 5 | ## How to install 6 | 7 | Prerequisites: 8 | 9 | * [Node.js](http://nodejs.org/) >=4.0.0 10 | 11 | Installation process: 12 | 1. Clone this repository 13 | 2. Run ```npm install``` to install dependencies 14 | 15 | ## Usage 16 | 17 | For project development with livereload run: 18 | ``` 19 | gulp serve 20 | ``` 21 | 22 | To build project run: (Result will be in ```dist/``` folder.) 23 | ``` 24 | gulp build [--force] 25 | ``` 26 | 27 | To serve built project run: 28 | ``` 29 | gulp serve:dist 30 | ``` 31 | 32 | Gulp help: 33 | ``` 34 | gulp help 35 | ``` 36 | 37 | 38 | ## Built-in features 39 | 40 | * Webserver with liverelaod 41 | * Pug compilation 42 | * Sass compilation 43 | * ES2016 transpiling with Babel.js 44 | * ES2015 modules bundling with Rollup.js 45 | * JS linting with ESLint 46 | * CSS autoprefixing 47 | * CSS minification 48 | * Image optimaliztion 49 | * Lean Modernizr builds 50 | * Improved file caching 51 | * Deploying via rsync/sftp 52 | <% if (includeMultiLanguage) { %>* Multi-language templates support <% } %> 53 | 54 | --- 55 | 56 | Project structure was generated by [generator-lb](https://github.com/lightingbeetle/generator-lb) using version <%= version %>. 57 | 58 | [![Lighting Beetle](https://github.com/adam-p/markdown-here/raw/master/src/common/images/icon48.png "Lighting Beetle")](http://www.lbstudio.sk) -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_styles.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var gulpif = require('gulp-if'); 5 | var sass = require('gulp-sass'); 6 | var sourcemaps = require('gulp-sourcemaps'); 7 | var postcss = require('gulp-postcss'); 8 | var plumber = require('gulp-plumber'); 9 | <% if (includeBootstrap) { %>var replace = require('gulp-replace');<% } %> 10 | 11 | var autoprefixer = require('autoprefixer'); 12 | var cssnano = require('cssnano'); 13 | 14 | var config = require('./../config.js'); 15 | var reload = require('./browserSync.js').reload; 16 | var handleError = require('./../utils/handleError.js'); 17 | var build = require('./../utils/buildHelper.js'); 18 | 19 | // Complie scss using libsass 20 | 21 | gulp.task('styles', 'Compile Sass to CSS', function () { 22 | var dest = build.isBuild() ? config.styles.destBuild : config.styles.dest; 23 | 24 | return gulp.src(config.styles.src) 25 | <% if (includeBootstrap) { %>.pipe(replace('bootstrap-sass/assets/fonts/bootstrap/', '../fonts/'))<% } %> 26 | .pipe(gulpif(config.sourceMaps && !build.isBuild(), sourcemaps.write('.'))) 27 | .pipe(sass(config.styles.sassCfg)) 28 | .on('error', handleError) 29 | .pipe(postcss([ 30 | autoprefixer(config.styles.autoprefixerCfg), 31 | build.isBuild() ? cssnano() : function() {} 32 | ])) 33 | .pipe(gulpif(config.sourceMaps && !build.isBuild(), sourcemaps.write('.'))) 34 | .pipe(gulp.dest(dest)) 35 | .pipe(reload({stream:true})); 36 | }); 37 | -------------------------------------------------------------------------------- /app/templates/scripts/_main.js: -------------------------------------------------------------------------------- 1 | <% if (includejQuery2 || includejQuery3) { %>import {} from './external/jquery';<% } -%> 2 | <% if (includeBootstrap) { %> 3 | // Import only bootstrap packages you need 4 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/affix'; 5 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/alert'; 6 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/button'; 7 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/carousel'; 8 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/collapse'; 9 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/dropdown'; 10 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/modal'; 11 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/scrollspy'; 12 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; 13 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/tab'; 14 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/transition'; 15 | // import {} from 'bootstrap-sass/assets/javascripts/bootstrap/popover'; 16 | // Or import everything 17 | import {} from 'bootstrap-sass'; // eslint-disable-line import/imports-first 18 | <% } -%> 19 | <% if (includeFoundation) { %> 20 | import {} from 'foundation-sites'; // eslint-disable-line import/imports-first 21 | <% } -%> 22 | import { sayHello } from './modules/utils'; 23 | 24 | <% if (includeFoundation) { -%> 25 | $(document).foundation(); 26 | <% } -%> 27 | 28 | sayHello(); 29 | -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp-help')(require('gulp')); 4 | var runSequence = require('run-sequence'); 5 | var size = require('gulp-size'); 6 | var notifier = require('node-notifier'); 7 | 8 | var config = require('./../config.js'); 9 | var build = require('./../utils/buildHelper.js'); 10 | 11 | // Output size of dist folder 12 | gulp.task('buildSize:css', false, function () { 13 | return gulp.src(config.buildSize.srcCss) 14 | .pipe(size(config.buildSize.cfgCss)); 15 | }); 16 | 17 | // Output size of dist folder 18 | gulp.task('buildSize:js', false, function () { 19 | return gulp.src(config.buildSize.srcJs) 20 | .pipe(size(config.buildSize.cfgJs)); 21 | }); 22 | 23 | // Output size of dist folder 24 | gulp.task('buildSize:images', false, function () { 25 | return gulp.src(config.buildSize.srcImages) 26 | .pipe(size(config.buildSize.cfgImages)); 27 | }); 28 | 29 | // Output size of dist folder 30 | gulp.task('buildSize', 'Determine size of `dist/` folder', ['buildSize:css', 'buildSize:js','buildSize:images'], function () { 31 | return gulp.src(config.buildSize.srcAll) 32 | .pipe(size(config.buildSize.cfgAll)); 33 | }); 34 | 35 | // run build in sequence - this shoud be implemented in Gulp 4 natively 36 | gulp.task('build', 'Build project (use with --force to force build)', function(cb) { 37 | build.setBuild(true); 38 | runSequence( 39 | ['clean'], 40 | ['styles', 'scripts'], 41 | ['images', 'copy', 'extras'<% if (includeModernizr) { %>, 'modernizr'<% }%>], 42 | 'templates', 43 | 'buildSize', 44 | config.cacheBust ? 'rev' : function() {}, 45 | function() { 46 | notifier.notify({ 47 | title: 'Build', 48 | message: 'Build was successful' 49 | }); 50 | cb(); 51 | } 52 | ); 53 | }); -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_scripts.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | 4 | var gulp = require('gulp-help')(require('gulp')); 5 | var gulpif = require('gulp-if'); 6 | 7 | var eslint = require('gulp-eslint'); 8 | 9 | var rollupStream = require('rollup-stream'); 10 | var rollup = require('rollup'); 11 | var babel = require('rollup-plugin-babel'); 12 | var nodeResolve = require('rollup-plugin-node-resolve'); 13 | var commonjs = require('rollup-plugin-commonjs'); 14 | var replace = require('rollup-plugin-replace'); 15 | var uglify = require('rollup-plugin-uglify'); 16 | var source = require('vinyl-source-stream') 17 | 18 | var buffer = require('vinyl-buffer'); 19 | var sourcemaps = require('gulp-sourcemaps'); 20 | 21 | var config = require('./../config.js'); 22 | var handleError = require('./../utils/handleError.js'); 23 | var build = require('./../utils/buildHelper.js'); 24 | 25 | // Lint .js files 26 | 27 | gulp.task('lintjs', 'Lint js files', function () { 28 | if (config.lintJs) { 29 | return gulp.src(config.eslint.src) 30 | .pipe(eslint({ignore: false})) // temp hack with ignore 31 | .pipe(eslint.format()) 32 | .pipe(eslint.failAfterError()) 33 | .on('error', handleError); 34 | } else { 35 | return; 36 | } 37 | }); 38 | 39 | var cache; 40 | 41 | gulp.task('scripts', 'Compile ES6 to ES5', ['lintjs'],function () { 42 | var dest = build.isBuild() ? config.scripts.destBuild : config.scripts.dest; 43 | 44 | config.scripts.rollupCfg.entry = config.scripts.src; 45 | config.scripts.rollupCfg.rollup = rollup; 46 | config.scripts.rollupCfg.sourceMap = config.sourceMaps && !build.isBuild(); 47 | config.scripts.rollupCfg.plugins = [ 48 | nodeResolve({ 49 | jsnext: true, 50 | browser: true, 51 | }), 52 | commonjs({ 53 | include: 'node_modules/**', 54 | }), 55 | babel({ 56 | exclude: 'node_modules/**', 57 | }), 58 | replace({ 59 | 'process.env.NODE_ENV': JSON.stringify(build.isBuild() ? 'production' : 'development' ) 60 | }), 61 | build.isBuild() ? uglify() : function() {}, 62 | ]; 63 | 64 | return rollupStream(config.scripts.rollupCfg) 65 | .on('bundle', function(bundle) { 66 | cache = bundle; 67 | }) 68 | .on('error', handleError) 69 | .pipe(source(path.basename(config.scripts.rollupCfg.entry))) 70 | .pipe(gulpif(config.sourceMaps && !build.isBuild(), buffer())) 71 | .pipe(gulpif(config.sourceMaps && !build.isBuild(), sourcemaps.init({loadMaps: true}))) 72 | .pipe(gulpif(config.sourceMaps && !build.isBuild(), sourcemaps.write('.'))) 73 | .pipe(gulp.dest(dest)); 74 | }); -------------------------------------------------------------------------------- /app/templates/gulp/tasks/_templates.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | <% if (includeMultiLanguage) { %>var path = require('path');<% } %> 3 | 4 | var gulp = require('gulp-help')(require('gulp')); 5 | var pug = require('gulp-pug'); 6 | var data = require('gulp-data'); 7 | var plumber = require('gulp-plumber'); 8 | var fs = require('fs'); 9 | var extend = require('gulp-extend'); 10 | var gulpif = require('gulp-if'); 11 | var rev = require('gulp-rev'); 12 | var revReplace = require('gulp-rev-replace'); 13 | var filter = require('gulp-filter'); 14 | <% if (includeMultiLanguage) { %>var merge = require('merge-stream');<% } %> 15 | 16 | <% if (includeDataYAML) { %>var yamlMerge = require('gulp-yaml-merge'); 17 | var yaml = require('js-yaml');<% } %> 18 | 19 | var config = require('./../config.js'); 20 | var handleError = require('./../utils/handleError.js'); 21 | var build = require('./../utils/buildHelper.js'); 22 | 23 | // Compile pug to html 24 | 25 | gulp.task('templates', 'Compile templates', ['templates:prepareData'], function() { 26 | var dest = build.isBuild() ? config.templates.destBuild : config.templates.dest; 27 | 28 | <% if (!includeMultiLanguage) { %>return gulp.src(config.templates.src) 29 | .pipe(plumber(handleError)) 30 | .pipe(data(function() { 31 | <% if (includeDataYAML) { %>return yaml.safeLoad(fs.readFileSync(config.templatesData.dataPath, 'utf8')); 32 | <% } else { %> return JSON.parse(fs.readFileSync(config.templatesData.dataPath));<% } %> 33 | })) 34 | .pipe(pug(config.templates.cfg)) 35 | .pipe(gulp.dest(dest)); 36 | <% } else { %> 37 | var languages = config.templates.languages.list.map(function(lang) { 38 | return gulp.src(config.templates.src) 39 | .pipe(plumber(handleError)) 40 | .pipe(data(function() { 41 | <% if (includeDataYAML) { %>var json = yaml.safeLoad(fs.readFileSync(config.templatesData.dataPath, 'utf8')); 42 | <% } else { %> var json = JSON.parse(fs.readFileSync(config.templatesData.dataPath));<% } %> 43 | json.language = lang; 44 | json.primaryLanguage = config.templates.languages.primary; 45 | return json; 46 | })) 47 | .pipe(pug(config.templates.cfg)) 48 | .pipe((config.templates.languages.primary === lang) ? gulp.dest(dest) : gulp.dest(path.join(dest, lang))); 49 | }); 50 | 51 | return merge(languages);<% } %> 52 | }); 53 | 54 | // Concat *.json file to single data.json 55 | 56 | gulp.task('templates:prepareData', 'Merge views data', function() { 57 | return gulp.src(config.templatesData.src) 58 | <% if (includeDataYAML) { %>.pipe(yamlMerge(config.templatesData.dataName)) 59 | <% } else { %>.pipe(extend(config.templatesData.dataName))<% } %> 60 | .pipe(gulp.dest(config.templatesData.dest)); 61 | }); 62 | -------------------------------------------------------------------------------- /app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= projectNameSlug %>", 3 | "repository": { 4 | "type": "git", 5 | "url": "" 6 | }, 7 | "version" : "0.0.1", 8 | "description": "<%= projectName %> description", 9 | "dependencies": <%- JSON.stringify(dependencies, null, 2) %>, 10 | "devDependencies": { 11 | "autoprefixer": "~6.5.0", 12 | "babel-plugin-external-helpers": "~6.8.0", 13 | "babel-plugin-transform-class-properties": "~6.16.0", 14 | "babel-plugin-transform-object-rest-spread": "~6.16.0", 15 | "babel-preset-es2015": "~6.16.0", 16 | "babel-preset-es2016": "~6.16.0", 17 | "browser-sync": "~2.17.0", 18 | "cssnano": "~3.7.6", 19 | "del": "~2.2.0", 20 | "dotenv": "~2.0.0", 21 | "gulp": "~3.9.0", 22 | "connect-modrewrite": "~0.9.0", 23 | "eslint": "~3.7.1", 24 | "eslint-config-airbnb-base": "~8.0.0", 25 | "eslint-plugin-import": "~2.0.0", 26 | "gulp-babel": "~6.1.1", 27 | "gulp-cache": "~0.4.1", 28 | "gulp-concat": "~2.6.0", 29 | "gulp-data": "~1.2.0", 30 | "gulp-eslint": "~3.0.1", 31 | "gulp-extend": "~0.2.0", 32 | "gulp-filter": "~4.0.0", 33 | "gulp-help": "~1.6.0", 34 | "gulp-if": "~2.0.0", 35 | "gulp-imagemin": "~3.0.1", 36 | "gulp-pug": "~3.1.0",<% if (includeModernizr) { %> 37 | "gulp-modernizr": "~1.0.0-alpha",<% } %> 38 | "gulp-notify": "~2.2.0", 39 | "gulp-postcss": "~6.2.0", 40 | "gulp-plumber": "~1.1.0", 41 | "gulp-rename": "~1.2.0",<% if (includeBootstrap) { %> 42 | "gulp-replace": "~0.5.0",<% } %> 43 | "gulp-rev": "~7.1.2", 44 | "gulp-rev-replace": "~0.4.0", 45 | "gulp-rollup": "~2.5.1", 46 | "gulp-rsync": "~0.0.5", 47 | "gulp-size": "~2.1.0", 48 | "gulp-sass": "~2.3.2", 49 | "gulp-sourcemaps": "~1.6.0", 50 | "gulp-util": "~3.0.4",<% if (includeDataYAML) { %> 51 | "gulp-yaml-merge": "adammockor/gulp-yaml-merge",<% } %> 52 | "pug": "2.0.0-beta6",<% if (includeDataYAML) { %> 53 | "js-yaml": "~3.6.0",<% } %> 54 | "map-stream": "~0.0.5",<% if (includeMultiLanguage) { %> 55 | "merge-stream": "~1.0.0",<% } %> 56 | "node-notifier": "~4.6.1", 57 | "node-sass": "~3.10.1", 58 | "require-dir": "~0.3.0", 59 | "rollup": "~0.36.1", 60 | "rollup-plugin-babel": "~2.6.1", 61 | "rollup-plugin-commonjs": "~5.0.4", 62 | "rollup-plugin-filesize": "~1.0.0", 63 | "rollup-plugin-node-resolve": "~2.0.0", 64 | "rollup-plugin-replace": "~1.1.1", 65 | "rollup-plugin-uglify": "~1.0.1", 66 | "rollup-stream": "~1.14.0", 67 | "run-sequence": "~1.2.2", 68 | "vinyl-buffer": "~1.0.0", 69 | "vinyl-source-stream": "~1.1.0", 70 | "yargs": "~6.0.0" 71 | }, 72 | "engines": { 73 | "node": ">=4.0.0" 74 | }, 75 | "scripts": 76 | { 77 | "build": "gulp build", 78 | "serve": "gulp serve" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Generator LB 2 | 3 | [![Build Status](https://travis-ci.org/lightingbeetle/generator-lb.svg?branch=master)](https://travis-ci.org/lightingbeetle/generator-lb) 4 | 5 | > Yeoman generator for creating static website/templates using Sass, Rollup, Pug, and Gulp, used by [Lighting Beetle](http://www.lbstudio.sk). 6 | 7 | ## Prereqisities 8 | 9 | * [Node.js](http://nodejs.org/) 10 | * [Yeoman](http://yeoman.io/) 11 | ```javascript 12 | (sudo) npm install -g yo 13 | ``` 14 | * [Gulp](http://gulpjs.com/) 15 | ```javascript 16 | (sudo) npm install -g gulp 17 | ``` 18 | 19 | ## Built-in tools 20 | 21 | * Gulp (Task Manager) 22 | * Sass (LibSass) (CSS Preprocessor) 23 | * Pug (HTML templating) 24 | * ES2016 compilation to ES5 using Babel 25 | * ES2015 modules bundling with Rollup.js 26 | * Bootstrap (Frontend framework) (Sass version) (optional) 27 | * Foundation (Frontend framework) (optional) 28 | * Modernizr (HTML5/CSS3 features detection) (optional) 29 | * jQuery 2.x/3.x(feature-rich JavaScript library) (optional) 30 | * LightingFly (SCSS mixins library) (optional) 31 | 32 | ## Built-in features 33 | 34 | * CSS autoprefixing (autoprefixer) 35 | * Webserver with liverelaod (browserSync) 36 | * Pug compilation 37 | * Sass compilation 38 | * YAML/JSON data sources for templates 39 | * CSS/JS concating and minification (cssnano) 40 | * JS linting (eslint with airbnb config) 41 | * ES2016 transpiling (babel) 42 | * ES2015 modules bundling (rollup) 43 | * Image optimaliztion (imagemin) 44 | * Lean Modernizr builds 45 | * Improved file caching 46 | * Deploying via rsync/sftp 47 | * Multi-language templates support (optional) 48 | * Advanced routing in development 49 | 50 | ## Installation guide 51 | 52 | 1. Install via npm `(sudo) npm install -g generator-lb` 53 | 2. Create folder for your project and run inside: `yo lb` 54 | 3. Complete installation 55 | 4. ? 56 | 5. Profit 57 | 58 | ## Usage 59 | 60 | Gulpfile contains some useful tasks: 61 | 62 | 1. `gulp serve` for development with livereload 63 | 2. `gulp build` for building from source to `dist` folder 64 | 3. `gulp serve:dist` for build preview 65 | 4. `gulp deploy` for deploying on dev server via sftp (config is in .env file) 66 | 5. `gulp deploy:prod` for deploying on prod server via sftp (config is in .env file) 67 | 6. `gulp help` for information about other tasks 68 | 69 | ## Folder structure 70 | 71 | ``` 72 | . 73 | +-- app 74 | +-- fonts 75 | +-- icons 76 | +-- images 77 | +-- scripts 78 | +-- external 79 | +-- jquery.js 80 | +-- modules 81 | +-- plugins 82 | +-- main.js 83 | +-- styles 84 | +-- modules 85 | +-- plugins 86 | +-- main.scss 87 | +-- views 88 | +-- data 89 | +-- index.[json/yaml] 90 | +-- helpers 91 | +-- layouts 92 | +-- _default.jade 93 | +-- modules 94 | +-- _footer.jade 95 | +-- _header.jade 96 | +-- index.jade 97 | +-- .htaccess 98 | +-- favicon.ico 99 | +-- robots.txt 100 | +-- bower_components 101 | +-- dist 102 | +-- gulp 103 | +-- tasks 104 | +-- browserSync.js 105 | +-- build.js 106 | +-- clean.js 107 | +-- copy.js 108 | +-- default.js 109 | +-- deploy.js 110 | +-- images.js 111 | +-- modernizr.js (optional) 112 | +-- rev.js 113 | +-- scripts.js 114 | +-- serve.js 115 | +-- styles.js 116 | +-- templates.js 117 | +-- watch.js 118 | +-- utils 119 | +-- buildHelper.js 120 | +-- handleError.js 121 | +-- config.js 122 | +-- node_modules 123 | +-- .babelrc 124 | +-- .editorconfig 125 | +-- .env 126 | +-- .gitattributes 127 | +-- .gitignore 128 | +-- .eslintrc 129 | +-- .yo-rc.json 130 | +-- gulpfile.js 131 | +-- package.json 132 | ``` 133 | 134 | ## Notes 135 | * Gulp tasks can be configured inside `config.js` file in `gulp` folder. 136 | * Data for jade templates can by stored as YAML/JSON objects inside. ```app/views/data/``` 137 | * Build can be forced with ```gulp build --force``` 138 | 139 | ## Contributors 140 | * Adam Močkoř (mockor@lbstudio.sk) 141 | 142 | --- 143 | [![Lighting Beetle](http://www.lbstudio.sk/static/imgs/lb-logo-orange.png "Lighting Beetle")](http://www.lbstudio.sk) 144 | 145 | -------------------------------------------------------------------------------- /test/generator-spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const assert = require('yeoman-assert'); 3 | const helpers = require('yeoman-test'); 4 | const test = require('tape'); 5 | 6 | const defaultPrompt = { 7 | optIn: false, 8 | name: 'test of generator', 9 | features: [], 10 | includeMultiLanguage: false, 11 | dataFormat: 'yaml', 12 | }; 13 | 14 | function testExpected(expected) { 15 | expected.forEach((file) => { 16 | if (typeof file === 'string') { 17 | assert.file(file); 18 | } else if (Array.isArray(file)) { 19 | assert.fileContent.apply(this, file); 20 | } 21 | }); 22 | } 23 | 24 | function runTest(prompt) { 25 | return helpers.run(path.join(__dirname, '../app')) 26 | .withOptions({ skipInstall: true }) 27 | .withPrompts(prompt) 28 | .toPromise(); 29 | } 30 | 31 | function handleError(err, t) { 32 | console.log(err); 33 | t.error(err); 34 | t.end(); 35 | } 36 | 37 | test('generator-lb:defaults', (t) => { 38 | const prompt = defaultPrompt; 39 | 40 | const expected = [ 41 | 'package.json', 42 | ['package.json', /\"name\": \"test-of-generator\"/], 43 | ['package.json', /node-sass/], 44 | ['package.json', /gulp-sass/], 45 | 'gulpfile.js', 46 | 'gulp/tasks/browserSync.js', 47 | 'gulp/tasks/clean.js', 48 | 'gulp/tasks/default.js', 49 | 'gulp/tasks/deploy.js', 50 | 'gulp/tasks/images.js', 51 | 'gulp/tasks/rev.js', 52 | 'gulp/tasks/templates.js', 53 | 'gulp/tasks/watch.js', 54 | 'gulp/utils/buildHelper.js', 55 | 'gulp/utils/handleError.js', 56 | 'gulp/config.js', 57 | 'gulp/tasks/build.js', 58 | 'gulp/tasks/copy.js', 59 | 'gulp/tasks/serve.js', 60 | 'gulp/tasks/scripts.js', 61 | 'gulp/tasks/styles.js', 62 | 'readme.md', 63 | '.gitignore', 64 | '.gitattributes', 65 | '.env', 66 | '.eslintrc', 67 | '.eslintignore', 68 | '.editorconfig', 69 | 'app/styles/main.scss', 70 | 'app/views/index.pug', 71 | 'app/views/layouts/_default.pug', 72 | 'app/views/modules/_header.pug', 73 | 'app/views/modules/_footer.pug', 74 | 'app/views/data/index.yaml', 75 | 'app/.htaccess', 76 | 'app/favicon.ico', 77 | 'app/robots.txt', 78 | 'app/scripts/main.js' 79 | ]; 80 | 81 | runTest(prompt) 82 | .then(() => { 83 | t.doesNotThrow(() => testExpected(expected), null, 'All files present'); 84 | t.end(); 85 | }) 86 | .catch((err) => handleError(err, t)); 87 | }); 88 | 89 | test('generator-lb:bootstrap', (t) => { 90 | const prompt = Object.assign(defaultPrompt, { 91 | features: ['includeFEFramework'], 92 | feFramework: 'includeBootstrap', 93 | }); 94 | 95 | const expected = [ 96 | ['package.json', /bootstrap-sass/], 97 | ['package.json', /\"jquery\":/], 98 | 'app/scripts/external/jquery.js', 99 | ]; 100 | 101 | runTest(prompt) 102 | .then(() => { 103 | t.doesNotThrow(() => testExpected(expected), null, 'bootstrap-sass present'); 104 | t.end(); 105 | }) 106 | .catch((err) => handleError(err, t)); 107 | }); 108 | 109 | test('generator-lb:foundation', (t) => { 110 | const prompt = Object.assign(defaultPrompt, { 111 | features: ['includeFEFramework'], 112 | feFramework: 'includeFoundation', 113 | }); 114 | 115 | const expected = [ 116 | ['package.json', /foundation-sites/], 117 | ['package.json', /\"jquery\": \"~2/], 118 | 'app/scripts/external/jquery.js', 119 | ]; 120 | 121 | runTest(prompt) 122 | .then(() => { 123 | t.doesNotThrow(() => testExpected(expected), null, 'foundation-sites present'); 124 | t.end(); 125 | }) 126 | .catch((err) => handleError(err, t)); 127 | }); 128 | 129 | test('generator-lb:modernizr', (t) => { 130 | const prompt = Object.assign(defaultPrompt, { 131 | features: ['includeModernizr'] 132 | }); 133 | 134 | const expected = [ 135 | 'gulp/tasks/modernizr.js', 136 | ['package.json', /gulp-modernizr/], 137 | ]; 138 | 139 | runTest(prompt) 140 | .then(() => { 141 | t.doesNotThrow(() => testExpected(expected), null, 'modernizr present'); 142 | t.end(); 143 | }) 144 | .catch((err) => handleError(err, t)); 145 | }); 146 | 147 | test('generator-lb:jquery2', (t) => { 148 | const prompt = Object.assign(defaultPrompt, { 149 | features: ['includejQuery'], 150 | jQuery: 'includejQuery2', 151 | }); 152 | 153 | const expected = [ 154 | ['package.json', /\"jquery\": \"~2/], 155 | 'app/scripts/external/jquery.js', 156 | ]; 157 | 158 | runTest(prompt) 159 | .then(() => { 160 | t.doesNotThrow(() => testExpected(expected), null, 'jQuery 2.x.x present'); 161 | t.end(); 162 | }) 163 | .catch((err) => handleError(err, t)); 164 | }); 165 | 166 | test('generator-lb:jquery3', (t) => { 167 | const prompt = Object.assign(defaultPrompt, { 168 | features: ['includejQuery'], 169 | jQuery: 'includejQuery3', 170 | }); 171 | 172 | const expected = [ 173 | ['package.json', /\"jquery\": \"~3/], 174 | 'app/scripts/external/jquery.js', 175 | ]; 176 | 177 | runTest(prompt) 178 | .then(() => { 179 | t.doesNotThrow(() => testExpected(expected), null, 'jQuery 3.x.x present'); 180 | t.end(); 181 | }) 182 | .catch((err) => handleError(err, t)); 183 | }); 184 | 185 | test('generator-lb:lightingFly', (t) => { 186 | const prompt = Object.assign(defaultPrompt, { 187 | features: ['includeLightingFly'], 188 | }); 189 | 190 | const expected = [ 191 | ['package.json', /lightingfly/], 192 | ]; 193 | 194 | runTest(prompt) 195 | .then(() => { 196 | t.doesNotThrow(() => testExpected(expected), null, 'lightingFly present'); 197 | t.end(); 198 | }) 199 | .catch((err) => handleError(err, t)); 200 | }); 201 | 202 | test('generator-lb:multiLanguage', (t) => { 203 | const prompt = Object.assign(defaultPrompt, { 204 | includeMultiLanguage: true, 205 | }); 206 | 207 | const expected = [ 208 | ['package.json', /merge-stream/], 209 | ['gulp/config.js', /var languages/] 210 | ]; 211 | 212 | runTest(prompt) 213 | .then(() => { 214 | t.doesNotThrow(() => testExpected(expected), null, 'multi-language support present'); 215 | t.end(); 216 | }) 217 | .catch((err) => handleError(err, t)); 218 | }); 219 | 220 | test('generator-lb:YAML', (t) => { 221 | const prompt = Object.assign(defaultPrompt, { 222 | dataFormat: 'yaml', 223 | }); 224 | 225 | const expected = [ 226 | 'app/views/data/index.yaml', 227 | ]; 228 | 229 | runTest(prompt) 230 | .then(() => { 231 | t.doesNotThrow(() => testExpected(expected), null, 'YAML formatting present'); 232 | t.end(); 233 | }) 234 | .catch((err) => handleError(err, t)); 235 | }); 236 | 237 | test('generator-lb:JSON', (t) => { 238 | const prompt = Object.assign(defaultPrompt, { 239 | dataFormat: 'json', 240 | }); 241 | 242 | const expected = [ 243 | 'app/views/data/index.json', 244 | ]; 245 | 246 | runTest(prompt) 247 | .then(() => { 248 | t.doesNotThrow(() => testExpected(expected), null, 'JSON formatting present'); 249 | t.end(); 250 | }) 251 | .catch((err) => handleError(err, t)); 252 | }); 253 | -------------------------------------------------------------------------------- /app/templates/gulp/_config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var modRewrite = require('connect-modrewrite'); 4 | 5 | // Default settings 6 | module.exports.uglifyJs = process.env.UGLIFYJS || true; // to remove .min sufix edit template manually 7 | module.exports.minifyCss = process.env.MINIFYCSS || true; // to remove .min sufix edit template manually 8 | module.exports.cacheBust = process.env.CACHEBUST || true; 9 | module.exports.optimizeImages = process.env.OPTIMIZEIMAGES || true; 10 | module.exports.lintJs = process.env.LINTJS || true; 11 | module.exports.sourceMaps = process.env.SOURCEMAPS || true; 12 | 13 | // Default paths 14 | var app = 'app'; 15 | var tmp = '.tmp'; 16 | var dist = 'dist'; 17 | var nodeDir = 'node_modules'; 18 | 19 | // Default paths in app folder 20 | var data = 'data'; 21 | var fonts = 'fonts'; 22 | var icons = 'icons'; 23 | var images = 'images'; 24 | var scripts = 'scripts'; 25 | var styles = 'styles'; 26 | var views = 'views'; 27 | 28 | <% if (includeMultiLanguage) { -%> 29 | var languages = { 30 | list: ['en', 'de', 'sk'], 31 | primary: 'en' 32 | }; 33 | <% } -%> 34 | 35 | // Rewrite rules enables removing .html extensions in development. 36 | // This are possible routes for same test.html file: 37 | // http://localhost:3000/test.html 38 | // http://localhost:3000/test 39 | var rewriteRules = [ 40 | '^/$ - [L]', // default site root handling (index.html) 41 | '.html$ - [L]', // ignore routes ends with '.html' 42 | '(.*)/$ $1/index.html [L]', // routes with trailing slash are directories -> rewrite to directory index.html 43 | '\\/\[a-zA-Z0-9_\\-\@.]+\\.\[a-zA-Z0-9]+$ - [L]', // ignore files with extension (eg. .css, .js, ...) 44 | '(.*)$ $1.html [L]' // redirect routes ends with string without trailing slash to original html 45 | ]; 46 | 47 | // Browser sync task config 48 | module.exports.browserSync = { 49 | dev: { 50 | server: { 51 | baseDir: [tmp, app], 52 | routes: { 53 | '/node_modules': nodeDir 54 | } 55 | }, 56 | notify: false, 57 | debugInfo: false, 58 | host: 'localhost', 59 | middleware: [ 60 | modRewrite(rewriteRules) 61 | ] 62 | }, 63 | dist: { 64 | server: { 65 | baseDir: dist 66 | }, 67 | notify: false, 68 | debugInfo: false, 69 | host: 'localhost', 70 | middleware: [ 71 | modRewrite(rewriteRules) 72 | ] 73 | } 74 | }; 75 | 76 | // Build size task config 77 | module.exports.buildSize = { 78 | srcAll: dist + '/**/*', 79 | cfgAll: { 80 | title: 'build', 81 | gzip: true 82 | }, 83 | srcCss: path.join(dist, styles, '/**/*'), 84 | cfgCss: { 85 | title: 'CSS', 86 | gzip: true 87 | }, 88 | srcJs: path.join(dist, scripts, '/**/*'), 89 | cfgJs: { 90 | title: 'JS', 91 | gzip: true 92 | }, 93 | srcImages: path.join(dist, images, '/**/*'), 94 | cfgImages: { 95 | title: 'Images', 96 | gzip: false 97 | } 98 | }; 99 | 100 | // Clean task config 101 | // Be carefull what you cleaning! 102 | module.exports.clean = [tmp, dist]; 103 | 104 | // Copy fonts task config 105 | module.exports.copyFonts = { 106 | src: [ 107 | path.join(app, fonts, '**/*')<% if (includeBootstrap) { %>, 'node_modules/bootstrap-sass/assets/fonts/**/*'<% } %> 108 | ], 109 | dest: path.join(dist, fonts) 110 | }; 111 | 112 | // Copy fonts task config 113 | module.exports.copyIcons = { 114 | src: path.join(app, icons, '**/*'), 115 | dest: dist + '/icons' 116 | }; 117 | 118 | // Copy extras task config 119 | module.exports.copyExtras = { 120 | src: [ 121 | app + '/*.*', 122 | '!' + app + '/*.html' 123 | ], 124 | dest: dist, 125 | cfg: { 126 | dot: true 127 | } 128 | }; 129 | 130 | // Deploy task config 131 | // FTP settings are in .env file 132 | module.exports.deploy = { 133 | src: dist + '/**', 134 | dev: { 135 | root: dist, 136 | hostname: process.env.FTP_DEV_HOSTNAME, 137 | username: process.env.FTP_DEV_USER, 138 | destination: process.env.FTP_DEV_DEST 139 | }, 140 | dist: { 141 | root: dist, 142 | hostname: process.env.FTP_DIST_HOSTNAME, 143 | username: process.env.FTP_DIST_USER, 144 | destination: process.env.FTP_DIST_DEST 145 | } 146 | }; 147 | 148 | // Images task config 149 | module.exports.images = { 150 | src: path.join(app, images, '**/*.{gif,png,jpg}'), 151 | srcSVG: path.join(app, images, '**/*.svg'), 152 | dest: dist + '/images', 153 | cfg: { 154 | progressive: true, 155 | interlaced: true, 156 | svgoPlugins: [{cleanupIDs: false}] 157 | } 158 | }; 159 | 160 | // JSHint task config 161 | module.exports.eslint = { 162 | src: [ 163 | path.join(app, scripts,'**/*.js'), 164 | path.join('!' + app, scripts,'plugins/**/*.js') // do not lint external plugins 165 | ] 166 | }; 167 | 168 | <% if (includeModernizr) { -%> 169 | // Modernizr task config 170 | module.exports.modernizr = { 171 | src: [ 172 | path.join(app, scripts,'**/*.js'), 173 | path.join(tmp, styles,'*.css') 174 | ], 175 | dest: path.join(tmp, scripts, 'plugins'), 176 | destBuild: path.join(dist, scripts, 'plugins'), 177 | cfg: { 178 | silent: true, 179 | options: [ 180 | 'setClasses', 181 | 'addTest', 182 | 'html5printshiv', 183 | 'testProp', 184 | 'fnBind' 185 | ], 186 | "excludeTests": [ 187 | 'hidden' 188 | ], 189 | } 190 | }; 191 | <% } -%> 192 | 193 | // Cachebusting task config 194 | module.exports.rev = { 195 | srcFiles: [ 196 | path.join(dist, '**/*.css'), 197 | path.join(dist, '**/*.js'), 198 | ], 199 | srcHtml: path.join(dist, '**/*.html'), 200 | manifestPath: path.join(dist, 'rev-manifest.json'), 201 | dest: path.join(dist), 202 | } 203 | 204 | // User scripts task 205 | module.exports.scripts = { 206 | src: path.join(app, scripts, 'main.js'), 207 | dest: path.join(tmp, scripts), 208 | rollupCfg: { 209 | format: 'iife', 210 | moduleName: '<%= projectNameSlug %>', 211 | }, 212 | destBuild: path.join(dist, scripts) 213 | }; 214 | 215 | // Styles task config 216 | module.exports.styles = { 217 | src: path.join(app, styles, '*.scss'), 218 | dest: path.join(tmp,styles), 219 | destBuild: path.join(dist, styles), 220 | sassCfg: { 221 | includePaths: 'node_modules', 222 | outputStyle: 'expanded' 223 | }, 224 | autoprefixerCfg: { 225 | browsers: ['last 2 version'] 226 | } 227 | }; 228 | 229 | // Templates task config 230 | module.exports.templates = { 231 | <% if (includeMultiLanguage) { %>languages: languages,<% } %> 232 | src: path.join(app, views, '*.pug'), 233 | dest: tmp, 234 | destBuild: path.join(dist), 235 | cfg: { 236 | pretty: true, 237 | compileDebug: true 238 | } 239 | }; 240 | 241 | // TemplatesData task config 242 | module.exports.templatesData = { 243 | src: path.join(app, views, data, '/**/*.<% if (includeDataYAML) { %>yaml<% } else { %>json<% } %>'), 244 | dest: path.join(tmp, '/data'), 245 | dataName: 'data.<% if (includeDataYAML) { %>yaml<% } else { %>json<% } %>', 246 | dataPath: path.join(tmp, 'data/data.<% if (includeDataYAML) { %>yaml<% } else { %>json<% } %>') 247 | }; 248 | 249 | // Watch task config 250 | module.exports.watch = { 251 | styles: path.join(app, styles, '/**/*.scss'), 252 | pug: [ 253 | path.join(app, views, '/**/*.pug'), 254 | path.join(app, views, data, '/**/*.<% if (includeDataYAML) { %>yaml<% } else { %>json<% } %>') 255 | ], 256 | scripts: path.join(app, scripts, '/**/*.js') 257 | }; 258 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Base = require('yeoman-generator').Base; 4 | const chalk = require('chalk'); 5 | const slugify = require('underscore.string').slugify; 6 | const Insight = require('insight'); 7 | const mkdir = require('mkdirp'); 8 | 9 | const ifFile = require('gulp-if'); 10 | const frep = require('gulp-frep'); 11 | 12 | function hasFeature(feat, features) { 13 | return features && features.indexOf(feat) !== -1; 14 | } 15 | 16 | const frepPatterns = [{ 17 | // Remove empty first line 18 | pattern: /^[\s\t]*[\n\r]/, 19 | replacement: '' 20 | }, { 21 | // Normalize and condense newlines 22 | pattern: /^\s*$[\n\r]{2,}/gm, 23 | replacement: '\n' 24 | } 25 | ]; 26 | 27 | module.exports = class Generator extends Base { 28 | 29 | constructor() { 30 | super(arguments[0], arguments[1]); 31 | // CLeanup after templating 32 | // Probably not best solution... 33 | 34 | // cleanup .js files 35 | this.registerTransformStream(ifFile('*.js', 36 | frep(frepPatterns) 37 | )); 38 | 39 | // cleanup .scss files 40 | this.registerTransformStream(ifFile('*.scss', 41 | frep(frepPatterns) 42 | )); 43 | 44 | // cleanup .pug files 45 | this.registerTransformStream(ifFile('*.pug', 46 | frep(frepPatterns) 47 | )); 48 | 49 | // welcome message 50 | if (!this.options['skip-welcome-message']) { 51 | this.log(chalk.yellow(require('yosay')('Welcome to Lighting Beetle generator. Hodd luck!'))); 52 | } 53 | } 54 | 55 | get initializing() { 56 | return function() { 57 | this.pkg = require('../package.json'); 58 | this.version = this.pkg.version; 59 | this.config.set('version', this.version); 60 | 61 | this.insight = new Insight({ 62 | // Google Analytics tracking code 63 | trackingCode: 'UA-27851629-19', 64 | pkg: this.pkg, 65 | version: this.version 66 | }); 67 | }; 68 | } 69 | 70 | get prompting() { 71 | return { 72 | askForAnalytics: function() { 73 | if (this.insight.optOut === undefined) { 74 | this.insight.askPermission('May generator-lb anonymously report usage statistics to improve the tool over time?', () => {}); 75 | } 76 | }, 77 | askForProjectName: function() { 78 | const prompts = [{ 79 | type: 'input', 80 | name: 'name', 81 | message: 'What is name of your project?', 82 | default : this.appname // default is current folder 83 | }]; 84 | 85 | return this.prompt(prompts).then((props) => { 86 | this.insight.track('install', 'start'); 87 | 88 | this.projectName = props.name; 89 | this.projectNameSlug = slugify(props.name); 90 | 91 | this.config.set('name', props.name); 92 | }); 93 | }, 94 | askForFeatures: function(){ 95 | 96 | const prompts = [{ 97 | type: 'checkbox', 98 | name: 'features', 99 | message: 'What more would you like?', 100 | choices: [{ 101 | name: 'Frontend framework (Bootstrap/Foundation)', 102 | value: 'includeFEFramework', 103 | checked: false 104 | },{ 105 | name: 'Modernizr', 106 | value: 'includeModernizr', 107 | checked: false 108 | },{ 109 | name: 'jQuery', 110 | value: 'includejQuery', 111 | checked: false 112 | },{ 113 | name: 'Lighting Fly', 114 | value: 'includeLightingFly', 115 | checked: false 116 | }] 117 | }, { 118 | when: function (props) { 119 | return props.features.indexOf('includeFEFramework') !== -1; 120 | }, 121 | type: 'list', 122 | name: 'feFramework', 123 | message: 'Please, choose frontend framework', 124 | choices: [{ 125 | name: 'Bootstrap 3 (jQuery3)', 126 | value: 'includeBootstrap' 127 | }, { 128 | name: 'Foundation 6 (jQuery2, Modernizr)', 129 | value: 'includeFoundation' 130 | }] 131 | }, { 132 | when: function (props) { 133 | return props.features.indexOf('includejQuery') !== -1; 134 | }, 135 | type: 'list', 136 | name: 'jQuery', 137 | message: 'Please, choose jQuery version', 138 | choices: [{ 139 | name: 'jQuery 3.x', 140 | value: 'includejQuery3' 141 | }, { 142 | name: 'jQuery 2.x', 143 | value: 'includejQuery2' 144 | }] 145 | }]; 146 | 147 | return this.prompt(prompts).then((props) => { 148 | // set features of aplication 149 | 150 | this.features = props.features; 151 | 152 | this.includeModernizr = hasFeature('includeModernizr', props.features); 153 | this.includeLightingFly = hasFeature('includeLightingFly', props.features); 154 | 155 | this.includejQuery2 = hasFeature('includejQuery2', props.jQuery); 156 | this.includejQuery3 = hasFeature('includejQuery3', props.jQuery); 157 | 158 | // set FE framework 159 | this.includeBootstrap = hasFeature('includeBootstrap', props.feFramework); 160 | this.includeFoundation = hasFeature('includeFoundation', props.feFramework); 161 | 162 | if (this.includeBootstrap) { 163 | if (this.includejQuery2 !== true || this.includejQuery3 !== true) { 164 | this.includejQuery2 = false; 165 | this.includejQuery3 = true; 166 | } 167 | } 168 | 169 | if (this.includeFoundation) { 170 | this.includejQuery2 = true; 171 | this.includejQuery3 = false; 172 | this.includeModernizr = true; 173 | } 174 | 175 | this.config.set('features', props.features); 176 | this.config.set('jQuery', props.jQuery); 177 | this.config.set('feFramework', props.feFramework); 178 | 179 | this.insight.track('modernizr', this.includeModernizr); 180 | this.insight.track('lightingFly', this.includeLightingFly); 181 | this.insight.track('jQuery2', this.includejQuery2); 182 | this.insight.track('jQuery3', this.includejQuery3); 183 | 184 | if (props.feFramework) { 185 | this.insight.track('bootstrap', this.includeBootstrap); 186 | this.insight.track('foundation', this.includeFoundation); 187 | } 188 | }); 189 | }, 190 | askForMultiLanguage: function() { 191 | const prompts = [{ 192 | type: 'confirm', 193 | name: 'includeMultiLanguage', 194 | message: 'Do you want support for multi-language templates?', 195 | default: false 196 | }]; 197 | 198 | return this.prompt(prompts).then((props) => { 199 | //testing framework 200 | this.includeMultiLanguage = props.includeMultiLanguage; 201 | 202 | this.insight.track('multiLanguage', props.includeMultiLanguage); 203 | 204 | this.config.set('multiLanguage', props.includeMultiLanguage); 205 | }); 206 | }, 207 | askForDataFormat: function() { 208 | const prompts = [{ 209 | type: 'list', 210 | name: 'dataFormat', 211 | message: 'What data source format do you prefer?', 212 | choices: [{ 213 | name: 'YAML', 214 | value: 'yaml', 215 | }, { 216 | name: 'JSON', 217 | value: 'json', 218 | }], 219 | default: 0 220 | }]; 221 | 222 | return this.prompt(prompts).then((props) => { 223 | 224 | this.includeDataYAML = hasFeature('yaml', props.dataFormat); 225 | this.includeDataJSON = hasFeature('json', props.dataFormat); 226 | 227 | this.insight.track('dataFormat', props.dataFormat); 228 | 229 | this.config.set('dataFormat', props.dataFormat); 230 | }); 231 | }, 232 | }; 233 | } 234 | 235 | get configuring() { 236 | return function() { 237 | this.config.save(); 238 | }; 239 | } 240 | 241 | get default() { 242 | return {}; 243 | } 244 | 245 | get writing() { 246 | return { 247 | gulp: function () { 248 | this.copy('_gulpfile.js','gulpfile.js'); 249 | 250 | this.copy('gulp/tasks/_browserSync.js', 'gulp/tasks/browserSync.js'); 251 | this.copy('gulp/tasks/_clean.js', 'gulp/tasks/clean.js'); 252 | this.copy('gulp/tasks/_default.js', 'gulp/tasks/default.js'); 253 | this.copy('gulp/tasks/_deploy.js', 'gulp/tasks/deploy.js'); 254 | this.copy('gulp/tasks/_images.js', 'gulp/tasks/images.js'); 255 | this.copy('gulp/tasks/_rev.js', 'gulp/tasks/rev.js'); 256 | this.copy('gulp/tasks/_templates.js', 'gulp/tasks/templates.js'); 257 | this.copy('gulp/tasks/_watch.js', 'gulp/tasks/watch.js'); 258 | 259 | this.copy('gulp/utils/_buildHelper.js', 'gulp/utils/buildHelper.js'); 260 | this.copy('gulp/utils/_handleError.js', 'gulp/utils/handleError.js'); 261 | 262 | this.template('gulp/_config.js', 'gulp/config.js'); 263 | 264 | this.template('gulp/tasks/_build.js', 'gulp/tasks/build.js'); 265 | this.template('gulp/tasks/_copy.js', 'gulp/tasks/copy.js'); 266 | this.template('gulp/tasks/_serve.js', 'gulp/tasks/serve.js'); 267 | this.template('gulp/tasks/_scripts.js', 'gulp/tasks/scripts.js'); 268 | this.template('gulp/tasks/_styles.js', 'gulp/tasks/styles.js'); 269 | 270 | if (this.includeModernizr) { 271 | this.copy('gulp/tasks/_modernizr.js', 'gulp/tasks/modernizr.js'); 272 | } 273 | }, 274 | 275 | dependencies: function () { 276 | this.dependencies = {}; 277 | 278 | if (this.includeBootstrap) { 279 | this.dependencies['bootstrap-sass'] = '~3.3.7'; 280 | } 281 | 282 | if (this.includeFoundation) { 283 | this.dependencies['foundation-sites'] = '~6.2.3'; 284 | } 285 | 286 | if (this.includejQuery2) { 287 | this.dependencies.jquery = '~2.2.4'; 288 | } 289 | 290 | if (this.includejQuery3) { 291 | this.dependencies.jquery = '~3.1.1'; 292 | } 293 | 294 | if (this.includeLightingFly) { 295 | this.dependencies.lightingfly = '~0.2.1'; 296 | } 297 | 298 | }, 299 | 300 | packageJSON: function () { 301 | this.template('_package.json', 'package.json'); 302 | }, 303 | 304 | readme : function() { 305 | this.template('_readme.md', 'readme.md'); 306 | }, 307 | 308 | git: function () { 309 | this.template('gitignore', '.gitignore'); 310 | this.copy('gitattributes', '.gitattributes'); 311 | }, 312 | 313 | env: function(){ 314 | this.copy('env.example', '.env.example'); 315 | this.copy('env', '.env'); 316 | }, 317 | 318 | eslint: function () { 319 | this.copy('eslintrc', '.eslintrc'); 320 | this.copy('eslintignore', '.eslintignore'); 321 | }, 322 | 323 | babel: function () { 324 | this.copy('babelrc', '.babelrc'); 325 | }, 326 | 327 | editorConfig: function () { 328 | this.copy('editorconfig', '.editorconfig'); 329 | }, 330 | 331 | mainStylesheet: function () { 332 | this.template('styles/_main.scss', 'app/styles/main.scss'); 333 | }, 334 | 335 | pug : function () { 336 | this.template('views/_index.pug', 'app/views/index.pug'); 337 | this.template('views/layouts/_default.pug', 'app/views/layouts/_default.pug'); 338 | this.template('views/modules/_header.pug', 'app/views/modules/_header.pug'); 339 | this.template('views/modules/_footer.pug', 'app/views/modules/_footer.pug'); 340 | mkdir('app/views/helpers'); 341 | mkdir('app/views/mixins'); 342 | if (this.includeMultiLanguage) { 343 | this.copy('views/helpers/_language.pug','app/views/helpers/_language.pug'); 344 | } 345 | 346 | // data template 347 | if (this.includeDataYAML) { 348 | this.template('views/data/_index.yaml','app/views/data/index.yaml'); 349 | } else { 350 | this.template('views/data/_index.json','app/views/data/index.json'); 351 | } 352 | }, 353 | 354 | js : function () { 355 | this.directory('app'); 356 | this.template('scripts/_main.js', 'app/scripts/main.js'); 357 | this.template('scripts/modules/_utils.js', 'app/scripts/modules/utils.js'); 358 | 359 | if (this.includejQuery2 || this.includejQuery3) { 360 | this.copy('scripts/external/jquery.js', 'app/scripts/external/jquery.js'); 361 | } 362 | 363 | mkdir('app/scripts/plugins'); 364 | }, 365 | 366 | app: function () { 367 | this.directory('app'); 368 | mkdir('app/scripts'); 369 | mkdir('app/styles'); 370 | mkdir('app/images'); 371 | mkdir('app/icons'); 372 | mkdir('app/fonts'); 373 | mkdir('app/styles/modules'); 374 | } 375 | }; 376 | } 377 | 378 | get install() { 379 | return function() { 380 | if (!this.options['skip-install']) { 381 | this.installDependencies({ 382 | skipMessage: this.options['skip-install-message'], 383 | skipInstall: this.options['skip-install'], 384 | bower: false, 385 | callback: () => { 386 | this.log(chalk.yellow(` 387 | __ ____ 388 | ____ ____ ____ ___ _________ _/ /_____ _____ / / /_ 389 | / __ \`/ _ \\/ __ \\/ _ \\/ ___/ __ \`/ __/ __ \\/ ___/_____/ / __ \\ 390 | / /_/ / __/ / / / __/ / / /_/ / /_/ /_/ / / /_____/ / /_/ / 391 | \\__, /\\___/_/ /_/\\___/_/ \\__,_/\\__/\\____/_/ /_/_.___/ 392 | /____/ 393 | `)); 394 | this.log(chalk.green('All done, hodd luck!')); 395 | this.log(chalk.green('')); 396 | this.log(chalk.cyan('Serve project with: $ gulp serve')); 397 | this.log(chalk.cyan('Build project with: $ gulp build')); 398 | this.log(chalk.cyan('Help: $ gulp help')); 399 | this.insight.track('install', 'done'); 400 | } 401 | }); 402 | } else { 403 | this.insight.track('install', 'skip-install'); 404 | } 405 | }; 406 | } 407 | 408 | get end() { 409 | return {}; 410 | } 411 | } 412 | -------------------------------------------------------------------------------- /app/templates/app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Server Configs v2.11.0 | MIT License 2 | # https://github.com/h5bp/server-configs-apache 3 | 4 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have 5 | # access to the main server configuration file (which is usually called 6 | # `httpd.conf`), you should add this logic there. 7 | # 8 | # https://httpd.apache.org/docs/current/howto/htaccess.html. 9 | 10 | # ###################################################################### 11 | # # CROSS-ORIGIN # 12 | # ###################################################################### 13 | 14 | # ---------------------------------------------------------------------- 15 | # | Cross-origin requests | 16 | # ---------------------------------------------------------------------- 17 | 18 | # Allow cross-origin requests. 19 | # 20 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS 21 | # http://enable-cors.org/ 22 | # http://www.w3.org/TR/cors/ 23 | 24 | # 25 | # Header set Access-Control-Allow-Origin "*" 26 | # 27 | 28 | # ---------------------------------------------------------------------- 29 | # | Cross-origin images | 30 | # ---------------------------------------------------------------------- 31 | 32 | # Send the CORS header for images when browsers request it. 33 | # 34 | # https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image 35 | # https://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 36 | 37 | 38 | 39 | 40 | SetEnvIf Origin ":" IS_CORS 41 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 42 | 43 | 44 | 45 | 46 | # ---------------------------------------------------------------------- 47 | # | Cross-origin web fonts | 48 | # ---------------------------------------------------------------------- 49 | 50 | # Allow cross-origin access to web fonts. 51 | 52 | 53 | 54 | Header set Access-Control-Allow-Origin "*" 55 | 56 | 57 | 58 | # ---------------------------------------------------------------------- 59 | # | Cross-origin resource timing | 60 | # ---------------------------------------------------------------------- 61 | 62 | # Allow cross-origin access to the timing information for all resources. 63 | # 64 | # If a resource isn't served with a `Timing-Allow-Origin` header that 65 | # would allow its timing information to be shared with the document, 66 | # some of the attributes of the `PerformanceResourceTiming` object will 67 | # be set to zero. 68 | # 69 | # http://www.w3.org/TR/resource-timing/ 70 | # http://www.stevesouders.com/blog/2014/08/21/resource-timing-practical-tips/ 71 | 72 | # 73 | # Header set Timing-Allow-Origin: "*" 74 | # 75 | 76 | 77 | # ###################################################################### 78 | # # ERRORS # 79 | # ###################################################################### 80 | 81 | # ---------------------------------------------------------------------- 82 | # | Custom error messages/pages | 83 | # ---------------------------------------------------------------------- 84 | 85 | # Customize what Apache returns to the client in case of an error. 86 | # https://httpd.apache.org/docs/current/mod/core.html#errordocument 87 | 88 | # ErrorDocument 404 /404.html 89 | 90 | # ---------------------------------------------------------------------- 91 | # | Error prevention | 92 | # ---------------------------------------------------------------------- 93 | 94 | # Disable the pattern matching based on filenames. 95 | # 96 | # This setting prevents Apache from returning a 404 error as the result 97 | # of a rewrite when the directory with the same name does not exist. 98 | # 99 | # https://httpd.apache.org/docs/current/content-negotiation.html#multiviews 100 | 101 | Options -MultiViews 102 | 103 | 104 | # ###################################################################### 105 | # # INTERNET EXPLORER # 106 | # ###################################################################### 107 | 108 | # ---------------------------------------------------------------------- 109 | # | Document modes | 110 | # ---------------------------------------------------------------------- 111 | 112 | # Force Internet Explorer 8/9/10 to render pages in the highest mode 113 | # available in the various cases when it may not. 114 | # 115 | # https://hsivonen.fi/doctype/#ie8 116 | # 117 | # (!) Starting with Internet Explorer 11, document modes are deprecated. 118 | # If your business still relies on older web apps and services that were 119 | # designed for older versions of Internet Explorer, you might want to 120 | # consider enabling `Enterprise Mode` throughout your company. 121 | # 122 | # http://msdn.microsoft.com/en-us/library/ie/bg182625.aspx#docmode 123 | # http://blogs.msdn.com/b/ie/archive/2014/04/02/stay-up-to-date-with-enterprise-mode-for-internet-explorer-11.aspx 124 | 125 | 126 | Header set X-UA-Compatible "IE=edge" 127 | # `mod_headers` cannot match based on the content-type, however, 128 | # the `X-UA-Compatible` response header should be send only for 129 | # HTML documents and not for the other resources. 130 | 131 | Header unset X-UA-Compatible 132 | 133 | 134 | 135 | # ---------------------------------------------------------------------- 136 | # | Iframes cookies | 137 | # ---------------------------------------------------------------------- 138 | 139 | # Allow cookies to be set from iframes in Internet Explorer. 140 | # 141 | # http://msdn.microsoft.com/en-us/library/ms537343.aspx 142 | # http://www.w3.org/TR/2000/CR-P3P-20001215/ 143 | 144 | # 145 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 146 | # 147 | 148 | 149 | # ###################################################################### 150 | # # MEDIA TYPES AND CHARACTER ENCODINGS # 151 | # ###################################################################### 152 | 153 | # ---------------------------------------------------------------------- 154 | # | Media types | 155 | # ---------------------------------------------------------------------- 156 | 157 | # Serve resources with the proper media types (f.k.a. MIME types). 158 | # 159 | # https://www.iana.org/assignments/media-types/media-types.xhtml 160 | # https://httpd.apache.org/docs/current/mod/mod_mime.html#addtype 161 | 162 | 163 | 164 | # Data interchange 165 | 166 | AddType application/json json map topojson 167 | AddType application/ld+json jsonld 168 | AddType application/vnd.geo+json geojson 169 | AddType application/xml atom rdf rss xml 170 | 171 | 172 | # JavaScript 173 | 174 | # Normalize to standard type. 175 | # https://tools.ietf.org/html/rfc4329#section-7.2 176 | 177 | AddType application/javascript js 178 | 179 | 180 | # Manifest files 181 | 182 | # If you are providing a web application manifest file (see 183 | # the specification: https://w3c.github.io/manifest/), it is 184 | # recommended that you serve it with the `application/manifest+json` 185 | # media type. 186 | # 187 | # Because the web application manifest file doesn't have its 188 | # own unique file extension, you can set its media type either 189 | # by matching: 190 | # 191 | # 1) the exact location of the file (this can be done using a 192 | # directive such as ``, but it will NOT work in 193 | # the `.htaccess` file, so you will have to do it in the main 194 | # server configuration file or inside of a `` 195 | # container) 196 | # 197 | # e.g.: 198 | # 199 | # 200 | # AddType application/manifest+json json 201 | # 202 | # 203 | # 2) the filename (this can be problematic as you will need to 204 | # ensure that you don't have any other file with the same name 205 | # as the one you gave to your web application manifest file) 206 | # 207 | # e.g.: 208 | # 209 | # 210 | # AddType application/manifest+json json 211 | # 212 | 213 | AddType application/x-web-app-manifest+json webapp 214 | AddType text/cache-manifest appcache manifest 215 | 216 | 217 | # Media files 218 | 219 | AddType audio/mp4 f4a f4b m4a 220 | AddType audio/ogg oga ogg opus 221 | AddType image/bmp bmp 222 | AddType image/svg+xml svg svgz 223 | AddType image/webp webp 224 | AddType video/mp4 f4v f4p m4v mp4 225 | AddType video/ogg ogv 226 | AddType video/webm webm 227 | AddType video/x-flv flv 228 | 229 | # Serving `.ico` image files with a different media type 230 | # prevents Internet Explorer from displaying then as images: 231 | # https://github.com/h5bp/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee 232 | 233 | AddType image/x-icon cur ico 234 | 235 | 236 | # Web fonts 237 | 238 | AddType application/font-woff woff 239 | AddType application/font-woff2 woff2 240 | AddType application/vnd.ms-fontobject eot 241 | 242 | # Browsers usually ignore the font media types and simply sniff 243 | # the bytes to figure out the font type. 244 | # https://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern 245 | # 246 | # However, Blink and WebKit based browsers will show a warning 247 | # in the console if the following font types are served with any 248 | # other media types. 249 | 250 | AddType application/x-font-ttf ttc ttf 251 | AddType font/opentype otf 252 | 253 | 254 | # Other 255 | 256 | AddType application/octet-stream safariextz 257 | AddType application/x-bb-appworld bbaw 258 | AddType application/x-chrome-extension crx 259 | AddType application/x-opera-extension oex 260 | AddType application/x-xpinstall xpi 261 | AddType text/vcard vcard vcf 262 | AddType text/vnd.rim.location.xloc xloc 263 | AddType text/vtt vtt 264 | AddType text/x-component htc 265 | 266 | 267 | 268 | # ---------------------------------------------------------------------- 269 | # | Character encodings | 270 | # ---------------------------------------------------------------------- 271 | 272 | # Serve all resources labeled as `text/html` or `text/plain` 273 | # with the media type `charset` parameter set to `UTF-8`. 274 | # 275 | # https://httpd.apache.org/docs/current/mod/core.html#adddefaultcharset 276 | 277 | AddDefaultCharset utf-8 278 | 279 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 280 | 281 | # Serve the following file types with the media type `charset` 282 | # parameter set to `UTF-8`. 283 | # 284 | # https://httpd.apache.org/docs/current/mod/mod_mime.html#addcharset 285 | 286 | 287 | AddCharset utf-8 .atom \ 288 | .bbaw \ 289 | .css \ 290 | .geojson \ 291 | .js \ 292 | .json \ 293 | .jsonld \ 294 | .rdf \ 295 | .rss \ 296 | .topojson \ 297 | .vtt \ 298 | .webapp \ 299 | .xloc \ 300 | .xml 301 | 302 | 303 | 304 | # ###################################################################### 305 | # # REWRITES # 306 | # ###################################################################### 307 | 308 | # ---------------------------------------------------------------------- 309 | # | Rewrite engine | 310 | # ---------------------------------------------------------------------- 311 | 312 | # (1) Turn on the rewrite engine (this is necessary in order for 313 | # the `RewriteRule` directives to work). 314 | # 315 | # https://httpd.apache.org/docs/current/mod/mod_rewrite.html#RewriteEngine 316 | # 317 | # (2) Enable the `FollowSymLinks` option if it isn't already. 318 | # 319 | # https://httpd.apache.org/docs/current/mod/core.html#options 320 | # 321 | # (3) If your web host doesn't allow the `FollowSymlinks` option, 322 | # you need to comment it out or remove it, and then uncomment 323 | # the `Options +SymLinksIfOwnerMatch` line (4), but be aware 324 | # of the performance impact. 325 | # 326 | # https://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 327 | # 328 | # (4) Some cloud hosting services will require you set `RewriteBase`. 329 | # 330 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-modrewrite-not-working-on-my-site 331 | # https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase 332 | # 333 | # (5) Depending on how your server is set up, you may also need to 334 | # use the `RewriteOptions` directive to enable some options for 335 | # the rewrite engine. 336 | # 337 | # https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriteoptions 338 | # 339 | # (6) Set %{ENV:PROTO} variable, to allow rewrites to redirect with the 340 | # appropriate schema automatically (http or https). 341 | 342 | 343 | 344 | # (1) 345 | RewriteEngine On 346 | 347 | # (2) 348 | Options +FollowSymlinks 349 | 350 | # (3) 351 | # Options +SymLinksIfOwnerMatch 352 | 353 | # (4) 354 | # RewriteBase / 355 | 356 | # (5) 357 | # RewriteOptions 358 | 359 | # (6) 360 | RewriteCond %{HTTPS} =on 361 | RewriteRule ^ - [env=proto:https] 362 | RewriteCond %{HTTPS} !=on 363 | RewriteRule ^ - [env=proto:http] 364 | 365 | # remove index.html 366 | RewriteRule ^index\.html$ / [R=301,L] 367 | RewriteRule ^(.*)/index\.html$ /$1/ [R=301,L] 368 | 369 | #/page will display the contents of /page.html 370 | RewriteCond %{REQUEST_FILENAME} !-d 371 | RewriteCond %{REQUEST_FILENAME}\.html -f 372 | RewriteRule ^(.*)$ $1.html 373 | 374 | 375 | 376 | # ---------------------------------------------------------------------- 377 | # | Forcing `https://` | 378 | # ---------------------------------------------------------------------- 379 | 380 | # Redirect from the `http://` to the `https://` version of the URL. 381 | # https://wiki.apache.org/httpd/RewriteHTTPToHTTPS 382 | 383 | # 384 | # RewriteEngine On 385 | # RewriteCond %{HTTPS} !=on 386 | # RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1 [R=301,L] 387 | # 388 | 389 | # ---------------------------------------------------------------------- 390 | # | Suppressing / Forcing the `www.` at the beginning of URLs | 391 | # ---------------------------------------------------------------------- 392 | 393 | # The same content should never be available under two different 394 | # URLs, especially not with and without `www.` at the beginning. 395 | # This can cause SEO problems (duplicate content), and therefore, 396 | # you should choose one of the alternatives and redirect the other 397 | # one. 398 | # 399 | # By default `Option 1` (no `www.`) is activated. 400 | # http://no-www.org/faq.php?q=class_b 401 | # 402 | # If you would prefer to use `Option 2`, just comment out all the 403 | # lines from `Option 1` and uncomment the ones from `Option 2`. 404 | # 405 | # (!) NEVER USE BOTH RULES AT THE SAME TIME! 406 | 407 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 408 | 409 | # Option 1: rewrite www.example.com → example.com 410 | 411 | 412 | RewriteEngine On 413 | RewriteCond %{HTTPS} !=on 414 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 415 | RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L] 416 | 417 | 418 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 419 | 420 | # Option 2: rewrite example.com → www.example.com 421 | # 422 | # Be aware that the following might not be a good idea if you use "real" 423 | # subdomains for certain parts of your website. 424 | 425 | # 426 | # RewriteEngine On 427 | # RewriteCond %{HTTPS} !=on 428 | # RewriteCond %{HTTP_HOST} !^www\. [NC] 429 | # RewriteCond %{SERVER_ADDR} !=127.0.0.1 430 | # RewriteCond %{SERVER_ADDR} !=::1 431 | # RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 432 | # 433 | 434 | 435 | # ###################################################################### 436 | # # SECURITY # 437 | # ###################################################################### 438 | 439 | # ---------------------------------------------------------------------- 440 | # | Clickjacking | 441 | # ---------------------------------------------------------------------- 442 | 443 | # Protect website against clickjacking. 444 | # 445 | # The example below sends the `X-Frame-Options` response header with 446 | # the value `DENY`, informing browsers not to display the content of 447 | # the web page in any frame. 448 | # 449 | # This might not be the best setting for everyone. You should read 450 | # about the other two possible values the `X-Frame-Options` header 451 | # field can have: `SAMEORIGIN` and `ALLOW-FROM`. 452 | # https://tools.ietf.org/html/rfc7034#section-2.1. 453 | # 454 | # Keep in mind that while you could send the `X-Frame-Options` header 455 | # for all of your website’s pages, this has the potential downside that 456 | # it forbids even non-malicious framing of your content (e.g.: when 457 | # users visit your website using a Google Image Search results page). 458 | # 459 | # Nonetheless, you should ensure that you send the `X-Frame-Options` 460 | # header for all pages that allow a user to make a state changing 461 | # operation (e.g: pages that contain one-click purchase links, checkout 462 | # or bank-transfer confirmation pages, pages that make permanent 463 | # configuration changes, etc.). 464 | # 465 | # Sending the `X-Frame-Options` header can also protect your website 466 | # against more than just clickjacking attacks: 467 | # https://cure53.de/xfo-clickjacking.pdf. 468 | # 469 | # https://tools.ietf.org/html/rfc7034 470 | # http://blogs.msdn.com/b/ieinternals/archive/2010/03/30/combating-clickjacking-with-x-frame-options.aspx 471 | # https://www.owasp.org/index.php/Clickjacking 472 | 473 | # 474 | # Header set X-Frame-Options "DENY" 475 | # # `mod_headers` cannot match based on the content-type, however, 476 | # # the `X-Frame-Options` response header should be send only for 477 | # # HTML documents and not for the other resources. 478 | # 479 | # Header unset X-Frame-Options 480 | # 481 | # 482 | 483 | # ---------------------------------------------------------------------- 484 | # | Content Security Policy (CSP) | 485 | # ---------------------------------------------------------------------- 486 | 487 | # Mitigate the risk of cross-site scripting and other content-injection 488 | # attacks. 489 | # 490 | # This can be done by setting a `Content Security Policy` which 491 | # whitelists trusted sources of content for your website. 492 | # 493 | # The example header below allows ONLY scripts that are loaded from the 494 | # current website's origin (no inline scripts, no CDN, etc). That almost 495 | # certainly won't work as-is for your website! 496 | # 497 | # For more details on how to craft a reasonable policy for your website, 498 | # read: http://www.html5rocks.com/en/tutorials/security/content-security-policy/ 499 | # (or the specification: http://www.w3.org/TR/CSP11/). Also, to make 500 | # things easier, you can use an online CSP header generator such as: 501 | # http://cspisawesome.com/. 502 | 503 | # 504 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 505 | # # `mod_headers` cannot match based on the content-type, however, 506 | # # the `Content-Security-Policy` response header should be send 507 | # # only for HTML documents and not for the other resources. 508 | # 509 | # Header unset Content-Security-Policy 510 | # 511 | # 512 | 513 | # ---------------------------------------------------------------------- 514 | # | File access | 515 | # ---------------------------------------------------------------------- 516 | 517 | # Block access to directories without a default document. 518 | # 519 | # You should leave the following uncommented, as you shouldn't allow 520 | # anyone to surf through every directory on your server (which may 521 | # includes rather private places such as the CMS's directories). 522 | 523 | 524 | Options -Indexes 525 | 526 | 527 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 528 | 529 | # Block access to all hidden files and directories with the exception of 530 | # the visible content from within the `/.well-known/` hidden directory. 531 | # 532 | # These types of files usually contain user preferences or the preserved 533 | # state of an utility, and can include rather private places like, for 534 | # example, the `.git` or `.svn` directories. 535 | # 536 | # The `/.well-known/` directory represents the standard (RFC 5785) path 537 | # prefix for "well-known locations" (e.g.: `/.well-known/manifest.json`, 538 | # `/.well-known/keybase.txt`), and therefore, access to its visible 539 | # content should not be blocked. 540 | # 541 | # https://www.mnot.net/blog/2010/04/07/well-known 542 | # https://tools.ietf.org/html/rfc5785 543 | 544 | 545 | RewriteEngine On 546 | RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC] 547 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 548 | RewriteCond %{SCRIPT_FILENAME} -f 549 | RewriteRule "(^|/)\." - [F] 550 | 551 | 552 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 553 | 554 | # Block access to files that can expose sensitive information. 555 | # 556 | # By default, block access to backup and source files that may be 557 | # left by some text editors and can pose a security risk when anyone 558 | # has access to them. 559 | # 560 | # http://feross.org/cmsploit/ 561 | # 562 | # (!) Update the `` regular expression from below to 563 | # include any files that might end up on your production server and 564 | # can expose sensitive information about your website. These files may 565 | # include: configuration files, files that contain metadata about the 566 | # project (e.g.: project dependencies), build scripts, etc.. 567 | 568 | 569 | 570 | # Apache < 2.3 571 | 572 | Order allow,deny 573 | Deny from all 574 | Satisfy All 575 | 576 | 577 | # Apache ≥ 2.3 578 | 579 | Require all denied 580 | 581 | 582 | 583 | 584 | # ---------------------------------------------------------------------- 585 | # | HTTP Strict Transport Security (HSTS) | 586 | # ---------------------------------------------------------------------- 587 | 588 | # Force client-side SSL redirection. 589 | # 590 | # If a user types `example.com` in their browser, even if the server 591 | # redirects them to the secure version of the website, that still leaves 592 | # a window of opportunity (the initial HTTP connection) for an attacker 593 | # to downgrade or redirect the request. 594 | # 595 | # The following header ensures that browser will ONLY connect to your 596 | # server via HTTPS, regardless of what the users type in the browser's 597 | # address bar. 598 | # 599 | # (!) Remove the `includeSubDomains` optional directive if the website's 600 | # subdomains are not using HTTPS. 601 | # 602 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 603 | # https://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14#section-6.1 604 | # http://blogs.msdn.com/b/ieinternals/archive/2014/08/18/hsts-strict-transport-security-attacks-mitigations-deployment-https.aspx 605 | 606 | # 607 | # Header set Strict-Transport-Security "max-age=16070400; includeSubDomains" 608 | # 609 | 610 | # ---------------------------------------------------------------------- 611 | # | Reducing MIME type security risks | 612 | # ---------------------------------------------------------------------- 613 | 614 | # Prevent some browsers from MIME-sniffing the response. 615 | # 616 | # This reduces exposure to drive-by download attacks and cross-origin 617 | # data leaks, and should be left uncommented, especially if the server 618 | # is serving user-uploaded content or content that could potentially be 619 | # treated as executable by the browser. 620 | # 621 | # http://www.slideshare.net/hasegawayosuke/owasp-hasegawa 622 | # http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx 623 | # http://msdn.microsoft.com/en-us/library/ie/gg622941.aspx 624 | # https://mimesniff.spec.whatwg.org/ 625 | 626 | 627 | Header set X-Content-Type-Options "nosniff" 628 | 629 | 630 | # ---------------------------------------------------------------------- 631 | # | Reflected Cross-Site Scripting (XSS) attacks | 632 | # ---------------------------------------------------------------------- 633 | 634 | # (1) Try to re-enable the cross-site scripting (XSS) filter built 635 | # into most web browsers. 636 | # 637 | # The filter is usually enabled by default, but in some cases it 638 | # may be disabled by the user. However, in Internet Explorer for 639 | # example, it can be re-enabled just by sending the 640 | # `X-XSS-Protection` header with the value of `1`. 641 | # 642 | # (2) Prevent web browsers from rendering the web page if a potential 643 | # reflected (a.k.a non-persistent) XSS attack is detected by the 644 | # filter. 645 | # 646 | # By default, if the filter is enabled and browsers detect a 647 | # reflected XSS attack, they will attempt to block the attack 648 | # by making the smallest possible modifications to the returned 649 | # web page. 650 | # 651 | # Unfortunately, in some browsers (e.g.: Internet Explorer), 652 | # this default behavior may allow the XSS filter to be exploited, 653 | # thereby, it's better to inform browsers to prevent the rendering 654 | # of the page altogether, instead of attempting to modify it. 655 | # 656 | # http://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities 657 | # 658 | # (!) Do not rely on the XSS filter to prevent XSS attacks! Ensure that 659 | # you are taking all possible measures to prevent XSS attacks, the 660 | # most obvious being: validating and sanitizing your website's inputs. 661 | # 662 | # http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx 663 | # http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx 664 | # https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29 665 | 666 | # 667 | # # (1) (2) 668 | # Header set X-XSS-Protection "1; mode=block" 669 | # # `mod_headers` cannot match based on the content-type, however, 670 | # # the `X-XSS-Protection` response header should be send only for 671 | # # HTML documents and not for the other resources. 672 | # 673 | # Header unset X-XSS-Protection 674 | # 675 | # 676 | 677 | # ---------------------------------------------------------------------- 678 | # | Server software information | 679 | # ---------------------------------------------------------------------- 680 | 681 | # Prevent Apache from sending in the `Server` response header its 682 | # exact version number, the description of the generic OS-type or 683 | # information about its compiled-in modules. 684 | # 685 | # (!) The `ServerTokens` directive will only work in the main server 686 | # configuration file, so don't try to enable it in the `.htaccess` file! 687 | # 688 | # https://httpd.apache.org/docs/current/mod/core.html#servertokens 689 | 690 | # ServerTokens Prod 691 | 692 | 693 | # ###################################################################### 694 | # # WEB PERFORMANCE # 695 | # ###################################################################### 696 | 697 | # ---------------------------------------------------------------------- 698 | # | Compression | 699 | # ---------------------------------------------------------------------- 700 | 701 | 702 | 703 | # Force compression for mangled `Accept-Encoding` request headers 704 | # https://developer.yahoo.com/blogs/ydn/pushing-beyond-gzipping-25601.html 705 | 706 | 707 | 708 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 709 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 710 | 711 | 712 | 713 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 714 | 715 | # Compress all output labeled with one of the following media types. 716 | # 717 | # (!) For Apache versions below version 2.3.7 you don't need to 718 | # enable `mod_filter` and can remove the `` 719 | # and `` lines as `AddOutputFilterByType` is still in 720 | # the core directives. 721 | # 722 | # https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype 723 | 724 | 725 | AddOutputFilterByType DEFLATE "application/atom+xml" \ 726 | "application/javascript" \ 727 | "application/json" \ 728 | "application/ld+json" \ 729 | "application/manifest+json" \ 730 | "application/rdf+xml" \ 731 | "application/rss+xml" \ 732 | "application/schema+json" \ 733 | "application/vnd.geo+json" \ 734 | "application/vnd.ms-fontobject" \ 735 | "application/x-font-ttf" \ 736 | "application/x-javascript" \ 737 | "application/x-web-app-manifest+json" \ 738 | "application/xhtml+xml" \ 739 | "application/xml" \ 740 | "font/eot" \ 741 | "font/opentype" \ 742 | "image/bmp" \ 743 | "image/svg+xml" \ 744 | "image/vnd.microsoft.icon" \ 745 | "image/x-icon" \ 746 | "text/cache-manifest" \ 747 | "text/css" \ 748 | "text/html" \ 749 | "text/javascript" \ 750 | "text/plain" \ 751 | "text/vcard" \ 752 | "text/vnd.rim.location.xloc" \ 753 | "text/vtt" \ 754 | "text/x-component" \ 755 | "text/x-cross-domain-policy" \ 756 | "text/xml" 757 | 758 | 759 | 760 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 761 | 762 | # Map the following filename extensions to the specified 763 | # encoding type in order to make Apache serve the file types 764 | # with the appropriate `Content-Encoding` response header 765 | # (do note that this will NOT make Apache compress them!). 766 | # 767 | # If these files types would be served without an appropriate 768 | # `Content-Enable` response header, client applications (e.g.: 769 | # browsers) wouldn't know that they first need to uncompress 770 | # the response, and thus, wouldn't be able to understand the 771 | # content. 772 | # 773 | # https://httpd.apache.org/docs/current/mod/mod_mime.html#addencoding 774 | 775 | 776 | AddEncoding gzip svgz 777 | 778 | 779 | 780 | 781 | # ---------------------------------------------------------------------- 782 | # | Content transformation | 783 | # ---------------------------------------------------------------------- 784 | 785 | # Prevent intermediate caches or proxies (e.g.: such as the ones 786 | # used by mobile network providers) from modifying the website's 787 | # content. 788 | # 789 | # https://tools.ietf.org/html/rfc2616#section-14.9.5 790 | # 791 | # (!) If you are using `mod_pagespeed`, please note that setting 792 | # the `Cache-Control: no-transform` response header will prevent 793 | # `PageSpeed` from rewriting `HTML` files, and, if the 794 | # `ModPagespeedDisableRewriteOnNoTransform` directive isn't set 795 | # to `off`, also from rewriting other resources. 796 | # 797 | # https://developers.google.com/speed/pagespeed/module/configuration#notransform 798 | 799 | # 800 | # Header merge Cache-Control "no-transform" 801 | # 802 | 803 | # ---------------------------------------------------------------------- 804 | # | ETags | 805 | # ---------------------------------------------------------------------- 806 | 807 | # Remove `ETags` as resources are sent with far-future expires headers. 808 | # 809 | # https://developer.yahoo.com/performance/rules.html#etags 810 | # https://tools.ietf.org/html/rfc7232#section-2.3 811 | 812 | # `FileETag None` doesn't work in all cases. 813 | 814 | Header unset ETag 815 | 816 | 817 | FileETag None 818 | 819 | # ---------------------------------------------------------------------- 820 | # | Expires headers | 821 | # ---------------------------------------------------------------------- 822 | 823 | # Serve resources with far-future expires headers. 824 | # 825 | # (!) If you don't control versioning with filename-based 826 | # cache busting, you should consider lowering the cache times 827 | # to something like one week. 828 | # 829 | # https://httpd.apache.org/docs/current/mod/mod_expires.html 830 | 831 | 832 | 833 | ExpiresActive on 834 | ExpiresDefault "access plus 1 month" 835 | 836 | # CSS 837 | ExpiresByType text/css "access plus 1 year" 838 | 839 | # Data interchange 840 | ExpiresByType application/atom+xml "access plus 1 hour" 841 | ExpiresByType application/rdf+xml "access plus 1 hour" 842 | ExpiresByType application/rss+xml "access plus 1 hour" 843 | 844 | ExpiresByType application/json "access plus 0 seconds" 845 | ExpiresByType application/ld+json "access plus 0 seconds" 846 | ExpiresByType application/schema+json "access plus 0 seconds" 847 | ExpiresByType application/vnd.geo+json "access plus 0 seconds" 848 | ExpiresByType application/xml "access plus 0 seconds" 849 | ExpiresByType text/xml "access plus 0 seconds" 850 | 851 | # Favicon (cannot be renamed!) and cursor images 852 | ExpiresByType image/vnd.microsoft.icon "access plus 1 week" 853 | ExpiresByType image/x-icon "access plus 1 week" 854 | 855 | # HTML 856 | ExpiresByType text/html "access plus 0 seconds" 857 | 858 | # JavaScript 859 | ExpiresByType application/javascript "access plus 1 year" 860 | ExpiresByType application/x-javascript "access plus 1 year" 861 | ExpiresByType text/javascript "access plus 1 year" 862 | 863 | # Manifest files 864 | ExpiresByType application/manifest+json "access plus 1 year" 865 | 866 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 867 | ExpiresByType text/cache-manifest "access plus 0 seconds" 868 | 869 | # Media files 870 | ExpiresByType audio/ogg "access plus 1 month" 871 | ExpiresByType image/bmp "access plus 1 month" 872 | ExpiresByType image/gif "access plus 1 month" 873 | ExpiresByType image/jpeg "access plus 1 month" 874 | ExpiresByType image/png "access plus 1 month" 875 | ExpiresByType image/svg+xml "access plus 1 month" 876 | ExpiresByType video/mp4 "access plus 1 month" 877 | ExpiresByType video/ogg "access plus 1 month" 878 | ExpiresByType video/webm "access plus 1 month" 879 | 880 | # Web fonts 881 | 882 | # Embedded OpenType (EOT) 883 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 884 | ExpiresByType font/eot "access plus 1 month" 885 | 886 | # OpenType 887 | ExpiresByType font/opentype "access plus 1 month" 888 | 889 | # TrueType 890 | ExpiresByType application/x-font-ttf "access plus 1 month" 891 | 892 | # Web Open Font Format (WOFF) 1.0 893 | ExpiresByType application/font-woff "access plus 1 month" 894 | ExpiresByType application/x-font-woff "access plus 1 month" 895 | ExpiresByType font/woff "access plus 1 month" 896 | 897 | # Web Open Font Format (WOFF) 2.0 898 | ExpiresByType application/font-woff2 "access plus 1 month" 899 | 900 | # Other 901 | ExpiresByType text/x-cross-domain-policy "access plus 1 week" 902 | 903 | 904 | 905 | # ---------------------------------------------------------------------- 906 | # | File concatenation | 907 | # ---------------------------------------------------------------------- 908 | 909 | # Allow concatenation from within specific files. 910 | # 911 | # e.g.: 912 | # 913 | # If you have the following lines in a file called, for 914 | # example, `main.combined.js`: 915 | # 916 | # 917 | # 918 | # 919 | # Apache will replace those lines with the content of the 920 | # specified files. 921 | 922 | # 923 | # 924 | # Options +Includes 925 | # AddOutputFilterByType INCLUDES application/javascript \ 926 | # application/x-javascript \ 927 | # text/javascript 928 | # SetOutputFilter INCLUDES 929 | # 930 | # 931 | # Options +Includes 932 | # AddOutputFilterByType INCLUDES text/css 933 | # SetOutputFilter INCLUDES 934 | # 935 | # 936 | 937 | # ---------------------------------------------------------------------- 938 | # | Filename-based cache busting | 939 | # ---------------------------------------------------------------------- 940 | 941 | # If you're not using a build process to manage your filename version 942 | # revving, you might want to consider enabling the following directives 943 | # to route all requests such as `/style.12345.css` to `/style.css`. 944 | # 945 | # To understand why this is important and even a better solution than 946 | # using something like `*.css?v231`, please see: 947 | # http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/ 948 | 949 | # 950 | # RewriteEngine On 951 | # RewriteCond %{REQUEST_FILENAME} !-f 952 | # RewriteRule ^(.+)\.(\d+)\.(bmp|css|cur|gif|ico|jpe?g|js|png|svgz?|webp)$ $1.$3 [L] 953 | # 954 | 955 | # ---------------------------------------------------------------------- 956 | # | Custom rules | 957 | # ---------------------------------------------------------------------- 958 | 959 | #domain.com/page will display the contents of domain.sk/page.html 960 | RewriteCond %{REQUEST_FILENAME} !-f 961 | RewriteCond %{REQUEST_FILENAME} !-d 962 | RewriteCond %{REQUEST_FILENAME}.html -f 963 | RewriteRule ^(.+)$ $1.html [L,QSA] --------------------------------------------------------------------------------