@joinbox/build-task
Advanced tools
Comparing version 0.1.0 to 0.2.0
{ | ||
"name": "@joinbox/build-task", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Re-usable gulp 4/webpack build tasks for Joinbox projects", | ||
@@ -42,2 +42,3 @@ "main": "./src/BuildTask.js", | ||
"del": "^3.0.0", | ||
"eslint-loader": "^2.0.0", | ||
"event-emitter": "^0.3.5", | ||
@@ -44,0 +45,0 @@ "glob": "^7.1.2", |
@@ -16,255 +16,255 @@ const colors = require('colors'); | ||
constructor() { | ||
constructor() { | ||
// Simple event emitter that can be called when changes happen; serve tasks listens for | ||
// events and updates on changes | ||
this.changeWatcher = new EventEmitter(); | ||
// Simple event emitter that can be called when changes happen; serve tasks listens for | ||
// events and updates on changes | ||
this.changeWatcher = new EventEmitter(); | ||
// All tasks; key: name, value: function | ||
this.tasks = new Map(); | ||
// All dev subtasks, will run after cleanup in default task | ||
this.devTasks = []; | ||
// All watch subtasks; will run after dev tasks in default task | ||
this.watchTasks = []; | ||
// All prod subtasks: Will be called in parallel after clean in 'prod' task | ||
this.prodTasks = []; | ||
// All tasks; key: name, value: function | ||
this.tasks = new Map(); | ||
// All dev subtasks, will run after cleanup in default task | ||
this.devTasks = []; | ||
// All watch subtasks; will run after dev tasks in default task | ||
this.watchTasks = []; | ||
// All prod subtasks: Will be called in parallel after clean in 'prod' task | ||
this.prodTasks = []; | ||
// Configuratio needed to create tasks | ||
this.config = defaultConfig; | ||
} | ||
// Configuratio needed to create tasks | ||
this.config = defaultConfig; | ||
} | ||
/** | ||
* Updates the certain parts of the configuration. | ||
* @param {string} path Path to the property (of this.config) to modify. | ||
* @param {*} value Value to set property to | ||
*/ | ||
setConfig(path, value) { | ||
const property = getProperty(this.config, path); | ||
property.reference.entity[property.reference.property] = value; | ||
} | ||
/** | ||
* Updates the certain parts of the configuration. | ||
* @param {string} path Path to the property (of this.config) to modify. | ||
* @param {*} value Value to set property to | ||
*/ | ||
setConfig(path, value) { | ||
const property = getProperty(this.config, path); | ||
property.reference.entity[property.reference.property] = value; | ||
} | ||
/** | ||
* Main methiod: Creates and returns the built tasks | ||
* @return {[type]} [description] | ||
*/ | ||
createTasks() { | ||
/** | ||
* Main methiod: Creates and returns the built tasks | ||
* @return {[type]} [description] | ||
*/ | ||
createTasks() { | ||
this.createScriptTasks(); | ||
this.createStyleTasks(); | ||
this.createTemplateTasks(); | ||
this.createServeTask(); | ||
this.createCleanTask(); | ||
this.createScriptTasks(); | ||
this.createStyleTasks(); | ||
this.createTemplateTasks(); | ||
this.createServeTask(); | ||
this.createCleanTask(); | ||
// Create default task (clears destination, calls all dev then watch tasks) | ||
this.tasks.set('default', gulp.series( | ||
// Remove dest folder | ||
this.tasks.get('clean'), | ||
// Execute all dev tasks (initial dev) | ||
gulp.parallel(this.devTasks), | ||
// Execute all watch tasks and serve (don't use serial here, all tasks will run | ||
// indefinitely) | ||
gulp.parallel(this.watchTasks.concat(() => { | ||
this.config.server ? this.tasks.get('serve')() : null; | ||
})), | ||
)); | ||
// Create default task (clears destination, calls all dev then watch tasks) | ||
this.tasks.set('default', gulp.series( | ||
// Remove dest folder | ||
this.tasks.get('clean'), | ||
// Execute all dev tasks (initial dev) | ||
gulp.parallel(this.devTasks), | ||
// Execute all watch tasks and serve (don't use serial here, all tasks will run | ||
// indefinitely) | ||
gulp.parallel(this.watchTasks.concat(() => { | ||
this.config.server ? this.tasks.get('serve')() : null; | ||
})), | ||
)); | ||
// Create prod task | ||
this.tasks.set('prod', gulp.series( | ||
this.tasks.get('clean'), | ||
(done) => { | ||
console.log(colors.cyan('BuildTask: Execute %d prod tasks'), this.prodTasks.length); | ||
done(); | ||
}, | ||
gulp.parallel(this.prodTasks), | ||
)); | ||
// Create prod task | ||
this.tasks.set('prod', gulp.series( | ||
this.tasks.get('clean'), | ||
(done) => { | ||
console.log(colors.cyan('BuildTask: Execute %d prod tasks'), this.prodTasks.length); | ||
done(); | ||
}, | ||
gulp.parallel(this.prodTasks), | ||
)); | ||
// Convert this.tasks (Map) into an object which will then be returned | ||
const returnObject = {}; | ||
this.tasks.forEach((task, name) => { | ||
returnObject[name] = task; | ||
}); | ||
// Convert this.tasks (Map) into an object which will then be returned | ||
const returnObject = {}; | ||
this.tasks.forEach((task, name) => { | ||
returnObject[name] = task; | ||
}); | ||
console.log(colors.cyan('BuildTask: Tasks are %s'), | ||
Object.keys(returnObject).join(', ')); | ||
console.log(colors.cyan('BuildTask: Tasks are %s'), | ||
Object.keys(returnObject).join(', ')); | ||
return returnObject; | ||
return returnObject; | ||
} | ||
} | ||
/** | ||
* Just emits a change on this.changeWatcher. this.changeWatcher is where our browsersync server | ||
* listens to and reloads when a 'change' event. Hook your watch tasks in here. | ||
*/ | ||
emitChange() { | ||
console.log(colors.cyan('BuildTask: Emit change')); | ||
this.changeWatcher.emit('change'); | ||
return Promise.resolve(); | ||
} | ||
/** | ||
* Just emits a change on this.changeWatcher. this.changeWatcher is where our browsersync server | ||
* listens to and reloads when a 'change' event. Hook your watch tasks in here. | ||
*/ | ||
emitChange() { | ||
console.log(colors.cyan('BuildTask: Emit change')); | ||
this.changeWatcher.emit('change'); | ||
return Promise.resolve(); | ||
} | ||
/** | ||
* Removes the destination folder | ||
* @private | ||
*/ | ||
createCleanTask() { | ||
this.tasks.set('clean', | ||
createCleanTasks(path.join(this.config.paths.base, this.config.paths.destination))); | ||
} | ||
/** | ||
* Removes the destination folder | ||
* @private | ||
*/ | ||
createCleanTask() { | ||
this.tasks.set('clean', | ||
createCleanTasks(path.join(this.config.paths.base, this.config.paths.destination))); | ||
} | ||
/** | ||
* @private | ||
*/ | ||
createScriptTasks() { | ||
/** | ||
* @private | ||
*/ | ||
createScriptTasks() { | ||
if (!this.config.scripts) return; | ||
if (!this.config.scripts) return; | ||
console.log(colors.cyan('BuildTask: Create script tasks')); | ||
const jsDev = createScriptTasks(this.config.scripts, this.config.paths, | ||
this.config.supportedBrowsers); | ||
const jsProd = createScriptTasks(this.config.scripts, this.config.paths, | ||
this.config.supportedBrowsers, 'production'); | ||
const fullJsDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.scripts.paths, 'destination')), | ||
jsDev, | ||
this.emitChange.bind(this), | ||
); | ||
console.log(colors.cyan('BuildTask: Create script tasks')); | ||
const jsDev = createScriptTasks(this.config.scripts, this.config.paths, | ||
this.config.supportedBrowsers); | ||
const jsProd = createScriptTasks(this.config.scripts, this.config.paths, | ||
this.config.supportedBrowsers, 'production'); | ||
const fullJsDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.scripts.paths, 'destination')), | ||
jsDev, | ||
this.emitChange.bind(this), | ||
); | ||
let watchPath = this.getWatchPath(this.config.scripts); | ||
console.log(colors.cyan('BuildTask: Watch JS files on %s'), watchPath); | ||
const jsWatch = () => gulp.watch(watchPath, gulp.series(jsDev, this.emitChange.bind(this))); | ||
this.tasks.set('jsDev', fullJsDev); | ||
this.tasks.set('jsWatch', jsWatch); | ||
this.tasks.set('jsProd', jsProd); | ||
this.tasks.set('js', gulp.series(fullJsDev, jsWatch)); | ||
let watchPath = this.getWatchPath(this.config.scripts); | ||
console.log(colors.cyan('BuildTask: Watch JS files on %s'), watchPath); | ||
const jsWatch = () => gulp.watch(watchPath, gulp.series(jsDev, this.emitChange.bind(this))); | ||
this.tasks.set('jsDev', fullJsDev); | ||
this.tasks.set('jsWatch', jsWatch); | ||
this.tasks.set('jsProd', jsProd); | ||
this.tasks.set('js', gulp.series(fullJsDev, jsWatch)); | ||
this.watchTasks.push(jsWatch); | ||
this.devTasks.push(jsDev); | ||
this.prodTasks.push(jsProd); | ||
this.watchTasks.push(jsWatch); | ||
this.devTasks.push(jsDev); | ||
this.prodTasks.push(jsProd); | ||
console.log(colors.cyan('BuildTask: Script tasks created')); | ||
console.log(colors.cyan('BuildTask: Script tasks created')); | ||
} | ||
} | ||
/** | ||
* Create all style tasks | ||
* @private | ||
*/ | ||
createStyleTasks() { | ||
/** | ||
* Create all style tasks | ||
* @private | ||
*/ | ||
createStyleTasks() { | ||
if (!this.config.styles) return; | ||
if (!this.config.styles) return; | ||
console.log(colors.cyan('BuildTask: Create style tasks')); | ||
const cssDev = createStyleTasks(this.config.styles, this.config.paths, | ||
this.config.supportedBrowsers); | ||
const cssProd = createStyleTasks(this.config.styles, this.config.paths, | ||
this.config.supportedBrowsers, 'production'); | ||
const fullCssDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.styles.paths, 'destination')), | ||
cssDev, | ||
this.emitChange.bind(this), | ||
); | ||
console.log(colors.cyan('BuildTask: Create style tasks')); | ||
const cssDev = createStyleTasks(this.config.styles, this.config.paths, | ||
this.config.supportedBrowsers); | ||
const cssProd = createStyleTasks(this.config.styles, this.config.paths, | ||
this.config.supportedBrowsers, 'production'); | ||
const fullCssDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.styles.paths, 'destination')), | ||
cssDev, | ||
this.emitChange.bind(this), | ||
); | ||
let watchPath = this.getWatchPath(this.config.styles); | ||
console.log(colors.cyan('BuildTask: Watch CSS files on %s'), watchPath); | ||
const cssWatch = () => gulp.watch(watchPath, gulp.series( | ||
cssDev, | ||
this.emitChange.bind(this), | ||
)); | ||
this.tasks.set('cssDev', fullCssDev); | ||
this.tasks.set('cssWatch', cssWatch); | ||
this.tasks.set('cssProd', cssProd); | ||
this.tasks.set('css', gulp.series(fullCssDev, cssWatch)); | ||
let watchPath = this.getWatchPath(this.config.styles); | ||
console.log(colors.cyan('BuildTask: Watch CSS files on %s'), watchPath); | ||
const cssWatch = () => gulp.watch(watchPath, gulp.series( | ||
cssDev, | ||
this.emitChange.bind(this), | ||
)); | ||
this.tasks.set('cssDev', fullCssDev); | ||
this.tasks.set('cssWatch', cssWatch); | ||
this.tasks.set('cssProd', cssProd); | ||
this.tasks.set('css', gulp.series(fullCssDev, cssWatch)); | ||
this.watchTasks.push(cssWatch); | ||
this.devTasks.push(cssDev); | ||
this.prodTasks.push(cssProd); | ||
this.watchTasks.push(cssWatch); | ||
this.devTasks.push(cssDev); | ||
this.prodTasks.push(cssProd); | ||
console.log(colors.cyan('BuildTask: Style tasks created')); | ||
console.log(colors.cyan('BuildTask: Style tasks created')); | ||
} | ||
} | ||
/** | ||
* Small helper: Returns a watch path for a config passed in | ||
* @private | ||
*/ | ||
getWatchPath(typeConfig) { | ||
let watchPath = getPath(this.config.paths, typeConfig.paths); | ||
watchPath = path.join(watchPath, typeConfig.paths.watch); | ||
return watchPath; | ||
} | ||
/** | ||
* Small helper: Returns a watch path for a config passed in | ||
* @private | ||
*/ | ||
getWatchPath(typeConfig) { | ||
let watchPath = getPath(this.config.paths, typeConfig.paths); | ||
watchPath = path.join(watchPath, typeConfig.paths.watch); | ||
return watchPath; | ||
} | ||
/** | ||
* @private | ||
*/ | ||
createTemplateTasks() { | ||
/** | ||
* @private | ||
*/ | ||
createTemplateTasks() { | ||
if (!this.config.templates) return; | ||
if (!this.config.templates) return; | ||
console.log(colors.cyan('BuildTask: Create template tasks')); | ||
console.log(colors.cyan('BuildTask: Create template tasks')); | ||
// For html files, there's no difference between dev and prod (yet) – we might use | ||
// minification one day. | ||
const htmlDev = createTemplateTasks(this.config.templates, this.config.paths); | ||
const htmlProd = createTemplateTasks(this.config.templates, this.config.paths, | ||
'production'); | ||
// For html files, there's no difference between dev and prod (yet) – we might use | ||
// minification one day. | ||
const htmlDev = createTemplateTasks(this.config.templates, this.config.paths); | ||
const htmlProd = createTemplateTasks(this.config.templates, this.config.paths, | ||
'production'); | ||
const fullHtmlDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.templates.paths, 'destination')), | ||
htmlDev, | ||
this.emitChange.bind(this), | ||
); | ||
const fullHtmlDev = gulp.series( | ||
createCleanTasks(getPath(this.config.paths, this.config.templates.paths, 'destination')), | ||
htmlDev, | ||
this.emitChange.bind(this), | ||
); | ||
let watchPath = this.getWatchPath(this.config.templates); | ||
console.log(colors.cyan('BuildTask: Watch HTML files on %s'), watchPath); | ||
const htmlWatch = () => gulp.watch(watchPath, gulp.series( | ||
htmlDev, | ||
this.emitChange.bind(this), | ||
)); | ||
this.tasks.set('htmlDev', fullHtmlDev); | ||
this.tasks.set('htmlWatch', htmlWatch); | ||
this.tasks.set('htmlProd', htmlProd); | ||
this.tasks.set('html', gulp.series(fullHtmlDev, htmlWatch)); | ||
let watchPath = this.getWatchPath(this.config.templates); | ||
console.log(colors.cyan('BuildTask: Watch HTML files on %s'), watchPath); | ||
const htmlWatch = () => gulp.watch(watchPath, gulp.series( | ||
htmlDev, | ||
this.emitChange.bind(this), | ||
)); | ||
this.tasks.set('htmlDev', fullHtmlDev); | ||
this.tasks.set('htmlWatch', htmlWatch); | ||
this.tasks.set('htmlProd', htmlProd); | ||
this.tasks.set('html', gulp.series(fullHtmlDev, htmlWatch)); | ||
this.watchTasks.push(htmlWatch); | ||
this.devTasks.push(htmlDev); | ||
this.prodTasks.push(htmlProd); | ||
this.watchTasks.push(htmlWatch); | ||
this.devTasks.push(htmlDev); | ||
this.prodTasks.push(htmlProd); | ||
console.log(colors.cyan('BuildTask: Template tasks created')); | ||
console.log(colors.cyan('BuildTask: Template tasks created')); | ||
} | ||
} | ||
/** | ||
* @private | ||
*/ | ||
createServeTask() { | ||
if (!this.config.server) return; | ||
/** | ||
* @private | ||
*/ | ||
createServeTask() { | ||
if (!this.config.server) return; | ||
console.log(colors.cyan('BuildTask: Create serve task')); | ||
const serve = createBrowserSync( | ||
this.config.server, | ||
this.config.paths, | ||
this.changeWatcher, | ||
); | ||
this.tasks.set('serve', serve); | ||
console.log(colors.cyan('BuildTask: Serve task created')); | ||
console.log(colors.cyan('BuildTask: Create serve task')); | ||
const serve = createBrowserSync( | ||
this.config.server, | ||
this.config.paths, | ||
this.changeWatcher, | ||
); | ||
this.tasks.set('serve', serve); | ||
console.log(colors.cyan('BuildTask: Serve task created')); | ||
} | ||
} | ||
}; |
@@ -8,4 +8,4 @@ const path = require('path'); | ||
* Create server | ||
* @param {object} serverConfig Config for server | ||
* @param {paths} paths Config for paths | ||
* @param {object} serverConfig Config for server | ||
* @param {paths} paths Config for paths | ||
* @param {EventEmitter} watcher Event emitter that emits events on change (js, css, html files | ||
@@ -15,19 +15,19 @@ * etc.) | ||
module.exports = function createBrowserSync(serverConfig, paths, watcher) { | ||
// Path where files are served from | ||
const htmlDestPath = getPath(paths, serverConfig.paths); | ||
// Path to watch – reload browserSync on changes | ||
const watchPath = path.join(htmlDestPath, serverConfig.paths.watch); | ||
// Path where files are served from | ||
const htmlDestPath = getPath(paths, serverConfig.paths); | ||
// Path to watch – reload browserSync on changes | ||
const watchPath = path.join(htmlDestPath, serverConfig.paths.watch); | ||
console.log(colors.blue('Serve: Serving from %s, watching %s'), htmlDestPath, watchPath); | ||
console.log(colors.blue('Serve: Serving from %s, watching %s'), htmlDestPath, watchPath); | ||
return function() { | ||
browserSync.init({ | ||
server: path.join(paths.base, paths.destination), | ||
startPath: serverConfig.paths.start, | ||
}); | ||
return function() { | ||
browserSync.init({ | ||
server: path.join(paths.base, paths.destination), | ||
startPath: serverConfig.paths.start, | ||
}); | ||
watcher.on('change', browserSync.reload); | ||
}; | ||
watcher.on('change', browserSync.reload); | ||
}; | ||
}; |
@@ -5,7 +5,7 @@ const colors = require('colors'); | ||
module.exports = function(path) { | ||
console.log(colors.grey('Remove %s'), path); | ||
return (done) => del(path).then((paths) => { | ||
console.log(colors.grey('Removed paths %s'), paths.join('\n')); | ||
done(); | ||
}); | ||
console.log(colors.grey('Remove %s'), path); | ||
return (done) => del(path).then((paths) => { | ||
console.log(colors.grey('Removed paths %s'), paths.join('\n')); | ||
done(); | ||
}); | ||
}; |
@@ -13,40 +13,47 @@ // gulp-notifier has write method which is needed but not documented – don't use it | ||
/** | ||
* Basic JS task for dev and production mode | ||
* Don't use webpack-stream in combination with gulp because: | ||
* - directories are not preserved, see https://github.com/shama/webpack-stream/issues/62 | ||
* - it's overly complex (error handling, sourcemaps, named() for multiple files …) | ||
* @return {object} Exported gup tasks | ||
*/ | ||
return function(done) { | ||
/** | ||
* Basic JS task for dev and production mode | ||
* Don't use webpack-stream in combination with gulp because: | ||
* - directories are not preserved, see https://github.com/shama/webpack-stream/issues/62 | ||
* - it's overly complex (error handling, sourcemaps, named() for multiple files …) | ||
* @return {object} Exported gup tasks | ||
*/ | ||
return function(done) { | ||
const webpackConfig = getWebpackConfig(scriptConfig, pathConfig, browsers, environment); | ||
//console.log('Scripts: webpack config is %o', webpackConfig); | ||
const webpackConfig = getWebpackConfig(scriptConfig, pathConfig, browsers, environment); | ||
//console.log('Scripts: webpack config is %o', webpackConfig); | ||
webpack(webpackConfig).run((err, stats) => { | ||
webpack(webpackConfig).run((err, stats) => { | ||
let notificationOptions; | ||
// Basic error | ||
if (err) notificationOptions = getNotificationOptions('Scripts', err); | ||
// Error when compiling a single or multiple files | ||
else if (stats.compilation.errors && stats.compilation.errors.length) { | ||
notificationOptions = getNotificationOptions('Scripts', | ||
new Error(stats.compilation.errors)); | ||
console.error(stats.compilation.errors); | ||
} | ||
// All fine | ||
else { | ||
const assets = Object.keys(stats.compilation.assets).join(', '); | ||
notificationOptions = getNotificationOptions('Scripts', assets); | ||
console.log(colors.yellow('Webpack: Result is %s'), stats.toString()); | ||
} | ||
//console.log(stats.compilation); | ||
notifier.notify(notificationOptions); | ||
done(); | ||
}); | ||
}; | ||
let notificationOptions; | ||
// Basic error | ||
if (err) notificationOptions = getNotificationOptions('Scripts', err); | ||
// Error when compiling a single or multiple files (webpack errors) | ||
else if (stats.compilation.errors && stats.compilation.errors.length) { | ||
notificationOptions = getNotificationOptions('Scripts', | ||
new Error(stats.compilation.errors)); | ||
console.error(colors.red(stats.compilation.errors)); | ||
} | ||
// Warnings (e.g. from esLint) | ||
else if (stats.compilation.warnings && stats.compilation.warnings.length) { | ||
const err = new Error(`Warning: ${ stats.compilation.warnings.join(', ')}`); | ||
err.name = 'LintError'; | ||
notificationOptions = getNotificationOptions('Scripts', err); | ||
} | ||
// All fine | ||
else { | ||
const assets = Object.keys(stats.compilation.assets).join(', '); | ||
notificationOptions = getNotificationOptions('Scripts', assets); | ||
console.log(colors.yellow('Webpack: Result is %s'), stats.toString()); | ||
} | ||
//console.log(stats.compilation); | ||
notifier.notify(notificationOptions); | ||
done(); | ||
}); | ||
}; | ||
}; | ||
@@ -19,63 +19,63 @@ const gulp = require('gulp'); | ||
module.exports = function createStyleTasks( | ||
styleConfig, | ||
paths, | ||
browsers, | ||
environment = 'development' | ||
styleConfig, | ||
paths, | ||
browsers, | ||
environment = 'development' | ||
) { | ||
const sassOptions = environment === 'development' ? { outputStyle: 'expanded'} : | ||
{ outputStyle: 'compressed' }; | ||
const sourcePath = getPath(paths, styleConfig.paths); | ||
const sources = styleConfig.paths.entries.map((entry) => path.join(sourcePath, entry)); | ||
const destinationPath = getPath(paths, styleConfig.paths, 'destination'); | ||
console.log(colors.green('Styles: Sources are %s, destination is %s'), sources.join(', '), | ||
destinationPath); | ||
const sassOptions = environment === 'development' ? { outputStyle: 'expanded'} : | ||
{ outputStyle: 'compressed' }; | ||
const sourcePath = getPath(paths, styleConfig.paths); | ||
const sources = styleConfig.paths.entries.map((entry) => path.join(sourcePath, entry)); | ||
const destinationPath = getPath(paths, styleConfig.paths, 'destination'); | ||
console.log(colors.green('Styles: Sources are %s, destination is %s'), sources.join(', '), | ||
destinationPath); | ||
return function() { | ||
return function() { | ||
let hasErrored = false; | ||
let hasErrored = false; | ||
return gulp.src(sources) | ||
.pipe(plumber({ | ||
errorHandler: () => (err) => notifier.notify(getNotificationOptions('Styles', err)) | ||
})) | ||
.pipe(print()) | ||
.pipe(sourcemaps.init()) | ||
return gulp.src(sources) | ||
.pipe(plumber({ | ||
errorHandler: () => (err) => notifier.notify(getNotificationOptions('Styles', err)) | ||
})) | ||
.pipe(print()) | ||
.pipe(sourcemaps.init()) | ||
.pipe(sassGlob()) | ||
.pipe( | ||
sass(sassOptions) | ||
// We need to handle Sass compilation errors specifically, they won't be cought | ||
// by plumber. Don't use arrow function as scope must be preserved for logError | ||
.on('error', function(err) { | ||
sass.logError.call(this, err); | ||
notifier.notify(getNotificationOptions('Styles', err)); | ||
hasErrored = true; | ||
}) | ||
) | ||
.pipe(sassGlob()) | ||
.pipe( | ||
sass(sassOptions) | ||
// We need to handle Sass compilation errors specifically, they won't be cought | ||
// by plumber. Don't use arrow function as scope must be preserved for logError | ||
.on('error', function(err) { | ||
sass.logError.call(this, err); | ||
notifier.notify(getNotificationOptions('Styles', err)); | ||
hasErrored = true; | ||
}) | ||
) | ||
.pipe(postcss([ | ||
autoprefixer({ | ||
browsers: browsers | ||
}), | ||
pxtorem({ | ||
propWhiteList: [], | ||
rootValue: 16 | ||
}), | ||
calc | ||
])) | ||
.pipe(postcss([ | ||
autoprefixer({ | ||
browsers: browsers | ||
}), | ||
pxtorem({ | ||
propWhiteList: [], | ||
rootValue: 16 | ||
}), | ||
calc | ||
])) | ||
.pipe(sourcemaps.write('.')) | ||
.pipe(gulp.dest(destinationPath)) | ||
//.pipe(browsersync.reload({stream: true})) | ||
//.pipe(browsersyncPatternlab.reload({stream: true})) | ||
.on('end', () => { | ||
if (hasErrored) return; | ||
notifier.notify(getNotificationOptions('Styles')); | ||
}); | ||
.pipe(sourcemaps.write('.')) | ||
.pipe(gulp.dest(destinationPath)) | ||
//.pipe(browsersync.reload({stream: true})) | ||
//.pipe(browsersyncPatternlab.reload({stream: true})) | ||
.on('end', () => { | ||
if (hasErrored) return; | ||
notifier.notify(getNotificationOptions('Styles')); | ||
}); | ||
}; | ||
}; | ||
}; | ||
@@ -11,25 +11,25 @@ const gulp = require('gulp'); | ||
const sources = getPath(paths, templateConfig.paths); | ||
const destination = getPath(paths, templateConfig.paths, 'destination'); | ||
const entries = templateConfig.paths.entries.map((entry) => { | ||
return path.join(sources, entry); | ||
}); | ||
const sources = getPath(paths, templateConfig.paths); | ||
const destination = getPath(paths, templateConfig.paths, 'destination'); | ||
const entries = templateConfig.paths.entries.map((entry) => { | ||
return path.join(sources, entry); | ||
}); | ||
console.log(colors.blue('createTemplateTasks: Entries are %s, destination %s'), entries, | ||
destination); | ||
console.log(colors.blue('createTemplateTasks: Entries are %s, destination %s'), entries, | ||
destination); | ||
return function() { | ||
return gulp.src(entries) | ||
.pipe(print()) | ||
// This is dangerous/unexpected, consult case 1 on https://github.com/robrich/gulp-if | ||
// Make sure gulpif comes straight before gulp.dest! | ||
.pipe(gulpif(environment === 'production', htmlmin({ | ||
// Options: https://github.com/kangax/html-minifier | ||
collapseWhitespace: true, | ||
conservativeCollapse: true, | ||
preserveLineBreaks: true, | ||
}))) | ||
.pipe(gulp.dest(destination)); | ||
}; | ||
return function() { | ||
return gulp.src(entries) | ||
.pipe(print()) | ||
// This is dangerous/unexpected, consult case 1 on https://github.com/robrich/gulp-if | ||
// Make sure gulpif comes straight before gulp.dest! | ||
.pipe(gulpif(environment === 'production', htmlmin({ | ||
// Options: https://github.com/kangax/html-minifier | ||
collapseWhitespace: true, | ||
conservativeCollapse: true, | ||
preserveLineBreaks: true, | ||
}))) | ||
.pipe(gulp.dest(destination)); | ||
}; | ||
}; |
const defaults = { | ||
// For sub-paths, use arrays! | ||
paths: { | ||
base: 'www', | ||
source: 'src', | ||
destination: 'dest', | ||
public: '', // Directory to serve files from | ||
}, | ||
// Use false to not expose scripts task | ||
scripts: { | ||
// use ['default'] or ['react'] (not both, react includes default) | ||
technologies: ['default'], | ||
paths: { | ||
source: 'js', | ||
watch: '**/*.js?(x)', | ||
destination: 'js', | ||
entries: ['main.js'], | ||
output: '[name].js', | ||
} | ||
}, | ||
// Use false to not expose styles task | ||
styles: { | ||
paths: { | ||
source: 'css', | ||
destination: 'css', | ||
watch: '**/*.scss', | ||
entries: ['main.scss'], | ||
} | ||
}, | ||
// Use false to not template scripts | ||
templates: { | ||
paths: { | ||
source: 'html', | ||
watch: '**/*.html', | ||
destination: 'html', | ||
entries: ['**/*.html'], | ||
}, | ||
}, | ||
production: { | ||
bustCache: false, // TBD | ||
}, | ||
// False to not add it to tasks | ||
server: { | ||
paths: { | ||
source: 'html', | ||
watch: '**/*.html', | ||
start: 'html' // Where to point to | ||
} | ||
}, | ||
// Browser applies to CSS prefixing and JS babel compilation, see | ||
// https://github.com/browserslist/browserslist#queries | ||
supportedBrowsers: [ | ||
'last 2 versions', | ||
], | ||
// For sub-paths, use arrays! | ||
paths: { | ||
base: 'www', | ||
source: 'src', | ||
destination: 'dest', | ||
public: '', // Directory to serve files from | ||
}, | ||
// Use false to not expose scripts task | ||
scripts: { | ||
// use ['default'] or ['react'] (not both, react includes default) | ||
technologies: ['default'], | ||
paths: { | ||
source: 'js', | ||
watch: '**/*.js?(x)', | ||
destination: 'js', | ||
entries: ['main.js'], | ||
output: '[name].js', | ||
} | ||
}, | ||
// Use false to not expose styles task | ||
styles: { | ||
paths: { | ||
source: 'css', | ||
destination: 'css', | ||
watch: '**/*.scss', | ||
entries: ['main.scss'], | ||
} | ||
}, | ||
// Use false to not template scripts | ||
templates: { | ||
paths: { | ||
source: 'html', | ||
watch: '**/*.html', | ||
destination: 'html', | ||
entries: ['**/*.html'], | ||
}, | ||
}, | ||
production: { | ||
bustCache: false, // TBD | ||
}, | ||
// False to not add it to tasks | ||
server: { | ||
paths: { | ||
source: 'html', | ||
watch: '**/*.html', | ||
start: 'html' // Where to point to | ||
} | ||
}, | ||
// Browser applies to CSS prefixing and JS babel compilation, see | ||
// https://github.com/browserslist/browserslist#queries | ||
supportedBrowsers: [ | ||
'last 2 versions', | ||
], | ||
}; | ||
module.exports = defaults; |
@@ -5,23 +5,33 @@ const colors = require('colors'); | ||
* Generates the config for notifications | ||
* @param {string} type 'Scripts' or 'styles' (or others) | ||
* @param {string|object} error Error object or a string; if it's an error object, an error | ||
* @param {string} type 'Scripts' or 'styles' (or others) | ||
* @param {string|object} error Error object or a string; if it's an error object, an error | ||
* message will be displayed | ||
*/ | ||
module.exports = function(type, content) { | ||
console.log(colors.grey('getNotifyOptions for %s, error %o'), type, content instanceof Error); | ||
const title = content instanceof Error ? `${ type } failed 😈` : `${ type } done 🚀`; | ||
const text = content instanceof Error ? | ||
content.message : | ||
`${ type } successfully compiled${ content ? ': ' + content : '' }.`; | ||
console.log(colors.grey(`getNotifyOptions: title ${ title }, text ${ text }.`)); | ||
return { | ||
// Only one notification per task, not per stream | ||
onLast: true, //error ? false : true, | ||
title: title, | ||
message: text, | ||
}; | ||
console.log(colors.grey('getNotifyOptions for %s, error %o'), type, content instanceof Error); | ||
let title, text; | ||
if (content instanceof Error && content.name === 'LintError') { | ||
title = `${ type } did not pass lint 🚑`; | ||
text = content.message; | ||
} | ||
else if (content instanceof Error) { | ||
title = `${ type } failed 😈`; | ||
text = content.message; | ||
} | ||
else { | ||
title = `${ type } done 🚀`; | ||
text = `${ type } successfully compiled${ content ? ': ' + content : '' }.`; | ||
} | ||
console.log(colors.grey(`getNotifyOptions: title ${ title }, text ${ text }.`)); | ||
return { | ||
// Only one notification per task, not per stream | ||
onLast: true, //error ? false : true, | ||
title: title, | ||
message: text, | ||
}; | ||
}; |
@@ -13,10 +13,10 @@ const path = require('path'); | ||
module.exports = function(paths, typeConfig, type = 'source') { | ||
//console.log(colors.grey('Get paths for %o %o and type %s'), paths, typeConfig, type); | ||
if (paths.base === undefined) throw new Error(`getSources: base property missing on paths | ||
${ JSON.stringify(paths) }`); | ||
if (paths[type] === undefined) throw new Error(`getSources: ${ type } property missing on | ||
paths ${ JSON.stringify(paths) }`); | ||
if (!typeConfig[type] === undefined) throw new Error(`getSources: ${ type } property missing on | ||
type config ${ JSON.stringify(typeConfig) }`); | ||
return path.join(process.cwd(), paths.base, paths[type], typeConfig[type]); | ||
//console.log(colors.grey('Get paths for %o %o and type %s'), paths, typeConfig, type); | ||
if (paths.base === undefined) throw new Error(`getSources: base property missing on paths | ||
${ JSON.stringify(paths) }`); | ||
if (paths[type] === undefined) throw new Error(`getSources: ${ type } property missing on | ||
paths ${ JSON.stringify(paths) }`); | ||
if (!typeConfig[type] === undefined) throw new Error(`getSources: ${ type } property missing on | ||
type config ${ JSON.stringify(typeConfig) }`); | ||
return path.join(process.cwd(), paths.base, paths[type], typeConfig[type]); | ||
}; |
@@ -11,19 +11,19 @@ /** | ||
module.exports = function(object, path) { | ||
const slices = path.split('.'); | ||
let currentObject = object; | ||
let parentObject; // Defaults to undefined | ||
slices.reduce((prev, slice) => { | ||
if (!currentObject[slice]) throw new Error(`getProperty: Property for path ${ prev } not | ||
found in object ${ JSON.stringify(object) }, full path is ${ path }.`); | ||
parentObject = currentObject; | ||
currentObject = currentObject[slice]; | ||
return prev + '.' + slice; | ||
}, ''); | ||
return { | ||
reference: { | ||
entity: parentObject, | ||
property: slices.pop(), | ||
}, | ||
value: currentObject, | ||
}; | ||
const slices = path.split('.'); | ||
let currentObject = object; | ||
let parentObject; // Defaults to undefined | ||
slices.reduce((prev, slice) => { | ||
if (!currentObject[slice]) throw new Error(`getProperty: Property for path ${ prev } not | ||
found in object ${ JSON.stringify(object) }, full path is ${ path }.`); | ||
parentObject = currentObject; | ||
currentObject = currentObject[slice]; | ||
return prev + '.' + slice; | ||
}, ''); | ||
return { | ||
reference: { | ||
entity: parentObject, | ||
property: slices.pop(), | ||
}, | ||
value: currentObject, | ||
}; | ||
}; |
@@ -5,47 +5,47 @@ const test = require('ava'); | ||
function setupData() { | ||
const object1 = { | ||
name: 'joinbox', | ||
address: { | ||
street: 'brückfeld' | ||
}, | ||
phones: { | ||
mobile: [ | ||
'1234', | ||
'4321', | ||
], | ||
}, | ||
}; | ||
return { object1 }; | ||
const object1 = { | ||
name: 'joinbox', | ||
address: { | ||
street: 'brückfeld' | ||
}, | ||
phones: { | ||
mobile: [ | ||
'1234', | ||
'4321', | ||
], | ||
}, | ||
}; | ||
return { object1 }; | ||
} | ||
test('throws on wrong path', (t) => { | ||
const { object1 } = setupData(); | ||
t.throws(() => getProperty(object1, 'name.subvalue'), /for path/); | ||
const { object1 } = setupData(); | ||
t.throws(() => getProperty(object1, 'name.subvalue'), /for path/); | ||
}); | ||
test('finds valid object paths', (t) => { | ||
const { object1 } = setupData(); | ||
t.deepEqual(getProperty(object1, 'name'), { | ||
value: 'joinbox', reference: { | ||
property: 'name', | ||
entity: object1, | ||
} | ||
}); | ||
t.deepEqual(getProperty(object1, 'address.street'), { | ||
value: 'brückfeld', | ||
reference: { | ||
entity: object1.address, | ||
property: 'street' | ||
} | ||
}); | ||
const { object1 } = setupData(); | ||
t.deepEqual(getProperty(object1, 'name'), { | ||
value: 'joinbox', reference: { | ||
property: 'name', | ||
entity: object1, | ||
} | ||
}); | ||
t.deepEqual(getProperty(object1, 'address.street'), { | ||
value: 'brückfeld', | ||
reference: { | ||
entity: object1.address, | ||
property: 'street' | ||
} | ||
}); | ||
}); | ||
test('works with arrays', (t) => { | ||
const { object1 } = setupData(); | ||
t.deepEqual(getProperty(object1, 'phones.mobile.1'), { | ||
value: '4321', reference: { | ||
property: '1', | ||
entity: object1.phones.mobile, | ||
} | ||
}); | ||
const { object1 } = setupData(); | ||
t.deepEqual(getProperty(object1, 'phones.mobile.1'), { | ||
value: '4321', reference: { | ||
property: '1', | ||
entity: object1.phones.mobile, | ||
} | ||
}); | ||
}); |
@@ -17,43 +17,43 @@ const glob = require('glob'); | ||
console.log(colors.yellow('Webpack: Create config; jsConfig is \n%s\npathConfig is \n%s'), | ||
JSON.stringify(jsConfig, null, 2), JSON.stringify(pathConfig, null, 2)); | ||
console.log(colors.yellow('Webpack: Create config; jsConfig is \n%s\npathConfig is \n%s'), | ||
JSON.stringify(jsConfig, null, 2), JSON.stringify(pathConfig, null, 2)); | ||
// Get paths; make sure you're running gulp from the directory where your gulp file lies | ||
const baseSourcePath = getPath(pathConfig, jsConfig.paths); | ||
const baseDestinationPath = getPath(pathConfig, jsConfig.paths, 'destination'); | ||
console.log(colors.yellow('Webpack: baseSourcePath is %s, baseDestinationPath is %s'), | ||
baseSourcePath, baseDestinationPath); | ||
// Get paths; make sure you're running gulp from the directory where your gulp file lies | ||
const baseSourcePath = getPath(pathConfig, jsConfig.paths); | ||
const baseDestinationPath = getPath(pathConfig, jsConfig.paths, 'destination'); | ||
console.log(colors.yellow('Webpack: baseSourcePath is %s, baseDestinationPath is %s'), | ||
baseSourcePath, baseDestinationPath); | ||
// Allow globs as entry points – and also (and especially) arrays of globs. Webpack does not | ||
// support it, do it manually: https://github.com/webpack/webpack/issues/370 | ||
const entries = jsConfig.paths.entries | ||
// 1. Resolve all array's items globs to files | ||
.reduce((prev, source) => { | ||
const sourceWithPath = path.join(baseSourcePath, source); | ||
console.log(colors.yellow('Webpack: Get files for %s from glob %s'), source, | ||
sourceWithPath); | ||
return [...prev, ...glob.sync(sourceWithPath)]; | ||
}, []) | ||
// 2. Make all paths absolute if it is not already | ||
.map((item) => path.isAbsolute(item) ? item : path.join(baseSourcePath, item)) | ||
// 3. Now make an object out of our files (if we pass an array as entry point, webpack will | ||
// create just one file, main.js). The object's key is the relative path from | ||
// baseSourcePath | ||
.reduce((prev, item) => { | ||
// Relative path is the difference from baseSourcePath to item and therefore | ||
// also the difference where we want to store the file relative to baseDestinationPath | ||
const relativePath = path.relative(baseSourcePath, item); | ||
// Remove the extension which will be added when saving – if we don't we'll have two | ||
// extensions | ||
const extension = path.extname(relativePath); | ||
const relativePathWithoutExtension = relativePath.substr(0, relativePath.length - | ||
extension.length); | ||
return {...prev, ...{ [relativePathWithoutExtension]: item} }; | ||
}, {}); | ||
// Allow globs as entry points – and also (and especially) arrays of globs. Webpack does not | ||
// support it, do it manually: https://github.com/webpack/webpack/issues/370 | ||
const entries = jsConfig.paths.entries | ||
// 1. Resolve all array's items globs to files | ||
.reduce((prev, source) => { | ||
const sourceWithPath = path.join(baseSourcePath, source); | ||
console.log(colors.yellow('Webpack: Get files for %s from glob %s'), source, | ||
sourceWithPath); | ||
return [...prev, ...glob.sync(sourceWithPath)]; | ||
}, []) | ||
// 2. Make all paths absolute if it is not already | ||
.map((item) => path.isAbsolute(item) ? item : path.join(baseSourcePath, item)) | ||
// 3. Now make an object out of our files (if we pass an array as entry point, webpack will | ||
// create just one file, main.js). The object's key is the relative path from | ||
// baseSourcePath | ||
.reduce((prev, item) => { | ||
// Relative path is the difference from baseSourcePath to item and therefore | ||
// also the difference where we want to store the file relative to baseDestinationPath | ||
const relativePath = path.relative(baseSourcePath, item); | ||
// Remove the extension which will be added when saving – if we don't we'll have two | ||
// extensions | ||
const extension = path.extname(relativePath); | ||
const relativePathWithoutExtension = relativePath.substr(0, relativePath.length - | ||
extension.length); | ||
return {...prev, ...{ [relativePathWithoutExtension]: item} }; | ||
}, {}); | ||
console.log(colors.yellow('Webpack: Sources are %s'), Object.values(entries).join(', ')); | ||
console.log(colors.yellow('Webpack: Sources are %s'), Object.values(entries).join(', ')); | ||
@@ -64,17 +64,17 @@ | ||
const webpackConfig = { | ||
mode: mode, | ||
entry: entries, | ||
output: { | ||
filename: jsConfig.paths.output, | ||
// Don't use __dirname as it resolves to the node's base directory (where | ||
// package.json lies) | ||
path: baseDestinationPath, | ||
}, | ||
devtool: 'source-map', | ||
module: { | ||
rules: getWebpackRules(jsConfig.technologies, browsers) | ||
} | ||
}; | ||
const webpackConfig = { | ||
mode: mode, | ||
entry: entries, | ||
output: { | ||
filename: jsConfig.paths.output, | ||
// Don't use __dirname as it resolves to the node's base directory (where | ||
// package.json lies) | ||
path: baseDestinationPath, | ||
}, | ||
devtool: 'source-map', | ||
module: { | ||
rules: getWebpackRules(jsConfig.technologies, browsers), | ||
}, | ||
}; | ||
@@ -85,7 +85,7 @@ | ||
console.log(colors.yellow('Webpack: Final config is \n%s'), | ||
JSON.stringify(webpackConfig, null, 2)); | ||
console.log(colors.yellow('Webpack: Final config is \n%s'), | ||
JSON.stringify(webpackConfig, null, 2)); | ||
return webpackConfig; | ||
return webpackConfig; | ||
}; |
@@ -5,28 +5,26 @@ const colors = require('colors'); | ||
const rules = []; | ||
const rules = []; | ||
// Default tech | ||
if (technologies.includes('default')) { | ||
console.log(colors.yellow('Webpack: Use default technologies')); | ||
rules.push(getDefaultRule(browsers)); | ||
} | ||
// React | ||
if (technologies.includes('react')) { | ||
console.log(colors.yellow('Webpack: Use react')); | ||
// Clone default rule before modifying it | ||
const reactRule = getDefaultRule(browsers); | ||
reactRule.test = /.*\.jsx?$/, | ||
reactRule.use.options.presets.push('@babel/react'); | ||
rules.push(reactRule); | ||
} | ||
rules.push(getEslintRule()); | ||
console.log(colors.yellow('WebpackRules: There are %d rules: %s'), rules.length, | ||
JSON.stringify(rules, null, 2)); | ||
return rules; | ||
// Default tech | ||
if (technologies.includes('default')) { | ||
console.log(colors.yellow('Webpack: Use default technologies')); | ||
rules.push(getDefaultRule(browsers)); | ||
} | ||
// React | ||
if (technologies.includes('react')) { | ||
console.log(colors.yellow('Webpack: Use react')); | ||
// Clone default rule before modifying it | ||
const reactRule = getDefaultRule(browsers); | ||
reactRule.test = /.*\.jsx?$/, | ||
reactRule.use.options.presets.push('@babel/react'); | ||
rules.push(reactRule); | ||
} | ||
console.log(colors.yellow('WebpackRules: There are %d rules'), rules.length); | ||
return rules; | ||
}; | ||
@@ -41,19 +39,38 @@ | ||
function getDefaultRule(browsers) { | ||
return { | ||
test: /.*\.js$/, | ||
use: { | ||
loader: 'babel-loader', | ||
options: { | ||
presets: [ | ||
[ | ||
'@babel/preset-env', { | ||
targets: browsers, | ||
}, | ||
], | ||
], | ||
babelrc: false, | ||
cacheDirectory: true, | ||
}, | ||
} | ||
}; | ||
return { | ||
test: /\.js$/, | ||
use: { | ||
loader: 'babel-loader', | ||
options: { | ||
presets: [ | ||
[ | ||
'@babel/preset-env', { | ||
targets: browsers, | ||
}, | ||
], | ||
], | ||
babelrc: false, | ||
cacheDirectory: true, | ||
}, | ||
} | ||
}; | ||
} | ||
function getEslintRule() { | ||
return { | ||
test: /\.jsx?$/, | ||
use: { | ||
loader: 'eslint-loader', | ||
// Location of .eslintrc: See webpack config, | ||
// https://github.com/webpack-contrib/eslint-loader/commit/ | ||
// cf48c8077ad63e689c56600f0cf2a81107fc8b56 | ||
options: { | ||
configFile: '../node_modules/@joinbox/eslint-config-joinbox/.eslintrc', | ||
enforce: 'pre', | ||
emitWarning: true, | ||
exclude: /node_modules/, | ||
} | ||
} | ||
} | ||
} |
import number from './module'; | ||
const obj = { number }; | ||
console.log({ ...obj, spread: true }); | ||
//console.log('change'); | ||
console.log({ ...obj, spread: true }); //eslint-disable-line | ||
// console.log('change'); |
export default function() { | ||
return 4; | ||
} | ||
return 4; | ||
} |
const a = 3; | ||
function multiply(factor) { | ||
return factor**factor; | ||
return factor**factor; | ||
} | ||
console.log(multiply(a)); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
36041
719
27