docutils-ts
Version:
Port of the Python Docutils library to TypeScript
282 lines (281 loc) • 10.7 kB
JavaScript
import { ApplicationError, InvalidStateError } from "./exceptions.js";
import { OptionParser } from "./frontend.js";
import * as readers from './readers.js';
import * as writers from './writers.js';
import SettingsSpec from './settingsSpec.js';
import FileInput from "./io/fileInput.js";
import FileOutput from "./io/fileOutput.js";
/*interface InputConstructor {
new (): Input;
}
*/
/**
* A facade encapsulating the high-level logic of a Docutils system.
*/
export class Publisher {
get document() {
return this._document;
}
constructor(args) {
this.debug = false;
const { reader, parser, writer, source, destination, settings, debugFn, sourceClass, destinationClass, logger, } = args;
this.logger = logger;
if (debugFn !== undefined) {
this.debugFn = debugFn;
}
else {
this.debugFn = this.logger.debug.bind(this.logger);
}
this._document = undefined;
this.reader = reader;
this.parser = parser;
this.writer = writer;
this.source = source;
this.destination = destination;
this.sourceClass = sourceClass || FileInput;
this.destinationClass = destinationClass || FileOutput;
this.settings = settings;
}
setReader(readerName, parser, parserName) {
if (readerName === undefined) {
return;
}
const ReaderClass = readers.getReaderClass(readerName);
this.reader = new ReaderClass({
parser, parserName, debug: this.debug, debugFn: this.debugFn,
logger: this.logger,
});
if (this.reader !== undefined) {
this.parser = this.reader.parser;
}
}
setWriter(writerName) {
if (writerName === undefined) {
return;
}
const WriterClass = writers.getWriterClass(writerName);
/* not setting document here, the write method takes it, which
* is confusing */
this.writer = new WriterClass({ logger: this.logger });
}
setComponents(readerName, parserName, writerName) {
if (!this.reader) {
this.setReader(readerName, this.parser, parserName);
}
if (!this.parser && this.reader !== undefined) {
if (!this.reader.parser && parserName !== undefined) {
this.reader.setParser(parserName);
}
this.parser = this.reader.parser;
}
if (!this.writer) {
this.setWriter(writerName);
}
}
setupOptionParser(args) {
const { usage, description, settingsSpec, configSection, defaults } = args;
let settingsSpec2 = settingsSpec;
if (configSection) {
if (!settingsSpec2) {
settingsSpec2 = new SettingsSpec();
}
settingsSpec2.configSection = configSection;
const parts = configSection.split(' '); //fixme check split
if (parts.length > 1 && parts[parts.length - 1] === 'application') {
settingsSpec2.configSectionDependencies = ['applications'];
}
}
settingsSpec2 = settingsSpec2;
if (!this.parser) {
throw new ApplicationError('no parser');
}
if (!this.reader) {
throw new ApplicationError('no reader');
}
if (!this.writer) {
throw new ApplicationError('no writer');
}
if (!settingsSpec2) {
//throw new Error('no settingsSpec');
}
const components = [this.parser, this.reader, this.writer];
if (settingsSpec2 !== undefined) {
components.push(settingsSpec2);
}
const oArgs = { logger: this.logger, components, defaults, readConfigFiles: true, usage, description };
const optionParser = new OptionParser(oArgs);
return optionParser;
}
process_programmatic_settings(settings_spec, settings_overrides, configSection) {
if (this.settings == null) {
const defaults = settings_overrides ? { ...settings_overrides } : {};
// Propagate exceptions by default when used programmatically:
if (!defaults.hasOwnProperty('traceback')) {
defaults['traceback'] = true;
}
this.get_settings({
settings_spec: settings_spec,
configSection: configSection,
...defaults
});
}
}
get_settings(options) {
const { usage = undefined, description = undefined, settingsSpec = undefined, configSection = undefined, ...defaults } = options;
const option_parser = this.setupOptionParser({
usage: usage,
description: description,
settingsSpec: settingsSpec,
configSection: configSection,
...defaults
});
this.settings = option_parser.getDefaultValues();
return this.settings;
}
processCommandLine(args) {
try {
const argParser = this.setupOptionParser({
usage: args.usage,
description: args.description,
});
let argv = args.argv;
if (argv === undefined) {
argv = process.argv.slice(2);
}
this.logger.silly('calling argParser.parseKnownArgs', { argv });
const [settings, restArgs] = argParser.parseKnownArgs(argv);
this.settings = argParser.checkValues(settings, restArgs);
}
catch (error) {
if (error instanceof Error) {
{
console.log(error.stack);
console.log(error.message);
}
throw error;
}
}
}
setIO(sourcePath, destinationPath) {
this.logger.silly('setIO');
if (this.source === undefined) {
this.setSource({ sourcePath });
}
if (this.destination === undefined) {
this.setDestination({ destinationPath });
}
this.logger.silly('departing setIO');
}
setSource(args) {
this.logger.silly('setSource');
let sourcePath = args.sourcePath;
let source = args.source;
if (typeof sourcePath === 'undefined') {
sourcePath = this.settings._source;
}
else {
this.settings._source = sourcePath;
}
try {
const SourceClass = this.sourceClass;
let inputEncoding = this.settings.inputEncoding;
if (SourceClass !== undefined) {
this.source = new SourceClass({
source,
sourcePath,
encoding: inputEncoding,
logger: this.logger,
});
}
}
catch (error) {
this.logger.error(error);
if (error instanceof Error && this.sourceClass) {
throw new ApplicationError(`Unable to instantiate Source class ${this.sourceClass.constructor.name}: ${error.message}`, { error });
}
}
}
setDestination(args) {
this.logger.silly('setDestination');
try {
let destinationPath = args.destinationPath;
let destination = args.destination;
if (destinationPath === undefined) {
destinationPath = this.settings._destination;
}
else {
this.settings._destination = destinationPath;
}
const DestinationClass = this.destinationClass;
const outputEncoding = this.settings.outputEncoding;
let outputEncodingErrorHandler = this.settings.outputEncodingErrorHandler;
this.destination = new DestinationClass({
logger: this.logger,
destination: destination,
destinationPath: destinationPath,
encoding: outputEncoding,
errorHandler: outputEncodingErrorHandler,
});
}
catch (error) {
console.log(error.message);
this.logger.error(`Got error ${error.message}`, { stack: error.stack });
}
}
applyTransforms() {
this.logger.silly('Publisher.applyTransforms');
const document1 = this.document;
if (document1 === undefined) {
throw new InvalidStateError('Document undefined');
}
if (this.source === undefined ||
this.reader === undefined ||
this.reader.parser === undefined ||
this.writer === undefined
// || this.destination === undefined
) {
throw new InvalidStateError('Component undefined');
}
document1.transformer.populateFromComponents(this.source, this.reader, this.reader.parser, this.writer, this.destination);
document1.transformer.applyTransforms();
}
async publish(args) {
this.logger.silly('Publisher.publish');
const { argv, usage, description, settingsSpec, settingsOverrides, configSection, enableExitStatus, } = args;
if (this.settings === undefined) {
this.processCommandLine({
argv, usage, description, settingsSpec, configSection, settingsOverrides,
});
}
this.setIO();
if (this.reader === undefined) {
throw new ApplicationError('Need defined reader with "read" method');
}
if (this.writer === undefined || this.source === undefined || this.parser === undefined) {
throw new InvalidStateError('need Writer and source');
}
const writer = this.writer;
if (this.settings === undefined) {
throw new InvalidStateError('need serttings');
}
this.logger.silly('calling read');
this._document = await this.reader.read(this.source, this.parser, this.settings);
if (this._document === undefined) {
throw new InvalidStateError('need document');
}
// If file output is used, the destination is set in the writer. (file or stdout)
// if (this.destination === undefined) {
// throw new InvalidStateError('need destination');
// }
this.applyTransforms();
const output = writer.write(this._document, this.destination);
writer.assembleParts();
this.debuggingDumps();
return output;
}
debuggingDumps() {
if (this.settings.dumpSettings) {
process.stderr.write(JSON.stringify(this.settings, null, 4));
}
}
}