@mdfriday/foundry
Version:
The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.
493 lines • 17.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PageMap = exports.PageMapQueryPagesInSectionImpl = exports.PageMapQueryPagesBelowPathImpl = exports.pagePredicates = void 0;
exports.addTrailingSlash = addTrailingSlash;
const pagetrees_1 = require("./pagetrees");
const pagesource_1 = require("./pagesource");
const type_1 = require("../../paths/type");
const doctree_1 = require("../../../../pkg/doctree");
const sort_1 = require("../vo/sort");
const log_1 = require("../../../../pkg/log");
// Create a domain-specific logger for content operations
const log = (0, log_1.getDomainLogger)('content', { component: 'pagemap' });
/**
* Path utility functions - simplified implementations
*/
function addTrailingSlash(path) {
if (!path.endsWith("/")) {
path += "/";
}
return path;
}
function addLeadingSlash(path) {
if (!path.startsWith("/")) {
path = "/" + path;
}
return path;
}
/**
* Ambiguous content node - used for reverse index conflicts
*/
const ambiguousContentNode = new pagetrees_1.PageTreesNode();
/**
* ContentTreeReverseIndexMap - manages reverse index mapping
*/
class ContentTreeReverseIndexMap {
constructor() {
this.initOnce = false;
this.m = new Map();
}
}
/**
* ContentTreeReverseIndex - reverse index for content tree
*/
class ContentTreeReverseIndex {
constructor(initFn) {
this.initFn = initFn;
this.contentTreeReverseIndexMap = new ContentTreeReverseIndexMap();
}
reset() {
this.contentTreeReverseIndexMap = new ContentTreeReverseIndexMap();
}
get(key) {
if (!this.contentTreeReverseIndexMap.initOnce) {
this.contentTreeReverseIndexMap.m = new Map();
this.initFn(this.contentTreeReverseIndexMap.m);
this.contentTreeReverseIndexMap.initOnce = true;
}
return this.contentTreeReverseIndexMap.m.get(key) || null;
}
}
/**
* Page predicates - exact replica of Go's pagePredicates
*/
exports.pagePredicates = {
kindPage: (p) => p.kind() === "page",
kindSection: (p) => p.kind() === "section",
kindHome: (p) => p.kind() === "home",
kindTerm: (p) => p.kind() === "term",
shouldListLocal: (p) => p.shouldList ? p.shouldList(false) : true,
shouldListGlobal: (p) => p.shouldList ? p.shouldList(true) : true,
shouldListAny: (p) => p.shouldListAny ? p.shouldListAny() : true,
shouldLink: (p) => p.noLink ? !p.noLink() : true
};
/**
* Implementation of PageMapQueryPagesBelowPath
*/
class PageMapQueryPagesBelowPathImpl {
constructor(path, keyPart, include = exports.pagePredicates.shouldListLocal) {
this.path = path;
this.keyPart = keyPart;
this.include = include;
}
key() {
return this.path + "/" + this.keyPart;
}
}
exports.PageMapQueryPagesBelowPathImpl = PageMapQueryPagesBelowPathImpl;
/**
* Implementation of PageMapQueryPagesInSection
*/
class PageMapQueryPagesInSectionImpl extends PageMapQueryPagesBelowPathImpl {
constructor(path, keyPart, recursive, includeSelf, index, include) {
super(path, keyPart, include);
this.recursive = recursive;
this.includeSelf = includeSelf;
this.index = index;
}
key() {
return "gagesInSection" + "/" + super.key() + "/" + this.recursive.toString() +
"/" + this.index.toString() + "/" + this.includeSelf.toString();
}
}
exports.PageMapQueryPagesInSectionImpl = PageMapQueryPagesInSectionImpl;
class PageMap extends pagetrees_1.PageTrees {
constructor(pageBuilder) {
super();
this.pageReverseIndex = null;
this.pageBuilder = pageBuilder;
}
/**
* SetupReverseIndex - exact replica of Go's SetupReverseIndex method
*/
setupReverseIndex() {
this.pageReverseIndex = new ContentTreeReverseIndex(async (rm) => {
const add = (k, n) => {
const existing = rm.get(k);
if (existing && existing !== ambiguousContentNode) {
rm.set(k, ambiguousContentNode);
}
else if (!existing) {
rm.set(k, n);
}
};
const walker = new doctree_1.NodeShiftTreeWalker({
tree: this.treePages,
lockType: doctree_1.LockType.LockTypeRead,
handle: async (s, n, match) => {
if (n) {
const [p, found] = n.getPage();
if (!found) {
return [false, null];
}
if (p.pageFile && p.pageFile()) {
add(p.paths().baseNameNoIdentifier(), n);
}
}
return [false, null];
}
});
try {
await walker.walk();
}
catch (err) {
log.error(`setupReverseIndex error: ${err}`);
}
});
}
/**
* InsertResourceNode - exact replica of Go's InsertResourceNode method
*/
insertResourceNode(key, node) {
const tree = this.treeResources;
const commit = tree.lock(true);
try {
tree.insertIntoValuesDimension(key, node);
}
finally {
commit();
}
}
async addFi(f) {
if (f.fileInfo().isDir()) {
return;
}
const ps = (0, pagesource_1.newPageSource)(f);
const key = ps.paths().base();
// Access bundleType from the file's getBundleType method
const bundleType = ps.file.getBundleType();
switch (bundleType) {
case type_1.PathType.File:
this.insertResourceNode(key, (0, pagetrees_1.newPageTreesNode)(ps));
break;
case type_1.PathType.ContentResource:
const p = await this.pageBuilder.withSource(ps).build();
if (!p) {
// Disabled page.
return;
}
this.insertResourceNode(key, (0, pagetrees_1.newPageTreesNode)(p));
break;
default:
const page = await this.pageBuilder.withSource(ps).build();
this.treePages.insertWithLock(ps.paths().base(), (0, pagetrees_1.newPageTreesNode)(page));
break;
}
}
/**
* Assemble - exact replica of Go's Assemble method
*/
async assemble() {
await this.assembleStructurePages();
await this.applyAggregates();
await this.cleanPages();
await this.assembleTerms();
}
/**
* AssembleTerms - exact replica of Go's assembleTerms method
*/
async assembleTerms() {
await this.pageBuilder.term.assemble(this.treePages, this.treeTaxonomyEntries, this.pageBuilder);
}
/**
* CleanPages - exact replica of Go's cleanPages method
*/
async cleanPages() {
// TODO: clean all the draft, expired, scheduled in the future pages
}
/**
* ApplyAggregates - exact replica of Go's applyAggregates method
*/
async applyAggregates() {
// TODO
// Apply cascade Aggregates to pages
// Apply Dates to home, section, and meta changed pages
// Apply cascade to source page
// Use linked list to connect all the cascade in the same path
// Restore all the changed pages in the cache
}
/**
* AssembleStructurePages - exact replica of Go's assembleStructurePages method
*/
async assembleStructurePages() {
await this.addMissingTaxonomies();
for (const idx of this.pageBuilder.langSvc.languageIndexes()) {
const tree = this.treePages.shape(0, idx);
await this.pageBuilder.section.assemble(tree, this.pageBuilder, idx);
}
await this.addMissingStandalone();
}
/**
* AddMissingTaxonomies - exact replica of Go's addMissingTaxonomies method
*/
async addMissingTaxonomies() {
const tree = this.treePages;
const commit = tree.lock(true);
try {
await this.pageBuilder.taxonomy.assemble(tree, this.pageBuilder);
}
finally {
commit();
}
}
/**
* AddMissingStandalone - exact replica of Go's addMissingStandalone method
*/
async addMissingStandalone() {
const tree = this.treePages;
const commit = tree.lock(true);
try {
await this.pageBuilder.standalone.assemble(tree, this.pageBuilder);
}
finally {
commit();
}
}
/**
* GetResourcesForPage - exact replica of Go's getResourcesForPage method
*/
async getResourcesForPage(ps) {
const res = [];
await this.forEachResourceInPage(ps, doctree_1.LockType.LockTypeNone, false, (resourceKey, n, match) => {
const [rs, found] = n.getResource();
if (found) {
res.push(rs);
}
return [false, null];
});
return res;
}
/**
* ForEachResourceInPage - exact replica of Go's forEachResourceInPage method
*/
async forEachResourceInPage(ps, lockType, exact, handle) {
let keyPage = ps.paths().base();
if (keyPage === "/") {
keyPage = "";
}
const prefix = addTrailingSlash(keyPage);
const isBranch = ps.kind() !== "page";
const rw = new doctree_1.NodeShiftTreeWalker({
tree: this.treeResources.shape(0, ps.pageIdentity().pageLanguageIndex()),
prefix: prefix,
lockType: lockType,
exact: exact,
handle: async (resourceKey, n, match) => {
if (isBranch) {
const [ownerKey] = this.treePages.longestPrefixAll(resourceKey);
if (ownerKey !== keyPage && this.pathDir(ownerKey) !== this.pathDir(resourceKey)) {
// Stop walking downwards, someone else owns this resource.
rw.skipPrefix(ownerKey + "/");
return [false, null];
}
}
return handle(resourceKey, n, match);
}
});
try {
await rw.walk();
}
catch (err) {
log.error(`forEachResourceInPage error: ${err}`);
}
}
/**
* GetPagesInSection - exact replica of Go's getPagesInSection method
*/
async getPagesInSection(langIndex, q) {
const cacheKey = q.key();
const tree = this.treePages.shape(0, langIndex);
// Simplified cache implementation - in real implementation would use proper cache
return this.getOrCreatePagesFromCacheSync(null, cacheKey, async (key) => {
const prefix = addTrailingSlash(q.path);
const pas = [];
let otherBranch = "";
const include = q.include;
const walker = new doctree_1.NodeShiftTreeWalker({
tree: tree,
prefix: prefix,
handle: async (key, n, match) => {
if (q.recursive) {
const [p, found] = n.getPage();
if (found && include(p)) {
pas.push(p);
}
return [false, null];
}
const [p, found] = n.getPage();
if (found && include(p)) {
pas.push(p);
}
if (!p.isPage || !p.isPage()) {
const currentBranch = key + "/";
if (otherBranch === "" || otherBranch !== currentBranch) {
walker.skipPrefix(currentBranch);
}
otherBranch = currentBranch;
}
return [false, null];
}
});
try {
await walker.walk();
if (q.includeSelf) {
const n = tree.get(q.path);
if (n) {
const [p, found] = n.getPage();
if (found && include(p)) {
pas.push(p);
}
}
}
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
(0, sort_1.sortByWeight)(pages);
return Promise.resolve(pages);
}
catch (err) {
log.error(`getPagesInSection error: ${err}`);
}
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
return Promise.resolve(pages);
});
}
/**
* GetOrCreatePagesFromCache - simplified synchronous version
*/
async getOrCreatePagesFromCacheSync(cache, key, create) {
// Faster than cache implementation for this example
return await create(key);
}
/**
* GetPagesWithTerm - exact replica of Go's getPagesWithTerm method
*/
async getPagesWithTerm(q) {
const key = q.key();
// Simplified cache implementation
return await this.getOrCreatePagesFromCacheSync(null, key, async (cacheKey) => {
const pas = [];
const include = q.include;
try {
await this.treeTaxonomyEntries.walkPrefix(doctree_1.LockType.LockTypeNone, addTrailingSlash(q.path), (s, n) => {
const [p, found] = n.getPage();
if (found && include(p)) {
pas.push(p);
}
return [false, null];
});
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
(0, sort_1.sortByDefault)(pages);
return pages;
}
catch (err) {
log.error(`getPagesWithTerm error: ${err}`);
}
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
return pages;
});
}
/**
* GetTermsForPageInTaxonomy - exact replica of Go's getTermsForPageInTaxonomy method
*/
getTermsForPageInTaxonomy(base, taxonomy) {
const prefix = addLeadingSlash(taxonomy);
return this.getOrCreatePagesFromCacheSync(null, prefix + base, async (cacheKey) => {
const pas = [];
try {
await this.treeTaxonomyEntries.walkPrefix(doctree_1.LockType.LockTypeNone, addTrailingSlash(prefix), (s, n) => {
if (s.endsWith(base)) {
pas.push(n.term.page);
}
return [false, null];
});
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
(0, sort_1.sortByDefault)(pages);
return pages;
}
catch (err) {
log.error(`getTermsForPageInTaxonomy error: ${err}`);
}
// Convert to Pages with len method
const pages = pas;
pages.len = () => pas.length;
return pages;
});
}
/**
* GetSections - exact replica of Go's getSections method
*/
async getSections(langIndex, prefix) {
const pagesList = [];
let currentBranchPrefix = "";
const tree = this.treePages.shape(0, langIndex);
const walker = new doctree_1.NodeShiftTreeWalker({
tree: tree,
prefix: prefix,
handle: async (ss, n, match) => {
const [p, found] = n.getPage();
if (!found) {
return [false, null];
}
if (p.isPage && p.isPage()) {
return [false, null];
}
if (currentBranchPrefix === "" || !ss.startsWith(currentBranchPrefix)) {
if (p.isSection && p.isSection() && p.shouldList && p.shouldList(false) && p.parent && p.parent() === p) {
pagesList.push(p);
}
else {
walker.skipPrefix(ss + "/");
}
}
currentBranchPrefix = ss + "/";
return [false, null];
}
});
try {
await walker.walk();
// Convert to Pages with len method
const pages = pagesList;
pages.len = () => pagesList.length;
(0, sort_1.sortByDefault)(pages);
return pages;
}
catch (err) {
log.error(`getSections error: ${err}`);
}
// Convert to Pages with len method
const pages = pagesList;
pages.len = () => pagesList.length;
return pages;
}
/**
* Helper method to get directory from path
*/
pathDir(path) {
const lastSlash = path.lastIndexOf('/');
if (lastSlash === -1) {
return '';
}
return path.substring(0, lastSlash);
}
}
exports.PageMap = PageMap;
//# sourceMappingURL=pagemap.js.map