akasharender
Version:
Rendering support for generating static HTML websites or EPUB eBooks
912 lines (910 loc) • 133 kB
JavaScript
#!/usr/bin/env node
/**
*
* 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.
*/
'use strict';
import { program } from 'commander';
import ghpages from 'gh-pages';
import fs from 'node:fs';
import { promises as fsp } from 'node:fs';
import path from 'node:path';
import util from 'node:util';
import * as data from './data.js';
import * as YAML from 'js-yaml';
import { refactorTag } from './refactor-tags.js';
import { SitemapValidator } from './sitemap-validator.js';
process.title = 'akasharender';
program.version('0.9.5');
program
.command('copy-assets <configFN>')
.description('Copy assets into output directory')
.action(async (configFN) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
await config.copyAssets();
await akasha.closeCaches();
}
catch (e) {
console.error(`copy-assets command ERRORED ${e.stack}`);
}
});
program
.command('document <configFN> <documentFN>')
.description('Show information about a document')
.action(async (configFN, documentFN) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const documents = akasha.filecache.documentsCache;
const doc = await documents.find(documentFN);
// data: ${doc.data}
// text: ${doc.text}
console.log(`
docpath: ${doc.vpath}
fspath: ${doc.fspath}
renderer: ${util.inspect(config.findRendererPath(doc.vpath))}
renderpath: ${doc.renderPath}
mounted: ${doc.mounted}
mountPoint: ${doc.mountPoint}
metadata: ${util.inspect(doc.metadata)}
`);
await akasha.closeCaches();
}
catch (e) {
console.error(`document command ERRORED ${e.stack}`);
}
});
function formatResult(result) {
return `
${result.renderFormat} ${result.vpath} ==> ${result.renderPath}
FIRST ${result.renderFirstElapsed} LAYOUT ${result.renderLayoutElapsed} MAHA ${result.renderMahaElapsed} TOTAL ${result.renderTotalElapsed}`;
}
program
.command('render-document <configFN> <documentFN>')
.description('Render a document into output directory')
.option('--perf-data-dir <dataDir>', 'Directory for output of Mahabhuta performance measurements')
.action(async (configFN, documentFN, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
if (typeof cmdObj?.perfDataDir === 'string') {
config.perfDataDir = cmdObj.perfDataDir;
}
let akasha = config.akasha;
await akasha.setup(config);
await data.removeAll();
// console.log(`render-document before renderPath ${documentFN}`);
let result = await akasha.renderPath2(config, documentFN);
// console.log(result);
console.log(formatResult(result));
if (Array.isArray(result.errors)
&& result.errors.length >= 1) {
for (const error of result.errors) {
console.log(error.stack);
}
}
await akasha.closeCaches();
}
catch (e) {
console.error(`render-document command ERRORED`, e);
}
});
program
.command('render <configFN>')
.description('Render a site into output directory')
.option('--quiet', 'Do not print the rendering report')
.option('--copy-assets', 'First, copy the assets')
.option('--results-to <resultFile>', 'Store the results into the named file')
.option('--perfresults <perfResultsFile>', 'Store the time to render each document')
.option('--caching-timeout <timeout>', 'The time, in miliseconds, to honor entries in the search cache')
.action(async (configFN, cmdObj) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
await data.removeAll();
if (cmdObj.copyAssets) {
await config.copyAssets();
}
if (typeof cmdObj.cachingTimeout === 'string') {
config.setCachingTimeout(Number.parseInt(cmdObj.cachingTimeout));
}
let results = await akasha.render2(config);
if (!cmdObj.quiet) {
for (let result of results) {
// TODO --- if AKASHARENDER_TRACE_RENDER then output tracing data
// TODO --- also set process.env.GLOBFS_TRACE=1
// console.log(result)
console.log(formatResult(result));
if (Array.isArray(result.errors)
&& result.errors.length >= 1) {
for (const error of result.errors) {
console.log(error.stack);
}
}
}
}
if (cmdObj.resultsTo) {
const output = fs.createWriteStream(cmdObj.resultsTo);
for (let result of results) {
output.write(formatResult(result));
if (Array.isArray(result.errors)
&& result.errors.length >= 1) {
for (const error of result.errors) {
output.write(error.stack);
}
}
}
output.close();
}
if (cmdObj.perfresults) {
const reports = [];
for (let result of results) {
const report = {
renderedPath: result.vpath,
format: result.renderFormat,
time: result.renderStart,
first: result.renderFirstElapsed,
second: result.renderLayoutElapsed,
mahabhuta: result.renderMahaElapsed,
rendered: result.renderTotalElapsed
};
reports.push(report);
}
fsp.writeFile(cmdObj.perfresults, JSON.stringify(reports, undefined, 4), 'utf-8');
}
await akasha.closeCaches();
}
catch (e) {
console.error(`render command ERRORED ${e.stack}`);
}
});
program
.command('gh-pages-publish <configFN>')
.description('Publish a site using Github Pages. Takes the rendering destination, adds it into a branch, and pushes that to Github')
.option('-b, --branch <branchName>', 'The branch to use for publishing to Github')
.option('-r, --repo <repoURL>', 'The repository URL to use if it must differ from the URL of the local directory')
.option('--remote <remoteName>', 'The Git remote name to use if it must differ from "origin"')
.option('--tag <tag>', 'Any tag to add when pushing to Github')
.option('--message <message>', 'Any Git commit message')
.option('--username <username>', 'Github user name to use')
.option('--email <email>', 'Github user email to use')
.option('--nopush', 'Do not push to Github, only commit')
.option('--cname <domain>', 'Write out a CNAME file with the domain name')
.action(async (configFN, cmdObj) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
let options = {
dotfiles: true
};
if (cmdObj.branch) {
options.branch = cmdObj.branch;
}
if (cmdObj.repoURL) {
options.repo = cmdObj.repoURL;
}
if (cmdObj.remote) {
options.remote = cmdObj.remote;
}
if (cmdObj.cname) {
options.cname = cmdObj.cname;
}
if (cmdObj.tag) {
options.tag = cmdObj.tag;
}
if (cmdObj.message) {
options.message = cmdObj.message;
}
if (cmdObj.username || cmdObj.email) {
options.user = {};
}
if (cmdObj.username) {
options.user.name = cmdObj.username;
}
if (cmdObj.email) {
options.user.email = cmdObj.email;
}
if (cmdObj.nopush) {
options.push = false;
}
options.nojekyll = true;
options.dotfiles = true;
// console.log(`gh-pages-publish options ${config.renderDestination} cmdObj ${util.inspect(cmdObj)} options ${util.inspect(options)}`);
ghpages.publish(config.renderDestination, options, function (err) {
if (err)
console.error(err);
else
console.log('OK');
});
}
catch (e) {
console.error(`gh-pages-publish command ERRORED ${e.stack}`);
}
});
program
.command('config <configFN>')
.description('Print a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
console.log(config);
}
catch (e) {
console.error(`config command ERRORED ${e.stack}`);
}
});
program
.command('plugins <configFN>')
.description('List the plugins')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
console.log(config.plugins);
}
catch (e) {
console.error(`config command ERRORED ${e.stack}`);
}
});
program
.command('docdirs <configFN>')
.description('List the documents directories in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(config.documentDirs);
await akasha.closeCaches();
}
catch (e) {
console.error(`docdirs command ERRORED ${e.stack}`);
}
});
program
.command('assetdirs <configFN>')
.description('List the assets directories in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(config.assetDirs);
await akasha.closeCaches();
}
catch (e) {
console.error(`assetdirs command ERRORED ${e.stack}`);
}
});
program
.command('partialdirs <configFN>')
.description('List the partials directories in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(config.partialsDirs);
await akasha.closeCaches();
}
catch (e) {
console.error(`partialdirs command ERRORED ${e.stack}`);
}
});
program
.command('layoutsdirs <configFN>')
.description('List the layouts directories in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(config.layoutDirs);
await akasha.closeCaches();
}
catch (e) {
console.error(`layoutsdirs command ERRORED ${e.stack}`);
}
});
program
.command('documents <configFN> [rootPath]')
.description('List the documents in a site configuration')
.action(async (configFN, rootPath) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache.documentsCache.paths(rootPath));
await akasha.closeCaches();
}
catch (e) {
console.error(`documents command ERRORED ${e.stack}`);
}
});
program
.command('docs-set-dates <configFN>')
.description('Set the aTime and mTime for documents in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(akasha.filecache.documentsCache.setTimes());
await akasha.closeCaches();
}
catch (e) {
console.error(`docs-set-dates command ERRORED ${e.stack}`);
}
});
program
.command('docinfo <configFN> <docFN>')
.description('Show information about a document in a site configuration')
.action(async (configFN, docFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const docinfo = await akasha.filecache.documentsCache.find(docFN);
console.log(`docFN ${docFN} `, docinfo);
await akasha.closeCaches();
}
catch (e) {
console.error(`docinfo command ERRORED ${e.stack}`);
}
});
program
.command('index-files <configFN> [rootPath]')
.description('List the index pages (index.html) under the given directory')
.action(async (configFN, rootPath) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const docinfo = await akasha.filecache.documentsCache.indexFiles(rootPath);
console.log(`indexes ${rootPath} `, docinfo.map(index => {
return {
vpath: index.vpath,
renderPath: index.renderPath,
mountPoint: index.mountPoint,
pathInMounted: index.pathInMounted,
dirname: index.dirname
};
}));
await akasha.closeCaches();
}
catch (e) {
console.error(`index-files command ERRORED ${e.stack}`);
}
});
program
.command('index-chain <configFN> startPath')
.description('List the index chain starting from the path')
.action(async (configFN, startPath) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const docinfo = await akasha.filecache.documentsCache.indexChain(startPath);
console.log(`index chain ${startPath} `, docinfo);
await akasha.closeCaches();
}
catch (e) {
console.error(`index-chain command ERRORED ${e.stack}`);
}
});
program
.command('siblings <configFN> <vpath>')
.description('List the sibling pages to the named document')
.action(async (configFN, vpath) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const docinfo = await akasha.filecache.documentsCache.siblings(vpath);
console.log(`siblings ${vpath} `, docinfo.map(index => {
return {
vpath: index.vpath,
renderPath: index.renderPath,
mountPoint: index.mountPoint,
pathInMounted: index.pathInMounted,
dirname: index.dirname
};
}));
await akasha.closeCaches();
}
catch (e) {
console.error(`siblings command ERRORED ${e.stack}`);
}
});
program
.command('docs-semantic <configFN> <searchFor>')
.description('List the document vpaths semantically matching the string')
.action(async (configFN, searchFor) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache.documentsCache.semanticSearchDocs(searchFor));
await akasha.closeCaches();
}
catch (e) {
console.error(`docs-semantic command ERRORED ${e.stack}`);
}
});
program
.command('tags <configFN>')
.description('List the tags')
.action(async (configFN) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const tags = await akasha.filecache.documentsCache.tags();
console.log(YAML.dump({ tags }, { indent: 4 }));
await akasha.closeCaches();
}
catch (e) {
console.error(`tags command ERRORED ${e.stack}`);
}
});
program
.command('similar-tags <configFN>')
.description('Find groups of similar tags')
.option('--threshold <n>', 'Levenshtein distance threshold', '2')
.action(async (configFN, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const threshold = parseInt(cmdObj.threshold, 10);
const groups = await akasha.filecache.documentsCache.findSimilarTags(threshold);
console.log(YAML.dump({ similarTagGroups: groups }, { indent: 4 }));
await akasha.closeCaches();
}
catch (e) {
console.error(`similar-tags command ERRORED ${e.stack}`);
}
});
program
.command('tags-without-descriptions <configFN>')
.description('List tags that have no description')
.action(async (configFN) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const tags = await akasha.filecache.documentsCache.tagsWithoutDescriptions();
console.log(YAML.dump({ tagsWithoutDescriptions: tags }, { indent: 4 }));
await akasha.closeCaches();
}
catch (e) {
console.error(`tags-without-descriptions command ERRORED ${e.stack}`);
}
});
program
.command('unused-tag-descriptions <configFN>')
.description('List tag descriptions that are not used by any document')
.action(async (configFN) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const tags = await akasha.filecache.documentsCache.unusedTagDescriptions();
console.log(YAML.dump({ unusedTagDescriptions: tags }, { indent: 4 }));
await akasha.closeCaches();
}
catch (e) {
console.error(`unused-tag-descriptions command ERRORED ${e.stack}`);
}
});
program
.command('refactor-tag <configFN> <oldTag> <newTag>')
.description('Rename a tag across all documents')
.option('--dry-run', 'List changes without modifying files', false)
.action(async (configFN, oldTag, newTag, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const result = await refactorTag(config, oldTag, newTag, {
dryRun: cmdObj.dryRun
});
console.log(YAML.dump({ refactorResult: result }, { indent: 4 }));
await akasha.closeCaches();
}
catch (e) {
console.error(`refactor-tag command ERRORED ${e.stack}`);
}
});
program
.command('docs-with-tag <configFN> <tags...>')
.description('List the document vpaths for given tags')
.action(async (configFN, tags) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache
.documentsCache.documentsWithTag(tags));
await akasha.closeCaches();
}
catch (e) {
console.error(`docs-with-tags command ERRORED ${e.stack}`);
}
});
program
.command('child-item-tree <configFN> <rootPath>')
.description('List the documents under a given location by tree')
.action(async (configFN, rootPath) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(YAML.dump({
tree: await akasha.filecache
.documentsCache.childItemTree(rootPath)
}, { indent: 4 })
// .map(item => {
// return {
// vpath: item.vpath,
// renderPath: item.renderPath
// }
// })
);
await akasha.closeCaches();
}
catch (e) {
console.error(`docs-with-tags command ERRORED ${e.stack}`);
}
});
program
.command('search <configFN>')
.description('Search for documents')
.option('--root <rootPath>', 'Select only files within the named directory')
.option('--match <pathmatch>', 'Select only files matching the regular expression')
.option('--rendermatch <renderpathmatch>', 'Select only files matching the regular expression')
.option('--glob <globmatch>', 'Select only files matching the glob expression')
.option('--renderglob <globmatch>', 'Select only files rendering to the glob expression')
.option('--layout <layout>', 'Select only files matching the layouts')
.option('--mime <mime>', 'Select only files matching the MIME type')
.option('--tag <tag>', 'Select only files with the tag')
.action(async (configFN, cmdObj) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
// console.log(cmdObj);
let options = {};
if (cmdObj.root)
options.rootPath = cmdObj.root;
if (cmdObj.match)
options.pathmatch = cmdObj.match;
if (cmdObj.rendermatch)
options.renderpathmatch = cmdObj.rendermatch;
if (cmdObj.glob)
options.glob = cmdObj.glob;
if (cmdObj.renderglob)
options.renderglob = cmdObj.renderglob;
if (cmdObj.layout)
options.layouts = [cmdObj.layout];
if (cmdObj.mime)
options.mime = cmdObj.mime;
if (cmdObj.tag)
options.tag = cmdObj.tag;
// console.log(options);
let docs = await akasha.filecache.documentsCache.search(options);
console.log(docs
// .map(doc => {
// return {
// vpath: doc.vpath,
// renderPath: doc.renderPath,
// dirname: doc.dirname
// }
// })
// .sort((a: any, b: any) => {
// var tagA = a.dirname.toLowerCase();
// var tagB = b.dirname.toLowerCase();
// if (tagA < tagB) return -1;
// if (tagA > tagB) return 1;
// return 0;
// })
// .reverse()
);
await akasha.closeCaches();
}
catch (e) {
console.error(`search command ERRORED ${e.stack}`);
}
});
program
.command('assets <configFN>')
.description('List the assets in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache.assetsCache.paths());
await akasha.closeCaches();
}
catch (e) {
console.error(`assets command ERRORED ${e.stack}`);
}
});
program
.command('assetinfo <configFN> <docFN>')
.description('Show information about an asset in a site configuration')
.action(async (configFN, assetFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const assetinfo = await akasha.filecache.assetsCache.find(assetFN);
console.log(assetinfo);
await akasha.closeCaches();
}
catch (e) {
console.error(`assetinfo command ERRORED ${e.stack}`);
}
});
program
.command('layouts <configFN>')
.description('List the layouts in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache.layoutsCache.paths());
await akasha.closeCaches();
}
catch (e) {
console.error(`layouts command ERRORED ${e.stack}`);
}
});
// TODO both test.html and test.html.njk match
// This is probably incorrect, since we do not render these files
// The partials directory has the same problem
// Some kind of flag on creating the FileCache to change the behavior
program
.command('layoutinfo <configFN> <docFN>')
.description('Show information about a layout in a site configuration')
.action(async (configFN, layoutFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const layoutinfo = await akasha.filecache.layoutsCache.find(layoutFN);
console.log(layoutinfo);
await akasha.closeCaches();
}
catch (e) {
console.error(`layoutinfo command ERRORED ${e.stack}`);
}
});
program
.command('partials <configFN>')
.description('List the partials in a site configuration')
.action(async (configFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
console.log(await akasha.filecache.partialsCache.paths());
await akasha.closeCaches();
}
catch (e) {
console.error(`partials command ERRORED ${e.stack}`);
}
});
// TODO both test.html and test.html.njk match
// This is probably incorrect, since we do not render these files
program
.command('partialinfo <configFN> <docFN>')
.description('Show information about a partial in a site configuration')
.action(async (configFN, partialFN) => {
// console.log(`render: akasha: ${util.inspect(akasha)}`);
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
await akasha.setup(config);
const partialinfo = await akasha.filecache.partialsCache.find(partialFN);
console.log(partialinfo);
await akasha.closeCaches();
}
catch (e) {
console.error(`partialinfo command ERRORED ${e.stack}`);
}
});
program
.command('index <configFN>')
.description('Loads configuration, indexes content, then exits')
.option('--verbose', 'Show detailed event tracking (added, ready, error events)')
.action(async (configFN, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
if (cmdObj.verbose) {
console.log('Indexing files with verbose output...\n');
const startTime = Date.now();
// Enable verbose mode in config
config.verbose = true;
await akasha.setup(config);
const setupTime = Date.now();
const elapsed = setupTime - startTime;
// Get file counts
const filecache = akasha.filecache;
const documentCount = (await filecache.documentsCache.paths()).length;
const assetCount = (await filecache.assetsCache.paths()).length;
const layoutCount = (await filecache.layoutsCache.paths()).length;
const partialCount = (await filecache.partialsCache.paths()).length;
console.log(`✓ Indexing completed in ${elapsed}ms\n`);
console.log('=== Summary ===');
console.log(`Documents: ${documentCount} files`);
console.log(`Assets: ${assetCount} files`);
console.log(`Layouts: ${layoutCount} files`);
console.log(`Partials: ${partialCount} files`);
console.log(`Total: ${documentCount + assetCount + layoutCount + partialCount} files`);
}
else {
// Normal mode - just setup
await akasha.setup(config);
}
await akasha.closeCaches();
}
catch (e) {
console.error(`index command ERRORED ${e.stack}`);
}
});
program
.command('check-ready <configFN>')
.description('Verify that all files are loaded before isReady triggers (diagnostic tool)')
.option('--verbose', 'Show detailed file-by-file tracking')
.option('--delay <ms>', 'Wait time in milliseconds to check for late additions (default: 2000)', '2000')
.action(async (configFN, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
let akasha = config.akasha;
console.log('Running isReady timing check...\n');
// Capture initial state
const startTime = Date.now();
await akasha.setup(config);
const setupTime = Date.now();
// Get counts immediately after setup
const filecache = akasha.filecache;
const countsAfterSetup = {
documents: (await filecache.documentsCache.paths()).length,
assets: (await filecache.assetsCache.paths()).length,
layouts: (await filecache.layoutsCache.paths()).length,
partials: (await filecache.partialsCache.paths()).length
};
console.log(`✓ Setup completed in ${setupTime - startTime}ms`);
console.log(` Documents: ${countsAfterSetup.documents}`);
console.log(` Assets: ${countsAfterSetup.assets}`);
console.log(` Layouts: ${countsAfterSetup.layouts}`);
console.log(` Partials: ${countsAfterSetup.partials}`);
// Wait specified delay to see if any additional files appear
const delayMs = parseInt(cmdObj.delay);
console.log(`\nWaiting ${delayMs}ms to check for late additions...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
const countsAfterDelay = {
documents: (await filecache.documentsCache.paths()).length,
assets: (await filecache.assetsCache.paths()).length,
layouts: (await filecache.layoutsCache.paths()).length,
partials: (await filecache.partialsCache.paths()).length
};
// Compare counts
let issueDetected = false;
const checkCache = (name) => {
const before = countsAfterSetup[name];
const after = countsAfterDelay[name];
if (before !== after) {
console.error(`\n❌ ISSUE DETECTED: ${name} count changed from ${before} to ${after}`);
console.error(` This indicates files were added after isReady!`);
issueDetected = true;
return false;
}
else {
if (cmdObj.verbose) {
console.log(`✓ ${name}: ${before} files (stable)`);
}
return true;
}
};
console.log('\nResults:');
const docsOk = checkCache('documents');
const assetsOk = checkCache('assets');
const layoutsOk = checkCache('layouts');
const partialsOk = checkCache('partials');
if (!issueDetected) {
console.log('\n✅ SUCCESS: No files added after isReady. Timing is correct.');
console.log('\nAll caches are stable:');
console.log(` ✓ Documents: ${countsAfterSetup.documents} files`);
console.log(` ✓ Assets: ${countsAfterSetup.assets} files`);
console.log(` ✓ Layouts: ${countsAfterSetup.layouts} files`);
console.log(` ✓ Partials: ${countsAfterSetup.partials} files`);
}
else {
console.error('\n⚠️ FAILURE: Files were added after isReady triggered!');
console.error(' This indicates a race condition that needs to be fixed.');
console.error('\n Please report this issue at:');
console.error(' https://github.com/akashacms/akasharender/issues');
}
await akasha.closeCaches();
if (issueDetected) {
process.exit(1);
}
}
catch (e) {
console.error(`check-ready command ERRORED ${e.stack}`);
process.exit(1);
}
});
program
.command('validate-sitemap <configFN>')
.description('Validate sitemap XML file against rendered output directory')
.option('--sitemap <filename>', 'Sitemap filename relative to output directory', 'sitemap.xml')
.option('--strict', 'Exit with error code if validation fails', false)
.option('--json', 'Output results as JSON', false)
.action(async (configFN, cmdObj) => {
try {
const config = (await import(path.join(process.cwd(), configFN))).default;
const validator = new SitemapValidator(config, cmdObj.sitemap);
const result = await validator.validate();
if (cmdObj.json) {
console.log(JSON.stringify(result, null, 2));
}
else {
console.log(SitemapValidator.formatReport(result));
}
if (cmdObj.strict && (result.invalidEntries > 0 || result.errors.length > 0)) {
process.exit(1);
}
}
catch (e) {
console.error(`validate-sitemap command ERRORED ${e.stack}`);
process.exit(1);
}
});
program.parse(process.argv);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vbGliL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBRUE7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBaUJHO0FBRUgsWUFBWSxDQUFDO0FBRWIsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNwQyxPQUFPLE9BQU8sTUFBTSxVQUFVLENBQUM7QUFDL0IsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sRUFBRSxRQUFRLElBQUksR0FBRyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzFDLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxTQUFTLENBQUM7QUFFaEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2pELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBSTFELE9BQU8sQ0FBQyxLQUFLLEdBQUcsY0FBYyxDQUFDO0FBQy9CLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7QUFFekIsT0FBTztLQUNGLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQztLQUNqQyxXQUFXLENBQUMsbUNBQW1DLENBQUM7S0FDaEQsTUFBTSxDQUFDLEtBQUssRUFBRSxRQUFRLEVBQUUsRUFBRTtJQUN2QixJQUFJLENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxDQUFDLE1BQU0sTUFBTSxDQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FDckMsQ0FBQyxDQUFDLE9BQU8sQ0FBQztRQUNYLElBQUksTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDM0IsTUFBTSxNQUFNLENBQUMsS0FB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