akasharender
Version:
Rendering support for generating static HTML websites or EPUB eBooks
242 lines (228 loc) • 7.23 kB
text/typescript
/**
*
* Copyright 2014-2025 David Herron
*
* This file is part of AkashaCMS (http://akashacms.com/).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { promises as fs } from 'fs';
import path from 'path';
import fastq from 'fastq';
import * as data from '../data.js';
import {
documentsCache, assetsCache, layoutsCache, partialsCache
} from './file-cache-sqlite.js';
async function renderVPath(config, info) {
await data.remove(info.mountPoint, info.vpath);
let result = await config.akasha.renderPath(config, info.vpath);
console.log(result);
}
/**
* Search for all documents that use the layout which changed, and
* rerender them. Obviously this function must only be called in
* the handlers for 'change' and 'add' in layouts.
*
* @param config
* @param info
*/
async function renderForLayout(config, info) {
const docs = await documentsCache.search({
layouts: [ info.vpath ]
});
const queue = fastq.promise(async function(item) {
try {
await data.remove(item.mountPoint, item.vpath);
let result = await config.akasha.renderPath(config, item.vpath);
console.log(result);
return "ok";
} catch (err) {
throw new Error(`renderForLayout FAIL to render ${item.vpath} because ${err.stack}`);
}
}, 10);
const waitFor = [];
for (let doc of docs) {
waitFor.push(queue.push(doc));
}
if (waitFor.length > 0) await Promise.all(waitFor);
console.log(`FINISH rendering ${waitFor.length} documents from changing template ${info.vpath}`);
}
async function rebuild(config) {
await data.removeAll();
let results = await config.akasha.render(config);
for (let result of results) {
if (result.error) {
console.error(result.error);
} else {
console.log(result.result);
}
}
return results;
}
async function unlinkVPath(config, info) {
if (!config.renderDestination) {
// console.log(`UNLINK ${collection} could not perform unlink (no renderDestination) for `, info);
return;
}
const renderer = config.findRendererPath(info.vpath);
let renderPath;
if (renderer) {
renderPath = renderer.filePath(info.vpath);
} else {
renderPath = info.vpath;
}
await fs.unlink(path.join(config.renderDestination, renderPath));
console.log(`UNLINK ${renderPath}`);
}
export async function watchman(config) {
documentsCache
.on('change', async (collection, info) => {
try {
await renderVPath(config, info);
} catch (e) {
documentsCache.emit('error', {
code: 'change',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('add', async (collection, info) => {
try {
await renderVPath(config, info);
} catch (e) {
documentsCache.emit('error', {
code: 'add',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('unlink', async (collection, info) => {
try {
// console.log(`UNLINK ${config.renderDestination} ${info.renderPath}`);
await unlinkVPath(config, info);
} catch (e) {
documentsCache.emit('error', {
code: 'unlink',
collection: collection,
vpath: info.vpath,
error: e
});
}
});
console.log('... watching documents');
assetsCache
.on('change', async (collection, info) => {
try {
const destFN = path.join(config.renderDestination, info.renderPath);
await fs.copyFile(info.fspath, destFN);
console.log(`CHANGE ${info.vpath} COPY==> ${info.renderPath}`);
} catch (e) {
assetsCache.emit('error', {
code: 'change',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('add', async (collection, info) => {
try {
const destFN = path.join(config.renderDestination, info.renderPath);
await fs.copyFile(info.fspath, destFN);
console.log(`ADD ${info.vpath} COPY==> ${info.renderPath}`);
} catch (e) {
assetsCache.emit('error', {
code: 'add',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('unlink', async (collection, info) => {
try {
await fs.unlink(path.join(config.renderDestination, info.renderPath));
console.log(`UNLINK ${info.renderPath}`);
} catch (e) {
assetsCache.emit('error', {
code: 'unlink',
collection: collection,
vpath: info.vpath,
error: e
});
}
});
console.log('... watching assets');
layoutsCache
.on('change', async (collection, info) => {
try {
await renderForLayout(config, info);
} catch (e) {
layoutsCache.emit('error', {
code: 'change',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('add', async (collection, info) => {
try {
await renderForLayout(config, info);
} catch (e) {
layoutsCache.emit('error', {
code: 'add',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('unlink', (collection, info) => {
// Nothing to do
});
console.log('... watching layouts');
partialsCache
.on('change', async (collection, info) => {
try {
await rebuild(config);
} catch (e) {
partialsCache.emit('error', {
code: 'change',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('add', async (collection, info) => {
try {
await rebuild(config);
} catch (e) {
partialsCache.emit('error', {
code: 'add',
collection: collection,
vpath: info.vpath,
error: e
});
}
})
.on('unlink', (collection, info) => {
// Nothing to do
});
console.log('... watching partials');
}