presentations/highlight-js/tools/utility.js

169 lines
4.3 KiB
JavaScript

'use strict';
let _ = require('lodash');
let bluebird = require('bluebird');
let glob = bluebird.promisify(require('glob'));
let path = require('path');
let Queue = require('gear').Queue;
let regex = {},
headerRegex = /^\s*\/\*((.|\r?\n)*?)\*/;
const REPLACES = {
'case_insensitive': 'cI',
'lexemes': 'l',
'contains': 'c',
'keywords': 'k',
'subLanguage': 'sL',
'className': 'cN',
'begin': 'b',
'beginKeywords': 'bK',
'end': 'e',
'endsWithParent': 'eW',
'illegal': 'i',
'excludeBegin': 'eB',
'excludeEnd': 'eE',
'returnBegin': 'rB',
'returnEnd': 'rE',
'relevance': 'r',
'variants': 'v',
'IDENT_RE': 'IR',
'UNDERSCORE_IDENT_RE': 'UIR',
'NUMBER_RE': 'NR',
'C_NUMBER_RE': 'CNR',
'BINARY_NUMBER_RE': 'BNR',
'RE_STARTERS_RE': 'RSR',
'BACKSLASH_ESCAPE': 'BE',
'APOS_STRING_MODE': 'ASM',
'QUOTE_STRING_MODE': 'QSM',
'PHRASAL_WORDS_MODE': 'PWM',
'C_LINE_COMMENT_MODE': 'CLCM',
'C_BLOCK_COMMENT_MODE': 'CBCM',
'HASH_COMMENT_MODE': 'HCM',
'NUMBER_MODE': 'NM',
'C_NUMBER_MODE': 'CNM',
'BINARY_NUMBER_MODE': 'BNM',
'CSS_NUMBER_MODE': 'CSSNM',
'REGEXP_MODE': 'RM',
'TITLE_MODE': 'TM',
'UNDERSCORE_TITLE_MODE': 'UTM',
'COMMENT': 'C',
'beginRe': 'bR',
'endRe': 'eR',
'illegalRe': 'iR',
'lexemesRe': 'lR',
'terminators': 't',
'terminator_end': 'tE'
};
regex.replaces = new RegExp(
`\\b(${Object.keys(REPLACES).join('|')})\\b`, 'g');
regex.classname = /(block|parentNode)\.cN/g;
regex.header = /^\s*(\/\*((.|\r?\n)*?)\*\/)?\s*/;
function replace(from, to) {
return { regex: from, replace: to };
}
function replaceClassNames(match) {
return REPLACES[match];
}
// All meta data, for each language definition, it store within the headers
// of each file in `src/languages`. `parseHeader` extracts that data and
// turns it into a useful object -- mainly for categories and what language
// this definition requires.
function parseHeader(content) {
let headers,
match = content.match(headerRegex);
if (!match) {
return null;
}
headers = _.compact(match[1].split('\n'));
return _.reduce(headers, function(result, header) {
let keyVal = header.trim().split(': '),
key = keyVal[0],
value = keyVal[1] || '';
if(key !== 'Description' && key !== 'Language') {
value = value.split(/\s*,\s*/);
}
result[key] = value;
return result;
}, {});
}
function filterByQualifiers(blob, languages, categories) {
if(_.isEmpty(languages) && _.isEmpty(categories)) return true;
let language = path.basename(blob.name, '.js'),
fileInfo = parseHeader(blob.result),
containsCategory = _.partial(_.includes, categories);
if(!fileInfo) return false;
let fileCategories = fileInfo.Category || [];
return _.includes(languages, language) ||
_.some(fileCategories, containsCategory);
}
// For the filter task in `tools/tasks.js`, this function will look for
// categories and languages specificed from the CLI.
function buildFilterCallback(qualifiers) {
const result = _.partition(qualifiers, { 0: ':' }),
languages = result[1],
categories = _.map(result[0], category => category.slice(1));
return blob => filterByQualifiers(blob, languages, categories);
}
function globDefaults(pattern, encoding) {
encoding = encoding || 'utf8';
// The limit option is a fix for issue #636 when the build script would
// EMFILE error for those systems who had a limit of open files per
// process.
//
// <https://github.com/highlightjs/highlight.js/issues/636>
return { pattern: pattern, limit: 50, encoding: encoding };
}
function getStyleNames() {
let stylesDir = 'src/styles/',
options = { ignore: `${stylesDir}default.css` };
return glob(`${stylesDir}*.css`, options)
.map(function(style) {
let basename = path.basename(style, '.css'),
name = _.startCase(basename),
pathName = path.relative('src', style);
return { path: pathName, name: name };
});
}
function toQueue(tasks, registry) {
return _.map(tasks, task => new Queue({ registry }).tasks(task));
}
module.exports = {
buildFilterCallback: buildFilterCallback,
getStyleNames: getStyleNames,
glob: globDefaults,
parseHeader: parseHeader,
regex: regex,
replace: replace,
replaceClassNames: replaceClassNames,
toQueue: toQueue
};