UNPKG

@mdfriday/foundry

Version:

The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.

450 lines 16.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PathUtils = exports.Path = void 0; const type_1 = require("../type"); const pathcomponents_1 = require("../vo/pathcomponents"); /** * Path entity implementation * Represents a parsed path with all its components and operations */ class Path { constructor(components) { this.components = components; this.shouldTrimLeadingSlash = false; } // PathInfo implementation component() { // Use the component stored in PathComponents if available if (this.components.component) { return this.components.component; } // Fallback to parsing from path for legacy support const path = this.components.normalized; const parts = path.split('/').filter(p => p.length > 0); if (parts.length === 0) { return 'content'; // Root path defaults to content } const firstPart = parts[0]; // Map known Hugo components const componentMap = { 'static': 'static', 'layouts': 'layouts', 'themes': 'themes', 'archetypes': 'archetypes', 'data': 'data', 'i18n': 'i18n', 'assets': 'assets' }; return componentMap[firstPart] || 'content'; } path() { return this.norm(this.components.normalized); } name() { if (this.components.positions.containerHigh > 0) { return this.components.normalized.substring(this.components.positions.containerHigh); } return this.components.normalized; } nameNoExt() { const firstId = this.components.firstIdentifier(); if (firstId) { return this.components.normalized.substring(this.components.positions.containerHigh, firstId.low - 1); } return this.components.normalized.substring(this.components.positions.containerHigh); } nameNoLang() { // Find language identifier position if (this.components.identifiers.length >= 2) { const lastId = this.components.identifiers[this.components.identifiers.length - 1]; // extension const secondToLastId = this.components.identifiers[this.components.identifiers.length - 2]; // potential language const langStr = this.components.normalized.substring(secondToLastId.low, secondToLastId.high); const knownLangCodes = ['en', 'fr', 'es', 'de', 'zh', 'ja', 'ko', 'pt', 'it', 'ru', 'ar']; if (knownLangCodes.includes(langStr)) { // Remove the language part (including the dot before it) const nameStart = this.components.positions.containerHigh; const beforeLang = this.components.normalized.substring(nameStart, secondToLastId.low - 1); const afterLang = this.components.normalized.substring(lastId.low - 1); // include dot before extension return beforeLang + afterLang; } } return this.name(); } dir() { let d = ''; if (this.components.positions.containerHigh > 0) { d = this.components.normalized.substring(0, this.components.positions.containerHigh - 1); } if (d === '') { d = '/'; } return this.norm(d); } ext() { if (this.components.identifiers.length === 0) { return ''; } // Extension is always the last identifier const lastIdentifier = this.components.identifiers[this.components.identifiers.length - 1]; const extStr = this.components.normalized.substring(lastIdentifier.low, lastIdentifier.high); return extStr ? '.' + extStr : ''; } lang() { // Look for language identifier (typically second to last identifier before extension) if (this.components.identifiers.length >= 2) { const secondToLastId = this.components.identifiers[this.components.identifiers.length - 2]; const langStr = this.components.normalized.substring(secondToLastId.low, secondToLastId.high); // Check if it's a known language code const knownLangCodes = ['en', 'fr', 'es', 'de', 'zh', 'ja', 'ko', 'pt', 'it', 'ru', 'ar']; if (knownLangCodes.includes(langStr)) { return '.' + langStr; } } return ''; } section() { if (this.components.positions.sectionHigh <= 0) { return ''; } const sectionPath = this.components.normalized.substring(1, this.components.positions.sectionHigh); // For root index files like /_index.md, section should be empty if (sectionPath === '_index.md' || sectionPath === 'index.md' || sectionPath.endsWith('/_index.md') || sectionPath.endsWith('/index.md')) { return ''; } return this.norm(sectionPath); } sections() { // Get the directory path (everything before the filename) const dirPath = this.dir(); // If directory is root, return empty array if (dirPath === '/' || dirPath === '') { return []; } // Remove leading slash and split by slash const cleanPath = dirPath.startsWith('/') ? dirPath.substring(1) : dirPath; const sections = []; const pathParts = cleanPath.split('/').filter(part => part.length > 0); // Build cumulative paths: docs -> docs/table-of-contents -> etc. let currentPath = ''; for (const part of pathParts) { if (currentPath === '') { currentPath = part; } else { currentPath += '/' + part; } sections.push(this.norm(currentPath)); } return sections; } container() { if (this.components.positions.containerLow === -1) { return ''; } return this.norm(this.components.normalized.substring(this.components.positions.containerLow, this.components.positions.containerHigh - 1)); } containerDir() { if (this.isLeafBundle()) { // For leaf bundles like /blog/my-post/index.md, return the parent dir (/blog) // const dirPath = this.dir(); // const lastSlash = dirPath.lastIndexOf('/'); // if (lastSlash <= 0) { // return '/'; // } // return dirPath.substring(0, lastSlash); return this.dir(); } else if (this.isBranchBundle()) { // For branch bundles like /blog/_index.md, return the dir itself (/blog) return this.dir(); } else { // For single pages like /blog/post.md, return the dir (/blog) return this.dir(); } } // PathOperations implementation base() { if (this.isBranchBundle() && this.components.normalized === '/_index.md') { // Root branch bundle should return "/" return '/'; } else if (this.isLeafBundle()) { return this.baseInternal(false, true); } else if (this.isContent() && !this.isBundle()) { // For content single files, remove extension return this.pathNoIdentifier(); } else if (!this.isContent()) { // For non-content files, preserve the full path including extension return this.path(); } else if (this.isBundle()) { // For bundles, return path to container return this.baseInternal(false, true); } return this.baseInternal(!this.isContentPage(), this.isBundle()); } baseNoLeadingSlash() { return this.base().substring(1); } baseNameNoIdentifier() { if (this.isBundle()) { return this.container(); } return this.nameNoIdentifier(); } nameNoIdentifier() { if (this.components.identifiers.length === 0) { return this.name(); } // Remove all identifiers from the name const firstIdentifier = this.components.identifiers[0]; const nameStart = this.components.positions.containerHigh; // Find the dot before the first identifier and take everything before it return this.components.normalized.substring(nameStart, firstIdentifier.low - 1); } pathNoLang() { return this.baseInternal(true, false); } pathNoIdentifier() { if (this.components.identifiers.length === 0) { return this.path(); } // Remove all identifiers from the path const firstIdentifier = this.components.identifiers[0]; // Find the dot before the first identifier const pathBeforeIdentifiers = this.components.normalized.substring(0, firstIdentifier.low - 1); return this.norm(pathBeforeIdentifiers); } pathRel(owner) { let ob = owner.base(); if (!ob.endsWith('/')) { ob += '/'; } return this.path().replace(new RegExp('^' + this.escapeRegExp(ob)), ''); } baseRel(owner) { let ob = owner.base(); if (ob === '/') { ob = ''; } return this.base().substring(ob.length + 1); } trimLeadingSlash() { const clonedComponents = this.components.clone(); const newPath = new Path(clonedComponents); newPath.setShouldTrimLeadingSlash(true); return newPath; } identifier(index) { // Reorder identifiers: extension first, then language codes const totalIdentifiers = this.components.identifiers.length; if (totalIdentifiers === 0 || index < 0 || index >= totalIdentifiers) { return ''; } let actualIndex; if (totalIdentifiers === 1) { // Only one identifier (extension) actualIndex = 0; } else { // Multiple identifiers: reverse order (extension first, then language codes) if (index === 0) { // First requested = last actual (extension) actualIndex = totalIdentifiers - 1; } else { // Other indices = totalIdentifiers - 1 - index actualIndex = totalIdentifiers - 1 - index; } } const identifierStr = this.identifierAsString(actualIndex); return identifierStr ? '.' + identifierStr : ''; } identifiers() { // Return identifiers in reordered format (extension first) const result = []; const totalIdentifiers = this.components.identifiers.length; for (let i = 0; i < totalIdentifiers; i++) { const identifier = this.identifier(i); if (identifier) { result.push(identifier); } } return result; } // PathMetadata implementation bundleType() { return this.components.bundleType; } isContent() { return this.bundleType() >= type_1.PathType.ContentResource; } isBundle() { return this.bundleType() > type_1.PathType.Leaf; } isBranchBundle() { return this.bundleType() === type_1.PathType.Branch; } isLeafBundle() { return this.bundleType() === type_1.PathType.Leaf; } isHTML() { const ext = this.ext().toLowerCase(); return type_1.PATH_CONSTANTS.HTML_EXTENSIONS.some(htmlExt => htmlExt === ext); } disabled() { return this.components.disabled; } forBundleType(type) { const newComponents = this.components.withBundleType(type); return new Path(newComponents); } // Main Path interface implementation unnormalized() { if (!this._unnormalized) { // If original and normalized are the same, return self if (this.components.original === this.components.normalized) { this._unnormalized = this; } else { // Create unnormalized version const unnormalizedComponents = new pathcomponents_1.PathComponentsImpl(this.components.original, this.components.original, this.components.positions, this.components.identifiers, this.components.bundleType, this.components.disabled); this._unnormalized = new Path(unnormalizedComponents); } } return this._unnormalized; } // Private helper methods setShouldTrimLeadingSlash(value) { this.shouldTrimLeadingSlash = value; } norm(s) { if (this.shouldTrimLeadingSlash) { return s.startsWith('/') ? s.substring(1) : s; } return s; } isContentPage() { return this.bundleType() >= type_1.PathType.ContentSingle; } baseInternal(preserveExt, isBundle) { if (this.components.identifiers.length === 0) { return this.norm(this.components.normalized); } if (preserveExt && this.components.identifiers.length === 1) { return this.norm(this.components.normalized); } const lastId = this.components.identifiers[this.components.identifiers.length - 1]; let high = lastId.low - 1; if (isBundle) { high = this.components.positions.containerHigh - 1; } if (high === 0) { high++; } if (!preserveExt) { return this.norm(this.components.normalized.substring(0, high)); } // For files like txt, preserve the extension const firstId = this.components.identifiers[0]; return this.norm(this.components.normalized.substring(0, high) + this.components.normalized.substring(firstId.low - 1, firstId.high)); } identifierAsString(i) { const index = this.identifierIndex(i); if (index === -1) { return ''; } const id = this.components.identifiers[index]; return this.components.normalized.substring(id.low, id.high); } identifierIndex(i) { if (i < 0 || i >= this.components.identifiers.length) { return -1; } return i; } escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } // Utility methods toString() { return `Path{path="${this.path()}", component="${this.component()}", bundleType=${type_1.PathType[this.bundleType()]}}`; } /** * Check if two paths are equal */ equals(other) { return this.path() === other.path() && this.component() === other.component() && this.bundleType() === other.bundleType(); } /** * Get a hash code for this path */ hashCode() { return `${this.component()}:${this.path()}:${this.bundleType()}`; } } exports.Path = Path; /** * Utility functions for working with Path instances */ class PathUtils { /** * Create a path from string components */ static fromString(component, path) { const components = new pathcomponents_1.PathComponentsImpl(path, path, { containerLow: -1, containerHigh: -1, sectionHigh: -1, identifierLanguage: -1 }, [], type_1.PathType.File, false); return new Path(components); } /** * Check if a path has a specific extension */ static hasExtension(path, extension) { const pathExt = path.ext(); const targetExt = extension.startsWith('.') ? extension : '.' + extension; return pathExt.toLowerCase() === targetExt.toLowerCase(); } /** * Check if child path is under parent path */ static isUnder(child, parent) { const childPath = child.path(); let parentPath; // For branch bundles like /blog/_index.md, the parent path is the directory (/blog) if (parent.isBranchBundle()) { parentPath = parent.dir(); } else { parentPath = parent.path(); } // Ensure paths end with slash for proper comparison const normalizedParent = parentPath === '/' ? '/' : parentPath + '/'; return childPath !== parentPath && childPath.startsWith(normalizedParent); } /** * Get the relative path from one path to another */ static relativeTo(from, to) { return to.pathRel(from); } /** * Compare two paths for sorting */ static compare(a, b) { const pathCmp = a.path().localeCompare(b.path()); if (pathCmp !== 0) return pathCmp; const componentCmp = a.component().localeCompare(b.component()); if (componentCmp !== 0) return componentCmp; return a.bundleType() - b.bundleType(); } } exports.PathUtils = PathUtils; //# sourceMappingURL=path.js.map