Christophe Dervieux | e1893ae | 2021-10-07 17:09:02 +0200 | [diff] [blame^] | 1 | const pkg = require('./package.json') |
| 2 | const path = require('path') |
| 3 | const glob = require('glob') |
| 4 | const yargs = require('yargs') |
| 5 | const colors = require('colors') |
| 6 | const through = require('through2'); |
| 7 | const qunit = require('node-qunit-puppeteer') |
| 8 | |
| 9 | const {rollup} = require('rollup') |
| 10 | const {terser} = require('rollup-plugin-terser') |
| 11 | const babel = require('@rollup/plugin-babel').default |
| 12 | const commonjs = require('@rollup/plugin-commonjs') |
| 13 | const resolve = require('@rollup/plugin-node-resolve').default |
| 14 | const sass = require('sass') |
| 15 | |
| 16 | const gulp = require('gulp') |
| 17 | const tap = require('gulp-tap') |
| 18 | const zip = require('gulp-zip') |
| 19 | const header = require('gulp-header') |
| 20 | const eslint = require('gulp-eslint') |
| 21 | const minify = require('gulp-clean-css') |
| 22 | const connect = require('gulp-connect') |
| 23 | const autoprefixer = require('gulp-autoprefixer') |
| 24 | |
| 25 | const root = yargs.argv.root || '.' |
| 26 | const port = yargs.argv.port || 8000 |
| 27 | |
| 28 | const banner = `/*! |
| 29 | * reveal.js ${pkg.version} |
| 30 | * ${pkg.homepage} |
| 31 | * MIT licensed |
| 32 | * |
| 33 | * Copyright (C) 2020 Hakim El Hattab, https://hakim.se |
| 34 | */\n` |
| 35 | |
| 36 | // Prevents warnings from opening too many test pages |
| 37 | process.setMaxListeners(20); |
| 38 | |
| 39 | const babelConfig = { |
| 40 | babelHelpers: 'bundled', |
| 41 | ignore: ['node_modules'], |
| 42 | compact: false, |
| 43 | extensions: ['.js', '.html'], |
| 44 | plugins: [ |
| 45 | 'transform-html-import-to-string' |
| 46 | ], |
| 47 | presets: [[ |
| 48 | '@babel/preset-env', |
| 49 | { |
| 50 | corejs: 3, |
| 51 | useBuiltIns: 'usage', |
| 52 | modules: false |
| 53 | } |
| 54 | ]] |
| 55 | }; |
| 56 | |
| 57 | // Our ES module bundle only targets newer browsers with |
| 58 | // module support. Browsers are targeted explicitly instead |
| 59 | // of using the "esmodule: true" target since that leads to |
| 60 | // polyfilling older browsers and a larger bundle. |
| 61 | const babelConfigESM = JSON.parse( JSON.stringify( babelConfig ) ); |
| 62 | babelConfigESM.presets[0][1].targets = { browsers: [ |
| 63 | 'last 2 Chrome versions', 'not Chrome < 60', |
| 64 | 'last 2 Safari versions', 'not Safari < 10.1', |
| 65 | 'last 2 iOS versions', 'not iOS < 10.3', |
| 66 | 'last 2 Firefox versions', 'not Firefox < 60', |
| 67 | 'last 2 Edge versions', 'not Edge < 16', |
| 68 | ] }; |
| 69 | |
| 70 | let cache = {}; |
| 71 | |
| 72 | // Creates a bundle with broad browser support, exposed |
| 73 | // as UMD |
| 74 | gulp.task('js-es5', () => { |
| 75 | return rollup({ |
| 76 | cache: cache.umd, |
| 77 | input: 'js/index.js', |
| 78 | plugins: [ |
| 79 | resolve(), |
| 80 | commonjs(), |
| 81 | babel( babelConfig ), |
| 82 | terser() |
| 83 | ] |
| 84 | }).then( bundle => { |
| 85 | cache.umd = bundle.cache; |
| 86 | return bundle.write({ |
| 87 | name: 'Reveal', |
| 88 | file: './dist/reveal.js', |
| 89 | format: 'umd', |
| 90 | banner: banner, |
| 91 | sourcemap: true |
| 92 | }); |
| 93 | }); |
| 94 | }) |
| 95 | |
| 96 | // Creates an ES module bundle |
| 97 | gulp.task('js-es6', () => { |
| 98 | return rollup({ |
| 99 | cache: cache.esm, |
| 100 | input: 'js/index.js', |
| 101 | plugins: [ |
| 102 | resolve(), |
| 103 | commonjs(), |
| 104 | babel( babelConfigESM ), |
| 105 | terser() |
| 106 | ] |
| 107 | }).then( bundle => { |
| 108 | cache.esm = bundle.cache; |
| 109 | return bundle.write({ |
| 110 | file: './dist/reveal.esm.js', |
| 111 | format: 'es', |
| 112 | banner: banner, |
| 113 | sourcemap: true |
| 114 | }); |
| 115 | }); |
| 116 | }) |
| 117 | gulp.task('js', gulp.parallel('js-es5', 'js-es6')); |
| 118 | |
| 119 | // Creates a UMD and ES module bundle for each of our |
| 120 | // built-in plugins |
| 121 | gulp.task('plugins', () => { |
| 122 | return Promise.all([ |
| 123 | { name: 'RevealHighlight', input: './plugin/highlight/plugin.js', output: './plugin/highlight/highlight' }, |
| 124 | { name: 'RevealMarkdown', input: './plugin/markdown/plugin.js', output: './plugin/markdown/markdown' }, |
| 125 | { name: 'RevealSearch', input: './plugin/search/plugin.js', output: './plugin/search/search' }, |
| 126 | { name: 'RevealNotes', input: './plugin/notes/plugin.js', output: './plugin/notes/notes' }, |
| 127 | { name: 'RevealZoom', input: './plugin/zoom/plugin.js', output: './plugin/zoom/zoom' }, |
| 128 | { name: 'RevealMath', input: './plugin/math/plugin.js', output: './plugin/math/math' }, |
| 129 | ].map( plugin => { |
| 130 | return rollup({ |
| 131 | cache: cache[plugin.input], |
| 132 | input: plugin.input, |
| 133 | plugins: [ |
| 134 | resolve(), |
| 135 | commonjs(), |
| 136 | babel({ |
| 137 | ...babelConfig, |
| 138 | ignore: [/node_modules\/(?!(highlight\.js|marked)\/).*/], |
| 139 | }), |
| 140 | terser() |
| 141 | ] |
| 142 | }).then( bundle => { |
| 143 | cache[plugin.input] = bundle.cache; |
| 144 | bundle.write({ |
| 145 | file: plugin.output + '.esm.js', |
| 146 | name: plugin.name, |
| 147 | format: 'es' |
| 148 | }) |
| 149 | |
| 150 | bundle.write({ |
| 151 | file: plugin.output + '.js', |
| 152 | name: plugin.name, |
| 153 | format: 'umd' |
| 154 | }) |
| 155 | }); |
| 156 | } )); |
| 157 | }) |
| 158 | |
| 159 | // a custom pipeable step to transform Sass to CSS |
| 160 | function compileSass() { |
| 161 | return through.obj( ( vinylFile, encoding, callback ) => { |
| 162 | const transformedFile = vinylFile.clone(); |
| 163 | |
| 164 | sass.render({ |
| 165 | data: transformedFile.contents.toString(), |
| 166 | includePaths: ['css/', 'css/theme/template'] |
| 167 | }, ( err, result ) => { |
| 168 | if( err ) { |
| 169 | console.log( vinylFile.path ); |
| 170 | console.log( err.formatted ); |
| 171 | } |
| 172 | else { |
| 173 | transformedFile.extname = '.css'; |
| 174 | transformedFile.contents = result.css; |
| 175 | callback( null, transformedFile ); |
| 176 | } |
| 177 | }); |
| 178 | }); |
| 179 | } |
| 180 | |
| 181 | gulp.task('css-themes', () => gulp.src(['./css/theme/source/*.{sass,scss}']) |
| 182 | .pipe(compileSass()) |
| 183 | .pipe(gulp.dest('./dist/theme'))) |
| 184 | |
| 185 | gulp.task('css-core', () => gulp.src(['css/reveal.scss']) |
| 186 | .pipe(compileSass()) |
| 187 | .pipe(autoprefixer()) |
| 188 | .pipe(minify({compatibility: 'ie9'})) |
| 189 | .pipe(header(banner)) |
| 190 | .pipe(gulp.dest('./dist'))) |
| 191 | |
| 192 | gulp.task('css', gulp.parallel('css-themes', 'css-core')) |
| 193 | |
| 194 | gulp.task('qunit', () => { |
| 195 | |
| 196 | let serverConfig = { |
| 197 | root, |
| 198 | port: 8009, |
| 199 | host: '0.0.0.0', |
| 200 | name: 'test-server' |
| 201 | } |
| 202 | |
| 203 | let server = connect.server( serverConfig ) |
| 204 | |
| 205 | let testFiles = glob.sync('test/*.html' ) |
| 206 | |
| 207 | let totalTests = 0; |
| 208 | let failingTests = 0; |
| 209 | |
| 210 | let tests = Promise.all( testFiles.map( filename => { |
| 211 | return new Promise( ( resolve, reject ) => { |
| 212 | qunit.runQunitPuppeteer({ |
| 213 | targetUrl: `http://${serverConfig.host}:${serverConfig.port}/${filename}`, |
| 214 | timeout: 20000, |
| 215 | redirectConsole: false, |
| 216 | puppeteerArgs: ['--allow-file-access-from-files'] |
| 217 | }) |
| 218 | .then(result => { |
| 219 | if( result.stats.failed > 0 ) { |
| 220 | console.log(`${'!'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.red); |
| 221 | // qunit.printResultSummary(result, console); |
| 222 | qunit.printFailedTests(result, console); |
| 223 | } |
| 224 | else { |
| 225 | console.log(`${'✔'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.green); |
| 226 | } |
| 227 | |
| 228 | totalTests += result.stats.total; |
| 229 | failingTests += result.stats.failed; |
| 230 | |
| 231 | resolve(); |
| 232 | }) |
| 233 | .catch(error => { |
| 234 | console.error(error); |
| 235 | reject(); |
| 236 | }); |
| 237 | } ) |
| 238 | } ) ); |
| 239 | |
| 240 | return new Promise( ( resolve, reject ) => { |
| 241 | |
| 242 | tests.then( () => { |
| 243 | if( failingTests > 0 ) { |
| 244 | reject( new Error(`${failingTests}/${totalTests} tests failed`.red) ); |
| 245 | } |
| 246 | else { |
| 247 | console.log(`${'✔'} Passed ${totalTests} tests`.green.bold); |
| 248 | resolve(); |
| 249 | } |
| 250 | } ) |
| 251 | .catch( () => { |
| 252 | reject(); |
| 253 | } ) |
| 254 | .finally( () => { |
| 255 | server.close(); |
| 256 | } ); |
| 257 | |
| 258 | } ); |
| 259 | } ) |
| 260 | |
| 261 | gulp.task('eslint', () => gulp.src(['./js/**', 'gulpfile.js']) |
| 262 | .pipe(eslint()) |
| 263 | .pipe(eslint.format())) |
| 264 | |
| 265 | gulp.task('test', gulp.series( 'eslint', 'qunit' )) |
| 266 | |
| 267 | gulp.task('default', gulp.series(gulp.parallel('js', 'css', 'plugins'), 'test')) |
| 268 | |
| 269 | gulp.task('build', gulp.parallel('js', 'css', 'plugins')) |
| 270 | |
| 271 | gulp.task('package', gulp.series('default', () => |
| 272 | |
| 273 | gulp.src([ |
| 274 | './index.html', |
| 275 | './dist/**', |
| 276 | './lib/**', |
| 277 | './images/**', |
| 278 | './plugin/**', |
| 279 | './**.md' |
| 280 | ]).pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./')) |
| 281 | |
| 282 | )) |
| 283 | |
| 284 | gulp.task('reload', () => gulp.src(['*.html', '*.md']) |
| 285 | .pipe(connect.reload())); |
| 286 | |
| 287 | gulp.task('serve', () => { |
| 288 | |
| 289 | connect.server({ |
| 290 | root: root, |
| 291 | port: port, |
| 292 | host: '0.0.0.0', |
| 293 | livereload: true |
| 294 | }) |
| 295 | |
| 296 | gulp.watch(['*.html', '*.md'], gulp.series('reload')) |
| 297 | |
| 298 | gulp.watch(['js/**'], gulp.series('js', 'reload', 'test')) |
| 299 | |
| 300 | gulp.watch(['plugin/**/plugin.js'], gulp.series('plugins', 'reload')) |
| 301 | |
| 302 | gulp.watch([ |
| 303 | 'css/theme/source/*.{sass,scss}', |
| 304 | 'css/theme/template/*.{sass,scss}', |
| 305 | ], gulp.series('css-themes', 'reload')) |
| 306 | |
| 307 | gulp.watch([ |
| 308 | 'css/*.scss', |
| 309 | 'css/print/*.{sass,scss,css}' |
| 310 | ], gulp.series('css-core', 'reload')) |
| 311 | |
| 312 | gulp.watch(['test/*.html'], gulp.series('test')) |
| 313 | |
| 314 | }) |