import chalk from 'chalk' import MagicString from 'magic-string' import { parseSync } from 'oxc-parser' import { normalizePath } from './normalize-path' import { createLocMapper } from './offset-to-loc' import { walk } from './ast-utils' function escapeForStringLiteral(str: string): string { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n') } export function enhanceConsoleLog(code: string, id: string, port: number) { const filePath = id.split('?')[0]! // Normalize filePath before stripping cwd: under Rspack on Windows the module // id has backslashes, so comparing against the forward-slashed cwd would fail // to strip and leak an absolute path into the "Go to Source" link. const location = normalizePath(filePath).replace( normalizePath(process.cwd()), '', ) try { const result = parseSync(filePath, code, { sourceType: 'module', lang: 'tsx', }) if (result.errors.length > 0) return const offsetToLoc = createLocMapper(code) const s = new MagicString(code) walk(result.program, (node) => { if (node.type !== 'CallExpression') return const callee = node.callee if ( callee.type === 'MemberExpression' && !callee.computed && callee.object.type === 'Identifier' && callee.object.name === 'console' && callee.property.type === 'Identifier' && (callee.property.name === 'log' || callee.property.name === 'error') ) { const loc = offsetToLoc(node.start) const [lineNumber, column] = [loc.line, loc.column] const finalPath = `${location}:${lineNumber}:${column + 1}` const logMessage = `${chalk.magenta('LOG')} ${chalk.blueBright(`${finalPath}`)}\n → ` const serverLogMessage = `["${escapeForStringLiteral(logMessage)}"]` const browserLogMessage = `["%c${'LOG'}%c %c${`Go to Source: http://localhost:${port}/__tsd/open-source?source=${encodeURIComponent(finalPath)}`}%c \\n \\u2192 ","color:#A0A","color:#FFF","color:#55F","color:#FFF"]` const spreadStr = `...(typeof window === "undefined" ? ${serverLogMessage} : ${browserLogMessage}), ` // Find the opening '(' of the call by scanning forward from callee end let parenOffset = callee.end while (parenOffset < code.length && code[parenOffset] !== '(') { parenOffset++ } // Insert right after '(' s.appendRight(parenOffset + 1, spreadStr) } }) if (!s.hasChanged()) return return { code: s.toString(), map: s.generateMap({ source: filePath, file: id, includeContent: true, }), } } catch (e) { return } }