@agility/cli
Version:
Agility CLI for working with your content. (Public Beta)
540 lines • 25 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SitemapHierarchy = void 0;
var fs = __importStar(require("fs"));
var path = __importStar(require("path"));
var state_1 = require("../../../core/state");
/**
* Load and parse sitemap hierarchy for hierarchical page chain analysis
*/
var SitemapHierarchy = /** @class */ (function () {
function SitemapHierarchy() {
// Configuration now comes from state internally
}
SitemapHierarchy.prototype.loadAllSitemaps = function (guid, locale) {
var _this = this;
var rootPath = state_1.state.rootPath, sourceGuid = state_1.state.sourceGuid;
var sitemapDir = path.join(rootPath, guid, locale, 'nestedsitemap');
var sitemaps = {};
fs.readdirSync(sitemapDir).forEach(function (fileName) {
if (!fileName.endsWith('.json')) {
return; // Skip non-JSON files
}
var channel = path.basename(fileName, '.json');
sitemaps[channel] = _this.loadNestedSitemap(path.join(sitemapDir, fileName));
});
return sitemaps;
};
/**
* Load nested sitemap from the file system
*/
SitemapHierarchy.prototype.loadNestedSitemap = function (filePath) {
try {
if (!fs.existsSync(filePath)) {
console.warn("Nested sitemap not found at: ".concat(filePath));
return null;
}
var sitemapData = fs.readFileSync(filePath, 'utf8');
var sitemap = JSON.parse(sitemapData);
// Loaded nested sitemap (silent)
return sitemap;
}
catch (error) {
console.error("Error loading nested sitemap: ".concat(error.message));
return null;
}
};
/**
* Build page hierarchy map from nested sitemap
*/
SitemapHierarchy.prototype.buildPageHierarchy = function (sitemap) {
var hierarchy = {};
var processNode = function (node) {
if (node.children && node.children.length > 0) {
// This node has children
hierarchy[node.pageID] = node.children.map(function (child) { return child.pageID; });
// Recursively process children
node.children.forEach(function (child) { return processNode(child); });
}
};
sitemap.forEach(function (node) { return processNode(node); });
return hierarchy;
};
/**
* Group pages hierarchically based on sitemap structure
*/
SitemapHierarchy.prototype.groupPagesHierarchically = function (pages, hierarchy) {
var _this = this;
var processedPages = new Set();
var hierarchicalGroups = [];
// Process each page that has children
pages.forEach(function (page) {
if (!processedPages.has(page.pageID) && hierarchy[page.pageID]) {
// This page has children, create a group for it
var group = _this.buildHierarchicalGroup(page, pages, hierarchy, processedPages);
hierarchicalGroups.push(group);
}
});
// Process remaining pages that don't have children and aren't children of processed pages
pages.forEach(function (page) {
if (!processedPages.has(page.pageID)) {
// This is an orphaned page (no children, not a child of any processed page)
var group = {
rootPage: page,
childPages: [],
allPageIds: new Set([page.pageID])
};
hierarchicalGroups.push(group);
processedPages.add(page.pageID);
}
});
return hierarchicalGroups;
};
/**
* Find the parent page ID for a given page (only if parent exists in our page list)
*/
SitemapHierarchy.prototype.findParentPageId = function (pageId, hierarchy, pages) {
var _loop_1 = function (parentId, childIds) {
if (childIds.includes(pageId)) {
// Check if the parent exists in our page list
var parentExists = pages.some(function (p) { return p.pageID === parseInt(parentId); });
if (parentExists) {
return { value: parseInt(parentId) };
}
}
};
for (var _i = 0, _a = Object.entries(hierarchy); _i < _a.length; _i++) {
var _b = _a[_i], parentId = _b[0], childIds = _b[1];
var state_2 = _loop_1(parentId, childIds);
if (typeof state_2 === "object")
return state_2.value;
}
return null;
};
/**
* Build a hierarchical group starting from a root page
*/
SitemapHierarchy.prototype.buildHierarchicalGroup = function (rootPage, allPages, hierarchy, processedPages) {
var group = {
rootPage: rootPage,
childPages: [],
allPageIds: new Set([rootPage.pageID])
};
// Mark root as processed
processedPages.add(rootPage.pageID);
// Collect ALL descendants with unlimited nesting levels
this.collectAllDescendants(rootPage.pageID, allPages, hierarchy, group, processedPages);
return group;
};
/**
* Collect all descendants with unlimited nesting levels (not just direct children)
* This enables proper display of deep hierarchies like PageID:A → PageID:B → PageID:C
*/
SitemapHierarchy.prototype.collectAllDescendants = function (parentPageId, allPages, hierarchy, group, processedPages) {
var _this = this;
var directChildIds = hierarchy[parentPageId] || [];
directChildIds.forEach(function (childId) {
var childPage = allPages.find(function (p) { return p.pageID === childId; });
if (childPage && !processedPages.has(childId)) {
// Add this child to the current level
group.childPages.push(childPage);
group.allPageIds.add(childId);
processedPages.add(childId);
// Recursively collect ALL descendants (grandchildren, great-grandchildren, etc.)
_this.collectAllDescendants(childId, allPages, hierarchy, group, processedPages);
}
});
};
/**
* Get orphaned pages (pages not in any hierarchical group)
*/
SitemapHierarchy.prototype.getOrphanedPages = function (pages, hierarchicalGroups) {
var allProcessedIds = new Set();
hierarchicalGroups.forEach(function (group) {
group.allPageIds.forEach(function (id) { return allProcessedIds.add(id); });
});
return pages.filter(function (page) { return !allProcessedIds.has(page.pageID); });
};
/**
* Debug: Log hierarchy structure
*/
SitemapHierarchy.prototype.debugLogHierarchy = function (hierarchy) {
console.log("\uD83D\uDD27 [DEBUG] Page hierarchy structure:");
Object.entries(hierarchy).forEach(function (_a) {
var parentId = _a[0], childIds = _a[1];
console.log(" Parent ".concat(parentId, " has children: ").concat(childIds.join(', ')));
});
};
/**
* ✅ NEW: Find page parent from source sitemap with comprehensive lookup
* Handles both template pages and dynamic page instances
*/
SitemapHierarchy.prototype.findPageParentInSourceSitemap = function (pageId, pageName, channelName) {
try {
var sitemap = this.loadNestedSitemap(channelName);
if (!sitemap || sitemap.length === 0) {
return { parentId: null, parentName: null, foundIn: 'no-sitemap' };
}
// Recursive function to search through sitemap
var searchSitemap_1 = function (nodes, parentNode) {
if (parentNode === void 0) { parentNode = null; }
for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
var node = nodes_1[_i];
// Check if this node is our target page
if (node.pageID === pageId || node.name === pageName) {
if (parentNode) {
console.log("\uD83C\uDFAF [DEBUG] Found ".concat(pageName, " (ID:").concat(pageId, ") under parent ").concat(parentNode.name, " (ID:").concat(parentNode.pageID, ")"));
return {
parentId: parentNode.pageID,
parentName: parentNode.name,
foundIn: 'direct-match'
};
}
else {
console.log("\uD83C\uDFE0 [DEBUG] Found ".concat(pageName, " (ID:").concat(pageId, ") at root level"));
return { parentId: null, parentName: null, foundIn: 'root-level' };
}
}
// Check if this node has children (dynamic page instances)
if (node.children && node.children.length > 0) {
// For dynamic pages: check if any child has same pageID as template
var dynamicMatch = node.children.find(function (child) { return child.pageID === pageId; });
if (dynamicMatch) {
console.log("\uD83C\uDFAF [DEBUG] Found dynamic page ".concat(pageName, " (ID:").concat(pageId, ") under parent ").concat(node.name, " (ID:").concat(node.pageID, ")"));
return {
parentId: node.pageID,
parentName: node.name,
foundIn: 'dynamic-child'
};
}
// Recursively search children
var childResult = searchSitemap_1(node.children, node);
if (childResult.parentId !== null) {
return childResult;
}
}
}
return { parentId: null, parentName: null, foundIn: 'not-found' };
};
var result = searchSitemap_1(sitemap);
console.log("\uD83D\uDCCD [DEBUG] Parent lookup result for ".concat(pageName, ":"), result);
return result;
}
catch (error) {
console.error("\u274C [DEBUG] Error looking up parent for ".concat(pageName, ":"), error.message);
return { parentId: null, parentName: null, foundIn: 'error' };
}
};
/**
* ✅ NEW: Enhanced hierarchy build that handles dynamic pages correctly
*/
SitemapHierarchy.prototype.buildPageHierarchyWithDynamicSupport = function (sitemap) {
var hierarchy = {};
var processNode = function (node, parentNode) {
if (parentNode === void 0) { parentNode = null; }
// If this node has children, add them to hierarchy
if (node.children && node.children.length > 0) {
hierarchy[node.pageID] = node.children.map(function (child) { return child.pageID; });
// Process children recursively
node.children.forEach(function (child) { return processNode(child, node); });
}
// Special handling for dynamic pages
// If this node has dynamic children (contentID present), also map those
if (node.children) {
node.children.forEach(function (child) {
if (child.contentID) {
// This is a dynamic page instance - ensure it knows its parent
if (!hierarchy[node.pageID]) {
hierarchy[node.pageID] = [];
}
if (!hierarchy[node.pageID].includes(child.pageID)) {
hierarchy[node.pageID].push(child.pageID);
}
}
});
}
};
sitemap.forEach(function (node) { return processNode(node); });
return hierarchy;
};
/**
* Calculate depth level for each page in the hierarchy
* Depth 0 = root pages (no parents), Depth 1 = direct children, etc.
*/
SitemapHierarchy.prototype.calculatePageDepths = function (pages, hierarchy) {
var pageDepths = new Map();
var visited = new Set();
// Build reverse lookup: child → parent
var childToParent = new Map();
Object.entries(hierarchy).forEach(function (_a) {
var parentIdStr = _a[0], childIds = _a[1];
var parentId = parseInt(parentIdStr);
childIds.forEach(function (childId) {
childToParent.set(childId, parentId);
});
});
// Calculate depth recursively for each page
var calculateDepth = function (pageId) {
if (visited.has(pageId)) {
// Circular reference detected - return high depth to process early
console.warn("Circular reference detected for page ".concat(pageId));
return 999;
}
if (pageDepths.has(pageId)) {
return pageDepths.get(pageId);
}
visited.add(pageId);
var parentId = childToParent.get(pageId);
if (!parentId) {
// Root page (no parent)
pageDepths.set(pageId, 0);
visited.delete(pageId);
return 0;
}
// Parent exists - depth is parent's depth + 1
var parentDepth = calculateDepth(parentId);
var depth = parentDepth + 1;
pageDepths.set(pageId, depth);
visited.delete(pageId);
return depth;
};
// Calculate depth for all pages
pages.forEach(function (page) {
calculateDepth(page.pageID);
});
return pageDepths;
};
/**
* Get pages grouped by depth level
* Returns map of depth → pages at that depth
*/
SitemapHierarchy.prototype.getPagesByDepth = function (pages, pageDepths) {
var pagesByDepth = new Map();
pages.forEach(function (page) {
var depth = pageDepths.get(page.pageID) || 0;
if (!pagesByDepth.has(depth)) {
pagesByDepth.set(depth, []);
}
pagesByDepth.get(depth).push(page);
});
return pagesByDepth;
};
/**
* Generate dependency-safe page processing order
* Returns pages ordered by depth (shallowest first) so parents are processed before children
*/
SitemapHierarchy.prototype.getProcessingOrder = function (pages, hierarchy) {
// Calculate page depths
var pageDepths = this.calculatePageDepths(pages, hierarchy);
// Group pages by depth
var pagesByDepth = this.getPagesByDepth(pages, pageDepths);
// Sort depth levels in ascending order (shallowest first = parents before children)
var sortedDepths = Array.from(pagesByDepth.keys()).sort(function (a, b) { return a - b; });
// Build ordered array with shallowest pages first (parents before children)
var orderedPages = [];
sortedDepths.forEach(function (depth) {
var pagesAtDepth = pagesByDepth.get(depth) || [];
// Sort pages within same depth by pageID for consistency
pagesAtDepth.sort(function (a, b) { return a.pageID - b.pageID; });
orderedPages.push.apply(orderedPages, pagesAtDepth);
});
// Page processing order calculated (silent)
return { orderedPages: orderedPages, depthInfo: pageDepths };
};
/**
* Validate page processing order is dependency-safe
* Ensures no page is processed before its parent
*/
SitemapHierarchy.prototype.validateProcessingOrder = function (orderedPages, hierarchy) {
var processedPageIds = new Set();
// Build reverse lookup: child → parent
var childToParent = new Map();
Object.entries(hierarchy).forEach(function (_a) {
var parentIdStr = _a[0], childIds = _a[1];
var parentId = parseInt(parentIdStr);
childIds.forEach(function (childId) {
childToParent.set(childId, parentId);
});
});
for (var _i = 0, orderedPages_1 = orderedPages; _i < orderedPages_1.length; _i++) {
var page = orderedPages_1[_i];
var parentId = childToParent.get(page.pageID);
if (parentId && !processedPageIds.has(parentId)) {
// This page's parent hasn't been processed yet - order is invalid
console.error("\u274C Invalid processing order: Page ".concat(page.pageID, " scheduled before parent ").concat(parentId));
return false;
}
processedPageIds.add(page.pageID);
}
// Processing order validation passed (silent)
return true;
};
/**
* Extract sibling ordering information from source sitemap
* Returns a map of pageID → nextSiblingPageID for proper insertion order
*/
SitemapHierarchy.prototype.extractSiblingOrderFromSitemap = function (sitemap) {
var siblingOrderMap = new Map();
var processSiblings = function (siblings, depth) {
if (depth === void 0) { depth = 0; }
for (var i = 0; i < siblings.length; i++) {
var currentPage = siblings[i];
var nextSibling = i < siblings.length - 1 ? siblings[i + 1] : null;
// Map current page to its next sibling (or null if last)
siblingOrderMap.set(currentPage.pageID, (nextSibling === null || nextSibling === void 0 ? void 0 : nextSibling.pageID) || null);
// Process child pages recursively
if (currentPage.children && currentPage.children.length > 0) {
processSiblings(currentPage.children, depth + 1);
}
}
};
processSiblings(sitemap, 0);
return siblingOrderMap;
};
/**
* Get the pageID that should come BEFORE the specified page (for insertBefore parameter)
* FIXED: Returns the NEXT sibling (what this page should go before), not the previous sibling
*/
SitemapHierarchy.prototype.getInsertBeforePageId = function (pageId, siblingOrder) {
// FIXED: Return the next sibling directly - this page should go BEFORE its next sibling
var nextSiblingId = siblingOrder.get(pageId) || null;
if (nextSiblingId) {
return nextSiblingId;
}
else {
return null; // No next sibling found (page is last in its group, will place at end)
}
};
/**
* Build comprehensive page ordering data including parent-child and sibling relationships
*/
SitemapHierarchy.prototype.buildPageOrderingData = function (sitemap) {
var hierarchy = this.buildPageHierarchyWithDynamicSupport(sitemap);
var siblingOrder = this.extractSiblingOrderFromSitemap(sitemap);
// Build parent-to-children mapping for quick lookup
var parentToChildrenMap = new Map();
Object.entries(hierarchy).forEach(function (_a) {
var parentIdStr = _a[0], childIds = _a[1];
var parentId = parseInt(parentIdStr);
parentToChildrenMap.set(parentId, childIds);
});
return {
hierarchy: hierarchy,
siblingOrder: siblingOrder,
parentToChildrenMap: parentToChildrenMap
};
};
/**
* Get processing order that preserves both parent-child dependencies AND sibling order
*/
SitemapHierarchy.prototype.getOrderedProcessingSequence = function (pages, sitemap) {
var _this = this;
var orderingData = this.buildPageOrderingData(sitemap);
var hierarchy = orderingData.hierarchy;
// Get dependency-safe order (parents before children)
var orderedPages = this.getProcessingOrder(pages, hierarchy).orderedPages;
// Within each depth level, sort by sibling order
var pageDepths = this.calculatePageDepths(pages, hierarchy);
var pagesByDepth = this.getPagesByDepth(pages, pageDepths);
// Rebuild ordered pages respecting sibling order within each depth
var finalOrderedPages = [];
var sortedDepths = Array.from(pagesByDepth.keys()).sort(function (a, b) { return a - b; });
sortedDepths.forEach(function (depth) {
var pagesAtDepth = pagesByDepth.get(depth) || [];
// Group pages by parent for sibling ordering
var pagesByParent = new Map();
pagesAtDepth.forEach(function (page) {
var parentId = _this.getParentPageId(page.pageID, hierarchy) || -1;
if (!pagesByParent.has(parentId)) {
pagesByParent.set(parentId, []);
}
pagesByParent.get(parentId).push(page);
});
// Sort each parent group by sibling order
pagesByParent.forEach(function (siblings, parentId) {
var sortedSiblings = _this.sortPagesBySiblingOrder(siblings, orderingData.siblingOrder);
finalOrderedPages.push.apply(finalOrderedPages, sortedSiblings);
});
});
return {
orderedPages: finalOrderedPages,
orderingData: orderingData
};
};
/**
* Sort pages by their sibling order from the sitemap
*/
SitemapHierarchy.prototype.sortPagesBySiblingOrder = function (pages, siblingOrder) {
// Create a map to track the position of each page in the sibling order
var pagePositions = new Map();
// Build position map by following the sibling chain
var position = 0;
var currentPageId = null;
// Find the first page (one that is not a next sibling of any other page)
var allNextSiblings = new Set(Array.from(siblingOrder.values()).filter(function (id) { return id !== null; }));
var firstPage = pages.find(function (page) { return !allNextSiblings.has(page.pageID); });
if (firstPage) {
currentPageId = firstPage.pageID;
// Follow the sibling chain to assign positions
while (currentPageId !== null) {
pagePositions.set(currentPageId, position++);
currentPageId = siblingOrder.get(currentPageId) || null;
}
}
// Sort pages by their positions (pages without positions go to end)
return pages.sort(function (a, b) {
var _a, _b;
var posA = (_a = pagePositions.get(a.pageID)) !== null && _a !== void 0 ? _a : 9999;
var posB = (_b = pagePositions.get(b.pageID)) !== null && _b !== void 0 ? _b : 9999;
return posA - posB;
});
};
/**
* Get parent page ID for a given page
*/
SitemapHierarchy.prototype.getParentPageId = function (pageId, hierarchy) {
for (var _i = 0, _a = Object.entries(hierarchy); _i < _a.length; _i++) {
var _b = _a[_i], parentIdStr = _b[0], childIds = _b[1];
if (childIds.includes(pageId)) {
return parseInt(parentIdStr);
}
}
return null;
};
return SitemapHierarchy;
}());
exports.SitemapHierarchy = SitemapHierarchy;
//# sourceMappingURL=sitemap-hierarchy.js.map