const gulp = require('gulp'), cp = require('child_process'), glob = require('glob'), fs = require('fs'), path = require('path'), p = require('./package.json'), zip = require('gulp-zip'), puppeteer = require('puppeteer'), argv = require('minimist')(process.argv.slice(2)); async function asyncForEach(array, callback) { for (let index = 0; index < array.length; index++) { await callback(array[index], index, array); } } const svgToPng = async (filePath, destination) => { filePath = path.join(__dirname, filePath); const htmlFilePath = path.join("file:", filePath); const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({ height: 24, width: 24, deviceScaleFactor: 10 }); await page.goto(htmlFilePath); await page.screenshot({ path: path.join(__dirname, destination), omitBackground: true, fullPage: true }); await browser.close(); return page; }; const createScreenshot = async (filePath) => { try { filePath = path.join(__dirname, filePath); const fileName = filePath.replace('.svg', ''); const htmlFilePath = path.join("file:", filePath); const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.setViewport({ height: 10, width: 10, deviceScaleFactor: 2 }); await page.goto(htmlFilePath); await page.screenshot({ path: `${fileName}.png`, omitBackground: false, fullPage: true }); await browser.close(); } catch (error) { console.error(error); throw Error(error); } }; const printChangelog = function (newIcons, modifiedIcons, renamedIcons, pretty = false) { if (newIcons.length > 0) { if(pretty) { console.log(`### ${newIcons.length} new icons:`); newIcons.forEach(function (icon, i) { console.log(`- \`${icon}\``); }); } else { let str = ''; str += `${newIcons.length} new icons: `; newIcons.forEach(function (icon, i) { str += `\`${icon}\``; if ((i + 1) <= newIcons.length - 1) { str += ', ' } }); console.log(str); } console.log(''); } if (modifiedIcons.length > 0) { let str = ''; str += `Fixed icons: `; modifiedIcons.forEach(function (icon, i) { str += `\`${icon}\``; if ((i + 1) <= modifiedIcons.length - 1) { str += ', ' } }); console.log(str); console.log(''); } if (renamedIcons.length > 0) { console.log(`Renamed icons: `); renamedIcons.forEach(function (icon, i) { console.log(`- \`${icon[0]}\` renamed to \`${icon[1]}\``); }); } }; const generateIconsPreview = function(files, destFile, cb, columnsCount = 17) { const padding = 29, paddingOuter = 5, iconSize = 24; const iconsCount = files.length, rowsCount = Math.ceil(iconsCount / columnsCount), width = columnsCount * (iconSize + padding) + 2 * paddingOuter - padding, height = rowsCount * (iconSize + padding) + 2 * paddingOuter - padding; let svgContentSymbols = '', svgContentIcons = '', x = paddingOuter, y = paddingOuter; files.forEach(function (file, i) { let name = path.basename(file, '.svg'); let svgFile = fs.readFileSync(file), svgFileContent = svgFile.toString(); svgFileContent = svgFileContent .replace('', '') .replace(/\n\s+/g, ''); svgContentSymbols += `\t${svgFileContent}\n`; svgContentIcons += `\t\n`; x += padding + iconSize; if (i % columnsCount === columnsCount - 1) { x = paddingOuter; y += padding + iconSize; } }); const svgContent = `\n${svgContentSymbols}\n${svgContentIcons}\n`; fs.writeFileSync(destFile, svgContent); createScreenshot(destFile); cb(); }; //********************************************************************************************* gulp.task('build-zip', function () { const version = p.version; return gulp.src('{icons/**/*,icons-png/**/*,tabler-sprite.svg,tabler-sprite-nostroke.svg}') .pipe(zip(`tabler-icons-${version}.zip`)) .pipe(gulp.dest('packages')) }); gulp.task('build-jekyll', function (cb) { cp.exec('bundle exec jekyll build', function () { cb(); }); }); gulp.task('build-copy', function (cb) { cp.exec('mkdir -p icons/ && rm -fd ./icons/* && cp ./_site/icons/* ./icons', function () { cb(); }); }); gulp.task('clean-png', function (cb) { cp.exec('rm -fd ./icons-png/*', function () { cb(); }); }); gulp.task('icons-sprite', function (cb) { glob("_site/icons/*.svg", {}, function (er, files) { let svgContent = ''; files.forEach(function (file, i) { let name = path.basename(file, '.svg'), svgFile = fs.readFileSync(file), svgFileContent = svgFile.toString(); svgFileContent = svgFileContent .replace(/]+>/g, '') .replace(/<\/svg>/g, '') .replace(/\n+/g, '') .replace(/>\s+<') .trim(); svgContent += `${svgFileContent}` }); let svg = `${svgContent}`; fs.writeFileSync('tabler-sprite.svg', svg); fs.writeFileSync('tabler-sprite-nostroke.svg', svg.replace(/stroke-width="2"\s/g, '')); cb(); }); }); gulp.task('icons-preview', function (cb) { glob("icons/*.svg", {}, function (er, files) { generateIconsPreview(files, '.github/icons.svg', cb); }); }); gulp.task('icons-stroke', gulp.series('build-jekyll', function (cb) { const icon = "disabled", strokes = ['.5', '1', '1.5', '2', '2.75'], svgFileContent = fs.readFileSync(`icons/${icon}.svg`).toString(), padding = 16, paddingOuter = 5, iconSize = 32, width = 882, height = iconSize + paddingOuter * 2; let svgContentSymbols = '', svgContentIcons = '', x = paddingOuter; strokes.forEach(function (stroke) { let svgFileContentStroked = svgFileContent .replace('', '') .replace(/\n\s+/g, ''); svgContentSymbols += `\t${svgFileContentStroked}\n`; svgContentIcons += `\t\n`; x += padding + iconSize; }); const svgContent = `\n${svgContentSymbols}\n${svgContentIcons}\n`; fs.writeFileSync('.github/icons-stroke.svg', svgContent); createScreenshot('.github/icons-stroke.svg'); cb(); })); gulp.task('optimize', function (cb) { glob("src/_icons/*.svg", {}, function (er, files) { files.forEach(function (file, i) { let svgFile = fs.readFileSync(file), svgFileContent = svgFile.toString(); svgFileContent = svgFileContent .replace(/><\/(polyline|line|rect|circle|path)>/g, '/>') .replace(/rx="([^"]+)"\s+ry="\1"/g, 'rx="$1"') .replace(/\s?\/>/g, ' />') .replace(/\n\s*<(line|circle|path|polyline|rect)/g, "\n <$1") .replace(/polyline points="([0-9.]+)\s([0-9.]+)\s([0-9.]+)\s([0-9.]+)"/g, 'line x1="$1" y1="$2" x2="$3" y2="$4"') .replace(/a\s?([0-9.]+)\s([0-9.]+)\s([0-9.]+)\s?([0-1])\s?([0-1])\s?(-?[0-9.]+)\s?(-?[0-9.]+)/g, 'a$1 $2 $3 $4 $5 $6 $7') .replace(/\n\n+/g, "\n"); fs.writeFileSync(file, svgFileContent); }); cb(); }); }); gulp.task('changelog-commit', function (cb) { cp.exec('git status', function (err, ret) { let newIcons = [], modifiedIcons = [], renamedIcons = []; ret.replace(/new file:\s+src\/_icons\/([a-z1-9-]+)\.svg/g, function (m, fileName) { newIcons.push(fileName); }); ret.replace(/modified:\s+src\/_icons\/([a-z1-9-]+)\.svg/g, function (m, fileName) { modifiedIcons.push(fileName); }); ret.replace(/renamed:\s+src\/_icons\/([a-z1-9-]+).svg -> src\/_icons\/([a-z1-9-]+).svg/g, function (m, fileNameBefore, fileNameAfter) { renamedIcons.push([fileNameBefore, fileNameAfter]); }); modifiedIcons = modifiedIcons.filter(function (el) { return newIcons.indexOf(el) < 0; }); printChangelog(newIcons, modifiedIcons, renamedIcons); cb(); }); }); gulp.task('changelog', function (cb) { const version = argv['latest-tag'] || `v${p.version}`; if(version) { cp.exec(`git diff ${version} HEAD --name-status`, function (err, ret) { let newIcons = [], modifiedIcons = [], renamedIcons = []; ret.replace(/A\s+src\/_icons\/([a-z1-9-]+)\.svg/g, function (m, fileName) { newIcons.push(fileName); }); ret.replace(/M\s+src\/_icons\/([a-z1-9-]+)\.svg/g, function (m, fileName) { modifiedIcons.push(fileName); }); ret.replace(/R[0-9]+\s+src\/_icons\/([a-z1-9-]+)\.svg\s+src\/_icons\/([a-z1-9-]+).svg/g, function (m, fileNameBefore, fileNameAfter) { renamedIcons.push([fileNameBefore, fileNameAfter]); }); modifiedIcons = modifiedIcons.filter(function (el) { return newIcons.indexOf(el) < 0; }); printChangelog(newIcons, modifiedIcons, renamedIcons, true); cb(); }); } }); gulp.task('changelog-image', function (cb) { const version = argv['latest-version'] || `${p.version}`, newVersion = argv['new-version'] || `${p.version}`; if(version) { cp.exec(`git diff v${version} HEAD --name-status`, function (err, ret) { let newIcons = []; ret.replace(/[AD]\s+src\/_icons\/([a-z1-9-]+)\.svg/g, function (m, fileName) { newIcons.push(fileName); }); newIcons = newIcons.map(function(icon){ return `./icons/${icon}.svg`; }); if(newIcons.length > 0) { generateIconsPreview(newIcons, `packages/tabler-icons-${newVersion}.svg`, cb, 6); } else { cb(); } }); } else { cb(); } }); gulp.task('svg-to-png', gulp.series('build-jekyll', 'clean-png', async (cb) => { let files = glob.sync("./icons/*.svg"); await asyncForEach(files, async function (file, i) { let name = path.basename(file, '.svg'); console.log('name', name); await svgToPng(file, `icons-png/${name}.png`); }); cb(); })); gulp.task('build', gulp.series('optimize', 'build-jekyll', 'build-copy', 'icons-sprite', 'icons-preview', 'svg-to-png', 'changelog-image', 'build-zip'));