njsparser/js/parser/urls.js
2026-02-15 01:34:37 +01:00

102 lines
2.7 KiB
JavaScript

/**
* URL utilities for NextJS static paths
*/
import { makeTree } from '../utils.js';
export const _N = '/_next';
export const _NS = `${_N}/static/`;
/**
* Get all NextJS static URLs from HTML
* @param {string} html - HTML string
* @param {DOMParser} DOMParser - DOMParser instance
* @returns {Array<string>|null} Array of URLs or null
*/
export function getNextStaticUrls(html, DOMParser) {
const doc = makeTree(html, DOMParser);
const result = [];
// Find all elements with href containing /_next/static/
const hrefElements = doc.querySelectorAll(`[href*="${_NS}"]`);
for (const el of hrefElements) {
const href = el.getAttribute('href');
if (href) result.push(href);
}
// Find all elements with src containing /_next/static/
const srcElements = doc.querySelectorAll(`[src*="${_NS}"]`);
for (const el of srcElements) {
const src = el.getAttribute('src');
if (src) result.push(src);
}
return result.length > 0 ? result : null;
}
/**
* Get base path from URLs or HTML
* @param {string|Array<string>} value - HTML string or array of URLs
* @param {DOMParser} DOMParser - DOMParser instance (required if value is HTML)
* @param {boolean} removeDomain - Remove domain from absolute URLs
* @returns {string|null} Base path or null
*/
export function getBasePath(value, DOMParser = null, removeDomain = false) {
let paths;
if (Array.isArray(value)) {
paths = value;
} else {
if (!DOMParser) {
throw new Error('DOMParser required when value is HTML string');
}
paths = getNextStaticUrls(value, DOMParser);
if (!paths) {
return null;
}
}
let globalIndex = null;
let basePath = null;
for (const path of paths) {
const index = path.lastIndexOf(_NS);
if (index < 0) {
throw new Error(`can't find '${_NS}' in path=${path}`);
}
if (globalIndex === null) {
globalIndex = index;
} else if (index !== globalIndex) {
throw new Error(`index=${index} of '${_NS}' in path=${path} is != globalIndex=${globalIndex}`);
}
const candidate = path.substring(0, index);
if (basePath === null) {
basePath = candidate;
} else if (candidate !== basePath) {
throw new Error(`basePath=${candidate} extracted from path=${path} is != globalBasePath=${basePath}`);
}
}
let result = basePath ?? '';
// Remove domain if requested
if (removeDomain) {
try {
const url = new URL(result);
const hostname = url.hostname;
if (hostname) {
const parts = result.split(hostname);
if (parts.length > 1) {
result = parts[1];
}
}
} catch (e) {
// Not a valid URL, ignore
}
}
return result;
}