UNPKG

use-m

Version:

use-m: dynamically import any JavaScript module

898 lines (820 loc) 33.3 kB
const extractCallerContext = (stack) => { // In browser environment, use the current document URL as fallback if (typeof window !== 'undefined' && window.location) { // For inline scripts in HTML, use the document's URL // This will be the fallback if we can't extract from stack const documentUrl = window.location.href; // Try to extract from stack first, but we'll fallback to document URL if (!stack) return documentUrl; } else if (!stack) { return null; } const lines = stack.split('\n'); // Look for the first file that isn't use.mjs - skip the first few frames // to get past our internal function calls for (const line of lines) { // Skip the first few frames which are internal to use.mjs if (line.includes('extractCallerContext') || line.includes('_use') || line.includes('makeUse') || line.includes('<anonymous>') && line.includes('/use.mjs')) { continue; } // Try to match http(s):// URLs for browser environments let match = line.match(/https?:\/\/[^\s)]+/); if (match && !match[0].endsWith('/use.mjs') && !match[0].endsWith('/use.js')) { // Remove line:column numbers if present const url = match[0].replace(/:\d+:\d+$/, ''); return url; } // Try to match file:// URLs match = line.match(/file:\/\/[^\s)]+/); if (match && !match[0].endsWith('/use.mjs')) { // Remove line:column numbers if present const url = match[0].replace(/:\d+:\d+$/, ''); return url; } // Special handling for Jest environment // Jest paths often look like: at Object.<anonymous> (/path/to/test.mjs:7:24) // Or: at /path/to/test.mjs:7:24 if (line.includes('.test.') || line.includes('.spec.')) { // Try to extract the actual test file path from Jest stack traces match = line.match(/\(([^)]+\.(?:test|spec)\.[^)]+):\d+:\d+\)/); if (!match) { match = line.match(/([^(\s]+\.(?:test|spec)\.[^(\s:]+):\d+:\d+/); } if (match && match[1]) { const testPath = match[1]; // Convert to file:// URL format if it's an absolute path if (testPath.startsWith('/')) { return `file://${testPath}`; } } } // For Node/Deno, try to match absolute paths (improved to handle more cases) match = line.match(/at\s+(?:Object\.<anonymous>\s+)?(?:async\s+)?[(]?(\/[^\s:)]+\.(?:m?js|json))(?::\d+:\d+)?\)?/); if (match && !match[1].endsWith('/use.mjs') && !match[1].includes('node_modules')) { return 'file://' + match[1]; } // Alternative pattern for Jest and other environments match = line.match(/at\s+[^(]*\(([^)]+\.(?:m?js|json)):\d+:\d+\)/); if (match && !match[1].endsWith('/use.mjs') && !match[1].includes('node_modules')) { return 'file://' + (match[1].startsWith('/') ? match[1] : '/' + match[1]); } } return null; }; const parseModuleSpecifier = (moduleSpecifier) => { if (!moduleSpecifier || typeof moduleSpecifier !== 'string' || moduleSpecifier.length <= 0) { throw new Error( `Name for a package to be imported is not provided. Please specify package name and an optional version (e.g., 'lodash', 'lodash@4.17.21' or '@chakra-ui/react@1.0.0').` ); } const regex = /^(?<packageName>(@[^@/]+\/)?[^@/]+)?(?:@(?<version>[^/]*))?(?<modulePath>(?:\/[^@]+)*)?$/; const match = moduleSpecifier.match(regex); if (!match || typeof match.groups.packageName !== 'string' || match.groups.packageName.trim() === '') { throw new Error( `Failed to parse package identifier '${moduleSpecifier}'. Please specify a package name, and an optional version (e.g.: 'lodash', 'lodash@4.17.21' or '@chakra-ui/react@1.0.0').` ); } let { packageName, version, modulePath } = match.groups; if (typeof version !== 'string' || version.trim() === '') { version = 'latest'; } if (typeof modulePath !== 'string' || modulePath.trim() === '') { modulePath = ''; } return { packageName, version, modulePath }; } // Built-in modules that we support across all environments // Always use lowercase names for consistency const supportedBuiltins = { // Universal modules 'console': { browser: () => ({ default: console, log: console.log, error: console.error, warn: console.warn, info: console.info }), node: () => import('node:console').then(m => ({ default: m.Console, ...m })) }, 'crypto': { browser: () => ({ default: crypto, subtle: crypto.subtle }), node: () => import('node:crypto').then(m => ({ default: m, ...m })) }, 'url': { browser: () => ({ default: URL, URL, URLSearchParams }), node: () => import('node:url').then(m => ({ default: m, ...m })) }, 'performance': { browser: () => ({ default: performance, now: performance.now.bind(performance) }), node: () => import('node:perf_hooks').then(m => ({ default: m.performance, performance: m.performance, now: m.performance.now.bind(m.performance), ...m })) }, // Node.js/Bun only modules 'fs': { browser: null, // Not available in browser node: () => import('node:fs').then(m => ({ default: m, ...m })) }, 'fs/promises': { browser: null, // Not available in browser node: async () => { const runtime = typeof Bun !== 'undefined' ? 'Bun' : typeof Deno !== 'undefined' ? 'Deno' : 'Node.js'; // For Bun and Deno, use a different approach since their node:fs/promises may not be fully compatible if (runtime === 'Bun' || runtime === 'Deno') { console.log(`[${runtime}] Using promisify fallback for fs/promises compatibility`); try { const fs = await import('node:fs'); const { promisify } = await import('node:util'); // Create wrapper functions that match native fs/promises signatures // These need to have the correct .length property and be async functions const createAsyncWrapper = (promisifiedFn, expectedLength) => { // Create an async function with the correct length const wrapper = { 1: async (a) => promisifiedFn(a), 2: async (a, b) => promisifiedFn(a, b), 3: async (a, b, c) => promisifiedFn(a, b, c), 4: async (a, b, c, d) => promisifiedFn(a, b, c, d) }[expectedLength]; // Copy the name if possible try { Object.defineProperty(wrapper, 'name', { value: promisifiedFn.name }); } catch (e) { // Ignore if name can't be set } return wrapper || promisifiedFn; }; // Helper to safely promisify functions that may not exist const safePromisify = (fn, expectedLength) => { if (typeof fn !== 'function') { return undefined; } return createAsyncWrapper(promisify(fn), expectedLength); }; const promisifiedFs = { access: safePromisify(fs.access, 2), appendFile: safePromisify(fs.appendFile, 3), chmod: safePromisify(fs.chmod, 2), chown: safePromisify(fs.chown, 3), copyFile: safePromisify(fs.copyFile, 3), lchmod: safePromisify(fs.lchmod, 2), lchown: safePromisify(fs.lchown, 3), link: safePromisify(fs.link, 2), lstat: safePromisify(fs.lstat, 2), mkdir: safePromisify(fs.mkdir, 2), mkdtemp: safePromisify(fs.mkdtemp, 2), open: safePromisify(fs.open, 3), readdir: safePromisify(fs.readdir, 2), readFile: safePromisify(fs.readFile, 2), readlink: safePromisify(fs.readlink, 2), realpath: safePromisify(fs.realpath, 2), rename: safePromisify(fs.rename, 2), rmdir: safePromisify(fs.rmdir, 2), stat: safePromisify(fs.stat, 2), symlink: safePromisify(fs.symlink, 3), truncate: safePromisify(fs.truncate, 2), unlink: safePromisify(fs.unlink, 1), utimes: safePromisify(fs.utimes, 3), writeFile: safePromisify(fs.writeFile, 3), constants: fs.constants }; // Add newer functions if they exist if (fs.rm) promisifiedFs.rm = safePromisify(fs.rm, 2); if (fs.cp) promisifiedFs.cp = safePromisify(fs.cp, 3); if (fs.lutimes) promisifiedFs.lutimes = safePromisify(fs.lutimes, 3); if (fs.opendir) promisifiedFs.opendir = safePromisify(fs.opendir, 2); if (fs.statfs) promisifiedFs.statfs = safePromisify(fs.statfs, 2); if (fs.watch) promisifiedFs.watch = fs.watch.bind(fs); // watch is not callback-based console.log(`[${runtime}] Fallback mkdir.length:`, promisifiedFs.mkdir?.length); console.log(`[${runtime}] Fallback mkdir.constructor.name:`, promisifiedFs.mkdir?.constructor.name); return { default: promisifiedFs, ...promisifiedFs }; } catch (error) { throw new Error(`Failed to create fs/promises fallback for ${runtime}: ${error.message}`, { cause: error }); } } // For Node.js, use the native implementation try { const m = await import('node:fs/promises'); return { default: m, ...m }; } catch (error) { throw new Error(`Failed to load fs/promises module: ${error.message}`, { cause: error }); } } }, 'dns/promises': { browser: null, // Not available in browser node: async () => { const m = await import('node:dns/promises'); return { default: m, ...m }; } }, 'stream/promises': { browser: null, // Not available in browser node: async () => { const m = await import('node:stream/promises'); return { default: m, ...m }; } }, 'readline/promises': { browser: null, // Not available in browser node: async () => { const m = await import('node:readline/promises'); return { default: m, ...m }; } }, 'timers/promises': { browser: null, // Not available in browser node: async () => { const m = await import('node:timers/promises'); return { default: m, ...m }; } }, 'path': { browser: null, // Not available in browser node: () => import('node:path').then(m => ({ default: m, ...m })) }, 'os': { browser: null, // Not available in browser node: () => import('node:os').then(m => ({ default: m, ...m })) }, 'util': { browser: null, // Not available in browser node: () => import('node:util').then(m => ({ default: m, ...m })) }, 'events': { browser: null, // Not available in browser node: () => import('node:events').then(m => ({ default: m.EventEmitter, EventEmitter: m.EventEmitter, ...m })) }, 'stream': { browser: null, // Not available in browser node: () => import('node:stream').then(m => ({ default: m.Stream, Stream: m.Stream, ...m })) }, 'buffer': { browser: null, // Not available in browser (would need polyfill) node: () => import('node:buffer').then(m => ({ default: m, Buffer: m.Buffer, ...m })) }, 'process': { browser: null, // Not available in browser node: () => { if (typeof Deno !== 'undefined') { // Deno 2.x has a process global, use it if available if (typeof process !== 'undefined') { // In Deno, process is an EventEmitter and spreading doesn't work properly // We need to explicitly copy the properties we need const proc = { default: process, pid: process.pid, platform: process.platform, version: process.version, versions: process.versions, argv: process.argv, env: process.env, exit: process.exit, cwd: process.cwd, chdir: process.chdir, // Add any other commonly used process properties nextTick: process.nextTick, stdout: process.stdout, stderr: process.stderr, stdin: process.stdin, }; return proc; } // This shouldn't happen but provide a fallback throw new Error(`Failed to resolve 'process' module in Deno environment.`); } return ({ default: process, ...process }); } }, 'child_process': { browser: null, node: () => import('node:child_process').then(m => ({ default: m, ...m })) }, 'http': { browser: null, node: () => import('node:http').then(m => ({ default: m, ...m })) }, 'https': { browser: null, node: () => import('node:https').then(m => ({ default: m, ...m })) }, 'net': { browser: null, node: () => import('node:net').then(m => ({ default: m, ...m })) }, 'dns': { browser: null, node: () => import('node:dns').then(m => ({ default: m, ...m })) }, 'zlib': { browser: null, node: () => import('node:zlib').then(m => ({ default: m, ...m })) }, 'querystring': { browser: null, node: () => import('node:querystring').then(m => ({ default: m, ...m })) }, 'assert': { browser: null, node: () => import('node:assert').then(m => ({ default: m.default || m, ...m })) } }; const resolvers = { builtin: async (moduleSpecifier, pathResolver) => { const { packageName, modulePath } = parseModuleSpecifier(moduleSpecifier); // Handle built-in modules with subpaths like 'node:fs/promises' let moduleName; if (packageName.startsWith('node:')) { // For node: modules, include the path in the module name moduleName = packageName.slice(5) + modulePath; } else { moduleName = packageName + modulePath; } // Check if we support this built-in module if (supportedBuiltins[moduleName]) { const builtinConfig = supportedBuiltins[moduleName]; if (!builtinConfig) { throw new Error(`Built-in module '${moduleName}' is not supported.`); } // Determine environment const isBrowser = typeof window !== 'undefined'; const environment = isBrowser ? 'browser' : 'node'; const moduleFactory = builtinConfig[environment]; if (!moduleFactory) { throw new Error(`Built-in module '${moduleName}' is not available in ${environment} environment.`); } try { // Execute the factory function to get the module const result = await moduleFactory(); return result; } catch (error) { throw new Error(`Failed to load built-in module '${moduleName}' in ${environment} environment.`, { cause: error }); } } // Not a supported built-in module return null; }, relative: async (moduleSpecifier, pathResolver, callerContext) => { // Check if this is a relative path (supports any depth: ./, ../, ../../, etc.) if (!moduleSpecifier.startsWith('./') && !moduleSpecifier.startsWith('../')) { return null; } // Try to get the caller's URL from the context or stack trace let callerUrl = callerContext; let resolvedPath = null; // If we have a caller URL, resolve relative to it if (callerUrl && (callerUrl.startsWith('file://') || callerUrl.startsWith('http://') || callerUrl.startsWith('https://'))) { try { // Try URL-based resolution for both file:// and http(s):// URLs const url = new URL(moduleSpecifier, callerUrl); // For Bun, return pathname instead of full URL if (typeof Bun !== 'undefined' && callerUrl.startsWith('file://')) { resolvedPath = url.pathname; } else { resolvedPath = url.href; } } catch (error) { // Fallback for non-URL basePath (only for file:// URLs) if (callerUrl.startsWith('file://')) { const path = await import('node:path'); const normalizedPath = new URL(callerUrl).pathname; resolvedPath = path.resolve(path.dirname(normalizedPath), moduleSpecifier); } } } // If we couldn't resolve with URL, try pathResolver if (!resolvedPath) { if (!pathResolver) { throw new Error('Path resolver is required for relative path resolution.'); } try { // Use the provided pathResolver to resolve the relative path resolvedPath = await pathResolver(moduleSpecifier); } catch (error) { throw new Error(`Failed to resolve relative path '${moduleSpecifier}'.`, { cause: error }); } } // Import the module and return it // Check if this is a JSON file and handle it specially if (resolvedPath.endsWith('.json')) { try { // For JSON files, we need to use import assertions const module = await import(resolvedPath, { with: { type: 'json' } }); return module.default || module; } catch (error) { // Fallback to baseUse if import assertions fail return baseUse(resolvedPath); } } return baseUse(resolvedPath); }, npm: async (moduleSpecifier, pathResolver) => { const path = await import('node:path'); const { exec } = await import('node:child_process'); const { promisify } = await import('node:util'); const { stat, readFile } = await import('node:fs/promises'); const execAsync = promisify(exec); if (!pathResolver) { throw new Error('Failed to get the current resolver.'); } const fileExists = async (filePath) => { try { const stats = await stat(filePath); return stats.isFile(); } catch (error) { if (error.code !== 'ENOENT') { throw error; } return false; } }; const directoryExists = async (directoryPath) => { try { const stats = await stat(directoryPath); return stats.isDirectory(); } catch (error) { if (error.code !== 'ENOENT') { throw error; } return false; } }; const tryResolveModule = async (packagePath) => { try { return await pathResolver(packagePath); } catch (error) { if (error.code !== 'MODULE_NOT_FOUND') { throw error; } // Attempt to resolve paths like 'yargs@18.0.0/helpers' to 'yargs-v-18.0.0/helpers/helpers.mjs' if (await directoryExists(packagePath)) { const directoryName = path.basename(packagePath); const resolvedPath = await tryResolveModule(path.join(packagePath, directoryName)); if (resolvedPath) { return resolvedPath; } // Attempt to resolve paths like 'octokit/core@latest' to 'octokit-core-v-latest/dist-src/index.js' (as it written in package.json) const packageJsonPath = path.join(packagePath, 'package.json'); if (await fileExists(packageJsonPath)) { const packageJson = await readFile(packageJsonPath, 'utf8'); const parsed = JSON.parse(packageJson); const exp = parsed.exports; if (exp) { let target = null; if (typeof exp === 'string') { target = exp; } else { const root = exp['.'] ?? exp; if (typeof root === 'string') { target = root; } else if (root && typeof root === 'object') { target = root.import || root.default || root.require || root.module || root.browser || null; } } if (typeof target === 'string') { const updatedPath = path.join(packagePath, target); return await tryResolveModule(updatedPath); } } } return null; } return null; } }; const getLatestVersion = async (packageName) => { const { stdout: version } = await execAsync(`npm show ${packageName} version`); return version.trim(); }; const getInstalledPackageVersion = async (packagePath) => { try { const packageJsonPath = path.join(packagePath, 'package.json'); const data = await readFile(packageJsonPath, 'utf8'); const { version } = JSON.parse(data); return version; } catch { return null; } }; const ensurePackageInstalled = async ({ packageName, version }) => { const alias = `${packageName.replace('@', '').replace('/', '-')}-v-${version}`; const { stdout: globalModulesPath } = await execAsync('npm root -g'); const packagePath = path.join(globalModulesPath.trim(), alias); if (version !== 'latest' && await directoryExists(packagePath)) { return packagePath; } if (version === 'latest') { const latestVersion = await getLatestVersion(packageName); const installedVersion = await getInstalledPackageVersion(packagePath); if (installedVersion === latestVersion) { return packagePath; } } try { await execAsync(`npm install -g ${alias}@npm:${packageName}@${version}`, { stdio: 'ignore' }); } catch (error) { throw new Error(`Failed to install ${packageName}@${version} globally.`, { cause: error }); } return packagePath; }; const { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); const packagePath = await ensurePackageInstalled({ packageName, version }); const packageModulePath = modulePath ? path.join(packagePath, modulePath) : packagePath; const resolvedPath = await tryResolveModule(packageModulePath); if (!resolvedPath) { throw new Error(`Failed to resolve the path to '${moduleSpecifier}' from '${packageModulePath}'.`); } return resolvedPath; }, bun: async (moduleSpecifier, pathResolver) => { const path = await import('node:path'); const { exec } = await import('node:child_process'); const { promisify } = await import('node:util'); const { stat, readFile } = await import('node:fs/promises'); const execAsync = promisify(exec); if (!pathResolver) { throw new Error('Failed to get the current resolver.'); } const fileExists = async (filePath) => { try { const stats = await stat(filePath); return stats.isFile(); } catch (error) { if (error.code !== 'ENOENT') { throw error; } return false; } }; const directoryExists = async (directoryPath) => { try { const stats = await stat(directoryPath); return stats.isDirectory(); } catch (error) { if (error.code !== 'ENOENT') { throw error; } return false; } }; const tryResolveModule = async (packagePath) => { try { return await pathResolver(packagePath); } catch (error) { if (error.code !== 'MODULE_NOT_FOUND') { throw error; } if (await directoryExists(packagePath)) { const directoryName = path.basename(packagePath); const resolvedPath = await tryResolveModule(path.join(packagePath, directoryName)); if (resolvedPath) { return resolvedPath; } const packageJsonPath = path.join(packagePath, 'package.json'); if (await fileExists(packageJsonPath)) { const packageJson = await readFile(packageJsonPath, 'utf8'); const parsed = JSON.parse(packageJson); const exp = parsed.exports; if (exp) { let target = null; if (typeof exp === 'string') { target = exp; } else { const root = exp['.'] ?? exp; if (typeof root === 'string') { target = root; } else if (root && typeof root === 'object') { target = root.import || root.default || root.require || root.module || root.browser || null; } } if (typeof target === 'string') { const updatedPath = path.join(packagePath, target); return await tryResolveModule(updatedPath); } } } return null; } return null; } }; const ensurePackageInstalled = async ({ packageName, version }) => { const alias = `${packageName.replace('@', '').replace('/', '-')}-v-${version}`; let binDir = ''; try { const { stdout } = await execAsync('bun pm bin -g'); binDir = stdout.trim(); } catch (error) { // In CI or fresh environments, the global directory might not exist // Try to get the default Bun install path const home = process.env.HOME || process.env.USERPROFILE; if (home) { binDir = path.join(home, '.bun', 'bin'); } else { throw new Error('Unable to determine Bun global directory.', { cause: error }); } } const bunInstallRoot = path.resolve(binDir, '..'); const globalModulesPath = path.join(bunInstallRoot, 'install', 'global', 'node_modules'); const packagePath = path.join(globalModulesPath, alias); if (version !== 'latest' && await directoryExists(packagePath)) { return packagePath; } try { await execAsync(`bun add -g ${alias}@npm:${packageName}@${version} --silent`, { stdio: 'ignore' }); } catch (error) { throw new Error(`Failed to install ${packageName}@${version} globally with Bun.`, { cause: error }); } return packagePath; }; const { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); const packagePath = await ensurePackageInstalled({ packageName, version }); const packageModulePath = modulePath ? path.join(packagePath, modulePath) : packagePath; const resolvedPath = await tryResolveModule(packageModulePath); if (!resolvedPath) { throw new Error(`Failed to resolve the path to '${moduleSpecifier}' from '${packageModulePath}'.`); } return resolvedPath; }, deno: async (moduleSpecifier, pathResolver) => { const { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); // Use esm.sh as the default CDN for Deno, which provides good Deno compatibility const resolvedPath = `https://esm.sh/${packageName}@${version}${modulePath}`; return resolvedPath; }, skypack: async (moduleSpecifier, pathResolver) => { const resolvedPath = `https://cdn.skypack.dev/${moduleSpecifier}`; return resolvedPath; }, jsdelivr: async (moduleSpecifier, pathResolver) => { const { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); // If no modulePath is provided, append /{packageName}.js let path = modulePath ? modulePath : `/${packageName}`; if (/\.(mc)?js$/.test(path) === false) { path += '.js'; } const resolvedPath = `https://cdn.jsdelivr.net/npm/${packageName}-es@${version}${path}`; return resolvedPath; }, unpkg: async (moduleSpecifier, pathResolver) => { const { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); // If no modulePath is provided, append /{packageName}.js let path = modulePath ? modulePath : `/${packageName}`; if (/\.(mc)?js$/.test(path) === false) { path += '.js'; } const resolvedPath = `https://unpkg.com/${packageName}-es@${version}${path}`; return resolvedPath; }, esm: async (moduleSpecifier, pathResolver) => { const resolvedPath = `https://esm.sh/${moduleSpecifier}`; return resolvedPath; }, jspm: async (moduleSpecifier, pathResolver) => { let { packageName, version, modulePath } = parseModuleSpecifier(moduleSpecifier); if (version === 'latest') { version = ''; } const resolvedPath = `https://jspm.dev/${packageName}${version ? `@${version}` : ''}${modulePath}`; return resolvedPath; }, } const baseUse = async (modulePath) => { // Dynamically import the module try { const module = await import(modulePath); // More robust default export handling for cross-environment compatibility const keys = Object.keys(module); // If it's a Module object with a default property, unwrap it if (module.default !== undefined) { // Check if this is likely a CommonJS module with only default export if (keys.length === 1 && keys[0] === 'default') { return module.default; } // Check if default is the main export and other keys are just function/module metadata const metadataKeys = new Set([ 'default', '__esModule', 'Symbol(Symbol.toStringTag)', 'length', 'name', 'prototype', 'constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable' ]); const nonMetadataKeys = keys.filter(key => !metadataKeys.has(key)); // If there are no significant non-metadata keys, return the default if (nonMetadataKeys.length === 0) { return module.default; } } // Return the whole module if it has multiple meaningful exports or no default return module; } catch (error) { throw new Error(`Failed to import module from '${modulePath}'.`, { cause: error }); } } const makeUse = async (options) => { let scriptPath = options?.scriptPath; if (!scriptPath && typeof global !== 'undefined' && typeof global['__filename'] !== 'undefined') { scriptPath = global['__filename']; } const metaUrl = options?.meta?.url; if (!scriptPath && metaUrl) { scriptPath = metaUrl; } let protocol; if (scriptPath) { try { protocol = new URL(scriptPath).protocol; } catch { // If scriptPath is a local file path, convert it to file:// URL if (scriptPath.startsWith('/') || scriptPath.includes('\\')) { protocol = 'file:'; } } } let specifierResolver = options?.specifierResolver; if (typeof specifierResolver !== 'function') { if (typeof window !== 'undefined' || (protocol && (protocol === 'http:' || protocol === 'https:'))) { specifierResolver = resolvers[specifierResolver || 'esm']; } else if (typeof Deno !== 'undefined') { specifierResolver = resolvers[specifierResolver || 'deno']; } else if (typeof Bun !== 'undefined') { specifierResolver = resolvers[specifierResolver || 'bun']; } else { specifierResolver = resolvers[specifierResolver || 'npm']; } } let pathResolver = options?.pathResolver; if (!pathResolver) { const isCJS = typeof module !== "undefined" && !!module.exports; const hasRequire = typeof require !== 'undefined'; const hasScriptPath = scriptPath && (!protocol || protocol === 'file:'); if (hasRequire && hasScriptPath) { if (isCJS) { pathResolver = require.resolve; } else { pathResolver = await import('node:module') .then(module => module.createRequire(scriptPath)) .then(require => require.resolve); } } else if (hasRequire) { pathResolver = require.resolve; } else if (hasScriptPath) { pathResolver = await import('node:module') .then(module => module.createRequire(scriptPath)) .then(require => require.resolve); } else { pathResolver = (path) => path; } } return async (moduleSpecifier, providedCallerContext) => { const stack = new Error().stack; // Use provided caller context or try to capture it from stack trace const callerContext = providedCallerContext || extractCallerContext(stack); // Always try built-in resolver first const builtinModule = await resolvers.builtin(moduleSpecifier, pathResolver); if (builtinModule) { return builtinModule; } // Try relative path resolver second (for ./, ../, ../../, etc.) const relativeModule = await resolvers.relative(moduleSpecifier, pathResolver, callerContext); if (relativeModule) { return relativeModule; } // If not a built-in or relative module, use the configured resolver const modulePath = await specifierResolver(moduleSpecifier, pathResolver); return baseUse(modulePath); }; } let __use = null; const use = async (moduleSpecifier) => { const stack = new Error().stack; // For Bun, we need to capture the stack trace before any other calls let bunCallerContext = null; if (typeof Bun !== 'undefined') { if (stack) { const lines = stack.split('\n'); // Look for any .mjs file that's not use.mjs for (const line of lines) { const match = line.match(/[(]?(\/[^\s:)]+\.m?js)/); if (match && !match[1].endsWith('/use.mjs')) { bunCallerContext = 'file://' + match[1]; break; } } } } // Capture the caller context here, before entering makeUse const callerContext = bunCallerContext || extractCallerContext(stack); if (!__use) { __use = await makeUse(); } return __use(moduleSpecifier, callerContext); } use.all = async (...moduleSpecifiers) => { if (!__use) { __use = await makeUse(); } return Promise.all(moduleSpecifiers.map(__use)); } module.exports = { parseModuleSpecifier, resolvers, makeUse, baseUse, use, };