import fs from 'fs' import path from 'path' import colors from 'picocolors' import type { RollupError } from 'rollup' import type { LogErrorOptions } from 'vite' import { NodeTypes } from '@vue/compiler-core' import { isString } from '@vue/shared' import { normalizePath } from '../utils' import { Formatter } from '../logs/format' import { EXTNAME_VUE_RE } from '../constants' import { parseVue } from '../vite/utils/ast' import { generateCodeFrame } from '../vite/plugins/vitejs/utils' const SIGNAL_H5_LOCAL = ' ➜ Local:' const SIGNAL_H5_NETWORK = ' ➜ Network:' const networkLogs: string[] = [] const ZERO_WIDTH_CHAR = { NOTE: '', WARNING: '\u200B', ERROR: '\u200C', backup0: '\u200D', backup1: '\u200E', backup2: '\u200F', backup3: '\uFEFF', } as const type ZERO_WIDTH_CHAR_KEY = keyof typeof ZERO_WIDTH_CHAR type ConsoleMethod = 'warn' | 'error' function overridedConsole( name: ConsoleMethod, oldFn: (...args: any[]) => any, char: typeof ZERO_WIDTH_CHAR[ZERO_WIDTH_CHAR_KEY] ) { console[name] = function (...args) { oldFn.apply( this, args.map((arg) => { let item if (typeof arg !== 'object') { item = `${char}${arg}${char}` } else { item = `${char}${JSON.stringify(arg)}${char}` } return item }) ) } } if (typeof console !== 'undefined') { overridedConsole('warn', console.warn, ZERO_WIDTH_CHAR.WARNING) // overridedConsole('error', console.error, ZERO_WIDTH_CHAR.ERROR) } export function formatAtFilename( filename: string, line?: number, column?: number ) { return `at ${colors.cyan( normalizePath( path.relative(process.env.UNI_INPUT_DIR, filename.split('?')[0]) ) + ':' + (line || 1) + ':' + (column || 0) )}` } export const h5ServeFormatter: Formatter = { test(msg) { return msg.includes(SIGNAL_H5_LOCAL) || msg.includes(SIGNAL_H5_NETWORK) }, format(msg) { if (msg.includes(SIGNAL_H5_NETWORK)) { networkLogs.push(msg.replace('➜ ', '*')) process.nextTick(() => { if (networkLogs.length) { // 延迟打印所有 network,仅最后一个 network 替换 ➜ 为 -,通知 hbx const len = networkLogs.length - 1 networkLogs[len] = networkLogs[len].replace('➜', '-') console.log(networkLogs.join('\n')) networkLogs.length = 0 } }) return '' } return msg.replace('➜ ', '*') }, } const REMOVED_MSGS = [ 'build started...', (msg: string) => { return /built in [0-9]+ms\./.test(msg) }, 'watching for file changes...', ] export const removeInfoFormatter: Formatter = { test(msg) { return !!REMOVED_MSGS.find((m) => (isString(m) ? msg.includes(m) : m(msg))) }, format() { return '' }, } const REMOVED_WARN_MSGS: string[] = [] export const removeWarnFormatter: Formatter = { test(msg) { return !!REMOVED_WARN_MSGS.find((m) => msg.includes(m)) }, format() { return '' }, } export const errorFormatter: Formatter = { test(_, opts) { return !!(opts && opts.error) }, format(_, opts) { return buildErrorMessage(opts!.error!, [], false) }, } function buildErrorMessage( err: RollupError, args: string[] = [], includeStack = true ): string { if (err.plugin) { args.push( `${colors.magenta('[plugin:' + err.plugin + ']')} ${colors.red( err.message )}` ) if ( err.loc && err.hook === 'transform' && err.plugin === 'rollup-plugin-dynamic-import-variables' && err.id && EXTNAME_VUE_RE.test(err.id) ) { try { const ast = parseVue(fs.readFileSync(err.id, 'utf8'), []) const scriptNode = ast.children.find( (node) => node.type === NodeTypes.ELEMENT && node.tag === 'script' ) if (scriptNode) { const scriptLoc = scriptNode.loc args.push( colors.yellow(pad(generateCodeFrame(scriptLoc.source, err.loc))) ) // correct error location err.loc.line = scriptLoc.start.line + err.loc.line - 1 } } catch (e: any) {} } } else { args.push(colors.red(err.message)) } if (err.id) { args.push(formatAtFilename(err.id, err.loc?.line, err.loc?.column)) } if (err.frame) { args.push(colors.yellow(pad(err.frame))) } if (includeStack && err.stack) { args.push(pad(cleanStack(err.stack))) } return args.join('\n') } function cleanStack(stack: string) { return stack .split(/\n/g) .filter((l) => /^\s*at/.test(l)) .join('\n') } const splitRE = /\r?\n/ function pad(source: string, n = 2): string { const lines = source.split(splitRE) return lines.map((l) => ` `.repeat(n) + l).join(`\n`) }