UNPKG

docutils-ts

Version:

Port of the Python Docutils library to TypeScript

282 lines (281 loc) 10.7 kB
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)); } } }