@bookbox/core
Version:
Bookbox — e-book format
161 lines (160 loc) • 6.09 kB
JavaScript
import { getBookLinkedSchema, getSchemaFromLinkedList } from './linkedSchema';
import { getBookMeta } from './meta';
import { getStore } from './store';
import { flatList, getByPath } from '../utils';
import { calculateResources, getActualResourceMap } from './resources';
import { getIterableBook } from './iterableBook';
import { calculateCounters } from './counters';
/**
* Добавление ключей по умолчанию всем элементам
*/
function addKeysToSchema(schema, keys = new Map()) {
const bumpKey = (name) => {
var _a;
keys.set(name, ((_a = keys.get(name)) !== null && _a !== void 0 ? _a : 0) + 1);
};
for (const item of schema) {
if (typeof item === 'string') {
continue;
}
if (item.props.key === undefined) {
bumpKey(item.name);
// TODO: разобраться, почему мутация затрагивает все элементы
item.props = Object.assign(Object.assign({}, item.props), { key: `${item.name}-${keys.get(item.name)}` });
}
addKeysToSchema(item.children, keys);
}
}
const prepareBookElem = {
code: item => {
if ((item.props.block || item.props.position !== 'inline') &&
item.children.length === 1 &&
typeof item.children[0] === 'string') {
item.children[0] = item.children[0].trim();
}
},
};
function prepareSchema(schema) {
for (const item of getIterableBook(schema)) {
if (typeof item === 'string')
continue;
const { name } = item;
if (name in prepareBookElem) {
prepareBookElem[name](item);
}
}
}
export function createBook({ schema: originalSchema, builder: builderParams, externalBuilder, resourceOptions, }) {
let schema = JSON.parse(JSON.stringify(originalSchema));
const builder = createBookBuilder(builderParams);
addKeysToSchema(schema);
calculateCounters(schema);
calculateResources(schema, getActualResourceMap({ schema, resourceOptions }));
prepareSchema(schema);
const linkedSchema = getBookLinkedSchema(schema, true);
const tokensSchema = getSchemaFromLinkedList(linkedSchema.tree);
const getBuild = (currentStore) => (localSchema) => builder({ schema: localSchema, store: currentStore, externalBuilder, build: getBuild(currentStore) });
const store = getStore({ builder, schema, externalBuilder, getBuild });
const build = getBuild(store);
const meta = getBookMeta({ schema, store, builder, build });
schema = tokensSchema;
return {
tokens: builder({ schema, store, externalBuilder, build }),
meta,
store,
};
}
const synteticElementNameSet = new Set(['text', 'page', 'error', 'empty']);
const emptyElementNameSet = new Set(['resource']);
const prepareElementNameSet = new Set(['code']);
export function createBookBuilder({ elements, synteticElements, }) {
const builder = ({ schema, store, externalBuilder, parents = [], build }) => {
const getItemBuilder = store => item => {
// leaf case
if (typeof item === 'string') {
return synteticElements.text({ raw: item, key: '' })({
children: [],
store,
parents,
build,
});
}
// choose name
let { name } = item;
if (emptyElementNameSet.has(name)) {
name = 'empty';
}
// choose namespace
let targetElements = elements;
if (synteticElementNameSet.has(name)) {
targetElements = synteticElements;
}
let elemBuilder;
if (name === 'external') {
if (externalBuilder) {
const { scope = 'custom', name: extName = 'local' } = item.props;
elemBuilder = getByPath([`${scope}`, `${extName}`], externalBuilder);
}
// default external builder
if (typeof elemBuilder !== 'function') {
elemBuilder = getByPath([name], elements);
}
}
else {
const path = name.split('.');
elemBuilder = getByPath(path, targetElements);
}
// error
const getError = (children, error) => {
elemBuilder = synteticElements.error;
return synteticElements.error({
props: item.props,
name: item.name,
error: error !== null && error !== void 0 ? error : 'error',
})({
children,
store,
parents,
build,
});
};
// render children
let childTokens = [];
try {
childTokens = builder({
schema: item.children,
store,
externalBuilder,
parents: [...parents, `${item.props.key}`],
build,
});
}
catch (e) {
console.error(e);
return getError([], String(e));
}
if (typeof elemBuilder !== 'function') {
return getError(childTokens, 'unknown element');
}
// ok
try {
return elemBuilder(item.props)({
children: childTokens,
store,
parents,
build,
});
}
catch (e) {
console.error(e);
return getError([], String(e));
}
};
const itemBuilder = getItemBuilder(store);
return flatList(schema.map(itemBuilder));
};
return builder;
}
export function createBookExternalBuilder({ element }) {
return { externalBuilder: element };
}