gulpfile.js 12 KB

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