gulpfile.js 11 KB

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