UNPKG

@mdfriday/foundry

Version:

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

205 lines 7.09 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.URL = void 0; const baseurl_1 = require("../valueobject/baseurl"); const paths_1 = require("../../../../pkg/paths"); const text_1 = require("../../../../pkg/text"); class URL { constructor(base, canonical = false) { this.baseURL = null; this.base = base; this.canonical = canonical; this.setup(); } setup() { try { this.baseURL = baseurl_1.BaseURL.fromString(this.base); } catch (err) { throw new Error(err?.message || 'Failed to setup URL'); } } isAbsURL(input) { // Fast path if (input.startsWith('http://') || input.startsWith('https://')) { return [true, null]; } const isAbsolute = /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(input); return [isAbsolute, null]; } startWithBaseUrlRoot(input) { if (!this.baseURL) return false; return input.startsWith(this.baseURL.getRoot(input)); } isProtocolRelPath(input) { return input.startsWith('//'); } trimBaseUrlRoot(input) { if (!this.baseURL) return input; const root = this.baseURL.getRoot(input); if (input.endsWith(root)) { return input.slice(0, -root.length); } return input; } addContextRoot(input) { if (!this.baseURL) return input; let output = input; if (!this.canonical) { output = (0, paths_1.addContextRoot)(this.baseURL.getRoot(input), input); } return output; } handleRootSuffix(input, url) { if (!this.baseURL) return url; if (input === '' && this.baseURL.getRoot(input).endsWith('/')) { url += '/'; } return url; } handlePrefix(url) { if (!url.startsWith('/')) { url = '/' + url; } return url; } relURL(input) { // 如果输入为空,返回空字符串 if (!input) { return ''; } // 检查是否为绝对 URL const [isAbs, err] = this.isAbsURL(input); if (err) { return input; } // 如果是绝对 URL 或协议相对路径,直接返回 if (isAbs || this.isProtocolRelPath(input)) { return input; } if (!this.baseURL) { return input; } // 处理相对路径 let result = input; // 如果输入以 / 开头,说明是相对于根目录的路径 if (input.startsWith('/')) { // 对于相对 BaseURL,直接返回输入 if (this.baseURL.isRelativeURL()) { return input; } // 对于绝对 BaseURL,返回不含路径的主机部分 + 输入路径 return this.baseURL.withoutPath + input; } // 处理不以 / 开头的相对路径(如 "fuse.min.js") // 这种情况下,我们需要将其相对于 baseURL 的路径部分 if (this.baseURL.isRelativeURL()) { // 相对 BaseURL:/public/ + fuse.min.js -> /public/fuse.min.js const basePath = this.baseURL.basePath.endsWith('/') ? this.baseURL.basePath : this.baseURL.basePath + '/'; result = basePath + input; } else { // 绝对 BaseURL:https://example.com/ + fuse.min.js -> https://example.com/fuse.min.js const baseWithPath = this.baseURL.withPath.endsWith('/') ? this.baseURL.withPath : this.baseURL.withPath + '/'; result = baseWithPath + input; } // 规范化路径,移除多余的斜杠,但保护协议中的 :// if (result.includes('://')) { // 对于包含协议的 URL,只规范化路径部分 const protocolIndex = result.indexOf('://'); const protocolPart = result.substring(0, protocolIndex + 3); const pathPart = result.substring(protocolIndex + 3); result = protocolPart + pathPart.replace(/\/+/g, '/'); } else { // 对于相对路径,直接规范化 result = result.replace(/\/+/g, '/'); } return result; } absURL(input, isMultiLang = false, langPrefix = '') { const [isAbs, err] = this.isAbsURL(input); if (err) { return input; } if (isAbs || this.isProtocolRelPath(input)) { return input; } if (!this.baseURL) return input; const baseURL = this.baseURL.getRoot(input); if (isMultiLang && langPrefix) { let hasPrefix = false; let in2 = input; if (input.startsWith('/')) { in2 = input.slice(1); } if (in2 === langPrefix) { hasPrefix = true; } else { hasPrefix = in2.startsWith(langPrefix + '/'); } if (!hasPrefix) { const addSlash = input === '' || input.endsWith('/'); input = this.joinPaths(langPrefix, input); if (addSlash) { input += '/'; } } } return (0, paths_1.makePermalink)(baseURL, input).toString(); } urlize(uri) { return this.urlEscape(this.makePathSanitized(uri)); } makePathSanitized(input) { return this.makePath(input).toLowerCase(); } urlEscape(uri) { try { // 如果包含协议,如 http://、https:// 等,使用 URL 解析 if (/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(uri)) { const parsed = new URL(uri); return parsed.toString(); // 自动 escape 各部分 } // 相对路径(不带协议),手动 encode 各部分 // 尝试解析 pathname、search、hash 三部分 const match = uri.match(/^([^?#]*)(\?[^#]*)?(#.*)?$/); if (!match) return encodeURI(uri); // fallback const [, pathname = '', search = '', hash = ''] = match; // encode URI 的各部分:仅转义非保留字符(保留 "/", "?", "#" 等结构符) return encodeURI(pathname) + (search || '') + (hash || ''); } catch { // 如果格式完全不合法,尽可能保留原始结构并转义 unicode return encodeURI(uri); } } makePath(input) { let s = (0, paths_1.sanitize)(input); s = (0, text_1.removeAccentsString)(s); return s; } basePathNoSlash() { return this.baseURL ? this.baseURL.basePathNoTrailingSlash : ''; } joinPaths(...paths) { return paths .filter(Boolean) .join('/') .replace(/\/+/g, '/') .replace(/\/$/, ''); } } exports.URL = URL; //# sourceMappingURL=url.js.map