gulpfile.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  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'))
  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. style: 'expanded',
  152. precision: 7,
  153. importer: (url, prev, done) => {
  154. if (url[0] === '~') {
  155. url = path.resolve('node_modules', url.substr(1))
  156. }
  157. return { file: url }
  158. },
  159. }).on('error', sass.logError))
  160. .pipe(postcss([
  161. require('autoprefixer'),
  162. ]))
  163. .pipe(gulp.dest(`${distDir}/css/`))
  164. .pipe(browserSync.reload({
  165. stream: true,
  166. }));
  167. })
  168. gulp.task('css-rtl', function () {
  169. return gulp.src(`${distDir}/css/*.css`)
  170. .pipe(rtlcss())
  171. .pipe(rename((path) => {
  172. path.basename += '.rtl'
  173. }))
  174. .pipe(gulp.dest(`${distDir}/css/`))
  175. });
  176. /**
  177. * CSS minify
  178. */
  179. gulp.task('css-minify', function () {
  180. return gulp.src(`${distDir}/css/!(*.min).css`)
  181. .pipe(debug())
  182. .pipe(cleanCSS())
  183. .pipe(rename((path) => {
  184. path.basename += '.min'
  185. }))
  186. .pipe(gulp.dest(`${distDir}/css/`))
  187. })
  188. /**
  189. * Compile JS files to dist directory
  190. */
  191. let cache = {}
  192. const compileJs = function (name, mjs = false) {
  193. if (!cache[name]) {
  194. cache[name] = null
  195. }
  196. const g = rollupStream({
  197. input: `${srcDir}/js/${name}.js`,
  198. cache: cache[name],
  199. output: {
  200. name: `${name}.js`,
  201. format: mjs ? 'es' : 'umd',
  202. ...(mjs ? { exports: 'named' } : {})
  203. },
  204. plugins: [
  205. rollupReplace({
  206. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  207. preventAssignment: false
  208. }),
  209. rollupBabel({
  210. exclude: 'node_modules/**'
  211. }),
  212. nodeResolve(),
  213. rollupCommonjs(),
  214. rollupCleanup()
  215. ]
  216. })
  217. .on('bundle', (bundle) => {
  218. cache[name] = bundle
  219. })
  220. .pipe(vinylSource(`${name}.js`))
  221. .pipe(vinylBuffer())
  222. .pipe(rename((path) => {
  223. path.dirname = ''
  224. }))
  225. .pipe(gulp.dest(`${distDir}/js/`))
  226. .pipe(browserSync.reload({
  227. stream: true,
  228. }))
  229. if (BUILD) {
  230. g.pipe(minifyJS())
  231. .pipe(rename((path) => {
  232. path.extname = '.min.js'
  233. }))
  234. .pipe(gulp.dest(`${distDir}/js/`))
  235. }
  236. return g
  237. }
  238. /**
  239. * Compile JS files to dist directory
  240. */
  241. gulp.task('js', () => {
  242. return compileJs('tabler')
  243. })
  244. gulp.task('js-demo', () => {
  245. return compileJs('demo')
  246. })
  247. gulp.task('js-demo-theme', () => {
  248. return compileJs('demo-theme')
  249. })
  250. /**
  251. * Compile JS module files to dist directory
  252. */
  253. gulp.task('mjs', () => {
  254. return compileJs('tabler.esm', true)
  255. })
  256. let cacheEsm
  257. gulp.task('mjs', () => {
  258. const g = rollupStream({
  259. input: `${srcDir}/js/tabler.esm.js`,
  260. cache: cacheEsm,
  261. output: {
  262. name: 'tabler.esm.js',
  263. format: 'es',
  264. exports: 'named'
  265. },
  266. plugins: [
  267. rollupReplace({
  268. 'process.env.NODE_ENV': JSON.stringify(BUILD ? 'production' : 'development'),
  269. preventAssignment: false
  270. }),
  271. rollupBabel({
  272. exclude: 'node_modules/**'
  273. }),
  274. nodeResolve(),
  275. rollupCommonjs(),
  276. rollupCleanup()
  277. ]
  278. })
  279. .on('bundle', (bundle) => {
  280. cacheEsm = bundle
  281. })
  282. .pipe(vinylSource('tabler.esm.js'))
  283. .pipe(vinylBuffer())
  284. .pipe(rename((path) => {
  285. path.dirname = ''
  286. }))
  287. .pipe(gulp.dest(`${distDir}/js/`))
  288. .pipe(browserSync.reload({
  289. stream: true,
  290. }))
  291. if (BUILD) {
  292. g.pipe(minifyJS())
  293. .pipe(rename((path) => {
  294. path.extname = '.min.js'
  295. }))
  296. .pipe(gulp.dest(`${distDir}/js/`))
  297. }
  298. return g
  299. })
  300. /**
  301. * Watch Jekyll files and build it to demo directory
  302. */
  303. gulp.task('watch-jekyll', (cb) => {
  304. browserSync.notify('Building Jekyll')
  305. return spawn('bundle', ['exec', 'jekyll', 'build', '--watch', '--incremental', '--destination', demoDir, '--trace'], { stdio: 'inherit' })
  306. .on('close', cb)
  307. })
  308. /**
  309. * Build Jekyll files do demo directory
  310. */
  311. gulp.task('build-jekyll', (cb) => {
  312. var env = Object.create(process.env)
  313. if (argv.preview) {
  314. env.JEKYLL_ENV = 'preview'
  315. } else {
  316. env.JEKYLL_ENV = 'production'
  317. }
  318. return spawn('bundle', ['exec', 'jekyll', 'build', '--destination', demoDir, '--trace', '--config', '_config.yml,_config_prod.yml'], {
  319. env: env,
  320. stdio: 'inherit'
  321. })
  322. .on('close', cb)
  323. })
  324. gulp.task('build-cleanup', () => {
  325. return gulp
  326. .src(`${demoDir}/redirects.json`, { read: false, allowEmpty: true })
  327. .pipe(clean())
  328. })
  329. gulp.task('build-purgecss', (cb) => {
  330. if (argv.preview) {
  331. return gulp.src('demo/dist/{libs,css}/**/*.css')
  332. .pipe(purgecss({
  333. content: ['demo/**/*.html']
  334. }))
  335. .pipe(gulp.dest('demo/dist/css'))
  336. }
  337. cb()
  338. })
  339. /**
  340. * Watch JS and SCSS files
  341. */
  342. gulp.task('watch', (cb) => {
  343. gulp.watch('./src/scss/**/*.scss', gulp.series('sass'))
  344. gulp.watch('./src/js/**/*.js', gulp.parallel('js', 'mjs', gulp.parallel('js-demo', 'js-demo-theme')))
  345. cb()
  346. })
  347. /**
  348. * Create BrowserSync server
  349. */
  350. gulp.task('browser-sync', () => {
  351. browserSync({
  352. watch: true,
  353. server: {
  354. baseDir: demoDir,
  355. routes: {
  356. '/node_modules': 'node_modules',
  357. '/dist/css': `${distDir}/css`,
  358. '/dist/js': `${distDir}/js`,
  359. '/dist/img': `${srcDir}/img`,
  360. '/static': `${srcDir}/static`,
  361. },
  362. },
  363. port: 3000,
  364. open: false,
  365. host: 'localhost',
  366. notify: false,
  367. reloadOnRestart: true
  368. })
  369. })
  370. /**
  371. * Copy libs used in tabler from npm to dist directory
  372. */
  373. gulp.task('copy-libs', (cb) => {
  374. const allLibs = require(`${srcDir}/pages/_data/libs`)
  375. let files = []
  376. Object.keys(allLibs.js).forEach((lib) => {
  377. files.push(Array.isArray(allLibs.js[lib]) ? allLibs.js[lib] : [allLibs.js[lib]])
  378. })
  379. Object.keys(allLibs.css).forEach((lib) => {
  380. files.push(Array.isArray(allLibs.css[lib]) ? allLibs.css[lib] : [allLibs.css[lib]])
  381. })
  382. Object.keys(allLibs['js-copy']).forEach((lib) => {
  383. files.push(allLibs['js-copy'][lib])
  384. })
  385. files = files.flat()
  386. files.forEach((file) => {
  387. if (!file.match(/^https?/)) {
  388. let dirname = path.dirname(file).replace('@', '')
  389. let cmd = `mkdir -p "${distDir}/libs/${dirname}" && cp -r node_modules/${dirname}/* ${distDir}/libs/${dirname}`
  390. cp.exec(cmd)
  391. }
  392. })
  393. cb()
  394. })
  395. /**
  396. * Copy static files (flags, payments images, etc) to dist directory
  397. */
  398. gulp.task('copy-images', () => {
  399. return gulp
  400. .src(`${srcDir}/img/**/*`)
  401. .pipe(gulp.dest(`${distDir}/img`))
  402. })
  403. /**
  404. * Copy static files (demo images, etc) to demo directory
  405. */
  406. gulp.task('copy-static', () => {
  407. return gulp
  408. .src(`${srcDir}/static/**/*`)
  409. .pipe(gulp.dest(`${demoDir}/static`))
  410. })
  411. /**
  412. * Copy Tabler dist files to demo directory
  413. */
  414. gulp.task('copy-dist', () => {
  415. return gulp
  416. .src(`${distDir}/**/*`)
  417. .pipe(gulp.dest(`${demoDir}/dist/`))
  418. })
  419. /**
  420. * Add banner to build JS and CSS files
  421. */
  422. gulp.task('add-banner', () => {
  423. return gulp.src(`${distDir}/{css,js}/**/*.{js,css}`)
  424. .pipe(header(getBanner()))
  425. .pipe(gulp.dest(`${distDir}`))
  426. })
  427. gulp.task('clean', gulp.series('clean-dirs', 'clean-jekyll'))
  428. 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')))
  429. 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'))
  430. gulp.task('build-demo', gulp.series('build-on', 'build-jekyll', 'copy-static', 'copy-dist', 'build-cleanup', 'build-purgecss'))
  431. gulp.task('build', gulp.series('build-core', 'build-demo'))