gulpfile.js 10 KB

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