recital / exporters / html / src / index.ts
index.ts
Raw
/**
 * Copyright (c) 2022 Amorphous
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

import { parseFlat } from '@a-morphous/recital'
import { convertWikiLinkToMd } from '@a-morphous/recital-ext-common-commands'
import smartypants from 'smartypants'

import { micromark } from 'micromark'

type StageHTMLOptions = {
	pureMarkdown?: boolean
	sceneTag?: string
	blockTag?: string
	sceneSeparator?: string
}

export const stageToHTML = (stageScriptString: string, opts: StageHTMLOptions = {}) => {
	let finalString = ''
	const flatScenes = opts?.pureMarkdown
		? parseFlat(stageScriptString)
		: convertWikiLinkToMd(parseFlat(stageScriptString))

	const defaultOpts = {
		sceneTag: 'section',
		blockTag: 'div',
		sceneSeparator: '',
	}

	opts = opts || {}
	opts = { ...defaultOpts, ...opts }

	for (let i = 0; i < flatScenes.length; i++) {
		const token = flatScenes[i]
		switch (token.type) {
			case 'scene':
				if (opts.sceneTag) {
					finalString += `<${opts.sceneTag}`
					if (token.id) {
						finalString += ' id="' + token.id + '"'
					}
					if (token.classes && token.classes.length) {
						finalString += ` class="${token.classes.join(' ')}"`
					}
					finalString += '>\n'
				}
				break
			case 'endScene':
				if (opts.sceneTag) {
					finalString += `</${opts.sceneTag}>\n`
				}
				if (opts.sceneSeparator && i < flatScenes.length - 1) {
					finalString += opts.sceneSeparator + '\n'
				}
				break
			case 'fragment':
				if (opts.blockTag) {
					finalString += `<${opts.blockTag}`
					if (token.id) {
						finalString += ' id="' + token.id + '"'
					}
					if (token.classes && token.classes.length) {
						finalString += ` class="${token.classes.join(' ')}"`
					}
					finalString += '>\n'
				}
				break
			case 'endFragment':
				if (opts.blockTag) {
					finalString += `</${opts.blockTag}>\n`
				}
				break
			case 'paragraph':
				const smartText = smartypants(token.text, 'qie')
				let renderedText = micromark(smartText.replace(/\-\-/gm, '\u2014')) + '\n'

				// add any meta...
				if (token.primary) {
					// we have to wrap it around in a div with those classes.
					const wrappedText = `<div${token.id ? ` id="${token.id}"` : ''}${
						token.classes ? ` class="${token.classes.join(' ')}"` : ''
					}>\n${renderedText}</div>\n`
					renderedText = wrappedText
				}

				finalString += renderedText
				break
			case 'rawText':
				let processedText = ''
				if (token.startTag) {
					processedText += token.startTag + '\n'
				}
				processedText += token.text
				if (token.endTag) {
					processedText += token.endTag + '\n'
				}

				finalString += micromark(processedText)
				break
		}
	}

	return finalString.trim()
}