UNPKG

@visulima/error

Version:

Error with more than just a message, stacktrace parsing.

208 lines (205 loc) 6.64 kB
const code = (s) => `\`\`\` ${s.trim()} \`\`\``; const bash = (s) => `\`\`\`bash ${s.trim()} \`\`\``; const ts = (s) => `\`\`\`ts ${s.trim()} \`\`\``; const js = (s) => `\`\`\`js ${s.trim()} \`\`\``; const has = (message, ...needles) => { const lower = message.toLowerCase(); return needles.some((n) => lower.includes(n.toLowerCase())); }; const rules = [ { name: "esm-cjs-interop", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has( message, "err_require_esm", "cannot use import statement outside a module", "must use import to load es module", "require() of es module", "does not provide an export named" )) { return { md: [ "Your project or a dependency may be mixing CommonJS and ES Modules.", "", "Try:", "- Ensure package.json has the correct `type` (either `module` or `commonjs`).", "- Use dynamic `import()` when requiring ESM from CJS.", "- Prefer ESM-compatible entrypoints from dependencies.", "- In Node, align `module` resolution with your bundler config.", "", "Check Node resolution:", bash("node -v\ncat package.json | jq .type"), "", "Example dynamic import in CJS:", js("(async () => { const mod = await import('some-esm'); mod.default(); })();") ].join("\n"), title: "ESM/CJS interop" }; } return void 0; } }, { name: "missing-default-export", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "default export not found", "has no default export", "does not provide an export named 'default'", "is not exported from")) { return { md: [ "Verify your import/export shapes.", "", "Default export example:", ts("export default function Component() {}\n// import Component from './file'"), "", "Named export example:", ts("export function Component() {}\n// import { Component } from './file'") ].join("\n"), title: "Export mismatch (default vs named)" }; } return void 0; } }, { name: "port-in-use", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "eaddrinuse", "address already in use", "listen eaddrinuse")) { return { md: [ "Another process is using the port.", "", "Change the port or stop the other process.", "", "On macOS/Linux:", bash("lsof -i :3000\nkill -9 <PID>"), "", "On Windows (PowerShell):", bash("netstat -ano | findstr :3000\ntaskkill /PID <PID> /F") ].join("\n"), title: "Port already in use" }; } return void 0; } }, { name: "file-not-found-or-case", test: (error, file) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "enoent", "module not found", "cannot find module")) { return { md: [ "Check the import path and filename case (Linux/macOS are case-sensitive).", "If using TS path aliases, verify `tsconfig.paths` and bundler aliases.", "", "Current file:", code(`${file.file}:${file.line}`) ].join("\n"), title: "Missing file or path case mismatch" }; } return void 0; } }, { name: "ts-path-mapping", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "ts2307", "cannot find module") || message.includes("TS2307")) { return { md: [ "If you use path aliases, align TS `paths` with Vite/Webpack resolve aliases.", "Ensure file extensions are correct and included in resolver.", "", "tsconfig.json excerpt:", ts(`{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] } } }`) ].join("\n"), title: "TypeScript path mapping / resolution" }; } return void 0; } }, { name: "network-dns-enotfound", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "enotfound", "getaddrinfo enotfound", "dns", "fetch failed", "ecconnrefused", "econnrefused")) { return { md: [ "The host may be unreachable or misconfigured.", "", "Try:", "- Verify the hostname and protocol (http/https).", "- Check VPN/proxy and firewall.", "- Confirm the service is running and listening on the expected port.", "", bash("ping <host>\nnslookup <host>\ncurl -v http://<host>:<port>") ].join("\n"), title: "Network/DNS connection issue" }; } return void 0; } }, { name: "undefined-property", test: (error) => { const message = (error?.message || String(error || "")).toString(); if (has(message, "cannot read properties of undefined", "reading '")) { return { md: [ "A variable or function returned `undefined`.", "", "Mitigations:", "- Add nullish checks before property access.", "- Validate function return values and input props/state.", "", ts("const value = maybe?.prop; // or: if (maybe) { use(maybe.prop) }") ].join("\n"), title: "Accessing property of undefined" }; } return void 0; } } ]; const ruleBasedFinder = { handle: async (error, file) => { try { const matches = rules.map((r) => { return { match: r.test(error, file), rule: r }; }).filter((x) => Boolean(x.match)); if (matches.length === 0) { return void 0; } const sections = matches.toSorted((a, b) => (a.match.priority || 0) - (b.match.priority || 0)).map((m) => `#### ${m.match.title} ${m.match.md}`).join("\n\n---\n\n"); if (sections === "") { return void 0; } return { body: sections, header: "### Potential fixes detected" }; } catch { return void 0; } }, name: "ruleBasedHints", priority: 0 }; export { ruleBasedFinder as default };