| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // ESM loader for module-alias
- // Provides resolve hooks for ES modules
- import { readFileSync, existsSync, statSync } from 'node:fs'
- import { join, resolve as pathResolve, dirname } from 'node:path'
- import { fileURLToPath, pathToFileURL } from 'node:url'
- let aliases = {}
- let moduleDirectories = []
- let base = process.cwd()
- let initialized = false
- export function init (options = {}) {
- if (initialized) return
- const thisDir = dirname(fileURLToPath(import.meta.url))
- const candidatePaths = options.base
- ? [pathResolve(options.base)]
- : [join(thisDir, '../..'), process.cwd()]
- let pkg
- for (const candidate of candidatePaths) {
- try {
- pkg = JSON.parse(readFileSync(join(candidate, 'package.json'), 'utf8'))
- base = candidate
- break
- } catch (e) {
- // Continue to next candidate
- }
- }
- if (!pkg) {
- console.warn('[module-alias] Unable to find package.json in:', candidatePaths.join(', '))
- initialized = true
- return
- }
- // Load _moduleAliases
- const pkgAliases = pkg._moduleAliases || {}
- for (const alias in pkgAliases) {
- const target = pkgAliases[alias]
- aliases[alias] = target.startsWith('/') ? target : join(base, target)
- }
- // Load _moduleDirectories
- if (Array.isArray(pkg._moduleDirectories)) {
- moduleDirectories = pkg._moduleDirectories
- .filter(d => d !== 'node_modules')
- .map(d => join(base, d))
- }
- initialized = true
- }
- export function isPathMatchesAlias (path, alias) {
- // Matching /^alias(\/|$)/
- if (path.indexOf(alias) === 0) {
- if (path.length === alias.length) return true
- if (path[alias.length] === '/') return true
- }
- return false
- }
- export function resolveAlias (specifier, parentURL) {
- init() // Ensure initialized
- // Sort aliases by length (longest first) for correct matching
- const sortedAliases = Object.keys(aliases).sort((a, b) => b.length - a.length)
- for (const alias of sortedAliases) {
- if (isPathMatchesAlias(specifier, alias)) {
- const target = aliases[alias]
- // Function-based resolver
- if (typeof target === 'function') {
- const parentPath = parentURL ? fileURLToPath(parentURL) : process.cwd()
- const result = target(parentPath, specifier, alias)
- if (!result || typeof result !== 'string') {
- throw new Error('[module-alias] Custom handler must return path')
- }
- return result
- }
- // String path - join target with remainder of specifier
- return join(target, specifier.slice(alias.length))
- }
- }
- // Check moduleDirectories
- for (const dir of moduleDirectories) {
- const modulePath = join(dir, specifier)
- // Check for directory with index.mjs/index.js
- if (existsSync(join(modulePath, 'index.mjs'))) {
- return join(modulePath, 'index.mjs')
- }
- if (existsSync(join(modulePath, 'index.js'))) {
- return join(modulePath, 'index.js')
- }
- // Check for file with extension
- if (existsSync(modulePath + '.mjs')) {
- return modulePath + '.mjs'
- }
- if (existsSync(modulePath + '.js')) {
- return modulePath + '.js'
- }
- // Check for exact file
- if (existsSync(modulePath) && !statSync(modulePath).isDirectory()) {
- return modulePath
- }
- }
- return null
- }
- export function addAlias (alias, target) {
- aliases[alias] = target
- }
- export function addAliases (aliasMap) {
- for (const alias in aliasMap) {
- addAlias(alias, aliasMap[alias])
- }
- }
- export function addPath (path) {
- moduleDirectories.push(path)
- }
- export function reset () {
- aliases = {}
- moduleDirectories = []
- base = process.cwd()
- initialized = false
- }
- // For Node 18-21: async loader hooks
- export async function resolve (specifier, context, nextResolve) {
- const resolved = resolveAlias(specifier, context.parentURL)
- if (resolved) {
- // If absolute path, convert to file URL
- if (resolved.startsWith('/')) {
- return { url: pathToFileURL(resolved).href, shortCircuit: true }
- }
- // Otherwise let Node resolve it (could be npm package)
- return nextResolve(resolved, context)
- }
- return nextResolve(specifier, context)
- }
- export async function initialize (data) {
- init(data || {})
- }
|