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