Commit 0dd44d539381c0dc85f289d28bf8658a1439111f

ExE Boss 2019-07-17T21:22:24

gulp: Use `async` functions & drop testing on Node v6 (#1783) This refactors the `gulpfile.js` to use async/await for clearer code. It also drops testing for all NodeJS versions before NodeJS v8 because they don't support async/await.

diff --git a/.travis.yml b/.travis.yml
index 47adfc5..8dbeab5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,5 @@
 language: node_js
 node_js:
-- '6'
 - '8'
 - '10'
 - 'node'
diff --git a/gulpfile.js b/gulpfile.js
index 0c6f8a2..9c09a76 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -6,6 +6,7 @@ const header = require('gulp-header');
 const concat = require('gulp-concat');
 const replace = require('gulp-replace');
 const pump = require('pump');
+const util = require('util');
 const fs = require('fs');
 const simpleGit = require('simple-git');
 const shelljs = require('shelljs');
@@ -79,12 +80,11 @@ function build(cb) {
 `), concat('prism.js'), dest('./')], cb);
 }
 
-function componentsJsonToJs(cb) {
-	componentsPromise.then(data => {
-		const js = `var components = ${JSON.stringify(data)};
+async function componentsJsonToJs() {
+	const data = await componentsPromise;
+	const js = `var components = ${JSON.stringify(data)};
 if (typeof module !== 'undefined' && module.exports) { module.exports = components; }`;
-		fs.writeFile(paths.componentsFileJS, js, cb);
-	});
+	return util.promisify(fs.writeFile)(paths.componentsFileJS, js);
 }
 
 function watchComponentsAndPlugins() {
@@ -92,102 +92,105 @@ function watchComponentsAndPlugins() {
 	watch(paths.plugins, parallel(minifyPlugins, build));
 }
 
-function languagePlugins(cb) {
-	componentsPromise.then(data => {
-		const languagesMap = {};
-		const dependenciesMap = {};
-		const aliasMap = {};
-
-		/**
-		 * Tries to guess the name of a language given its id.
-		 *
-		 * From `prism-show-language.js`.
-		 *
-		 * @param {string} id The language id.
-		 * @returns {string}
-		 */
-		function guessTitle(id) {
-			if (!id) {
-				return id;
-			}
-			return (id.substring(0, 1).toUpperCase() + id.substring(1)).replace(/s(?=cript)/, 'S');
+async function languagePlugins() {
+	const data = await componentsPromise;
+	const languagesMap = {};
+	const dependenciesMap = {};
+	const aliasMap = {};
+
+	/**
+	 * Tries to guess the name of a language given its id.
+	 *
+	 * From `prism-show-language.js`.
+	 *
+	 * @param {string} id The language id.
+	 * @returns {string}
+	 */
+	function guessTitle(id) {
+		if (!id) {
+			return id;
 		}
-
-		function addLanguageTitle(key, title) {
-			if (!languagesMap[key] && guessTitle(key) !== title) {
-				languagesMap[key] = title;
-			}
+		return (id.substring(0, 1).toUpperCase() + id.substring(1)).replace(/s(?=cript)/, 'S');
+	}
+
+	/**
+	 * @param {string} key
+	 * @param {string} title
+	 */
+	function addLanguageTitle(key, title) {
+		if (!languagesMap[key] && guessTitle(key) !== title) {
+			languagesMap[key] = title;
 		}
+	}
 
-		for (const id in data.languages) {
-			if (id !== 'meta') {
-				const language = data.languages[id];
-				const title = language.displayTitle || language.title;
+	for (const id in data.languages) {
+		if (id !== 'meta') {
+			const language = data.languages[id];
+			const title = language.displayTitle || language.title;
 
-				addLanguageTitle(id, title);
+			addLanguageTitle(id, title);
 
-				for (const name in language.aliasTitles) {
-					addLanguageTitle(name, language.aliasTitles[name]);
-				}
+			for (const name in language.aliasTitles) {
+				addLanguageTitle(name, language.aliasTitles[name]);
+			}
 
-				if (language.alias) {
-					if (typeof language.alias === 'string') {
-						aliasMap[language.alias] = id;
-						addLanguageTitle(language.alias, title);
-					} else {
-						language.alias.forEach(function (alias) {
-							aliasMap[alias] = id;
-							addLanguageTitle(alias, title);
-						});
-					}
+			if (language.alias) {
+				if (typeof language.alias === 'string') {
+					aliasMap[language.alias] = id;
+					addLanguageTitle(language.alias, title);
+				} else {
+					language.alias.forEach(function (alias) {
+						aliasMap[alias] = id;
+						addLanguageTitle(alias, title);
+					});
 				}
+			}
 
-				if (language.require) {
-					dependenciesMap[id] = language.require;
-				}
+			if (language.require) {
+				dependenciesMap[id] = language.require;
 			}
 		}
-
-		function formattedStringify(json) {
-			return JSON.stringify(json, null, '\t').replace(/\n/g, '\n\t');
+	}
+
+	function formattedStringify(json) {
+		return JSON.stringify(json, null, '\t').replace(/\n/g, '\n\t');
+	}
+
+	const jsonLanguagesMap = formattedStringify(languagesMap);
+	const jsonDependenciesMap = formattedStringify(dependenciesMap);
+	const jsonAliasMap = formattedStringify(aliasMap);
+
+	const tasks = [
+		{
+			plugin: paths.showLanguagePlugin,
+			maps: { languages: jsonLanguagesMap}
+		},
+		{
+			plugin: paths.autoloaderPlugin,
+			maps: { aliases: jsonAliasMap, dependencies: jsonDependenciesMap }
 		}
+	];
 
-		const jsonLanguagesMap = formattedStringify(languagesMap);
-		const jsonDependenciesMap = formattedStringify(dependenciesMap);
-		const jsonAliasMap = formattedStringify(aliasMap);
-
-		const tasks = [
-			{
-				plugin: paths.showLanguagePlugin,
-				maps: { languages: jsonLanguagesMap}
-			},
-			{
-				plugin: paths.autoloaderPlugin,
-				maps: { aliases: jsonAliasMap, dependencies: jsonDependenciesMap }
-			}
-		];
-
-		let cpt = 0;
-		const l = tasks.length;
-		const done = () => {
-			cpt++;
-			if (cpt === l) {
-				cb && cb();
-			}
-		};
-
-		for (const task of tasks) {
-			const stream = src(task.plugin)
-				.pipe(replace(
-					/\/\*(\w+)_placeholder\[\*\/[\s\S]*?\/\*\]\*\//g,
-					(m, mapName) => `/*${mapName}_placeholder[*/${task.maps[mapName]}/*]*/`
-				))
-				.pipe(dest(task.plugin.substring(0, task.plugin.lastIndexOf('/'))));
-
-			stream.on('error', done);
-			stream.on('end', done);
-		}
-	});
+	// TODO: Use `Promise.allSettled` (https://github.com/tc39/proposal-promise-allSettled)
+	const taskResults = await Promise.all(tasks.map(task => new Promise((resolve, reject) => {
+		const stream = src(task.plugin)
+			.pipe(replace(
+				/\/\*(\w+)_placeholder\[\*\/[\s\S]*?\/\*\]\*\//g,
+				(m, mapName) => `/*${mapName}_placeholder[*/${task.maps[mapName]}/*]*/`
+			))
+			.pipe(dest(task.plugin.substring(0, task.plugin.lastIndexOf('/'))));
+
+		stream.on('error', reject);
+		stream.on('end', resolve);
+	}).then(
+		/** @type {<T>(value: T) => {status: 'fulfilled', value: T}} */ value => ({status: 'fulfilled', value}),
+		/** @type {<T>(error: T) => {status: 'rejected', reason: T}} */ error => ({status: 'rejected', reason: error}),
+	)));
+
+	const rejectedTasks = taskResults.filter(/** @return {r is {status: 'rejected', reason: any}} */ r => r.status === 'rejected');
+	if (rejectedTasks.length > 0) {
+		throw rejectedTasks.map(r => r.reason);
+	}
 }
 
 const ISSUE_RE = /#(\d+)(?![\d\]])/g;