|  | 
|  | 1 | +// Post-build fixer for strict Node ESM support. | 
|  | 2 | +// - Renames esm files from .js to .mjs | 
|  | 3 | +// - Rewrites relative import/export specifiers to include explicit extensions | 
|  | 4 | +//   and use .mjs (including directory index resolution) | 
|  | 5 | +const fs = require('fs'); | 
|  | 6 | +const path = require('path'); | 
|  | 7 | + | 
|  | 8 | +const esmRoot = path.resolve(__dirname, '..', 'esm'); | 
|  | 9 | + | 
|  | 10 | +/** @param {string} p */ | 
|  | 11 | +function isDir(p) { | 
|  | 12 | +  try { | 
|  | 13 | +    return fs.statSync(p).isDirectory(); | 
|  | 14 | +  } catch { | 
|  | 15 | +    return false; | 
|  | 16 | +  } | 
|  | 17 | +} | 
|  | 18 | + | 
|  | 19 | +/** @param {string} dir */ | 
|  | 20 | +function walk(dir) { | 
|  | 21 | +  const entries = fs.readdirSync(dir, { withFileTypes: true }); | 
|  | 22 | +  /** @type {string[]} */ | 
|  | 23 | +  const files = []; | 
|  | 24 | +  for (const e of entries) { | 
|  | 25 | +    const full = path.join(dir, e.name); | 
|  | 26 | +    if (e.isDirectory()) files.push(...walk(full)); | 
|  | 27 | +    else files.push(full); | 
|  | 28 | +  } | 
|  | 29 | +  return files; | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +function ensureRelativeWithExt(spec, fromFile) { | 
|  | 33 | +  if (!spec.startsWith('./') && !spec.startsWith('../')) return spec; // external | 
|  | 34 | +  // Normalize and resolve target on disk relative to fromFile | 
|  | 35 | +  const fromDir = path.dirname(fromFile); | 
|  | 36 | +  const raw = path.resolve(fromDir, spec); | 
|  | 37 | + | 
|  | 38 | +  // If spec already has an extension, normalize to .mjs when it points into esm | 
|  | 39 | +  if (/\.(js|mjs|cjs)$/.test(spec)) { | 
|  | 40 | +    // Convert .js to .mjs for intra-esm imports | 
|  | 41 | +    return spec.replace(/\.js$/i, '.mjs'); | 
|  | 42 | +  } | 
|  | 43 | + | 
|  | 44 | +  // Try file.mjs | 
|  | 45 | +  if (fs.existsSync(raw + '.mjs')) { | 
|  | 46 | +    return spec + '.mjs'; | 
|  | 47 | +  } | 
|  | 48 | +  // Try file.js (before rename step) or when resolving precomputed graph | 
|  | 49 | +  if (fs.existsSync(raw + '.js')) { | 
|  | 50 | +    return spec + '.mjs'; | 
|  | 51 | +  } | 
|  | 52 | +  // Try directory index | 
|  | 53 | +  if (isDir(raw)) { | 
|  | 54 | +    if (fs.existsSync(path.join(raw, 'index.mjs'))) { | 
|  | 55 | +      return spec.replace(/\/?$/, '/') + 'index.mjs'; | 
|  | 56 | +    } | 
|  | 57 | +    if (fs.existsSync(path.join(raw, 'index.js'))) { | 
|  | 58 | +      return spec.replace(/\/?$/, '/') + 'index.mjs'; | 
|  | 59 | +    } | 
|  | 60 | +  } | 
|  | 61 | +  // Fallback: append .mjs | 
|  | 62 | +  return spec + '.mjs'; | 
|  | 63 | +} | 
|  | 64 | + | 
|  | 65 | +function rewriteFile(file) { | 
|  | 66 | +  let code = fs.readFileSync(file, 'utf8'); | 
|  | 67 | +  // import ... from '...';  export ... from '...';  dynamic import('...') | 
|  | 68 | +  code = code.replace(/(import\s+[^'"()]+?from\s*["'])([^"']+)(["'])/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b); | 
|  | 69 | +  code = code.replace(/(export\s+[^'"()]+?from\s*["'])([^"']+)(["'])/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b); | 
|  | 70 | +  code = code.replace(/(import\s*\(\s*["'])([^"']+)(["']\s*\))/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b); | 
|  | 71 | +  fs.writeFileSync(file, code); | 
|  | 72 | +} | 
|  | 73 | + | 
|  | 74 | +function main() { | 
|  | 75 | +  if (!fs.existsSync(esmRoot)) return; | 
|  | 76 | +  // First rename all .js files to .mjs to ensure ESM mode regardless of package type | 
|  | 77 | +  const files = walk(esmRoot).filter((f) => f.endsWith('.js')); | 
|  | 78 | +  // Rename leaf files first to avoid conflicts | 
|  | 79 | +  for (const f of files.sort((a, b) => b.length - a.length)) { | 
|  | 80 | +    fs.renameSync(f, f.slice(0, -3) + '.mjs'); | 
|  | 81 | +  } | 
|  | 82 | +  // Now rewrite imports in all .mjs files | 
|  | 83 | +  const mjsFiles = walk(esmRoot).filter((f) => f.endsWith('.mjs')); | 
|  | 84 | +  for (const f of mjsFiles) rewriteFile(f); | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +main(); | 
0 commit comments