pandoc-converter
Version:
Another wrapper for pandoc, including automatic binary download
278 lines (256 loc) • 10.6 kB
JavaScript
const os = require('os');
const cp = require('child_process');
const fs = require('fs');
const path = require('path');
const AdmZip = require("adm-zip");
/** @type {import('github-packager').GitHubReleasePackagerPlugin} */
exports.github = {
Name: __filename,
getDownloadURL: async (repository, version, defaultPlugin) => {
var platform;
var extension = 'zip';
switch (process.platform.toLowerCase()) {
case 'win32':
switch (process.arch.toLowerCase()) {
case 'x64':
platform = 'windows-x86_64';
break;
default:
platform = 'windows-i386';
break;
}
break;
case 'linux':
switch (process.arch.toLowerCase()) {
case 'x64':
platform = 'linux-amd64';
extension = 'tar.gz';
break;
default:
throw Error(`Release is not available for this architecture (platform = '${process.platform}', arch = '${process.arch}').`);
}
break;
case 'darwin':
platform = 'macOS';
break;
default:
platform = `${process.platform}-${process.arch}`;
break;
}
return `https://github.com/${repository.owner}/${repository.name}/releases/download/${version}/${repository.name}-${version}-${platform}.${extension}`;
},
/** @type {import('github-packager').GitHubReleasePackagerProcessBinaryCallback} */
processBinary: async (file, folder, defaultPlugin) => {
if (process.platform.toLowerCase() === 'linux') {
require('tar4').extract({ file: file, cwd: folder, sync: true });
} else if (file.endsWith(".zip")) {
const zip = new AdmZip(file);
zip.extractAllTo(folder, true);
} else {
await defaultPlugin.processBinary(file, folder, defaultPlugin);
}
},
/** @type {import('github-packager').GitHubReleasePackagerExecutablesCallback} */
getExecutables: async (repository, version, folder, defaultPlugin) => {
var executablePath = path.join(folder, `${repository.name}-${version}`);
return {
pandoc: {
win32: {
default: path.join(executablePath, 'pandoc.exe')
},
linux: {
x64: path.join(executablePath, 'bin', 'pandoc')
},
darwin: {
default: path.join(executablePath, 'bin', 'pandoc')
}
}
};
},
/** @type {import('github-packager').GitHubReleasePackagerPostProcessCallback} */
postProcess: async (repository, version, folder, executables, defaultPlugin) => {
var executableFile = await defaultPlugin.GetExecutable('pandoc', executables, defaultPlugin);
// #region temporary, to debug darwin
// if (['linux', 'darwin'].includes(process.platform)) {
// cp.execSync(`ls -laR ${folder}`, { stdio: 'inherit' });
// cp.execSync(`chmod 755 ${executableFile}`, { stdio: 'inherit' });
// cp.execSync(`ls -laR ${folder}`, { stdio: 'inherit' });
// }
// #endregion
if (!fs.existsSync(executableFile)) {
throw Error(`Pandoc executale '${executableFile}' could not be found.`);
} else {
var formats = {};
formats.input = cp.execSync(`"${executableFile}" --list-input-formats`).toString().trim().split(os.EOL);
formats.output = cp.execSync(`"${executableFile}" --list-output-formats`).toString().trim().split(os.EOL);
/** @type {string[]} */
formats.options = cp.execSync(`"${executableFile}" --help`).toString().trim().split(os.EOL);
formats.options = formats.options.slice(1);
formats.options.forEach((option, index) => {
var raw = option.substr(24);
var opt = raw.match(/--[A-Za-z0-9-=\[\]:|]+/); // eslint-disable-line no-useless-escape
if (opt.length === 1) {
formats.options[index] = opt[0];
} else {
console.warn(`Found ${opt.length} matches in '${option}'.`);
formats.options[index] = raw;
}
});
fs.writeFileSync(path.join(path.dirname(executableFile), 'formats.json'), JSON.stringify(formats, null, 2), { encoding: 'utf8' });
var packagePath = path.dirname(path.dirname(folder));
var libPath = path.join(packagePath, 'lib');
var typPath = path.join(libPath, 'pandoc-types.js');
var formatLines = [];
var lines = [];
var typeDefs = {};
// #region Header and Format typedefs
lines.push('/* cSpell:disable */');
lines.push('/** @module pandoc-types */');
lines.push(`/** @typedef {'${formats.input.join('\'|\'')}'} InputFormat */`);
lines.push(`/** @typedef {'${formats.output.join('\'|\'')}'} OutputFormat */`);
lines.push('');
// #endregion
// #region PandocOptions typedef
var literals = {};
lines.push('/** @typedef {object} PandocOptions');
formats.options.forEach(option => {
var literal = option.match(/^--([A-Za-z0-9-]+)/).pop();
var name = literal;
var dashes = name.match(/-./g);
if (dashes) {
dashes.forEach(dash => {
name = name.replace(dash, dash[1].toUpperCase());
});
}
literals[name] = literal;
var shortOption = option;
// Find and mark optional parameters
var paramsOptional = [];
var params = shortOption.match(/(\[=.+\]|\[:.+\])/g);
if (params) {
params.forEach(param => {
var paramShort = param.substr(1, param.length - 2);
paramsOptional.push(paramShort.substr(1));
shortOption = shortOption.replace(param, paramShort);
});
}
if (paramsOptional.length > 0) {
console.debug(`Shortened option ${option} to ${shortOption}, optional parameters are: ${paramsOptional.join(', ')}`);
}
// Find parameters and determine parameter and option types
var optionTypes = ['boolean'];
params = shortOption.match(/(=.+)/g);
if (params) {
params.forEach(param => {
var paramShort = param.substr(1);
// If param is an enum type, format and bail out
var enumParam = paramShort.split('|');
if (enumParam.length > 1) {
if (paramShort === paramShort.toUpperCase()) {
optionTypes = ['string'];
} else {
optionTypes = [`'${enumParam.join('\'|\'')}'`];
}
return;
}
// If param is a complex type, create type and bail out
var cplxParams = paramShort.split(':');
if (cplxParams.length > 1) {
var typeDef = {};
var typeDefName = 'Pandoc' + name[0].toUpperCase() + name.substr(1);
optionTypes = [typeDefName + '[]'];
cplxParams.forEach(cplxParam => {
if (paramsOptional.includes(cplxParam.toUpperCase())) {
typeDef[cplxParam.toLowerCase()] = 'string=';
} else {
typeDef[cplxParam.toLowerCase()] = 'string';
}
});
typeDefs[typeDefName] = typeDef;
return;
}
// If param isn't specified otherwise
switch (paramShort.toUpperCase()) {
case 'NUMBER':
optionTypes = ['number'];
break;
case 'FORMAT':
switch (name.toUpperCase()) {
case 'FROM':
console.debug(`Using input format type for option ${name}.`);
optionTypes = ['InputFormat'];
break;
case 'TO':
console.debug(`Using output format type for option ${name}.`);
optionTypes = ['OutputFormat'];
break;
default:
console.debug(`Using unspecific format type for option ${name}.`);
optionTypes = ['InputFormat', 'OutputFormat'];
break;
}
break;
default:
optionTypes = ['string'];
break;
}
});
}
lines.push(` * @property {${optionTypes.length > 1 ? `(${optionTypes.join('|')})` : optionTypes[0]}=} ${name} \`${option}\``);
});
lines.push(' */');
lines.push('//');
lines.push('');
// #endregion
// #region Pandoc Options Parameter typedefs
Object.keys(typeDefs).forEach(key => {
lines.push(`/** @typedef {object} ${key}`);
var typeDef = typeDefs[key];
Object.keys(typeDef).forEach(propKey => {
lines.push(` * @property {${typeDef[propKey]}} ${propKey} The ${propKey} property.`);
});
lines.push(' */');
lines.push('//');
lines.push('');
});
// #endregion
// #region Literals enum
lines.push('/** @enum {string} */');
lines.push('exports.Literals = {');
formatLines = [];
Object.keys(literals).forEach(literal => {
formatLines.push(`${literal}: '--${literals[literal]}'`);
});
lines.push(' ' + formatLines.join(`,${os.EOL} `));
lines.push('};');
lines.push('');
// #endregion
// #region InputFormats enum
lines.push('/** @enum {string} */');
lines.push('exports.InputFormats = {');
formatLines = [];
formats.input.forEach(format => {
formatLines.push(`${format}: '${format}'`);
});
lines.push(' ' + formatLines.join(`,${os.EOL} `));
lines.push('};');
lines.push('');
// #endregion
// #region OutputFormats enum
lines.push('/** @enum {string} */');
lines.push('exports.OutputFormats = {');
formatLines = [];
formats.output.forEach(format => {
formatLines.push(`${format}: '${format}'`);
});
lines.push(' ' + formatLines.join(`,${os.EOL} `));
lines.push('};');
// #endregion
fs.writeFileSync(typPath, lines.join(os.EOL) + os.EOL, { encoding: 'utf8' });
}
// There are neither platform independent executables or scripts in pandoc,
// nor do we provide anything like that here, so don't add anything to
// the bin object of the package file.
return {};
}
};