gulpfile.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. const gulp = require('gulp'),
  2. debug = require('gulp-debug'),
  3. clean = require('gulp-clean'),
  4. sass = require('gulp-sass')(require('node-sass')),
  5. postcss = require('gulp-postcss'),
  6. header = require('gulp-header'),
  7. cleanCSS = require('gulp-clean-css'),
  8. rtlcss = require('gulp-rtlcss'),
  9. minifyJS = require('gulp-minify'),
  10. rename = require('gulp-rename'),
  11. purgecss = require('gulp-purgecss'),
  12. rollupStream = require('@rollup/stream'),
  13. rollupBabel = require('rollup-plugin-babel'),
  14. rollupCleanup = require('rollup-plugin-cleanup'),
  15. {nodeResolve} = require('@rollup/plugin-node-resolve'),
  16. rollupCommonjs = require('@rollup/plugin-commonjs'),
  17. rollupReplace = require('@rollup/plugin-replace'),
  18. vinylSource = require('vinyl-source-stream'),
  19. vinylBuffer = require('vinyl-buffer'),
  20. critical = require('critical').stream,
  21. browserSync = require('browser-sync'),
  22. glob = require('glob'),
  23. spawn = require('cross-spawn'),
  24. fs = require('fs'),
  25. path = require('path'),
  26. YAML = require('yaml'),
  27. yargs = require('yargs/yargs'),
  28. cp = require('child_process'),
  29. pkg = require('./package.json'),
  30. year = new Date().getFullYear(),
  31. argv = yargs(process.argv).argv;
  32. let BUILD = false,
  33. distDir = './.tmp',
  34. demoDir = './.tmp',
  35. srcDir = './src';
  36. /**
  37. * Enable BUILD mode and set directories
  38. */
  39. gulp.task('build-on', (cb) => {
  40. BUILD = true;
  41. distDir = './dist';
  42. demoDir = './demo';
  43. cb();
  44. });
  45. /**
  46. * Return banner added to CSS and JS dist files
  47. */
  48. const getBanner = () => {
  49. return `/*!
  50. * Tabler v${pkg.version} (${pkg.homepage})
  51. * @version ${pkg.version}
  52. * @link ${pkg.homepage}
  53. * Copyright 2018-${year} The Tabler Authors
  54. * Copyright 2018-${year} codecalm.net Paweł Kuna
  55. * Licensed under MIT (https://github.com/tabler/tabler/blob/master/LICENSE)
  56. */
  57. `;
  58. };
  59. /**
  60. * Array.flat polyfill
  61. */
  62. if (!Array.prototype.flat) {
  63. Object.defineProperty(Array.prototype, 'flat', {
  64. value: function (depth = 1) {
  65. return this.reduce(function (flat, toFlatten) {
  66. return flat.concat((Array.isArray(toFlatten) && (depth > 1)) ? toFlatten.flat(depth - 1) : toFlatten);
  67. }, []);
  68. }
  69. });
  70. }
  71. /**
  72. * Import tabler-icons form npm and generate Jekyll `.yml` data files
  73. */
  74. gulp.task('svg-icons', (cb) => {
  75. const prepareSvgFile = (svg) => {
  76. return svg.replace(/\n/g, '').replace(/>\s+</g, '><');
  77. };
  78. const generateIconsYml = (dir, filename) => {
  79. const files = glob.sync(dir);
  80. let svgList = {};
  81. files.forEach((file) => {
  82. const basename = path.basename(file, '.svg');
  83. svgList[basename] = prepareSvgFile(fs.readFileSync(file).toString());
  84. });
  85. fs.writeFileSync(filename, YAML.stringify(svgList));
  86. };
  87. generateIconsYml("./node_modules/@tabler/icons/icons/*.svg", `${srcDir}/pages/_data/icons.yml`);
  88. cb();
  89. });
  90. /**
  91. * Check unused Jekyll partials
  92. */
  93. gulp.task('unused-files', (cb) => {
  94. let foundFiles = [];
  95. glob.sync(`${srcDir}/pages/**/*.{html,md}`).forEach((file) => {
  96. let fileContent = fs.readFileSync(file);
  97. fileContent.toString().replace(/\{% include(_cached)? ([a-z0-9\/_-]+\.html)/g, (f, c, filename) => {
  98. filename = `${srcDir}/pages/_includes/${filename}`;
  99. if (!foundFiles.includes(filename)) {
  100. foundFiles.push(filename);
  101. }
  102. });
  103. });
  104. let includeFiles = glob.sync(`${srcDir}/pages/_includes/**/*.html`);
  105. includeFiles.forEach((file) => {
  106. if (!foundFiles.includes(file)) {
  107. console.log('file', file);
  108. }
  109. });
  110. cb();
  111. });
  112. /**
  113. * Clean `dist` folder before build
  114. */
  115. gulp.task('clean-dirs', () => {
  116. return gulp
  117. .src(`{${distDir}/*,${demoDir}/*}`, { read: false })
  118. .pipe(clean());
  119. });
  120. gulp.task('clean-jekyll', (cb) => {
  121. return spawn('bundle', ['exec', 'jekyll', 'clean'], { stdio: 'inherit' })
  122. .on('close', cb);
  123. });
  124. /**
  125. * Compile SASS to CSS and move it to dist directory
  126. */
  127. gulp.task('sass', () => {
  128. return gulp
  129. .src(`${srcDir}/scss/!(_)*.scss`)
  130. .pipe(debug())
  131. .pipe(sass({
  132. style: 'expanded',
  133. precision: 7,
  134. importer: (url, prev, done) => {
  135. if (url[0] === '~') {
  136. url = path.resolve('node_modules', url.substr(1));
  137. }
  138. return { file: url };
  139. },
  140. }).on('error', sass.logError))
  141. .pipe(postcss([
  142. require('autoprefixer'),
  143. ]))
  144. .pipe(gulp.dest(`${distDir}/css/`))
  145. .pipe(browserSync.reload({
  146. stream: true,
  147. }))
  148. .pipe(rtlcss())
  149. .pipe(rename((path) => {
  150. path.basename += '.rtl';
  151. }))
  152. .pipe(gulp.dest(`${distDir}/css/`));
  153. });
  154. /**
  155. * CSS minify
  156. */
  157. gulp.task('css-minify', function(){
  158. return gulp.src(`${distDir}/css/!(*.min).css`)
  159. .pipe(debug())
  160. .pipe(cleanCSS())
  161. .pipe(rename((path) => {
  162. path.basename += '.min';
  163. }))
  164. .pipe(gulp.dest(`${distDir}/css/`));
  165. });
  166. /**
  167. * Compile JS files to dist directory
  168. */
  169. let cache;
  170. gulp.task('js', () => {
  171. const g = rollupStream({
  172. input: `${srcDir}/js/tabler.js`,
  173. cache,
  174. output: {
  175. name: 'tabler.js',
  176. format: 'umd',
  177. },
  178. plugins: [
  179. rollupReplace({
  180. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  181. preventAssignment: false
  182. }),
  183. rollupBabel({
  184. exclude: 'node_modules/**'
  185. }),
  186. nodeResolve(),
  187. rollupCommonjs(),
  188. rollupCleanup()
  189. ]
  190. })
  191. .on('bundle', (bundle) => {
  192. cache = bundle;
  193. })
  194. .pipe(vinylSource('tabler.js'))
  195. .pipe(vinylBuffer())
  196. .pipe(rename((path) => {
  197. path.dirname = '';
  198. }))
  199. .pipe(gulp.dest(`${distDir}/js/`))
  200. .pipe(browserSync.reload({
  201. stream: true,
  202. }));
  203. if (BUILD) {
  204. g.pipe(minifyJS({
  205. ext: {
  206. src: '.js',
  207. min: '.min.js'
  208. },
  209. }))
  210. .pipe(gulp.dest(`${distDir}/js/`));
  211. }
  212. return g;
  213. });
  214. /**
  215. * Compile JS module files to dist directory
  216. */
  217. let cacheEsm;
  218. gulp.task('mjs', () => {
  219. const g = rollupStream({
  220. input: `${srcDir}/js/tabler.esm.js`,
  221. cache: cacheEsm,
  222. output: {
  223. name: 'tabler.esm.js',
  224. format: 'es',
  225. exports: 'named'
  226. },
  227. plugins: [
  228. rollupReplace({
  229. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  230. preventAssignment: false
  231. }),
  232. rollupBabel({
  233. exclude: 'node_modules/**'
  234. }),
  235. nodeResolve(),
  236. rollupCommonjs(),
  237. rollupCleanup()
  238. ]
  239. })
  240. .on('bundle', (bundle) => {
  241. cacheEsm = bundle;
  242. })
  243. .pipe(vinylSource('tabler.esm.js'))
  244. .pipe(vinylBuffer())
  245. .pipe(rename((path) => {
  246. path.dirname = '';
  247. }))
  248. .pipe(gulp.dest(`${distDir}/js/`))
  249. .pipe(browserSync.reload({
  250. stream: true,
  251. }));
  252. if (BUILD) {
  253. g.pipe(minifyJS({
  254. ext: {
  255. src: '.js',
  256. min: '.min.js'
  257. },
  258. }))
  259. .pipe(gulp.dest(`${distDir}/js/`));
  260. }
  261. return g;
  262. });
  263. /**
  264. * Watch Jekyll files and build it to demo directory
  265. */
  266. gulp.task('watch-jekyll', (cb) => {
  267. browserSync.notify('Building Jekyll');
  268. return spawn('bundle', ['exec', 'jekyll', 'build', '--watch', '--destination', demoDir, '--trace'], { stdio: 'inherit' })
  269. .on('close', cb);
  270. });
  271. /**
  272. * Build Jekyll files do demo directory
  273. */
  274. gulp.task('build-jekyll', (cb) => {
  275. var env = Object.create(process.env);
  276. if(argv.preview) {
  277. env.JEKYLL_ENV = 'preview';
  278. }
  279. else {
  280. env.JEKYLL_ENV = 'production';
  281. }
  282. return spawn('bundle', ['exec', 'jekyll', 'build', '--destination', demoDir, '--trace'], { env: env, stdio: 'inherit' })
  283. .on('close', cb);
  284. });
  285. gulp.task('build-cleanup', () => {
  286. return gulp
  287. .src(`${demoDir}/redirects.json`, { read: false, allowEmpty: true })
  288. .pipe(clean());
  289. });
  290. gulp.task('build-purgecss', (cb) => {
  291. if(argv.preview) {
  292. return gulp.src('demo/dist/{libs,css}/**/*.css')
  293. .pipe(purgecss({
  294. content: ['demo/**/*.html']
  295. }))
  296. .pipe(gulp.dest('demo/dist/css'))
  297. }
  298. cb();
  299. });
  300. gulp.task('build-critical', (cb) => {
  301. if(argv.preview) {
  302. return gulp
  303. .src('demo/**/*.html')
  304. .pipe(
  305. critical({
  306. base: 'demo/',
  307. inline: true,
  308. css: ['demo/dist/css/tabler.css'],
  309. ignore: {
  310. atrule: ['@font-face', '@import'],
  311. decl: (node, value) => {
  312. /url\(/.test(value)
  313. },
  314. },
  315. })
  316. )
  317. .on('error', err => {
  318. console.log(err.message);
  319. })
  320. .pipe(gulp.dest('demo'));
  321. }
  322. cb();
  323. });
  324. /**
  325. * Watch JS and SCSS files
  326. */
  327. gulp.task('watch', (cb) => {
  328. gulp.watch('./src/scss/**/*.scss', gulp.series('sass'));
  329. gulp.watch('./src/js/**/*.js', gulp.series('js', 'mjs'));
  330. cb();
  331. });
  332. /**
  333. * Create BrowserSync server
  334. */
  335. gulp.task('browser-sync', () => {
  336. browserSync({
  337. watch: true,
  338. server: {
  339. baseDir: demoDir,
  340. routes: {
  341. '/node_modules': 'node_modules',
  342. '/dist/css': `${distDir}/css`,
  343. '/dist/js': `${distDir}/js`,
  344. '/dist/img': `${srcDir}/img`,
  345. '/static': `${srcDir}/static`,
  346. },
  347. },
  348. port: 3000,
  349. open: false,
  350. host: 'localhost',
  351. notify: false,
  352. reloadOnRestart: true
  353. });
  354. });
  355. /**
  356. * Copy libs used in tabler from npm to dist directory
  357. */
  358. gulp.task('copy-libs', (cb) => {
  359. const allLibs = require(`${srcDir}/pages/_data/libs`);
  360. let files = [];
  361. Object.keys(allLibs.js).forEach((lib) => {
  362. files.push(Array.isArray(allLibs.js[lib]) ? allLibs.js[lib] : [allLibs.js[lib]]);
  363. });
  364. Object.keys(allLibs.css).forEach((lib) => {
  365. files.push(Array.isArray(allLibs.css[lib]) ? allLibs.css[lib] : [allLibs.css[lib]]);
  366. });
  367. files = files.flat();
  368. files.forEach((file) => {
  369. if (!file.match(/^https?/)) {
  370. let dirname = path.dirname(file).replace('@', '');
  371. let cmd = `mkdir -p "dist/libs/${dirname}" && cp -r node_modules/${file} ${distDir}/libs/${file.replace('@', '')}`;
  372. cp.exec(cmd)
  373. }
  374. });
  375. cb();
  376. });
  377. /**
  378. * Copy static files (flags, payments images, etc) to dist directory
  379. */
  380. gulp.task('copy-images', () => {
  381. return gulp
  382. .src(`${srcDir}/img/**/*`)
  383. .pipe(gulp.dest(`${distDir}/img`));
  384. });
  385. /**
  386. * Copy static files (demo images, etc) to demo directory
  387. */
  388. gulp.task('copy-static', () => {
  389. return gulp
  390. .src(`${srcDir}/static/**/*`)
  391. .pipe(gulp.dest(`${demoDir}/static`));
  392. });
  393. /**
  394. * Copy Tabler dist files to demo directory
  395. */
  396. gulp.task('copy-dist', () => {
  397. return gulp
  398. .src(`${distDir}/**/*`)
  399. .pipe(gulp.dest(`${demoDir}/dist/`));
  400. });
  401. /**
  402. * Add banner to build JS and CSS files
  403. */
  404. gulp.task('add-banner', () => {
  405. return gulp.src(`${distDir}/{css,js}/**/*.{js,css}`)
  406. .pipe(header(getBanner()))
  407. .pipe(gulp.dest(`${distDir}`))
  408. });
  409. gulp.task('clean', gulp.series('clean-dirs', 'clean-jekyll'));
  410. gulp.task('start', gulp.series('clean', 'sass', 'js', 'mjs', 'build-jekyll', gulp.parallel('watch-jekyll', 'watch', 'browser-sync')));
  411. gulp.task('build-core', gulp.series('build-on', 'clean', 'sass', 'css-minify', 'js', 'mjs', 'copy-images', 'copy-libs', 'add-banner'));
  412. gulp.task('build-demo', gulp.series('build-on', 'build-jekyll', 'copy-static', 'copy-dist', 'build-cleanup', 'build-purgecss'/*, 'build-critical'*/));
  413. gulp.task('build', gulp.series('build-core', 'build-demo'));