/******************************* * Build Task *******************************/ const gulp = require('gulp'), // node dependencies console = require('better-console'), // gulp dependencies autoprefixer = require('gulp-autoprefixer'), chmod = require('gulp-chmod'), concatCSS = require('gulp-concat-css'), dedupe = require('gulp-dedupe'), flatten = require('gulp-flatten'), gulpif = require('gulp-if'), header = require('gulp-header'), less = require('gulp-less'), minifyCSS = require('gulp-clean-css'), normalize = require('normalize-path'), plumber = require('gulp-plumber'), print = require('gulp-print').default, rename = require('gulp-rename'), replace = require('gulp-replace'), replaceExt = require('replace-ext'), rtlcss = require('gulp-rtlcss'), // config config = require('../config/user'), docsConfig = require('../config/docs'), tasks = require('../config/tasks'), install = require('../config/project/install'), // shorthand globs = config.globs, assets = config.paths.assets, banner = tasks.banner, filenames = tasks.filenames, comments = tasks.regExp.comments, log = tasks.log, settings = tasks.settings ; /** * Builds the css * @param src * @param type * @param compress * @param config * @param opts * @return {*} */ function build(src, type, compress, config, opts) { let fileExtension; if (type === 'rtl' && compress) { fileExtension = settings.rename.rtlMinCSS; } else if (type === 'rtl') { fileExtension = settings.rename.rtlCSS; } else if (compress) { fileExtension = settings.rename.minCSS; } return gulp.src(src, opts) .pipe(plumber(settings.plumber.less)) .pipe(less(settings.less)) .pipe(autoprefixer(settings.prefix)) .pipe(gulpif(type === 'rtl', rtlcss())) .pipe(replace(comments.variables.in, comments.variables.out)) .pipe(replace(comments.license.in, comments.license.out)) .pipe(replace(comments.large.in, comments.large.out)) .pipe(replace(comments.small.in, comments.small.out)) .pipe(replace(comments.tiny.in, comments.tiny.out)) .pipe(flatten()) .pipe(replace( config.paths.assets.source, compress ? config.paths.assets.compressed : config.paths.assets.uncompressed )) .pipe(gulpif(compress, minifyCSS(settings.minify))) .pipe(gulpif(fileExtension, rename(fileExtension))) .pipe(gulpif(config.hasPermissions, chmod(config.parsedPermissions))) .pipe(gulp.dest(compress ? config.paths.output.compressed : config.paths.output.uncompressed)) .pipe(print(log.created)) ; } /** * Packages the css files in dist * @param {string} type - type of the css processing (none, rtl, docs) * @param {boolean} compress - should the output be compressed */ function pack(type, compress) { const output = type === 'docs' ? docsConfig.paths.output : config.paths.output; const ignoredGlobs = type === 'rtl' ? globs.ignoredRTL + '.rtl.css' : globs.ignored + '.css'; let concatenatedCSS; if (type === 'rtl') { concatenatedCSS = compress ? filenames.concatenatedMinifiedRTLCSS : filenames.concatenatedRTLCSS; } else { concatenatedCSS = compress ? filenames.concatenatedMinifiedCSS : filenames.concatenatedCSS; } let src = output.uncompressed + '/**/' + globs.components + ignoredGlobs; if (globs.components.indexOf('table') < 0 && globs.components.indexOf('tab') > 0) { src = [src, '!' + output.uncompressed + '/**/table.css']; } return gulp.src(src) .pipe(plumber()) .pipe(dedupe()) .pipe(replace(assets.uncompressed, assets.packaged)) .pipe(concatCSS(concatenatedCSS, settings.concatCSS)) .pipe(gulpif(config.stripHeaders, replace(comments.header.in, comments.header.out))) .pipe(gulpif(config.hasPermissions, chmod(config.parsedPermissions))) .pipe(gulpif(compress, minifyCSS(settings.concatMinify))) .pipe(header(banner, settings.header)) .pipe(gulp.dest(output.packaged)) .pipe(print(log.created)) ; } function buildCSS(src, type, config, opts, callback) { if (!install.isSetup()) { console.error('Cannot build CSS files. Run "gulp install" to set-up Fomantic'); callback(); return; } if (callback === undefined) { callback = opts; opts = config; config = type; type = src; src = config.paths.source.definitions + '/**/' + config.globs.components + '.less'; } if (globs.individuals !== undefined && typeof src === 'string') { const components = config.globs.components.replace(/[{}]/g, '') + ',' + config.globs.individuals.replace(/[{}]/g, ''); src = config.paths.source.definitions + '/**/{' + components + '}.less'; } const buildUncompressed = () => build(src, type, false, config, opts); buildUncompressed.displayName = 'Building uncompressed CSS'; const buildCompressed = () => build(src, type, true, config, opts); buildCompressed.displayName = 'Building compressed CSS'; const packUncompressed = () => pack(type, false); packUncompressed.displayName = 'Packing uncompressed CSS'; const packCompressed = () => pack(type, true); packCompressed.displayName = 'Packing compressed CSS'; gulp.parallel( gulp.series( buildUncompressed, gulp.parallel(packUncompressed, packCompressed) ), gulp.series(buildCompressed) )(callback); } function rtlAndNormal(src, callback) { if (callback === undefined) { callback = src; src = config.paths.source.definitions + '/**/' + config.globs.components + '.less'; } const rtl = (callback) => buildCSS(src, 'rtl', config, {}, callback); rtl.displayName = 'CSS Right-To-Left'; const css = (callback) => buildCSS(src, 'default', config, {}, callback); css.displayName = 'CSS'; if (config.rtl === true || config.rtl === 'Yes') { rtl(callback); } else if (config.rtl === 'both') { gulp.series(rtl, css)(callback); } else { css(callback); } } function docs(src, callback) { if (callback === undefined) { callback = src; src = config.paths.source.definitions + '/**/' + config.globs.components + '.less'; } const func = (callback) => buildCSS(src, 'docs', config, {}, callback); func.displayName = 'CSS Docs'; func(callback); } // Default tasks module.exports = rtlAndNormal; // We keep the changed files in an array to call build with all of them at the same time let timeout, files = [] ; /** * Watch changes in CSS files and call the correct build pipe * @param type * @param config */ module.exports.watch = function (type, config) { const method = type === 'docs' ? docs : rtlAndNormal; // Watch theme.config file gulp.watch([ normalize(config.paths.source.config), normalize(config.paths.source.site + '/**/site.variables'), normalize(config.paths.source.themes + '/**/site.variables'), ]) .on('all', function () { // Clear timeout and reset files if (timeout) { clearTimeout(timeout); } files = []; return gulp.series(method)(); }) ; // Watch any less / overrides / variables files gulp.watch([ normalize(config.paths.source.definitions + '/**/*.less'), normalize(config.paths.source.site + '/**/*.{overrides,variables}'), normalize(config.paths.source.themes + '/**/*.{overrides,variables}'), ]) .on('all', function (event, path) { // We don't handle deleted files yet if (event === 'unlink' || event === 'unlinkDir') { return; } // Clear timeout if (timeout) { clearTimeout(timeout); } // Determine which LESS file has to be recompiled let lessPath; if (path.indexOf('site.variables') !== -1) { return; } if (path.indexOf(config.paths.source.themes) !== -1) { console.log('Change detected in packaged theme'); lessPath = replaceExt(path, '.less'); lessPath = lessPath.replace(tasks.regExp.theme, config.paths.source.definitions); } else if (path.indexOf(config.paths.source.site) !== -1) { console.log('Change detected in site theme'); lessPath = replaceExt(path, '.less'); lessPath = lessPath.replace(config.paths.source.site, config.paths.source.definitions); } else { console.log('Change detected in definition'); lessPath = path; } // Add file to internal changed files array if (!files.includes(lessPath)) { files.push(lessPath); } // Update timeout timeout = setTimeout(() => { // Copy files to build in another array const buildFiles = [...files]; // Call method gulp.series((callback) => method(buildFiles, callback))(); // Reset internal changed files array files = []; }, 1000); }) ; }; // Expose build css method module.exports.buildCSS = buildCSS;