blob: 6b0adcc2774b0eb4f1c8489c89135a290a384f97 [file] [log] [blame]
Marc Kupietzcf6e9982026-08-15 15:37:40 +02001const fs = require('fs');
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02002const pkg = require('./package.json')
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02003const glob = require('glob')
4const yargs = require('yargs')
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02005const through = require('through2');
6const qunit = require('node-qunit-puppeteer')
7
8const {rollup} = require('rollup')
Marc Kupietz9c036a42024-05-14 13:17:25 +02009const terser = require('@rollup/plugin-terser')
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020010const babel = require('@rollup/plugin-babel').default
11const commonjs = require('@rollup/plugin-commonjs')
12const resolve = require('@rollup/plugin-node-resolve').default
13const sass = require('sass')
14
15const gulp = require('gulp')
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020016const zip = require('gulp-zip')
Marc Kupietzcf6e9982026-08-15 15:37:40 +020017const header = require('gulp-header-comment')
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020018const eslint = require('gulp-eslint')
19const minify = require('gulp-clean-css')
20const connect = require('gulp-connect')
21const autoprefixer = require('gulp-autoprefixer')
22
23const root = yargs.argv.root || '.'
24const port = yargs.argv.port || 8000
Christophe Dervieux8afae132021-12-06 15:16:42 +010025const host = yargs.argv.host || 'localhost'
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020026
Marc Kupietzcf6e9982026-08-15 15:37:40 +020027const cssLicense = `
28reveal.js ${pkg.version}
29${pkg.homepage}
30MIT licensed
31
32Copyright (C) 2011-2024 Hakim El Hattab, https://hakim.se
33`;
34
35const jsLicense = `/*!
36 * reveal.js ${pkg.version}
37 * ${pkg.homepage}
38 * MIT licensed
39 *
40 * Copyright (C) 2011-2024 Hakim El Hattab, https://hakim.se
41 */\n`;
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020042
43// Prevents warnings from opening too many test pages
44process.setMaxListeners(20);
45
46const babelConfig = {
47 babelHelpers: 'bundled',
48 ignore: ['node_modules'],
49 compact: false,
50 extensions: ['.js', '.html'],
51 plugins: [
52 'transform-html-import-to-string'
53 ],
54 presets: [[
55 '@babel/preset-env',
56 {
57 corejs: 3,
58 useBuiltIns: 'usage',
59 modules: false
60 }
61 ]]
62};
63
64// Our ES module bundle only targets newer browsers with
65// module support. Browsers are targeted explicitly instead
66// of using the "esmodule: true" target since that leads to
67// polyfilling older browsers and a larger bundle.
68const babelConfigESM = JSON.parse( JSON.stringify( babelConfig ) );
69babelConfigESM.presets[0][1].targets = { browsers: [
Christophe Dervieux8afae132021-12-06 15:16:42 +010070 'last 2 Chrome versions',
71 'last 2 Safari versions',
72 'last 2 iOS versions',
73 'last 2 Firefox versions',
74 'last 2 Edge versions',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020075] };
76
77let cache = {};
78
79// Creates a bundle with broad browser support, exposed
80// as UMD
81gulp.task('js-es5', () => {
82 return rollup({
83 cache: cache.umd,
84 input: 'js/index.js',
85 plugins: [
86 resolve(),
87 commonjs(),
88 babel( babelConfig ),
89 terser()
90 ]
91 }).then( bundle => {
92 cache.umd = bundle.cache;
93 return bundle.write({
94 name: 'Reveal',
95 file: './dist/reveal.js',
96 format: 'umd',
Marc Kupietzcf6e9982026-08-15 15:37:40 +020097 banner: jsLicense,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020098 sourcemap: true
99 });
100 });
101})
102
103// Creates an ES module bundle
104gulp.task('js-es6', () => {
105 return rollup({
106 cache: cache.esm,
107 input: 'js/index.js',
108 plugins: [
109 resolve(),
110 commonjs(),
111 babel( babelConfigESM ),
112 terser()
113 ]
114 }).then( bundle => {
115 cache.esm = bundle.cache;
116 return bundle.write({
117 file: './dist/reveal.esm.js',
118 format: 'es',
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200119 banner: jsLicense,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200120 sourcemap: true
121 });
122 });
123})
124gulp.task('js', gulp.parallel('js-es5', 'js-es6'));
125
126// Creates a UMD and ES module bundle for each of our
127// built-in plugins
128gulp.task('plugins', () => {
129 return Promise.all([
130 { name: 'RevealHighlight', input: './plugin/highlight/plugin.js', output: './plugin/highlight/highlight' },
131 { name: 'RevealMarkdown', input: './plugin/markdown/plugin.js', output: './plugin/markdown/markdown' },
132 { name: 'RevealSearch', input: './plugin/search/plugin.js', output: './plugin/search/search' },
133 { name: 'RevealNotes', input: './plugin/notes/plugin.js', output: './plugin/notes/notes' },
134 { name: 'RevealZoom', input: './plugin/zoom/plugin.js', output: './plugin/zoom/zoom' },
135 { name: 'RevealMath', input: './plugin/math/plugin.js', output: './plugin/math/math' },
136 ].map( plugin => {
137 return rollup({
138 cache: cache[plugin.input],
139 input: plugin.input,
140 plugins: [
141 resolve(),
142 commonjs(),
143 babel({
144 ...babelConfig,
145 ignore: [/node_modules\/(?!(highlight\.js|marked)\/).*/],
146 }),
147 terser()
148 ]
149 }).then( bundle => {
150 cache[plugin.input] = bundle.cache;
151 bundle.write({
152 file: plugin.output + '.esm.js',
153 name: plugin.name,
154 format: 'es'
155 })
156
157 bundle.write({
158 file: plugin.output + '.js',
159 name: plugin.name,
160 format: 'umd'
161 })
162 });
163 } ));
164})
165
166// a custom pipeable step to transform Sass to CSS
167function compileSass() {
168 return through.obj( ( vinylFile, encoding, callback ) => {
169 const transformedFile = vinylFile.clone();
170
171 sass.render({
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200172 silenceDeprecations: ['legacy-js-api'],
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200173 data: transformedFile.contents.toString(),
Marc Kupietz09b75752023-10-07 09:32:19 +0200174 file: transformedFile.path,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200175 }, ( err, result ) => {
176 if( err ) {
Marc Kupietz09b75752023-10-07 09:32:19 +0200177 callback(err);
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200178 }
179 else {
180 transformedFile.extname = '.css';
181 transformedFile.contents = result.css;
182 callback( null, transformedFile );
183 }
184 });
185 });
186}
187
188gulp.task('css-themes', () => gulp.src(['./css/theme/source/*.{sass,scss}'])
189 .pipe(compileSass())
190 .pipe(gulp.dest('./dist/theme')))
191
192gulp.task('css-core', () => gulp.src(['css/reveal.scss'])
193 .pipe(compileSass())
194 .pipe(autoprefixer())
195 .pipe(minify({compatibility: 'ie9'}))
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200196 .pipe(header(cssLicense))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200197 .pipe(gulp.dest('./dist')))
198
199gulp.task('css', gulp.parallel('css-themes', 'css-core'))
200
201gulp.task('qunit', () => {
202
203 let serverConfig = {
204 root,
205 port: 8009,
Christophe Dervieux8afae132021-12-06 15:16:42 +0100206 host: 'localhost',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200207 name: 'test-server'
208 }
209
210 let server = connect.server( serverConfig )
211
212 let testFiles = glob.sync('test/*.html' )
213
214 let totalTests = 0;
215 let failingTests = 0;
216
217 let tests = Promise.all( testFiles.map( filename => {
218 return new Promise( ( resolve, reject ) => {
219 qunit.runQunitPuppeteer({
220 targetUrl: `http://${serverConfig.host}:${serverConfig.port}/${filename}`,
221 timeout: 20000,
222 redirectConsole: false,
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200223 puppeteerArgs: ['--allow-file-access-from-files', '--no-sandbox']
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200224 })
225 .then(result => {
226 if( result.stats.failed > 0 ) {
227 console.log(`${'!'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.red);
228 // qunit.printResultSummary(result, console);
229 qunit.printFailedTests(result, console);
230 }
231 else {
232 console.log(`${'✔'} ${filename} [${result.stats.passed}/${result.stats.total}] in ${result.stats.runtime}ms`.green);
233 }
234
235 totalTests += result.stats.total;
236 failingTests += result.stats.failed;
237
238 resolve();
239 })
240 .catch(error => {
241 console.error(error);
242 reject();
243 });
244 } )
245 } ) );
246
247 return new Promise( ( resolve, reject ) => {
248
249 tests.then( () => {
250 if( failingTests > 0 ) {
251 reject( new Error(`${failingTests}/${totalTests} tests failed`.red) );
252 }
253 else {
254 console.log(`${'✔'} Passed ${totalTests} tests`.green.bold);
255 resolve();
256 }
257 } )
258 .catch( () => {
259 reject();
260 } )
261 .finally( () => {
262 server.close();
263 } );
264
265 } );
266} )
267
268gulp.task('eslint', () => gulp.src(['./js/**', 'gulpfile.js'])
269 .pipe(eslint())
270 .pipe(eslint.format()))
271
272gulp.task('test', gulp.series( 'eslint', 'qunit' ))
273
274gulp.task('default', gulp.series(gulp.parallel('js', 'css', 'plugins'), 'test'))
275
276gulp.task('build', gulp.parallel('js', 'css', 'plugins'))
277
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200278gulp.task('package', gulp.series(async () => {
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200279
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200280 let dirs = [
281 './index.html',
282 './dist/**',
283 './plugin/**',
284 './*/*.md'
285 ];
286
287 if (fs.existsSync('./lib')) dirs.push('./lib/**');
288 if (fs.existsSync('./images')) dirs.push('./images/**');
289 if (fs.existsSync('./slides')) dirs.push('./slides/**');
290
291 return gulp.src( dirs, { base: './', encoding: false } )
Marc Kupietz09b75752023-10-07 09:32:19 +0200292 .pipe(zip('reveal-js-presentation.zip')).pipe(gulp.dest('./'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200293
Marc Kupietzcf6e9982026-08-15 15:37:40 +0200294}))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200295
Marc Kupietz09b75752023-10-07 09:32:19 +0200296gulp.task('reload', () => gulp.src(['index.html'])
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200297 .pipe(connect.reload()));
298
299gulp.task('serve', () => {
300
301 connect.server({
302 root: root,
303 port: port,
Christophe Dervieux8afae132021-12-06 15:16:42 +0100304 host: host,
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200305 livereload: true
306 })
307
Marc Kupietz09b75752023-10-07 09:32:19 +0200308 const slidesRoot = root.endsWith('/') ? root : root + '/'
309 gulp.watch([
310 slidesRoot + '**/*.html',
311 slidesRoot + '**/*.md',
312 `!${slidesRoot}**/node_modules/**`, // ignore node_modules
313 ], gulp.series('reload'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200314
Christophe Dervieux8afae132021-12-06 15:16:42 +0100315 gulp.watch(['js/**'], gulp.series('js', 'reload', 'eslint'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200316
Marc Kupietz09b75752023-10-07 09:32:19 +0200317 gulp.watch(['plugin/**/plugin.js', 'plugin/**/*.html'], gulp.series('plugins', 'reload'))
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200318
319 gulp.watch([
Marc Kupietz09b75752023-10-07 09:32:19 +0200320 'css/theme/source/**/*.{sass,scss}',
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +0200321 'css/theme/template/*.{sass,scss}',
322 ], gulp.series('css-themes', 'reload'))
323
324 gulp.watch([
325 'css/*.scss',
326 'css/print/*.{sass,scss,css}'
327 ], gulp.series('css-core', 'reload'))
328
329 gulp.watch(['test/*.html'], gulp.series('test'))
330
Marc Kupietz09b75752023-10-07 09:32:19 +0200331})