UNPKG

multithreading

Version:

The missing standard library for multithreading in JavaScript (Works in the browser, Node.js, Deno, Bun).

178 lines (177 loc) 6.58 kB
/** * Patches dynamic imports to work in Data URIs / Workers. * Universal version: Works in Browser, Deno, and Node.js. */ export function patchDynamicImports(code, callerLocation) { // Normalize callerLocation to be a valid URL (file:// or http://). let normalizedCaller = callerLocation; if (!/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(normalizedCaller)) { // Windows fix normalizedCaller = normalizedCaller.replace(/\\/g, "/"); if (!normalizedCaller.startsWith("/")) { normalizedCaller = "/" + normalizedCaller; } normalizedCaller = "file://" + normalizedCaller; } const importStartPattern = /\bimport\s*\(/g; const replacements = []; let match; while ((match = importStartPattern.exec(code)) !== null) { const importStartIndex = match.index; const openParenIndex = importStartIndex + match[0].length - 1; const argBounds = findArgumentBounds(code, openParenIndex + 1); if (argBounds) { const { start, end } = argBounds; const originalArgument = code.slice(start, end); const isStringLiteral = /^["'`]/.test(originalArgument); // Default assumption: We should apply the fallback patch (new URL wrap) // unless we determine it's a package that should be resolved or skipped. let shouldApplyFallback = true; if (isStringLiteral) { const content = originalArgument.slice(1, -1); // Check if it is an explicit Path or URL (./, ../, /, http:) const isExplicitPath = /^\.{0,2}[/\\]|^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(content); // Handle packages (Bare Specifiers) if (!isExplicitPath) { shouldApplyFallback = false; try { // Try to resolve it to an absolute URL const resolvedUrl = import.meta.resolve(content); replacements.push({ start: start, end: end, text: JSON.stringify(resolvedUrl), }); } catch (e) { // Resolution failed (package not found). } } } // Handle relative paths and variables (fallback) if (shouldApplyFallback) { // Standard base: relative to the file provided by caller let base = `new URL(${JSON.stringify(normalizedCaller)}, import.meta.url).href`; if (isStringLiteral) { const content = originalArgument.slice(1, -1); // If the path is Root-Relative (starts with /), // we cannot rely on import.meta.url if it is a Data URI (worker). // We must use location.origin in the browser, or import.meta.url in Node. if (content.startsWith("/") || content.startsWith("\\")) { base = `(typeof location !== "undefined" ? location.origin : import.meta.url)`; } } const newArgument = `new URL(${originalArgument}, ${base}).href`; replacements.push({ start: start, end: end, text: newArgument, }); } } } // Apply replacements replacements.sort((a, b) => b.start - a.start); let modifiedCode = code; for (let i = 0; i < replacements.length; i++) { const rep = replacements[i]; const before = modifiedCode.slice(0, rep.start); const after = modifiedCode.slice(rep.end); modifiedCode = before + rep.text + after; } return modifiedCode; } // Helper: Finds the bounds of the import() argument function findArgumentBounds(code, startIndex) { let index = startIndex; const len = code.length; let inString = null; let inComment = null; let parenDepth = 0; let argStart = -1; let argEnd = -1; for (; index < len; index++) { const char = code[index]; const nextChar = code[index + 1]; // Strings if (inString) { if (char === "\\" && index + 1 < len) { index++; argEnd = index + 1; continue; } if (char === inString) inString = null; if (argStart === -1) argStart = index; argEnd = index + 1; continue; } // Comments if (inComment) { if (inComment === "//" && char === "\n") inComment = null; else if (inComment === "/*" && char === "*" && nextChar === "/") { inComment = null; index++; } continue; } // Start Strings if (char === '"' || char === "'" || char === "`") { inString = char; if (argStart === -1) argStart = index; argEnd = index + 1; continue; } // Start Comments if (char === "/" && nextChar === "/") { inComment = "//"; index++; continue; } if (char === "/" && nextChar === "*") { inComment = "/*"; index++; continue; } // Syntax if (char === "(") { parenDepth++; if (argStart === -1) argStart = index; argEnd = index + 1; } else if (char === ")") { if (parenDepth === 0) { if (argStart === -1) return null; return { start: argStart, end: argEnd }; } parenDepth--; if (argStart === -1) argStart = index; argEnd = index + 1; } else if (char === ",") { if (parenDepth === 0) { if (argStart === -1) return null; return { start: argStart, end: argEnd }; } if (argStart === -1) argStart = index; argEnd = index + 1; } else { if (!/\s/.test(char)) { if (argStart === -1) argStart = index; argEnd = index + 1; } } } return null; }