@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
JavaScript
"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