fix lint errors

This commit is contained in:
Yanzhen Yu
2026-04-01 12:00:00 +08:00
parent 5745afa622
commit 2ee3926f25
4 changed files with 129 additions and 82 deletions

View File

@@ -132,7 +132,9 @@ function initMoveObserver(
const threshold = const threshold =
typeof sampling.mousemove === 'number' ? sampling.mousemove : 50; typeof sampling.mousemove === 'number' ? sampling.mousemove : 50;
const callbackThreshold = const callbackThreshold =
typeof sampling.mousemoveCallback === 'number' ? sampling.mousemoveCallback : 500; typeof sampling.mousemoveCallback === 'number'
? sampling.mousemoveCallback
: 500;
let positions: mousePosition[] = []; let positions: mousePosition[] = [];
let timeBaseline: number | null; let timeBaseline: number | null;
@@ -261,18 +263,18 @@ function initScrollObserver(
function initViewportResizeObserver( function initViewportResizeObserver(
cb: viewportResizeCallback, cb: viewportResizeCallback,
): listenerHandler { ): listenerHandler {
let last_h = -1; let lastH = -1;
let last_w = -1; let lastW = -1;
const updateDimension = throttle(() => { const updateDimension = throttle(() => {
const height = getWindowHeight(); const height = getWindowHeight();
const width = getWindowWidth(); const width = getWindowWidth();
if (last_h !== height || last_w != width) { if (lastH !== height || lastW !== width) {
cb({ cb({
width: Number(width), width: Number(width),
height: Number(height), height: Number(height),
}); });
last_h = height; lastH = height;
last_w = width; lastW = width;
} }
}, 200); }, 200);
return on('resize', updateDimension, window); return on('resize', updateDimension, window);
@@ -562,24 +564,30 @@ function initLogObserver(
logOptions: LogRecordOptions, logOptions: LogRecordOptions,
): listenerHandler { ): listenerHandler {
const logger = logOptions.logger; const logger = logOptions.logger;
if (!logger) return () => {}; if (!logger) {
return () => {};
}
let logCount = 0; let logCount = 0;
const cancelHandlers: any[] = []; const cancelHandlers: listenerHandler[] = [];
// add listener to thrown errors // add listener to thrown errors
if (logOptions.level!.includes('error')) { if (logOptions.level!.includes('error')) {
if (window) { if (window) {
const originalOnError = window.onerror; const originalOnError = window.onerror;
// tslint:disable-next-line:no-any
window.onerror = (...args: any[]) => { window.onerror = (...args: any[]) => {
originalOnError && originalOnError.apply(this, args); if (originalOnError) {
let stack: Array<string> = []; originalOnError.apply(this, args);
if (args[args.length - 1] instanceof Error) }
let stack: string[] = [];
if (args[args.length - 1] instanceof Error) {
// 0(the second parameter) tells parseStack that every stack in Error is useful // 0(the second parameter) tells parseStack that every stack in Error is useful
stack = parseStack(args[args.length - 1].stack, 0); stack = parseStack(args[args.length - 1].stack, 0);
}
const payload = [stringify(args[0], logOptions.stringifyOptions)]; const payload = [stringify(args[0], logOptions.stringifyOptions)];
cb({ cb({
level: 'error', level: 'error',
trace: stack, trace: stack,
payload: payload, payload,
}); });
}; };
cancelHandlers.push(() => { cancelHandlers.push(() => {
@@ -587,8 +595,9 @@ function initLogObserver(
}); });
} }
} }
for (const levelType of logOptions.level!) for (const levelType of logOptions.level!) {
cancelHandlers.push(replace(logger, levelType)); cancelHandlers.push(replace(logger, levelType));
}
return () => { return () => {
cancelHandlers.forEach((h) => h()); cancelHandlers.forEach((h) => h());
}; };
@@ -598,10 +607,13 @@ function initLogObserver(
* @param logger the logger object such as Console * @param logger the logger object such as Console
* @param level the name of log function to be replaced * @param level the name of log function to be replaced
*/ */
function replace(logger: Logger, level: LogLevel) { function replace(_logger: Logger, level: LogLevel) {
if (!logger[level]) return () => {}; if (!_logger[level]) {
return () => {};
}
// replace the logger.{level}. return a restore function // replace the logger.{level}. return a restore function
return patch(logger, level, (original) => { return patch(_logger, level, (original) => {
// tslint:disable-next-line:no-any
return (...args: any[]) => { return (...args: any[]) => {
original.apply(this, args); original.apply(this, args);
try { try {
@@ -610,13 +622,13 @@ function initLogObserver(
stringify(s, logOptions.stringifyOptions), stringify(s, logOptions.stringifyOptions),
); );
logCount++; logCount++;
if (logCount < logOptions.lengthThreshold!) if (logCount < logOptions.lengthThreshold!) {
cb({ cb({
level: level, level,
trace: stack, trace: stack,
payload: payload, payload,
}); });
else if (logCount === logOptions.lengthThreshold) } else if (logCount === logOptions.lengthThreshold) {
// notify the user // notify the user
cb({ cb({
level: 'warn', level: 'warn',
@@ -625,6 +637,7 @@ function initLogObserver(
stringify('The number of log records reached the threshold.'), stringify('The number of log records reached the threshold.'),
], ],
}); });
}
} catch (error) { } catch (error) {
original('rrweb logger error:', error, ...args); original('rrweb logger error:', error, ...args);
} }
@@ -639,7 +652,7 @@ function initLogObserver(
function parseStack( function parseStack(
stack: string | undefined, stack: string | undefined,
omitDepth: number = 1, omitDepth: number = 1,
): Array<string> { ): string[] {
let stacks: string[] = []; let stacks: string[] = [];
if (stack) { if (stack) {
stacks = stack stacks = stack

View File

@@ -1,3 +1,4 @@
// tslint:disable:no-any no-bitwise forin
/** /**
* this file is used to serialize log message to string * this file is used to serialize log message to string
* *
@@ -14,18 +15,21 @@ function pathToSelector(node: HTMLElement): string | '' {
return ''; return '';
} }
var path = ''; let path = '';
while (node.parentElement) { while (node.parentElement) {
var name = node.localName; let name = node.localName;
if (!name) break; if (!name) {
break;
}
name = name.toLowerCase(); name = name.toLowerCase();
var parent = node.parentElement; let parent = node.parentElement;
var domSiblings = []; let domSiblings = [];
if (parent.children && parent.children.length > 0) { if (parent.children && parent.children.length > 0) {
for (var i = 0; i < parent.children.length; i++) { // tslint:disable-next-line:prefer-for-of
var sibling = parent.children[i]; for (let i = 0; i < parent.children.length; i++) {
let sibling = parent.children[i];
if (sibling.localName && sibling.localName.toLowerCase) { if (sibling.localName && sibling.localName.toLowerCase) {
if (sibling.localName.toLowerCase() === name) { if (sibling.localName.toLowerCase() === name) {
domSiblings.push(sibling); domSiblings.push(sibling);
@@ -56,45 +60,55 @@ export function stringify(
numOfKeysLimit: 50, numOfKeysLimit: 50,
}; };
Object.assign(options, stringifyOptions); Object.assign(options, stringifyOptions);
let stack: any[] = [], const stack: any[] = [];
keys: any[] = []; const keys: any[] = [];
return JSON.stringify(obj, function (key, value) { return JSON.stringify(obj, function (key, value) {
/** /**
* forked from https://github.com/moll/json-stringify-safe/blob/master/stringify.js * forked from https://github.com/moll/json-stringify-safe/blob/master/stringify.js
* to deCycle the object * to deCycle the object
*/ */
if (stack.length > 0) { if (stack.length > 0) {
var thisPos = stack.indexOf(this); const thisPos = stack.indexOf(this);
~thisPos ? stack.splice(thisPos + 1) : stack.push(this); ~thisPos ? stack.splice(thisPos + 1) : stack.push(this);
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key);
if (~stack.indexOf(value)) { if (~stack.indexOf(value)) {
if (stack[0] === value) value = '[Circular ~]'; if (stack[0] === value) {
else value = '[Circular ~]';
} else {
value = value =
'[Circular ~.' + '[Circular ~.' +
keys.slice(0, stack.indexOf(value)).join('.') + keys.slice(0, stack.indexOf(value)).join('.') +
']'; ']';
}
} }
} else stack.push(value); } else {
stack.push(value);
}
/* END of the FORK */ /* END of the FORK */
if (value === null || value === undefined) return value; if (value === null || value === undefined) {
return value;
}
if (shouldToString(value)) { if (shouldToString(value)) {
return toString(value); return toString(value);
} }
if (value instanceof Event) { if (value instanceof Event) {
const eventResult: any = {}; const eventResult: any = {};
for (const key in value) { for (const eventKey in value) {
const eventValue = (value as any)[key]; const eventValue = (value as any)[eventKey];
if (Array.isArray(eventValue)) if (Array.isArray(eventValue)) {
eventResult[key] = pathToSelector( eventResult[eventKey] = pathToSelector(
eventValue.length ? eventValue[0] : null, eventValue.length ? eventValue[0] : null,
); );
else eventResult[key] = eventValue; } else {
eventResult[eventKey] = eventValue;
}
} }
return eventResult; return eventResult;
} else if (value instanceof Node) { } else if (value instanceof Node) {
if (value instanceof HTMLElement) return value ? value.outerHTML : ''; if (value instanceof HTMLElement) {
return value ? value.outerHTML : '';
}
return value.nodeName; return value.nodeName;
} }
return value; return value;
@@ -103,21 +117,24 @@ export function stringify(
/** /**
* whether we should call toString function of this object * whether we should call toString function of this object
*/ */
function shouldToString(obj: object): boolean { function shouldToString(_obj: object): boolean {
if ( if (
typeof obj === 'object' && typeof _obj === 'object' &&
Object.keys(obj).length > options.numOfKeysLimit Object.keys(_obj).length > options.numOfKeysLimit
) ) {
return true; return true;
if (typeof obj === 'function') return true; }
if (typeof _obj === 'function') {
return true;
}
return false; return false;
} }
/** /**
* limit the toString() result according to option * limit the toString() result according to option
*/ */
function toString(obj: object): string { function toString(_obj: object): string {
let str = obj.toString(); let str = _obj.toString();
if (options.stringLengthLimit && str.length > options.stringLengthLimit) { if (options.stringLengthLimit && str.length > options.stringLengthLimit) {
str = `${str.slice(0, options.stringLengthLimit)}...`; str = `${str.slice(0, options.stringLengthLimit)}...`;
} }

View File

@@ -52,7 +52,11 @@ const SKIP_TIME_INTERVAL = 5 * 1000;
const mitt = (mittProxy as any).default || mittProxy; const mitt = (mittProxy as any).default || mittProxy;
const REPLAY_CONSOLE_PREFIX = '[replayer]'; const REPLAY_CONSOLE_PREFIX = '[replayer]';
const SCROLL_ATTRIBUTE_NAME = '__rrweb_scroll__'; const ORIGINAL_ATTRIBUTE_NAME = '__rrweb_original__';
type PatchedConsoleLog = {
[ORIGINAL_ATTRIBUTE_NAME]: typeof console.log;
};
const defaultMouseTailConfig = { const defaultMouseTailConfig = {
duration: 500, duration: 500,
@@ -141,8 +145,9 @@ export class Replayer {
logConfig: defaultLogConfig, logConfig: defaultLogConfig,
}; };
this.config = Object.assign({}, defaultConfig, config); this.config = Object.assign({}, defaultConfig, config);
if (!this.config.logConfig.replayLogger) if (!this.config.logConfig.replayLogger) {
this.config.logConfig.replayLogger = this.getConsoleLogger(); this.config.logConfig.replayLogger = this.getConsoleLogger();
}
this.handleResize = this.handleResize.bind(this); this.handleResize = this.handleResize.bind(this);
this.getCastFn = this.getCastFn.bind(this); this.getCastFn = this.getCastFn.bind(this);
@@ -1020,8 +1025,9 @@ export class Replayer {
try { try {
const logData = e.data as logData; const logData = e.data as logData;
const replayLogger = this.config.logConfig.replayLogger!; const replayLogger = this.config.logConfig.replayLogger!;
if (typeof replayLogger[logData.level] === 'function') if (typeof replayLogger[logData.level] === 'function') {
replayLogger[logData.level]!(logData); replayLogger[logData.level]!(logData);
}
} catch (error) { } catch (error) {
if (this.config.showWarning) { if (this.config.showWarning) {
console.warn(error); console.warn(error);
@@ -1319,7 +1325,9 @@ export class Replayer {
* @param data the log data * @param data the log data
*/ */
private formatMessage(data: logData): string { private formatMessage(data: logData): string {
if (data.trace.length === 0) return ''; if (data.trace.length === 0) {
return '';
}
const stackPrefix = '\n\tat '; const stackPrefix = '\n\tat ';
let result = stackPrefix; let result = stackPrefix;
result += data.trace.join(stackPrefix); result += data.trace.join(stackPrefix);
@@ -1330,29 +1338,38 @@ export class Replayer {
* generate a console log replayer which implement the interface ReplayLogger * generate a console log replayer which implement the interface ReplayLogger
*/ */
private getConsoleLogger(): ReplayLogger { private getConsoleLogger(): ReplayLogger {
const rrwebOriginal = SCROLL_ATTRIBUTE_NAME;
const replayLogger: ReplayLogger = {}; const replayLogger: ReplayLogger = {};
for (const level of this.config.logConfig.level!) for (const level of this.config.logConfig.level!) {
if (level === 'trace') if (level === 'trace') {
replayLogger[level] = (data: logData) => { replayLogger[level] = (data: logData) => {
const logger = (console.log as any)[rrwebOriginal] const logger = ((console.log as unknown) as PatchedConsoleLog)[
? (console.log as any)[rrwebOriginal] ORIGINAL_ATTRIBUTE_NAME
]
? ((console.log as unknown) as PatchedConsoleLog)[
ORIGINAL_ATTRIBUTE_NAME
]
: console.log; : console.log;
logger( logger(
...data.payload.map((s) => JSON.parse(s)), ...data.payload.map((s) => JSON.parse(s)),
this.formatMessage(data), this.formatMessage(data),
); );
}; };
else } else {
replayLogger[level] = (data: logData) => { replayLogger[level] = (data: logData) => {
const logger = (console[level] as any)[rrwebOriginal] const logger = ((console[level] as unknown) as PatchedConsoleLog)[
? (console[level] as any)[rrwebOriginal] ORIGINAL_ATTRIBUTE_NAME
]
? ((console[level] as unknown) as PatchedConsoleLog)[
ORIGINAL_ATTRIBUTE_NAME
]
: console[level]; : console[level];
logger( logger(
...data.payload.map((s) => JSON.parse(s)), ...data.payload.map((s) => JSON.parse(s)),
this.formatMessage(data), this.formatMessage(data),
); );
}; };
}
}
return replayLogger; return replayLogger;
} }

View File

@@ -401,25 +401,25 @@ export type LogLevel =
/* fork from interface Console */ /* fork from interface Console */
// all kinds of console functions // all kinds of console functions
export type Logger = { export type Logger = {
assert?: (value: any, message?: string, ...optionalParams: any[]) => void; assert?: typeof console.assert;
clear?: () => void; clear?: typeof console.clear;
count?: (label?: string) => void; count?: typeof console.count;
countReset?: (label?: string) => void; countReset?: typeof console.countReset;
debug?: (message?: any, ...optionalParams: any[]) => void; debug?: typeof console.debug;
dir?: (obj: any, options?: NodeJS.InspectOptions) => void; dir?: typeof console.dir;
dirxml?: (...data: any[]) => void; dirxml?: typeof console.dirxml;
error?: (message?: any, ...optionalParams: any[]) => void; error?: typeof console.error;
group?: (...label: any[]) => void; group?: typeof console.group;
groupCollapsed?: (label?: any[]) => void; groupCollapsed?: typeof console.groupCollapsed;
groupEnd?: () => void; groupEnd?: () => void;
info?: (message?: any, ...optionalParams: any[]) => void; info?: typeof console.info;
log?: (message?: any, ...optionalParams: any[]) => void; log?: typeof console.log;
table?: (tabularData: any, properties?: ReadonlyArray<string>) => void; table?: typeof console.table;
time?: (label?: string) => void; time?: typeof console.time;
timeEnd?: (label?: string) => void; timeEnd?: typeof console.timeEnd;
timeLog?: (label?: string, ...data: any[]) => void; timeLog?: typeof console.timeLog;
trace?: (message?: any, ...optionalParams: any[]) => void; trace?: typeof console.trace;
warn?: (message?: any, ...optionalParams: any[]) => void; warn?: typeof console.warn;
}; };
/** /**
@@ -430,8 +430,8 @@ export type ReplayLogger = Partial<Record<LogLevel, (data: logData) => void>>;
export type LogParam = { export type LogParam = {
level: LogLevel; level: LogLevel;
trace: Array<string>; trace: string[];
payload: Array<string>; payload: string[];
}; };
export type fontCallback = (p: fontParam) => void; export type fontCallback = (p: fontParam) => void;
@@ -511,7 +511,7 @@ export type playerConfig = {
}; };
export type LogReplayConfig = { export type LogReplayConfig = {
level?: Array<LogLevel> | undefined; level?: LogLevel[] | undefined;
replayLogger: ReplayLogger | undefined; replayLogger: ReplayLogger | undefined;
}; };
@@ -583,7 +583,7 @@ export type StringifyOptions = {
}; };
export type LogRecordOptions = { export type LogRecordOptions = {
level?: Array<LogLevel> | undefined; level?: LogLevel[] | undefined;
lengthThreshold?: number; lengthThreshold?: number;
stringifyOptions?: StringifyOptions; stringifyOptions?: StringifyOptions;
logger?: Logger; logger?: Logger;