blob: 988e4bff7f5c02ec4ff80621d57ee7a06c0d2eb0 [file] [log] [blame]
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02001const pkg = require('./package.json')
2const path = require('path')
3const glob = require('glob')
4const yargs = require('yargs')
5const colors = require('colors')
6const through = require('through2');
7const qunit = require('node-qunit-puppeteer')
8
9const {rollup} = require('rollup')
10const {terser} = require('rollup-plugin-terser')
11const babel = require('@rollup/plugin-babel').default
12const commonjs = require('@rollup/plugin-commonjs')
13const resolve = require('@rollup/plugin-node-resolve').default
14const sass = require('sass')
15
16const gulp = require('gulp')
17const tap = require('gulp-tap')
18const zip = require('gulp-zip')
19const header = require('gulp-header')
20const eslint = require('gulp-eslint')
21const minify = require('gulp-clean-css')
22const connect = require('gulp-connect')
23const autoprefixer = require('gulp-autoprefixer')
24
25const root = yargs.argv.root || '.'
26const port = yargs.argv.port || 8000
Christophe Dervieux8afae132021-12-06 15:16:42 +010027const host = yargs.argv.host || 'localhost'
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020028
29const banner = `/*!
30* reveal.js ${pkg.version}
31* ${pkg.homepage}
32* MIT licensed
33*
Marc Kupietz09b75752023-10-07 09:32:19 +020034* Copyright (C) 2011-2023 Hakim El Hattab, https://hakim.se
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020035*/\n`
36
37// Prevents warnings from opening too many test pages
38process.setMaxListeners(20);
39
40const babelConfig = {
41 babelHelpers: 'bundled',
42 ignore: ['node_modules'],
43 compact: false,
44 extensions: ['.js', '.html'],
45 plugins: [
46 'transform-html-import-to-string'
47 ],
48 presets: [[
49 '@babel/preset-env',
50 {
51 corejs: 3,
52 useBuiltIns: 'usage',
53 modules: false
54 }
55 ]]
56};
57
58// Our ES module bundle only targets newer browsers with
59// module support. Browsers are targeted explicitly instead
60// of using the "esmodule: true" target since that leads to
61// polyfilling older browsers and a larger bundle.
62const babelConfigESM = JSON.parse( JSON.stringify( babelConfig ) );
63babelConfigESM.presets[0][1].targets = { browsers: [
Christophe Dervieux8afae132021-12-06 15:16:42 +010064 'last 2 Chrome versions',
65 'last 2 Safari versions',
66 'last 2 iOS versions',
67 'last 2 Firefox versions',
68 'last 2 Edge versions',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020069] };
70
71let cache = {};
72
73// Creates a bundle with broad browser support, exposed
74// as UMD
75gulp.task('js-es5', () => {
76 return rollup({
77 cache: cache.umd,
78 input: 'js/index.js',
79 plugins: [
80 resolve(),
81 commonjs(),
82 babel( babelConfig ),
83 terser()
84 ]
85 }).then( bundle => {
86 cache.umd = bundle.cache;
87 return bundle.write({
88 name: 'Reveal',
89 file: './dist/reveal.js',
90 format: 'umd',
91 banner: banner,
92 sourcemap: true
93 });
94 });
95})
96
97// Creates an ES module bundle
98gulp.task('js-es6', () => {
99 return rollup({
100 cache: cache.esm,
101 input: 'js/index.js',
102 plugins: [
103 resolve(),
104 commonjs(),
105 babel( babelConfigESM ),
106 terser()
107 ]
108 }).then( bundle => {
109 cache.esm = bundle.cache;
110 return bundle.write({
111 file: './dist/reveal.esm.js',
112 format: 'es',
113 banner: banner,
114 sourcemap: true
115 });
116 });
117})
118gulp.task('js', gulp.parallel('js-es5', 'js-es6'));
119
120// Creates a UMD and ES module bundle for each of our
121// built-in plugins
122gulp.task('plugins', () => {
123 return Promise.all([
124 { name: 'RevealHighlight', input: './plugin/highlight/plugin.js', output: './plugin/highlight/highlight' },
125 { name: 'RevealMarkdown', input: './plugin/markdown/plugin.js', output: './plugin/markdown/markdown' },
126 { name: 'RevealSearch', input: './plugin/search/plugin.js', output: './plugin/search/search' },
127 { name: 'RevealNotes', input: './plugin/notes/plugin.js', output: './plugin/notes/notes' },
128 { name: 'RevealZoom', input: './plugin/zoom/plugin.js', output: './plugin/zoom/zoom' },
129 { name: 'RevealMath', input: './plugin/math/plugin.js', output: './plugin/math/math' },
130 ].map( plugin => {
131 return rollup({
132 cache: cache[plugin.input],
133 input: plugin.input,
134 plugins: [
135 resolve(),
136 commonjs(),
137 babel({
138 ...babelConfig,
139 ignore: [/node_modules\/(?!(highlight\.js|marked)\/).*/],
140 }),
141 terser()
142 ]
143 }).then( bundle => {
144 cache[plugin.input] = bundle.cache;
145 bundle.write({
146 file: plugin.output + '.esm.js',
147 name: plugin.name,
148 format: 'es'
149 })
150
151 bundle.write({
152 file: plugin.output + '.js',
153 name: plugin.name,
154 format: 'umd'
155 })
156 });
157 } ));
158})
159
160// a custom pipeable step to transform Sass to CSS
161function compileSass() {
162 return through.obj( ( vinylFile, encoding, callback ) => {
163 const transformedFile = vinylFile.clone();
164
165 sass.render({
166 data: transformedFile.contents.toString(),
Marc Kupietz09b75752023-10-07 09:32:19 +0200167 file: transformedFile.path,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200168 }, ( err, result ) => {
169 if( err ) {
Marc Kupietz09b75752023-10-07 09:32:19 +0200170 callback(err);
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200171 }
172 else {
173 transformedFile.extname = '.css';
174 transformedFile.contents = result.css;
175 callback( null, transformedFile );
176 }
177 });
178 });
179}
180
181gulp.task('css-themes', () => gulp.src(['./css/theme/source/*.{sass,scss}'])
182 .pipe(compileSass())
183 .pipe(gulp.dest('./dist/theme')))
184
185gulp.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
192gulp.task('css', gulp.parallel('css-themes', 'css-core'))
193
194gulp.task('qunit', () => {
195
196 let serverConfig = {
197 root,
198 port: 8009,
Christophe Dervieux8afae132021-12-06 15:16:42 +0100199 host: 'localhost',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200200 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
261gulp.task('eslint', () => gulp.src(['./js/**', 'gulpfile.js'])
262 .pipe(eslint())
263 .pipe(eslint.format()))
264
265gulp.task('test', gulp.series( 'eslint', 'qunit' ))
266
267gulp.task('default', gulp.series(gulp.parallel('js', 'css', 'plugins'), 'test'))
268
269gulp.task('build', gulp.parallel('js', 'css', 'plugins'))
270
Marc Kupietz09b75752023-10-07 09:32:19 +0200271gulp.task('package', gulp.series(() =>
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200272
Marc Kupietz09b75752023-10-07 09:32:19 +0200273 gulp.src(
274 [
275 './index.html',
276 './dist/**',
277 './lib/**',
278 './images/**',
279 './plugin/**',
280 './**/*.md'
281 ],
282 { base: './' }
283 )
284 .pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200285
286))
287
Marc Kupietz09b75752023-10-07 09:32:19 +0200288gulp.task('reload', () => gulp.src(['index.html'])
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200289 .pipe(connect.reload()));
290
291gulp.task('serve', () => {
292
293 connect.server({
294 root: root,
295 port: port,
Christophe Dervieux8afae132021-12-06 15:16:42 +0100296 host: host,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200297 livereload: true
298 })
299
Marc Kupietz09b75752023-10-07 09:32:19 +0200300 const slidesRoot = root.endsWith('/') ? root : root + '/'
301 gulp.watch([
302 slidesRoot + '**/*.html',
303 slidesRoot + '**/*.md',
304 `!${slidesRoot}**/node_modules/**`, // ignore node_modules
305 ], gulp.series('reload'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200306
Christophe Dervieux8afae132021-12-06 15:16:42 +0100307 gulp.watch(['js/**'], gulp.series('js', 'reload', 'eslint'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200308
Marc Kupietz09b75752023-10-07 09:32:19 +0200309 gulp.watch(['plugin/**/plugin.js', 'plugin/**/*.html'], gulp.series('plugins', 'reload'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200310
311 gulp.watch([
Marc Kupietz09b75752023-10-07 09:32:19 +0200312 'css/theme/source/**/*.{sass,scss}',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200313 'css/theme/template/*.{sass,scss}',
314 ], gulp.series('css-themes', 'reload'))
315
316 gulp.watch([
317 'css/*.scss',
318 'css/print/*.{sass,scss,css}'
319 ], gulp.series('css-core', 'reload'))
320
321 gulp.watch(['test/*.html'], gulp.series('test'))
322
Marc Kupietz09b75752023-10-07 09:32:19 +0200323})