@intlayer/core
Version:
Includes core Intlayer functions like translation, dictionary, and utility functions shared across multiple packages.
117 lines (115 loc) • 4.8 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
//#region src/localization/rewriteUtils.ts
/**
* True when rewrite rules are explicitly disabled at build time
* (INTLAYER_ROUTING_REWRITE_RULES === 'false').
*/
const TREE_SHAKE_REWRITE = process.env["INTLAYER_ROUTING_REWRITE_RULES"] === "false";
/**
* Normalizes legacy Record format or extracts specialized rules from RewriteObject.
*/
const getRewriteRules = (rewrite, context = "url") => {
if (!rewrite || TREE_SHAKE_REWRITE) return void 0;
if ("url" in rewrite) return rewrite[context];
return { rules: Object.entries(rewrite).map(([canonical, localized]) => ({
canonical: canonical.startsWith("/") ? canonical.replace(/\[([^\]]+)\]/g, ":$1") : `/${canonical.replace(/\[([^\]]+)\]/g, ":$1")}`,
localized: Object.fromEntries(Object.entries(localized).map(([locale, pattern]) => {
return [locale, pattern?.startsWith("/") ? pattern.replace(/\[([^\]]+)\]/g, ":$1") : `/${(pattern || "").replace(/\[([^\]]+)\]/g, ":$1")}`];
}))
})) };
};
/**
* Converts normalized pattern to Regex.
* Internal syntax supports:
* - :param -> ([^/]+) (one segment)
* - :param* -> (.*) (zero or more segments)
* - :param+ -> (.+) (one or more segments)
* - :param? -> ([^/]*) (zero or one segment)
*/
const patternToRegex = (pattern) => {
const regexString = pattern.replace(/\//g, "\\/").replace(/\\\/:(?:[^/\\*+?]+)\*/g, "(?:\\/(.*))?").replace(/\\\/:(?:[^/\\*+?]+)\?/g, "(?:\\/([^\\/]+))?").replace(/:([^/\\*+?]+)\*/g, "(.*)").replace(/:([^/\\*+?]+)\?/g, "([^\\/]*)").replace(/:([^/\\*+?]+)\+/g, "(.+)").replace(/:([^/\\*+?]+)/g, "([^\\/]+)");
return new RegExp(`^${regexString}$`);
};
/**
* Replaces route parameters in a path with provided values.
*/
const fillPath = (pattern, params) => {
let index = 0;
return pattern.replace(/:([^/\\*+?]+)[*+?]?/g, () => params[index++] ?? "").replace(/\/+/g, "/").replace(/\/$/, "") || "/";
};
/**
* Extract values from a URL based on a pattern.
*/
const extractParams = (url, pattern) => {
const regex = patternToRegex(pattern);
const match = url.match(regex);
return match ? match.slice(1) : null;
};
/**
* Given a localized URL (e.g., "/produits/123"), finds the canonical internal path (e.g., "/products/123").
* If locale is provided, only check for that locale. Otherwise, check for all locales.
*/
const getCanonicalPath = (localizedPath, locale, rewriteRules) => {
if (!rewriteRules || TREE_SHAKE_REWRITE) return localizedPath;
for (const rule of rewriteRules.rules) {
const { canonical, localized } = rule;
const localesToCheck = locale ? [locale] : Object.keys(localized);
for (const loc of localesToCheck) {
const localizedPattern = localized[loc];
if (!localizedPattern) continue;
const params = extractParams(localizedPath, localizedPattern);
if (params) return fillPath(canonical, params);
}
}
return localizedPath;
};
/**
* Given a canonical path (e.g., "/products/123"), finds the localized URL pattern (e.g., "/produits/123").
*/
const getLocalizedPath = (canonicalPath, locale, rewriteRules) => {
if (!rewriteRules || TREE_SHAKE_REWRITE) return {
path: canonicalPath,
isRewritten: false
};
for (const rule of rewriteRules.rules) {
const { canonical, localized } = rule;
const params = extractParams(canonicalPath, canonical);
if (params) {
const targetPattern = localized[locale];
if (targetPattern) return {
path: fillPath(targetPattern, params),
isRewritten: true
};
}
}
return {
path: canonicalPath,
isRewritten: false
};
};
/**
* Returns the internal path for a given canonical path and locale.
* Ensures the locale prefix is present exactly once.
*/
const getInternalPath = (canonicalPath, locale) => {
const pathWithLeadingSlash = canonicalPath.startsWith("/") ? canonicalPath : `/${canonicalPath}`;
if (pathWithLeadingSlash.startsWith(`/${locale}/`) || pathWithLeadingSlash === `/${locale}`) return pathWithLeadingSlash;
return `/${locale}${pathWithLeadingSlash === "/" ? "" : pathWithLeadingSlash}`;
};
/**
* Given a current pathname and locale, returns the pretty localized path if a rewrite rule exists and the path is not already localized.
*/
const getRewritePath = (pathname, locale, rewrite) => {
if (TREE_SHAKE_REWRITE) return void 0;
const rules = getRewriteRules(rewrite, "url");
if (!rules) return void 0;
const { path: localizedPath, isRewritten } = getLocalizedPath(getCanonicalPath(pathname, void 0, rules), locale, rules);
if (isRewritten && localizedPath !== pathname) return localizedPath;
};
//#endregion
exports.getCanonicalPath = getCanonicalPath;
exports.getInternalPath = getInternalPath;
exports.getLocalizedPath = getLocalizedPath;
exports.getRewritePath = getRewritePath;
exports.getRewriteRules = getRewriteRules;
//# sourceMappingURL=rewriteUtils.cjs.map