asciidoctor-chunker
Version:
Creates chunked (multi-page) HTML from Asciidoctor's single HTML file with supporting the fine-tuned splits in chapters, sections and any depth of subsections.
178 lines (169 loc) • 6.44 kB
JavaScript
/*
* This file is a part of Asciidoctor Chunker project.
* Copyright (c) 2021 Wataru Shito (@waterloo_jp)
*/
;
import Node from './Node.mjs';
/*
extract inside the bracket:
[#preamble]
[.sect0, some nodes]
[.sect1,
+-- some nodes]
+-- [.sect2
+-- some nodes]
+-- [sect3
+-- some nodes
+-- and so on...]
[.sect1, ...]
[.sect0, ...]
So #preamble and .sect0 do not go into recursively.
sect0 is for Part. sect1 is for Chapter.
sect1 (h2) -- sect5 (h6)
*/
/**
* This is the factory function that returns the function
* which processes the nodes with 'sectN' classname
* where N >= 1 recursively.
*
* This factory takes the processor callback as the argument.
* The processor callback handles the actual task on each
* visitng node.
*
* The processor function expects the following parameters:
*
* @param {object} config: The configuration object which has
* `depth` property to specify the maximum sectLevel to extract.
* The default is 1 which extracts parts and chapters.
* @param {object} config.depth The configuration to specify the
* maximum sectLevel to extract. The example format is as follows:
* ```
* depth: {
* default: 1, // the default is to extract only chapters
* 2: 4, // extracts subsubsections in chap 2
* 3: 2, // extracts sections in chap 3
* }
* ```
* @param {string} basename The basename of the target file.
* @param {Node} docRoot The root Node instance of the asciidoctor's
* single HTML source.
* @param {Node} node The current section node extracted from DOM.
* @param {boolean} isFirstPage true if this is the index.html page.
*
* By delegating the actual task to the processor callback,
* this function can be used both for creation of ID-filename
* hashtable as well as extracting and writing out the chapters
* to the html file.
*
* The returned function from this factory is to be used as
* a callback function for the content processor, i.e.
* the `processContents()` defined in `ContentProcessor.mjs`.
*
* The returned function by this factory has the following
* parameters.
*
* @param {object} config: The configuration object which has
* `depth` property to specify the maximum sectLevel to extract.
* The default is 1 which extracts parts and chapters.
* @param {object} config.depth The configuration to specify the
* maximum sectLevel to extract. The example format is as follows:
* ```
* depth: {
* default: 1, // the default is to extract only chapters
* 2: 4, // extracts subsubsections in chap 2
* 3: 2, // extracts sections in chap 3
* }
* ```
* @param {Node} docRoot The root Node instance of the asciidoctor's
* single HTML source.
* @param {Node} node The current section node extracted from DOM.
* @param {number} thisSectLevel the current node's section level
* where chapter is level 1, section is level 2, and so on.
* @param {string} fnamePrefix The filename prefix.
* @param {(
* fnamePrefix: string,
* thisSecLevel: number,
* sectionNumber: number
* ) => string} basenameMaker The function that generates the
* basename for output html.
* @param {number} sectionNumber The section number in the
* current section level.
* @param {boolean} isFirstPage true if this is the index.html page.
*/
const getChapterProcessor = (processor) => {
// this inner function will be invoked by ContentProcessor
const _processChapters = (config, docRoot, node, thisSectLevel, fnamePrefix, basenameMaker, sectionNumber, isFirstPage) => {
const maxLevel = config.depth[sectionNumber] || config.depth.default;
const basename = isFirstPage ? 'index' : basenameMaker(fnamePrefix, thisSectLevel, sectionNumber);
// case with no extraction
if (maxLevel === thisSectLevel) {
processor(config, basename, docRoot, node, isFirstPage)
return;
}
const childSelector = `div.sect${thisSectLevel+1}`;
// extract myself
processor(config, basename, docRoot,
node.clone().remove$(childSelector), isFirstPage);
// get children nodes
const children = node.find(childSelector);
if (children.length === 0) {
return;
}
// go into children nodes to extract.
// make sure to return to make it tail call to minimize the stack
return children.each((ele, i) =>
_processChapters(config, docRoot,
ele, // the Node instance
thisSectLevel + 1,
basename,
basenameMaker,
i + 1,
false)); // isFirstPage = false
};
return _processChapters;
};
/**
* This is the factory function that returns the function
* that extracts the chapter contents and pass them to
* the printer function.
*
* This function utilizes `getChapterProcessor(processor)`
* to embed the printer function and the document maker function
* as a processor. So the returned function expects the
* same parameters as described in `getChapterProcessor()`.
*
*
* @param {(fnamePrefix: string, dom: Node) => void} printer
* The callback function that actually handles the extracted
* chapter contents. The printer function takes the basename
* of the output file and the Node instance of the html to
* print out to the file.
* @param {Node} container The dom of the root which has
* empty `div#content` and is used as a skelton.
* The `printer` uses the cloned DOM to write out the
* chunked html.
* @param {(
* fnamePrefix: string,
* thisSecLevel: number,
* sectionNumber: number
* ) => string} basenameMaker `NOT IMPLEMENTED` The function that generates the
* basename for output html.
* @param {(
* config: object,
* basename: string,
* container: Node,
* contents: Node*
* ) => {Node}} documentMaker The function that clones the
* given container and appends nodes under div#content.
*/
const getChapterExtractor = (printer, container, documentMaker) =>
// Pass the processor function that is invoked by _processChapters().
// The processor function is the printer to write out the html file.
getChapterProcessor(
// docRoot is the DOM of single html source
(config, basename, docRoot, node, isFirstPage) => {
printer(basename,
// container is the DOM root of which div#content is empty
documentMaker(config, basename, container, node));
});
export { getChapterExtractor, getChapterProcessor };