gulpfile.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. yargs = require('yargs/yargs'),
  26. cp = require('child_process'),
  27. pkg = require('./package.json'),
  28. year = new Date().getFullYear(),
  29. replace = require('gulp-replace'),
  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. * Check unused Jekyll partials
  72. */
  73. gulp.task('unused-files', (cb) => {
  74. let foundFiles = []
  75. glob.sync(`${srcDir}/pages/**/*.{html,md}`).forEach((file) => {
  76. let fileContent = fs.readFileSync(file)
  77. fileContent.toString().replace(/\{% include(_cached)? ([a-z0-9\/_-]+\.html)/g, (f, c, filename) => {
  78. filename = `${srcDir}/pages/_includes/${filename}`
  79. if (!foundFiles.includes(filename)) {
  80. foundFiles.push(filename)
  81. }
  82. })
  83. })
  84. let includeFiles = glob.sync(`${srcDir}/pages/_includes/**/*.html`)
  85. includeFiles.forEach((file) => {
  86. if (!foundFiles.includes(file)) {
  87. console.log('file', file)
  88. }
  89. })
  90. cb()
  91. })
  92. /**
  93. * Clean `dist` folder before build
  94. */
  95. gulp.task('clean-dirs', () => {
  96. return gulp
  97. .src(`{${distDir}/*,${demoDir}/*}`, { read: false })
  98. .pipe(clean())
  99. })
  100. gulp.task('clean-jekyll', (cb) => {
  101. return spawn('bundle', ['exec', 'jekyll', 'clean'], { stdio: 'inherit' })
  102. .on('close', cb)
  103. })
  104. /**
  105. * Compile SASS to CSS and move it to dist directory
  106. */
  107. gulp.task('sass', () => {
  108. return gulp
  109. .src(argv.withPlugins || BUILD ? `${srcDir}/scss/!(_)*.scss` : `${srcDir}/scss/+(tabler|demo).scss`)
  110. .pipe(debug())
  111. .pipe(sass({
  112. includePaths: ['node_modules'],
  113. style: 'expanded',
  114. precision: 7,
  115. importer: (url, prev, done) => {
  116. if (url[0] === '~') {
  117. url = path.resolve('node_modules', url.substr(1))
  118. }
  119. return { file: url }
  120. },
  121. }))
  122. .on('error', function (err) {
  123. throw err;
  124. })
  125. .pipe(postcss([
  126. require('autoprefixer'),
  127. ]))
  128. .pipe(gulp.dest(`${distDir}/css/`))
  129. .pipe(browserSync.reload({
  130. stream: true,
  131. }));
  132. })
  133. gulp.task('css-rtl', function () {
  134. return gulp.src(`${distDir}/css/*.css`)
  135. .pipe(rtlcss())
  136. .pipe(rename((path) => {
  137. path.basename += '.rtl'
  138. }))
  139. .pipe(gulp.dest(`${distDir}/css/`))
  140. });
  141. /**
  142. * CSS minify
  143. */
  144. gulp.task('css-minify', function () {
  145. return gulp.src(`${distDir}/css/!(*.min).css`)
  146. .pipe(debug())
  147. .pipe(cleanCSS())
  148. .pipe(rename((path) => {
  149. path.basename += '.min'
  150. }))
  151. .pipe(gulp.dest(`${distDir}/css/`))
  152. })
  153. /**
  154. * Compile JS files to dist directory
  155. */
  156. let cache = {}
  157. const compileJs = function (name, mjs = false) {
  158. if (!cache[name]) {
  159. cache[name] = null
  160. }
  161. const g = rollupStream({
  162. input: `${srcDir}/js/${name}.js`,
  163. cache: cache[name],
  164. output: {
  165. name: `${name}.js`,
  166. format: mjs ? 'es' : 'umd',
  167. ...(mjs ? { exports: 'named' } : {})
  168. },
  169. plugins: [
  170. rollupReplace({
  171. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  172. preventAssignment: false
  173. }),
  174. rollupBabel({
  175. exclude: 'node_modules/**'
  176. }),
  177. nodeResolve(),
  178. rollupCommonjs(),
  179. rollupCleanup()
  180. ]
  181. })
  182. .on('bundle', (bundle) => {
  183. cache[name] = bundle
  184. })
  185. .pipe(vinylSource(`${name}.js`))
  186. .pipe(vinylBuffer())
  187. .pipe(rename((path) => {
  188. path.dirname = ''
  189. }))
  190. .pipe(gulp.dest(`${distDir}/js/`))
  191. .pipe(browserSync.reload({
  192. stream: true,
  193. }))
  194. if (BUILD) {
  195. g.pipe(minifyJS())
  196. .pipe(rename((path) => {
  197. path.extname = '.min.js'
  198. }))
  199. .pipe(gulp.dest(`${distDir}/js/`))
  200. }
  201. return g
  202. }
  203. /**
  204. * Compile JS files to dist directory
  205. */
  206. gulp.task('js', () => {
  207. return compileJs('tabler')
  208. })
  209. gulp.task('js-demo', () => {
  210. return compileJs('demo')
  211. })
  212. gulp.task('js-demo-theme', () => {
  213. return compileJs('demo-theme')
  214. })
  215. /**
  216. * Compile JS module files to dist directory
  217. */
  218. gulp.task('mjs', () => {
  219. return compileJs('tabler.esm', true)
  220. })
  221. let cacheEsm
  222. gulp.task('mjs', () => {
  223. const g = rollupStream({
  224. input: `${srcDir}/js/tabler.esm.js`,
  225. cache: cacheEsm,
  226. output: {
  227. name: 'tabler.esm.js',
  228. format: 'es',
  229. exports: 'named'
  230. },
  231. plugins: [
  232. rollupReplace({
  233. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  234. preventAssignment: false
  235. }),
  236. rollupBabel({
  237. exclude: 'node_modules/**'
  238. }),
  239. nodeResolve(),
  240. rollupCommonjs(),
  241. rollupCleanup()
  242. ]
  243. })
  244. .on('bundle', (bundle) => {
  245. cacheEsm = bundle
  246. })
  247. .pipe(vinylSource('tabler.esm.js'))
  248. .pipe(vinylBuffer())
  249. .pipe(rename((path) => {
  250. path.dirname = ''
  251. }))
  252. .pipe(gulp.dest(`${distDir}/js/`))
  253. .pipe(browserSync.reload({
  254. stream: true,
  255. }))
  256. if (BUILD) {
  257. g.pipe(minifyJS())
  258. .pipe(rename((path) => {
  259. path.extname = '.min.js'
  260. }))
  261. .pipe(gulp.dest(`${distDir}/js/`))
  262. }
  263. return g
  264. })
  265. /**
  266. * Watch Jekyll files and build it to demo directory
  267. */
  268. gulp.task('watch-jekyll', (cb) => {
  269. browserSync.notify('Building Jekyll')
  270. return spawn('bundle', ['exec', 'jekyll', 'build', '--watch', '--destination', demoDir, '--trace'], { stdio: 'inherit' })
  271. .on('close', cb)
  272. })
  273. /**
  274. * Build Jekyll files do demo directory
  275. */
  276. gulp.task('build-jekyll', (cb) => {
  277. var env = Object.create(process.env)
  278. if (argv.preview) {
  279. env.JEKYLL_ENV = 'preview'
  280. } else {
  281. env.JEKYLL_ENV = 'production'
  282. }
  283. return spawn('bundle', ['exec', 'jekyll', 'build', '--destination', demoDir, '--trace', '--config', '_config.yml,_config_prod.yml'], {
  284. env: env,
  285. stdio: 'inherit'
  286. })
  287. .on('close', cb)
  288. })
  289. gulp.task('build-cleanup', () => {
  290. return gulp
  291. .src(`${demoDir}/redirects.json`, { read: false, allowEmpty: true })
  292. .pipe(clean())
  293. })
  294. gulp.task('build-purgecss', (cb) => {
  295. if (argv.preview) {
  296. return gulp.src('demo/dist/{libs,css}/**/*.css')
  297. .pipe(purgecss({
  298. content: ['demo/**/*.html']
  299. }))
  300. .pipe(gulp.dest('demo/dist/css'))
  301. }
  302. cb()
  303. })
  304. /**
  305. * Watch JS and SCSS files
  306. */
  307. gulp.task('watch', (cb) => {
  308. gulp.watch('./src/scss/**/*.scss', gulp.series('sass'))
  309. gulp.watch('./src/js/**/*.js', gulp.parallel('js', 'mjs', gulp.parallel('js-demo', 'js-demo-theme')))
  310. cb()
  311. })
  312. /**
  313. * Create BrowserSync server
  314. */
  315. gulp.task('browser-sync', () => {
  316. browserSync({
  317. watch: true,
  318. server: {
  319. baseDir: demoDir,
  320. routes: {
  321. '/node_modules': 'node_modules',
  322. '/dist/css': `${distDir}/css`,
  323. '/dist/js': `${distDir}/js`,
  324. '/dist/img': `${srcDir}/img`,
  325. '/static': `${srcDir}/static`,
  326. },
  327. },
  328. port: 3000,
  329. open: false,
  330. host: 'localhost',
  331. notify: false,
  332. reloadOnRestart: true
  333. })
  334. })
  335. /**
  336. * Copy libs used in tabler from npm to dist directory
  337. */
  338. gulp.task('copy-libs', (cb) => {
  339. const allLibs = require(`${srcDir}/pages/_data/libs`)
  340. let files = []
  341. Object.keys(allLibs.js).forEach((lib) => {
  342. files.push(Array.isArray(allLibs.js[lib]) ? allLibs.js[lib] : [allLibs.js[lib]])
  343. })
  344. Object.keys(allLibs.css).forEach((lib) => {
  345. files.push(Array.isArray(allLibs.css[lib]) ? allLibs.css[lib] : [allLibs.css[lib]])
  346. })
  347. Object.keys(allLibs['js-copy']).forEach((lib) => {
  348. files.push(allLibs['js-copy'][lib])
  349. })
  350. files = files.flat()
  351. files.forEach((file) => {
  352. if (!file.match(/^https?/)) {
  353. let dirname = path.dirname(file).replace('@', '')
  354. let cmd = `mkdir -p "${distDir}/libs/${dirname}" && cp -r node_modules/${path.dirname(file)}/* ${distDir}/libs/${dirname}`
  355. cp.exec(cmd)
  356. }
  357. })
  358. cb()
  359. })
  360. /**
  361. * Copy static files (flags, payments images, etc) to dist directory
  362. */
  363. gulp.task('copy-images', () => {
  364. return gulp
  365. .src(`${srcDir}/img/**/*`)
  366. .pipe(gulp.dest(`${distDir}/img`))
  367. })
  368. /**
  369. * Copy static files (demo images, etc) to demo directory
  370. */
  371. gulp.task('copy-static', () => {
  372. return gulp
  373. .src(`${srcDir}/static/**/*`)
  374. .pipe(gulp.dest(`${demoDir}/static`))
  375. })
  376. /**
  377. * Copy Tabler dist files to demo directory
  378. */
  379. gulp.task('copy-dist', () => {
  380. return gulp
  381. .src(`${distDir}/**/*`)
  382. .pipe(gulp.dest(`${demoDir}/dist/`))
  383. })
  384. /**
  385. * Add banner to build JS and CSS files
  386. */
  387. gulp.task('add-banner', () => {
  388. return gulp.src(`${distDir}/{css,js}/**/*.{js,css}`)
  389. .pipe(header(getBanner()))
  390. .pipe(replace(/^([\s\S]+)(@charset "UTF-8";)\n?/, '$2\n$1'))
  391. .pipe(gulp.dest(`${distDir}`))
  392. })
  393. gulp.task('clean', gulp.series('clean-dirs', 'clean-jekyll'))
  394. 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')))
  395. 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'))
  396. gulp.task('build-demo', gulp.series('build-on', 'build-jekyll', 'copy-static', 'copy-dist', 'build-cleanup', 'build-purgecss'))
  397. gulp.task('build', gulp.series('build-core', 'build-demo'))