* Chore: Add move most types from rrweb to @rrweb/types package * Split off type imports * Split off type import to its own line * Get vite to generate type definitions * Apply formatting changes * noEmit not allowed in tsconfig, moved it to build step * Migrate rrdom-nodejs build to vite * Apply formatting changes * Migrate rrweb-snapshot to vite * Unify configs * Chore: Migrate rrdom to vite Turns out what we where doing by overwriting `public textContent: string | undefined` as a getter in a subclass is something that isn't allowed in typescript. Because we where using `// @ts-ignore` to hide this error our bundler chose to allow the overwrite. Vite choses to disallow the overwrite making all subclasses' `textContent` undefined. To mitigate this we're using an abstract class, which does allow sub classes to decide if they wan't to use getters or not. * Chore: Migrate rrweb to vite WIP * build:browser was removed (for now) * BREAKING: moved rrweb-plugin-console to its own npm module This removes console from rrweb-all.js * Support cjs files in startServer * Move canvas-webrtc plugin to its own package * Chore: move sequential-id plugin to its own package * Chore: Configure rrweb's vite bundling * `Id` had lowercase `d` before, making it lowercase again * Test: Move console tests to their own package * remove unused utils from rrdom * pull in latest version of master something when wrong earlier when resolving merge conflicts, this should be correct * Fix type casting issue in diff.ts * Fix typo * Fix duplicate entries in package.json and tsconfig.json * Apply formatting changes * Update dependencies in package.json files * Update dependencies to use Vite 5.2.8 in package.json files * Get tests passing for rrdom `apply virtual style rules to node` tests need to be moved to rrweb to avoid circular dependencies * Fix image loading issue in integration tests * Move pack/unpack to its own @rrweb/packer module * Get tests to work in rrdom-nodejs * Port tests in rrweb-snapshot to vitest and fix them * Fix tests for rrweb-plugin-console-record * Add @rrweb/all package * Fix publint and attw errors for rrdom and @rrweb/types * Use shared vitest.config.ts in rrweb-snapshot package * Fix publint and attw issues for rrweb-snapshot * Export `ReplayPlugin` type directly from rrweb * Fix publint and attw issues for packages * Fix publint & attw issue. I was bumping into this issue:3729bc2a3c/docs/problems/NoResolution.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * Fix publint & attw issue. I was bumping into this issue:3729bc2a3c/docs/problems/NoResolution.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * move some rrdom tests that require rrweb to rrweb package * Use pre-jest 29 syntax for snapshotting * get rrweb passing publint and attw * const enum does not work with isolated modules flag * Fix script tag type in webgl.test.ts.snap and update rrweb.umd.cjs path in webgl.test.ts * Fix paths * Move tests for console record plugin and fix bundle path * Fix tests for rrweb * pack integration tests were moved to @rrweb/all * Update rrweb bundle path in test files * Fix flaky scroll emit from test * Migrate rrweb's tests over to vitest and make them pass * Make sure benchmarks & updating tests work * Remove jest from rrweb * Fix paths * always use rrweb's own cssom * Update tsconfig.json for rrweb-plugin-sequential-id-record Fixes this error: Error: @rrweb/rrweb-plugin-sequential-id-record:prepublish: tsconfig.json(9,5): error TS6377: Cannot write file '/home/runner/work/rrweb/rrweb/tsconfig.tsbuildinfo' because it will overwrite '.tsbuildinfo' file generated by referenced project '/home/runner/work/rrweb/rrweb/packages/rrweb' * Add tsbuildinfo config to extended tsconfig files * Move rrdom over to vitest * Apply formatting changes * Update rrweb imports to use the new package structure * extend rrweb-snapshot's tsconfig from monorepo base config * extend @rrweb/types's tsconfig from monorepo base config * extend rrdom's tsconfig from monorepo base config * extend rrdom-nodejs's tsconfig from monorepo base config * extend web-extension's tsconfig from monorepo base config * unify tsconfigs * Continue when tests fail * Add stricter type checking * Add check-types global command * remove jest * Remove unused code * Add check-types command to build script * Fix linting issues * Add setup Chrome action for CI/CD workflow * Update puppeteer version in package.json for rrweb * Update Chrome setup in CI/CD workflow * Update Chrome setup in CI/CD workflow * Add Chrome setup and test cache location * Update CI/CD workflow to test chrome cache location * Add chrome installation step to CI/CD workflow * Update Puppeteer configuration for headless testing * Update dependencies and workflow configuration * Use same version of chrome on CI as is run locally * Use version of chrome that seems to work with rrdom tests * Try using puppeteerrc to define chrome version * Add .cache directory to .gitignore * Move global flag to vitest config * Update puppeteer version to 20.9.0 * Update console log messages in rrweb-plugin-console-record for new puppeteer version * Remove redundant Chrome setup from CI/CD workflow * Add minification and umd for all built files * Update import paths for rrweb dist files * Add @rrweb/replay and @rrweb/record * Add script to lint packages * Apply formatting changes * exclude styles export from typescript package type checking * WIP Move rrweb-player over to vite * Apply formatting changes * chore: Update rrweb plugin import paths * Remove rollup from rrweb-player * Fix typing issues * Fix typing issues * chore: Update rrweb-player to use vite for build process * Apply formatting changes * chore: Export Player class in rrweb-player/src/main.ts Makes attw happy * Apply formatting changes * Gets wiped by yarn workspaces-to-typescript-project-references * Add .eslintignore and .eslintrc.cjs files for rrweb-player package * Apply formatting changes * Update dependencies in rrweb-player/package.json * Apply formatting changes * chore: Update eslint configuration for rrweb-player package * Apply formatting changes * chore: Remove unused files from rrweb-player package * Apply formatting changes * chore: Update rrweb-player import path to use rrweb-player.cjs * chore: Update addEventListener signature in rrweb-player * Apply formatting changes * Add .eslintignore and update .gitignore files for to root * Apply formatting changes * Update documentation * Update @rrweb/types package description * Apply formatting changes * Update build and run commands in CONTRIBUTING.md * Apply formatting changes * Update package versions to 2.0.0-alpha.13 * Apply formatting changes * Apply formatting changes * Fix import statement in media/index.ts * Apply formatting changes * chore: Update .gitignore to exclude build and dist directories * Apply formatting changes * Apply formatting changes * Migrate setTimeout to vitest * Apply formatting changes * Apply formatting changes * Fix isNativeShadowDom function signature in utils.ts * try out jsr * Apply formatting changes * Update package versions to 2.0.0-alpha.14 * Apply formatting changes * Fix name of rrwebSnapshot object * Apply formatting changes * Remove unused lock files * Apply formatting changes * Update rrweb bundle path to use umd.cjs format * Apply formatting changes * Trigger tests to run again * Rename snapshots for vitest * Apply formatting changes * Ping CI * Apply formatting changes * Ping CI * Apply formatting changes * Ignore files generated by svelte-kit for prettier * Correct Player object
253 lines
8.4 KiB
TypeScript
253 lines
8.4 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
|
/**
|
|
* Class StackFrame is a fork of https://github.com/stacktracejs/stackframe/blob/master/stackframe.js
|
|
* I fork it because:
|
|
* 1. There are some build issues when importing this package.
|
|
* 2. Rewrites into typescript give us a better type interface.
|
|
* 3. StackFrame contains some functions we don't need.
|
|
*/
|
|
export class StackFrame {
|
|
private fileName: string;
|
|
private functionName: string;
|
|
private lineNumber?: number;
|
|
private columnNumber?: number;
|
|
|
|
constructor(obj: {
|
|
fileName?: string;
|
|
functionName?: string;
|
|
lineNumber?: number;
|
|
columnNumber?: number;
|
|
}) {
|
|
this.fileName = obj.fileName || '';
|
|
this.functionName = obj.functionName || '';
|
|
this.lineNumber = obj.lineNumber;
|
|
this.columnNumber = obj.columnNumber;
|
|
}
|
|
|
|
toString() {
|
|
const lineNumber = this.lineNumber || '';
|
|
const columnNumber = this.columnNumber || '';
|
|
if (this.functionName)
|
|
return `${this.functionName} (${this.fileName}:${lineNumber}:${columnNumber})`;
|
|
return `${this.fileName}:${lineNumber}:${columnNumber}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* ErrorStackParser is a fork of https://github.com/stacktracejs/error-stack-parser/blob/master/error-stack-parser.js
|
|
* I fork it because:
|
|
* 1. There are some build issues when importing this package.
|
|
* 2. Rewrites into typescript give us a better type interface.
|
|
*/
|
|
const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
|
|
const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
|
|
const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
|
|
export const ErrorStackParser = {
|
|
/**
|
|
* Given an Error object, extract the most information from it.
|
|
*/
|
|
parse: function (error: Error): StackFrame[] {
|
|
// https://github.com/rrweb-io/rrweb/issues/782
|
|
if (!error) {
|
|
return [];
|
|
}
|
|
if (
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
typeof error.stacktrace !== 'undefined' ||
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
typeof error['opera#sourceloc'] !== 'undefined'
|
|
) {
|
|
return this.parseOpera(
|
|
error as {
|
|
stacktrace?: string;
|
|
message: string;
|
|
stack?: string;
|
|
},
|
|
);
|
|
} else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
|
|
return this.parseV8OrIE(error as { stack: string });
|
|
} else if (error.stack) {
|
|
return this.parseFFOrSafari(error as { stack: string });
|
|
} else {
|
|
console.warn(
|
|
'[console-record-plugin]: Failed to parse error object:',
|
|
error,
|
|
);
|
|
return [];
|
|
}
|
|
},
|
|
// Separate line and column numbers from a string of the form: (URI:Line:Column)
|
|
extractLocation: function (urlLike: string) {
|
|
// Fail-fast but return locations like "(native)"
|
|
if (urlLike.indexOf(':') === -1) {
|
|
return [urlLike];
|
|
}
|
|
|
|
const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
|
|
const parts = regExp.exec(urlLike.replace(/[()]/g, ''));
|
|
if (!parts) throw new Error(`Cannot parse given url: ${urlLike}`);
|
|
return [parts[1], parts[2] || undefined, parts[3] || undefined];
|
|
},
|
|
parseV8OrIE: function (error: { stack: string }) {
|
|
const filtered = error.stack.split('\n').filter(function (line) {
|
|
return !!line.match(CHROME_IE_STACK_REGEXP);
|
|
}, this);
|
|
|
|
return filtered.map(function (line) {
|
|
if (line.indexOf('(eval ') > -1) {
|
|
// Throw away eval information until we implement stacktrace.js/stackframe#8
|
|
line = line
|
|
.replace(/eval code/g, 'eval')
|
|
.replace(/(\(eval at [^()]*)|(\),.*$)/g, '');
|
|
}
|
|
let sanitizedLine = line.replace(/^\s+/, '').replace(/\(eval code/g, '(');
|
|
|
|
// capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
|
|
// case it has spaces in it, as the string is split on \s+ later on
|
|
const location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/);
|
|
|
|
// remove the parenthesized location from the line, if it was matched
|
|
sanitizedLine = location
|
|
? sanitizedLine.replace(location[0], '')
|
|
: sanitizedLine;
|
|
|
|
const tokens = sanitizedLine.split(/\s+/).slice(1);
|
|
// if a location was matched, pass it to extractLocation() otherwise pop the last token
|
|
const locationParts = this.extractLocation(
|
|
location ? location[1] : tokens.pop(),
|
|
);
|
|
const functionName = tokens.join(' ') || undefined;
|
|
const fileName =
|
|
['eval', '<anonymous>'].indexOf(locationParts[0]) > -1
|
|
? undefined
|
|
: locationParts[0];
|
|
|
|
return new StackFrame({
|
|
functionName,
|
|
fileName,
|
|
lineNumber: locationParts[1],
|
|
columnNumber: locationParts[2],
|
|
});
|
|
}, this);
|
|
},
|
|
parseFFOrSafari: function (error: { stack: string }) {
|
|
const filtered = error.stack.split('\n').filter(function (line) {
|
|
return !line.match(SAFARI_NATIVE_CODE_REGEXP);
|
|
}, this);
|
|
|
|
return filtered.map(function (line) {
|
|
// Throw away eval information until we implement stacktrace.js/stackframe#8
|
|
if (line.indexOf(' > eval') > -1) {
|
|
line = line.replace(
|
|
/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
|
|
':$1',
|
|
);
|
|
}
|
|
|
|
if (line.indexOf('@') === -1 && line.indexOf(':') === -1) {
|
|
// Safari eval frames only have function names and nothing else
|
|
return new StackFrame({
|
|
functionName: line,
|
|
});
|
|
} else {
|
|
const functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
|
|
const matches = line.match(functionNameRegex);
|
|
const functionName = matches && matches[1] ? matches[1] : undefined;
|
|
const locationParts = this.extractLocation(
|
|
line.replace(functionNameRegex, ''),
|
|
);
|
|
|
|
return new StackFrame({
|
|
functionName,
|
|
fileName: locationParts[0],
|
|
lineNumber: locationParts[1],
|
|
columnNumber: locationParts[2],
|
|
});
|
|
}
|
|
}, this);
|
|
},
|
|
parseOpera: function (e: {
|
|
stacktrace?: string;
|
|
message: string;
|
|
stack?: string;
|
|
}): StackFrame[] {
|
|
if (
|
|
!e.stacktrace ||
|
|
(e.message.indexOf('\n') > -1 &&
|
|
e.message.split('\n').length > e.stacktrace.split('\n').length)
|
|
) {
|
|
return this.parseOpera9(e as { message: string });
|
|
} else if (!e.stack) {
|
|
return this.parseOpera10(e as { stacktrace: string });
|
|
} else {
|
|
return this.parseOpera11(e as { stack: string });
|
|
}
|
|
},
|
|
parseOpera9: function (e: { message: string }) {
|
|
const lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
|
|
const lines = e.message.split('\n');
|
|
const result = [];
|
|
|
|
for (let i = 2, len = lines.length; i < len; i += 2) {
|
|
const match = lineRE.exec(lines[i]);
|
|
if (match) {
|
|
result.push(
|
|
new StackFrame({
|
|
fileName: match[2],
|
|
lineNumber: parseFloat(match[1]),
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
parseOpera10: function (e: { stacktrace: string }) {
|
|
const lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
|
|
const lines = e.stacktrace.split('\n');
|
|
const result = [];
|
|
|
|
for (let i = 0, len = lines.length; i < len; i += 2) {
|
|
const match = lineRE.exec(lines[i]);
|
|
if (match) {
|
|
result.push(
|
|
new StackFrame({
|
|
functionName: match[3] || undefined,
|
|
fileName: match[2],
|
|
lineNumber: parseFloat(match[1]),
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
// Opera 10.65+ Error.stack very similar to FF/Safari
|
|
parseOpera11: function (error: { stack: string }) {
|
|
const filtered = error.stack.split('\n').filter(function (line) {
|
|
return (
|
|
!!line.match(FIREFOX_SAFARI_STACK_REGEXP) &&
|
|
!line.match(/^Error created at/)
|
|
);
|
|
}, this);
|
|
|
|
return filtered.map(function (line: string) {
|
|
const tokens = line.split('@');
|
|
const locationParts = this.extractLocation(tokens.pop());
|
|
const functionCall = tokens.shift() || '';
|
|
const functionName =
|
|
functionCall
|
|
.replace(/<anonymous function(: (\w+))?>/, '$2')
|
|
.replace(/\([^)]*\)/g, '') || undefined;
|
|
return new StackFrame({
|
|
functionName,
|
|
fileName: locationParts[0],
|
|
lineNumber: locationParts[1],
|
|
columnNumber: locationParts[2],
|
|
});
|
|
}, this);
|
|
},
|
|
};
|