Chore: Migrate build to vite (#1033)

* 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.md

And 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.md

And 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
This commit is contained in:
Justin Halsall
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 4055c69e26
commit 5a85ddf013
266 changed files with 11616 additions and 9614 deletions

179
packages/rrdom/README.md Normal file
View File

@@ -0,0 +1,179 @@
# rrdom
`rrdom` is a virtual dom library that is used by `rrweb` to replay DOM mutations. It is a standalone library that can be used to create a virtual dom tree and apply patches to the real dom. It's used in `rrweb` to optimize replay performance especially when seeking.
See the [guide](../../guide.md) for more info on rrweb.
## Sponsors
[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site.
### Gold Sponsors 🥇
<div dir="auto">
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/0/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/1/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/2/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/3/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/4/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/5/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/6/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
</div>
### Silver Sponsors 🥈
<div dir="auto">
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/0/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/1/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/2/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/3/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/4/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/5/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/6/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
</div>
### Bronze Sponsors 🥉
<div dir="auto">
<a href="https://opencollective.com/rrweb/tiers/sponsors/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/0/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/1/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/2/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/3/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/4/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/5/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/6/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/7/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/7/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
<a href="https://opencollective.com/rrweb/tiers/sponsors/8/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/8/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
</div>
### Backers
<a href="https://opencollective.com/rrweb#sponsor" rel="nofollow"><img src="https://opencollective.com/rrweb/tiers/backers.svg?avatarHeight=36"></a>
## Core Team Members
<table>
<tr>
<td align="center">
<a href="https://github.com/Yuyz0112">
<img
src="https://avatars.githubusercontent.com/u/13651389?s=100"
width="100px;"
alt=""
/>
<br /><sub><b>Yuyz0112</b></sub>
<br /><br />
</a>
</td>
<td align="center">
<a href="https://github.com/YunFeng0817">
<img
src="https://avatars.githubusercontent.com/u/27533910?s=100"
width="100px;"
alt=""
/>
<br /><sub><b>Yun Feng</b></sub>
<br /><br />
</a>
</td>
<td align="center">
<a href="https://github.com/eoghanmurray">
<img
src="https://avatars.githubusercontent.com/u/156780?s=100"
width="100px;"
alt=""
/>
<br /><sub><b>eoghanmurray</b></sub>
<br /><br />
</a>
</td>
<td align="center">
<a href="https://github.com/Juice10">
<img
src="https://avatars.githubusercontent.com/u/4106?s=100"
width="100px;"
alt=""
/>
<br /><sub><b>Juice10</b></sub>
<br /><sub>open for rrweb consulting</sub>
</a>
</td>
</tr>
</table>
## Who's using rrweb?
<table>
<tr>
<td align="center">
<a href="http://www.smartx.com/" target="_blank">
<img width="195px" src="https://www.rrweb.io/logos/smartx.png">
</a>
</td>
<td align="center">
<a href="https://posthog.com?utm_source=rrweb&utm_medium=sponsorship&utm_campaign=open-source-sponsorship" target="_blank">
<img width="195px" src="https://www.rrweb.io/logos/posthog.png">
</a>
</td>
<td align="center">
<a href="https://statcounter.com/session-replay/" target="_blank">
<img width="195px" src="https://statcounter.com/images/logo-statcounter-arc-blue.svg">
</a>
</td>
<td align="center">
<a href="https://recordonce.com/" target="_blank">
<img width="195px" alt="Smart screen recording for SaaS" src="https://uploads-ssl.webflow.com/5f3d133183156245630d4446/5f3d1940abe8db8612c23521_Record-Once-logo-554x80px.svg">
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://cux.io" target="_blank">
<img style="padding: 8px" alt="The first ever UX automation tool" width="195px" src="https://cux.io/cux-logo.svg">
</a>
</td>
<td align="center">
<a href="https://remsupp.com" target="_blank">
<img style="padding: 8px" alt="Remote Access & Co-Browsing" width="195px" src="https://remsupp.com/images/logo.png">
</a>
</td>
<td align="center">
<a href="https://highlight.io" target="_blank">
<img style="padding: 8px" alt="The open source, fullstack Monitoring Platform." width="195px" src="https://github.com/highlight/highlight/raw/main/highlight.io/public/images/logo.png">
</a>
</td>
<td align="center">
<a href="https://analyzee.io" target="_blank">
<img style="padding: 8px" alt="Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions." width="195px" src="https://cdn.analyzee.io/assets/analyzee-logo.png">
</a>
</td>
</tr>
<tr>
<td align="center">
<a href="https://requestly.io" target="_blank">
<img style="padding: 8px" alt="Intercept, Modify, Record & Replay HTTP Requests." width="195px" src="https://github.com/requestly/requestly/assets/16779465/652552db-c867-44cb-9bb5-94a2026e04ca">
</a>
</td>
<td align="center">
<a href="https://gleap.io" target="_blank">
<img style="padding: 8px" alt="In-app bug reporting & customer feedback platform." width="195px" src="https://assets-global.website-files.com/6506f3f29c68b1724807619d/6506f56010237164c6306591_GleapLogo.svg">
</a>
</td>
<td align="center">
<a href="https://uxwizz.com" target="_blank">
<img style="padding: 8px" alt="Self-hosted website analytics with heatmaps and session recordings." width="195px" src="https://github.com/UXWizz/public-files/raw/main/assets/logo.png">
</a>
</td>
<td align="center">
<a href="https://www.howdygo.com" target="_blank">
<img style="padding: 8px" alt="Interactive product demos for small marketing teams" width="195px" src="https://assets-global.website-files.com/650afb446f1dd5bd410f00cc/650b2cec6188ff54dd9b01e1_Logo.svg">
</a>
</td>
</tr>
</table>

View File

@@ -1,5 +1,14 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */
export default {
preset: 'ts-jest',
testEnvironment: 'node',
/**
* Keeps old (pre-jest 29) snapshot format
* its a bit ugly and harder to read than the new format,
* so we might want to remove this in its own PR
*/
snapshotFormat: {
escapeString: true,
printBasicPrototype: true,
},
};

View File

@@ -3,48 +3,53 @@
"version": "2.0.0-alpha.14",
"homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme",
"license": "MIT",
"main": "lib/rrdom.cjs",
"module": "es/rrdom.js",
"typings": "es",
"unpkg": "dist/rrdom.js",
"type": "module",
"main": "./dist/rrdom.cjs",
"module": "./dist/rrdom.js",
"unpkg": "./dist/rrdom.umd.cjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/rrdom.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/rrdom.cjs"
}
}
},
"files": [
"dist",
"lib",
"es",
"typings"
"package.json"
],
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/rrweb-io/rrweb.git"
},
"scripts": {
"dev": "rollup -c -w",
"bundle": "rollup --config",
"bundle:es-only": "cross-env ES_ONLY=true rollup --config",
"dev": "vite build --watch",
"build": "tsc -noEmit && vite build",
"check-types": "tsc -noEmit",
"test": "jest",
"prepublish": "npm run bundle",
"test": "vitest run",
"test:watch": "vitest",
"prepublish": "npm run build",
"lint": "yarn eslint src/**/*.ts"
},
"bugs": {
"url": "https://github.com/rrweb-io/rrweb/issues"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^20.0.0",
"@rrweb/types": "^2.0.0-alpha.14",
"@types/jest": "^27.4.1",
"@types/puppeteer": "^5.4.4",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"eslint": "^8.15.0",
"jest": "^27.5.1",
"puppeteer": "^17.1.3",
"rollup": "^2.56.3",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.31.2",
"rollup-plugin-web-worker-loader": "^1.6.1",
"ts-jest": "^27.1.3"
"typescript": "^4.9.0",
"vite": "^5.2.8",
"vite-plugin-dts": "^3.8.1"
},
"dependencies": {
"rrweb-snapshot": "^2.0.0-alpha.14"

View File

@@ -1,103 +0,0 @@
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import typescript from 'rollup-plugin-typescript2';
import webWorkerLoader from 'rollup-plugin-web-worker-loader';
import pkg from './package.json';
function toMinPath(path) {
return path.replace(/\.js$/, '.min.js');
}
const basePlugins = [
resolve({ browser: true }),
commonjs(),
// supports bundling `web-worker:..filename` from rrweb
webWorkerLoader(),
typescript({
tsconfigOverride: { compilerOptions: { module: 'ESNext' } },
}),
];
const baseConfigs = [
{
input: './src/index.ts',
name: pkg.name,
path: pkg.name,
},
];
let configs = [];
let extraConfigs = [];
for (let config of baseConfigs) {
configs.push(
// ES module
{
input: config.input,
plugins: basePlugins,
output: [
{
format: 'esm',
file: pkg.module.replace(pkg.name, config.path),
},
],
},
);
extraConfigs.push(
// browser
{
input: config.input,
plugins: basePlugins,
output: [
{
name: config.name,
format: 'iife',
file: pkg.unpkg.replace(pkg.name, config.path),
},
],
},
{
input: config.input,
plugins: basePlugins.concat(terser()),
output: [
{
name: config.name,
format: 'iife',
file: toMinPath(pkg.unpkg).replace(pkg.name, config.path),
sourcemap: true,
},
],
},
// CommonJS
{
input: config.input,
plugins: basePlugins,
output: [
{
format: 'cjs',
file: pkg.main.replace(pkg.name, config.path),
},
],
},
// ES module (packed)
{
input: config.input,
plugins: basePlugins.concat(terser()),
output: [
{
format: 'esm',
file: toMinPath(pkg.module).replace(pkg.name, config.path),
sourcemap: true,
},
],
},
);
}
if (!process.env.ES_ONLY) {
configs.push(...extraConfigs);
}
export default configs;

View File

@@ -237,7 +237,7 @@ function diffAfterUpdatingChildren(
case 'AUDIO':
case 'VIDEO': {
const oldMediaElement = oldTree as HTMLMediaElement;
const newMediaRRElement = newRRElement as RRMediaElement;
const newMediaRRElement = newRRElement as unknown as RRMediaElement;
if (newMediaRRElement.paused !== undefined)
newMediaRRElement.paused
? void oldMediaElement.pause()

View File

@@ -121,31 +121,26 @@ export interface IRRCDATASection extends IRRNode {
data: string;
}
type ConstrainedConstructor<T = Record<string, unknown>> = new (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[]
) => T;
/**
* This is designed as an abstract class so it should never be instantiated.
*/
export class BaseRRNode implements IRRNode {
export abstract class BaseRRNode implements IRRNode {
public parentElement: IRRNode | null = null;
public parentNode: IRRNode | null = null;
public ownerDocument: IRRDocument;
public ownerDocument!: IRRDocument;
public firstChild: IRRNode | null = null;
public lastChild: IRRNode | null = null;
public previousSibling: IRRNode | null = null;
public nextSibling: IRRNode | null = null;
public textContent: string | null;
public abstract textContent: string | null;
public readonly ELEMENT_NODE: number = NodeType.ELEMENT_NODE;
public readonly TEXT_NODE: number = NodeType.TEXT_NODE;
// corresponding nodeType value of standard HTML Node
public readonly nodeType: number;
public readonly nodeName: string;
public readonly RRNodeType: RRNodeType;
public readonly nodeType!: number;
public readonly nodeName!: string;
public readonly RRNodeType!: RRNodeType;
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
constructor(..._args: any[]) {
@@ -200,476 +195,428 @@ export class BaseRRNode implements IRRNode {
}
}
export function BaseRRDocumentImpl<
RRNode extends ConstrainedConstructor<IRRNode>,
>(RRNodeClass: RRNode) {
return class BaseRRDocument extends RRNodeClass implements IRRDocument {
public readonly nodeType: number = NodeType.DOCUMENT_NODE;
public readonly nodeName: '#document' = '#document';
public readonly compatMode: 'BackCompat' | 'CSS1Compat' = 'CSS1Compat';
public readonly RRNodeType = RRNodeType.Document;
export class BaseRRDocument extends BaseRRNode implements IRRDocument {
public readonly nodeType: number = NodeType.DOCUMENT_NODE;
public readonly nodeName = '#document' as const;
public readonly compatMode: 'BackCompat' | 'CSS1Compat' = 'CSS1Compat';
public readonly RRNodeType = RRNodeType.Document;
public textContent: string | null = null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(...args: any[]) {
super(args);
this.textContent = null;
this.ownerDocument = this;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
constructor(...args: any[]) {
super(args);
this.ownerDocument = this;
}
public get documentElement(): IRRElement | null {
return (
(this.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'HTML',
) as IRRElement) || null
);
}
public get documentElement(): IRRElement | null {
return (
(this.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'HTML',
) as IRRElement) || null
);
}
public get body(): IRRElement | null {
return (
(this.documentElement?.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'BODY',
) as IRRElement) || null
);
}
public get body(): IRRElement | null {
return (
(this.documentElement?.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'BODY',
) as IRRElement) || null
);
}
public get head(): IRRElement | null {
return (
(this.documentElement?.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'HEAD',
) as IRRElement) || null
);
}
public get head(): IRRElement | null {
return (
(this.documentElement?.childNodes.find(
(node) =>
node.RRNodeType === RRNodeType.Element &&
(node as IRRElement).tagName === 'HEAD',
) as IRRElement) || null
);
}
public get implementation(): IRRDocument {
return this;
}
public get implementation(): IRRDocument {
return this;
}
public get firstElementChild(): IRRElement | null {
return this.documentElement;
}
public get firstElementChild(): IRRElement | null {
return this.documentElement;
}
public appendChild(newChild: IRRNode): IRRNode {
const nodeType = newChild.RRNodeType;
if (
nodeType === RRNodeType.Element ||
nodeType === RRNodeType.DocumentType
) {
if (this.childNodes.some((s) => s.RRNodeType === nodeType)) {
throw new Error(
`RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one ${
nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype'
} on RRDocument allowed.`,
);
}
}
const child = appendChild(this, newChild);
child.parentElement = null;
return child;
}
public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode {
const nodeType = newChild.RRNodeType;
if (
nodeType === RRNodeType.Element ||
nodeType === RRNodeType.DocumentType
) {
if (this.childNodes.some((s) => s.RRNodeType === nodeType)) {
throw new Error(
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one ${
nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype'
} on RRDocument allowed.`,
);
}
}
const child = insertBefore(this, newChild, refChild);
child.parentElement = null;
return child;
}
public removeChild(node: IRRNode): IRRNode {
return removeChild(this, node);
}
public open() {
this.firstChild = null;
this.lastChild = null;
}
public close() {
//
}
/**
* Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot).
* There are two lines used this function:
* 1. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""\>')
* 2. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ""\>')
*/
public write(content: string) {
let publicId;
if (
content ===
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">'
)
publicId = '-//W3C//DTD XHTML 1.0 Transitional//EN';
else if (
content ===
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">'
)
publicId = '-//W3C//DTD HTML 4.0 Transitional//EN';
if (publicId) {
const doctype = this.createDocumentType('html', publicId, '');
this.open();
this.appendChild(doctype);
public appendChild(newChild: IRRNode): IRRNode {
const nodeType = newChild.RRNodeType;
if (
nodeType === RRNodeType.Element ||
nodeType === RRNodeType.DocumentType
) {
if (this.childNodes.some((s) => s.RRNodeType === nodeType)) {
throw new Error(
`RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one ${
nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype'
} on RRDocument allowed.`,
);
}
}
createDocument(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_namespace: string | null,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_qualifiedName: string | null,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_doctype?: DocumentType | null,
): IRRDocument {
return new BaseRRDocument();
const child = appendChild(this, newChild);
child.parentElement = null;
return child;
}
public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode {
const nodeType = newChild.RRNodeType;
if (
nodeType === RRNodeType.Element ||
nodeType === RRNodeType.DocumentType
) {
if (this.childNodes.some((s) => s.RRNodeType === nodeType)) {
throw new Error(
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one ${
nodeType === RRNodeType.Element ? 'RRElement' : 'RRDoctype'
} on RRDocument allowed.`,
);
}
}
createDocumentType(
qualifiedName: string,
publicId: string,
systemId: string,
): IRRDocumentType {
const doctype = new (BaseRRDocumentTypeImpl(BaseRRNode))(
qualifiedName,
publicId,
systemId,
);
doctype.ownerDocument = this;
return doctype;
}
createElement(tagName: string): IRRElement {
const element = new (BaseRRElementImpl(BaseRRNode))(tagName);
element.ownerDocument = this;
return element;
}
createElementNS(_namespaceURI: string, qualifiedName: string): IRRElement {
return this.createElement(qualifiedName);
}
createTextNode(data: string): IRRText {
const text = new (BaseRRTextImpl(BaseRRNode))(data);
text.ownerDocument = this;
return text;
}
createComment(data: string): IRRComment {
const comment = new (BaseRRCommentImpl(BaseRRNode))(data);
comment.ownerDocument = this;
return comment;
}
createCDATASection(data: string): IRRCDATASection {
const CDATASection = new (BaseRRCDATASectionImpl(BaseRRNode))(data);
CDATASection.ownerDocument = this;
return CDATASection;
}
toString() {
return 'RRDocument';
}
};
}
export function BaseRRDocumentTypeImpl<
RRNode extends ConstrainedConstructor<IRRNode>,
>(RRNodeClass: RRNode) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return class BaseRRDocumentType
extends RRNodeClass
implements IRRDocumentType
{
public readonly nodeType: number = NodeType.DOCUMENT_TYPE_NODE;
public readonly RRNodeType = RRNodeType.DocumentType;
public readonly nodeName: string;
public readonly name: string;
public readonly publicId: string;
public readonly systemId: string;
constructor(qualifiedName: string, publicId: string, systemId: string) {
super();
this.name = qualifiedName;
this.publicId = publicId;
this.systemId = systemId;
this.nodeName = qualifiedName;
this.textContent = null;
}
toString() {
return 'RRDocumentType';
}
};
}
export function BaseRRElementImpl<
RRNode extends ConstrainedConstructor<IRRNode>,
>(RRNodeClass: RRNode) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return class BaseRRElement extends RRNodeClass implements IRRElement {
public readonly nodeType: number = NodeType.ELEMENT_NODE;
public readonly RRNodeType = RRNodeType.Element;
public readonly nodeName: string;
public tagName: string;
public attributes: Record<string, string> = {};
public shadowRoot: IRRElement | null = null;
public scrollLeft?: number;
public scrollTop?: number;
constructor(tagName: string) {
super();
this.tagName = tagName.toUpperCase();
this.nodeName = tagName.toUpperCase();
}
public get textContent(): string {
let result = '';
this.childNodes.forEach((node) => (result += node.textContent));
return result;
}
public set textContent(textContent: string) {
this.firstChild = null;
this.lastChild = null;
this.appendChild(this.ownerDocument.createTextNode(textContent));
}
public get classList(): ClassList {
return new ClassList(
this.attributes.class as string | undefined,
(newClassName) => {
this.attributes.class = newClassName;
},
);
}
public get id() {
return this.attributes.id || '';
}
public get className() {
return this.attributes.class || '';
}
public get style() {
const style = (
this.attributes.style ? parseCSSText(this.attributes.style) : {}
) as CSSStyleDeclaration;
const hyphenateRE = /\B([A-Z])/g;
style.setProperty = (
name: string,
value: string | null,
priority?: string,
) => {
if (hyphenateRE.test(name)) return;
const normalizedName = camelize(name);
if (!value) delete style[normalizedName];
else style[normalizedName] = value;
if (priority === 'important') style[normalizedName] += ' !important';
this.attributes.style = toCSSText(style);
};
style.removeProperty = (name: string) => {
if (hyphenateRE.test(name)) return '';
const normalizedName = camelize(name);
const value = style[normalizedName] || '';
delete style[normalizedName];
this.attributes.style = toCSSText(style);
return value;
};
return style;
}
public getAttribute(name: string) {
return this.attributes[name] || null;
}
public setAttribute(name: string, attribute: string) {
this.attributes[name] = attribute;
}
public setAttributeNS(
_namespace: string | null,
qualifiedName: string,
value: string,
): void {
this.setAttribute(qualifiedName, value);
}
public removeAttribute(name: string) {
delete this.attributes[name];
}
public appendChild(newChild: IRRNode): IRRNode {
return appendChild(this, newChild);
}
public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode {
return insertBefore(this, newChild, refChild);
}
public removeChild(node: IRRNode): IRRNode {
return removeChild(this, node);
const child = insertBefore(this, newChild, refChild);
child.parentElement = null;
return child;
}
public removeChild(node: IRRNode): IRRNode {
return removeChild(this, node);
}
public open() {
this.firstChild = null;
this.lastChild = null;
}
public close() {
//
}
/**
* Adhoc implementation for setting xhtml namespace in rebuilt.ts (rrweb-snapshot).
* There are two lines used this function:
* 1. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ""\>')
* 2. doc.write('\<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ""\>')
*/
public write(content: string) {
let publicId;
if (
content ===
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">'
)
publicId = '-//W3C//DTD XHTML 1.0 Transitional//EN';
else if (
content ===
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "">'
)
publicId = '-//W3C//DTD HTML 4.0 Transitional//EN';
if (publicId) {
const doctype = this.createDocumentType('html', publicId, '');
this.open();
this.appendChild(doctype);
}
}
createDocument(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public attachShadow(_init: ShadowRootInit): IRRElement {
const shadowRoot = this.ownerDocument.createElement('SHADOWROOT');
this.shadowRoot = shadowRoot;
return shadowRoot;
}
_namespace: string | null,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public dispatchEvent(_event: Event) {
return true;
}
toString() {
let attributeString = '';
for (const attribute in this.attributes) {
attributeString += `${attribute}="${this.attributes[attribute]}" `;
}
return `${this.tagName} ${attributeString}`;
}
};
}
export function BaseRRMediaElementImpl<
RRElement extends ConstrainedConstructor<IRRElement>,
>(RRElementClass: RRElement) {
return class BaseRRMediaElement extends RRElementClass {
public currentTime?: number;
public volume?: number;
public paused?: boolean;
public muted?: boolean;
public playbackRate?: number;
public loop?: boolean;
_qualifiedName: string | null,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
attachShadow(_init: ShadowRootInit): IRRElement {
throw new Error(
`RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`,
);
}
public play() {
this.paused = false;
}
public pause() {
this.paused = true;
}
};
_doctype?: DocumentType | null,
): IRRDocument {
return new BaseRRDocument();
}
createDocumentType(
qualifiedName: string,
publicId: string,
systemId: string,
): IRRDocumentType {
const doctype = new BaseRRDocumentType(qualifiedName, publicId, systemId);
doctype.ownerDocument = this;
return doctype;
}
createElement(tagName: string): IRRElement {
const element = new BaseRRElement(tagName);
element.ownerDocument = this;
return element;
}
createElementNS(_namespaceURI: string, qualifiedName: string): IRRElement {
return this.createElement(qualifiedName);
}
createTextNode(data: string): IRRText {
const text = new BaseRRText(data);
text.ownerDocument = this;
return text;
}
createComment(data: string): IRRComment {
const comment = new BaseRRComment(data);
comment.ownerDocument = this;
return comment;
}
createCDATASection(data: string): IRRCDATASection {
const CDATASection = new BaseRRCDATASection(data);
CDATASection.ownerDocument = this;
return CDATASection;
}
toString() {
return 'RRDocument';
}
}
export function BaseRRTextImpl<RRNode extends ConstrainedConstructor<IRRNode>>(
RRNodeClass: RRNode,
) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return class BaseRRText extends RRNodeClass implements IRRText {
public readonly nodeType: number = NodeType.TEXT_NODE;
public readonly nodeName: '#text' = '#text';
public readonly RRNodeType = RRNodeType.Text;
public data: string;
export class BaseRRDocumentType extends BaseRRNode implements IRRDocumentType {
public readonly nodeType: number = NodeType.DOCUMENT_TYPE_NODE;
public readonly RRNodeType = RRNodeType.DocumentType;
declare readonly nodeName: string;
public readonly name: string;
public readonly publicId: string;
public readonly systemId: string;
public textContent: string | null = null;
constructor(data: string) {
super();
this.data = data;
}
constructor(qualifiedName: string, publicId: string, systemId: string) {
super();
this.name = qualifiedName;
this.publicId = publicId;
this.systemId = systemId;
this.nodeName = qualifiedName;
}
public get textContent(): string {
return this.data;
}
public set textContent(textContent: string) {
this.data = textContent;
}
toString() {
return `RRText text=${JSON.stringify(this.data)}`;
}
};
toString() {
return 'RRDocumentType';
}
}
export function BaseRRCommentImpl<
RRNode extends ConstrainedConstructor<IRRNode>,
>(RRNodeClass: RRNode) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return class BaseRRComment extends RRNodeClass implements IRRComment {
public readonly nodeType: number = NodeType.COMMENT_NODE;
public readonly nodeName: '#comment' = '#comment';
public readonly RRNodeType = RRNodeType.Comment;
public data: string;
export class BaseRRElement extends BaseRRNode implements IRRElement {
public readonly nodeType: number = NodeType.ELEMENT_NODE;
public readonly RRNodeType = RRNodeType.Element;
declare readonly nodeName: string;
public tagName: string;
public attributes: Record<string, string> = {};
public shadowRoot: IRRElement | null = null;
public scrollLeft?: number;
public scrollTop?: number;
constructor(data: string) {
super();
this.data = data;
}
constructor(tagName: string) {
super();
this.tagName = tagName.toUpperCase();
this.nodeName = tagName.toUpperCase();
}
public get textContent(): string {
return this.data;
}
public get textContent(): string {
let result = '';
this.childNodes.forEach((node) => (result += node.textContent));
return result;
}
public set textContent(textContent: string) {
this.data = textContent;
}
public set textContent(textContent: string) {
this.firstChild = null;
this.lastChild = null;
this.appendChild(this.ownerDocument.createTextNode(textContent));
}
toString() {
return `RRComment text=${JSON.stringify(this.data)}`;
public get classList(): ClassList {
return new ClassList(
this.attributes.class as string | undefined,
(newClassName) => {
this.attributes.class = newClassName;
},
);
}
public get id() {
return this.attributes.id || '';
}
public get className() {
return this.attributes.class || '';
}
public get style() {
const style = (
this.attributes.style ? parseCSSText(this.attributes.style) : {}
) as CSSStyleDeclaration;
const hyphenateRE = /\B([A-Z])/g;
style.setProperty = (
name: string,
value: string | null,
priority?: string | null,
) => {
if (hyphenateRE.test(name)) return;
const normalizedName = camelize(name);
if (!value) delete style[normalizedName];
else style[normalizedName] = value;
if (priority === 'important') style[normalizedName] += ' !important';
this.attributes.style = toCSSText(style);
};
style.removeProperty = (name: string) => {
if (hyphenateRE.test(name)) return '';
const normalizedName = camelize(name);
const value = style[normalizedName] || '';
delete style[normalizedName];
this.attributes.style = toCSSText(style);
return value;
};
return style;
}
public getAttribute(name: string): string | null {
return this.attributes[name] || null;
}
public setAttribute(name: string, attribute: string) {
this.attributes[name] = attribute;
}
public setAttributeNS(
_namespace: string | null,
qualifiedName: string,
value: string,
): void {
this.setAttribute(qualifiedName, value);
}
public removeAttribute(name: string) {
delete this.attributes[name];
}
public appendChild(newChild: IRRNode): IRRNode {
return appendChild(this, newChild);
}
public insertBefore(newChild: IRRNode, refChild: IRRNode | null): IRRNode {
return insertBefore(this, newChild, refChild);
}
public removeChild(node: IRRNode): IRRNode {
return removeChild(this, node);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public attachShadow(_init: ShadowRootInit): IRRElement {
const shadowRoot = this.ownerDocument.createElement('SHADOWROOT');
this.shadowRoot = shadowRoot;
return shadowRoot;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public dispatchEvent(_event: Event) {
return true;
}
toString() {
let attributeString = '';
for (const attribute in this.attributes) {
attributeString += `${attribute}="${this.attributes[attribute]}" `;
}
};
return `${this.tagName} ${attributeString}`;
}
}
export function BaseRRCDATASectionImpl<
RRNode extends ConstrainedConstructor<IRRNode>,
>(RRNodeClass: RRNode) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return class BaseRRCDATASection
extends RRNodeClass
implements IRRCDATASection
{
public readonly nodeName: '#cdata-section' = '#cdata-section';
public readonly nodeType: number = NodeType.CDATA_SECTION_NODE;
public readonly RRNodeType = RRNodeType.CDATA;
public data: string;
export class BaseRRMediaElement extends BaseRRElement {
public currentTime?: number;
public volume?: number;
public paused?: boolean;
public muted?: boolean;
public playbackRate?: number;
public loop?: boolean;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
attachShadow(_init: ShadowRootInit): IRRElement {
throw new Error(
`RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`,
);
}
public play() {
this.paused = false;
}
public pause() {
this.paused = true;
}
}
constructor(data: string) {
super();
this.data = data;
}
export class BaseRRText extends BaseRRNode implements IRRText {
public readonly nodeType: number = NodeType.TEXT_NODE;
public readonly nodeName = '#text' as const;
public readonly RRNodeType = RRNodeType.Text;
public data: string;
public get textContent(): string {
return this.data;
}
constructor(data: string) {
super();
this.data = data;
}
public set textContent(textContent: string) {
this.data = textContent;
}
public get textContent(): string {
return this.data;
}
toString() {
return `RRCDATASection data=${JSON.stringify(this.data)}`;
}
};
public set textContent(textContent: string) {
this.data = textContent;
}
toString() {
return `RRText text=${JSON.stringify(this.data)}`;
}
}
export class BaseRRComment extends BaseRRNode implements IRRComment {
public readonly nodeType: number = NodeType.COMMENT_NODE;
public readonly nodeName = '#comment' as const;
public readonly RRNodeType = RRNodeType.Comment;
public data: string;
constructor(data: string) {
super();
this.data = data;
}
public get textContent(): string {
return this.data;
}
public set textContent(textContent: string) {
this.data = textContent;
}
toString() {
return `RRComment text=${JSON.stringify(this.data)}`;
}
}
export class BaseRRCDATASection extends BaseRRNode implements IRRCDATASection {
public readonly nodeName = '#cdata-section' as const;
public readonly nodeType: number = NodeType.CDATA_SECTION_NODE;
public readonly RRNodeType = RRNodeType.CDATA;
public data: string;
constructor(data: string) {
super();
this.data = data;
}
public get textContent(): string {
return this.data;
}
public set textContent(textContent: string) {
this.data = textContent;
}
toString() {
return `RRCDATASection data=${JSON.stringify(this.data)}`;
}
}
export class ClassList {

View File

@@ -17,23 +17,23 @@ import type {
} from '@rrweb/types';
import {
BaseRRNode as RRNode,
BaseRRCDATASectionImpl,
BaseRRCommentImpl,
BaseRRDocumentImpl,
BaseRRDocumentTypeImpl,
BaseRRElementImpl,
BaseRRMediaElementImpl,
BaseRRTextImpl,
IRRDocument,
IRRElement,
IRRNode,
BaseRRCDATASection,
BaseRRComment,
BaseRRDocument,
BaseRRDocumentType,
BaseRRElement,
BaseRRMediaElement,
BaseRRText,
type IRRDocument,
type IRRElement,
type IRRNode,
NodeType,
IRRDocumentType,
IRRText,
IRRComment,
type IRRDocumentType,
type IRRText,
type IRRComment,
} from './document';
export class RRDocument extends BaseRRDocumentImpl(RRNode) {
export class RRDocument extends BaseRRDocument {
private UNSERIALIZED_STARTING_ID = -2;
// In the rrweb replayer, there are some unserialized nodes like the element that stores the injected style rules.
// These unserialized nodes may interfere the execution of the diff algorithm.
@@ -142,14 +142,14 @@ export class RRDocument extends BaseRRDocumentImpl(RRNode) {
}
}
export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode);
export const RRDocumentType = BaseRRDocumentType;
export class RRElement extends BaseRRElementImpl(RRNode) {
export class RRElement extends BaseRRElement {
inputData: inputData | null = null;
scrollData: scrollData | null = null;
}
export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
export class RRMediaElement extends BaseRRMediaElement {}
export class RRCanvasElement extends RRElement implements IRRElement {
public rr_dataURL: string | null = null;
@@ -177,13 +177,13 @@ export class RRIFrameElement extends RRElement {
}
}
export const RRText = BaseRRTextImpl(RRNode);
export const RRText = BaseRRText;
export type RRText = typeof RRText;
export const RRComment = BaseRRCommentImpl(RRNode);
export const RRComment = BaseRRComment;
export type RRComment = typeof RRComment;
export const RRCDATASection = BaseRRCDATASectionImpl(RRNode);
export const RRCDATASection = BaseRRCDATASection;
export type RRCDATASection = typeof RRCDATASection;
interface RRElementTagNameMap {
@@ -483,5 +483,5 @@ function walk(node: IRRNode, mirror: IMirror<IRRNode>, blankSpace: string) {
export { RRNode };
export { diff, createOrGetNode, ReplayerHandler } from './diff';
export { diff, createOrGetNode, type ReplayerHandler } from './diff';
export * from './document';

View File

@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`RRDocument for browser environment create a RRDocument from a html document can build from a common html 1`] = `
exports[`RRDocument for browser environment > create a RRDocument from a html document > can build from a common html 1`] = `
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"
@@ -54,7 +54,7 @@ exports[`RRDocument for browser environment create a RRDocument from a html docu
"
`;
exports[`RRDocument for browser environment create a RRDocument from a html document can build from a html containing nested shadow doms 1`] = `
exports[`RRDocument for browser environment > create a RRDocument from a html document > can build from a html containing nested shadow doms 1`] = `
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"
@@ -89,14 +89,14 @@ exports[`RRDocument for browser environment create a RRDocument from a html docu
"
`;
exports[`RRDocument for browser environment create a RRDocument from a html document can build from a xml page 1`] = `
exports[`RRDocument for browser environment > create a RRDocument from a html document > can build from a xml page 1`] = `
"-2 RRDocument
-3 XML
-4 RRCDATASection data=\\"Some <CDATA> data & then some\\"
"
`;
exports[`RRDocument for browser environment create a RRDocument from a html document can build from an iframe html 1`] = `
exports[`RRDocument for browser environment > create a RRDocument from a html document > can build from an iframe html 1`] = `
"-2 RRDocument
-3 RRDocumentType
-4 HTML lang=\\"en\\"

View File

@@ -1,13 +1,15 @@
/**
* @jest-environment jsdom
*/
import * as fs from 'fs';
import * as path from 'path';
import * as puppeteer from 'puppeteer';
import { vi, MockInstance } from 'vitest';
import {
NodeType as RRNodeType,
serializedNodeWithId,
createMirror,
Mirror as NodeMirror,
serializedNodeWithId,
} from 'rrweb-snapshot';
import {
buildFromDom,
@@ -25,15 +27,8 @@ import {
sameNodeType,
} from '../src/diff';
import type { IRRElement, IRRNode } from '../src/document';
import { Replayer } from 'rrweb';
import type {
eventWithTime,
canvasMutationData,
styleDeclarationData,
styleSheetRuleData,
} from '@rrweb/types';
import type { canvasMutationData, styleSheetRuleData } from '@rrweb/types';
import { EventType, IncrementalSource } from '@rrweb/types';
import { compileTSCode } from './utils';
const elementSn = {
type: RRNodeType.Element,
@@ -107,7 +102,7 @@ function shuffle(list: number[]) {
describe('diff algorithm for rrdom', () => {
let mirror: NodeMirror;
let replayer: ReplayerHandler;
let warn: jest.SpyInstance;
let warn: MockInstance;
beforeEach(() => {
mirror = createMirror();
@@ -121,7 +116,7 @@ describe('diff algorithm for rrdom', () => {
};
document.write('<!DOCTYPE html><html><head></head><body></body></html>');
// Mock the original console.warn function to make the test fail once console.warn is called.
warn = jest.spyOn(console, 'warn');
warn = vi.spyOn(console, 'warn');
});
afterEach(() => {
@@ -146,7 +141,7 @@ describe('diff algorithm for rrdom', () => {
x: 0,
y: 0,
};
const applyScrollFn = jest.spyOn(replayer, 'applyScroll');
const applyScrollFn = vi.spyOn(replayer, 'applyScroll');
diff(document, rrNode, replayer);
expect(document.childNodes.length).toEqual(1);
expect(document.childNodes[0]).toBeInstanceOf(DocumentType);
@@ -169,7 +164,7 @@ describe('diff algorithm for rrdom', () => {
x: 0,
y: 0,
};
const applyScrollFn = jest.spyOn(replayer, 'applyScroll');
const applyScrollFn = vi.spyOn(replayer, 'applyScroll');
diff(element, rrNode, replayer);
expect(applyScrollFn).toHaveBeenCalledTimes(1);
applyScrollFn.mockRestore();
@@ -185,7 +180,7 @@ describe('diff algorithm for rrdom', () => {
id: 0,
isChecked: false,
};
replayer.applyInput = jest.fn();
replayer.applyInput = vi.fn();
diff(element, rrNode, replayer);
expect(replayer.applyInput).toHaveBeenCalledTimes(1);
});
@@ -215,7 +210,7 @@ describe('diff algorithm for rrdom', () => {
],
};
rrStyle.rules = [styleData];
replayer.applyStyleSheetMutation = jest.fn();
replayer.applyStyleSheetMutation = vi.fn();
diff(element, rrStyle, replayer);
expect(replayer.applyStyleSheetMutation).toHaveBeenCalledTimes(1);
expect(replayer.applyStyleSheetMutation).toHaveBeenCalledWith(
@@ -245,7 +240,7 @@ describe('diff algorithm for rrdom', () => {
mutation: canvasMutation,
}),
);
replayer.applyCanvas = jest.fn();
replayer.applyCanvas = vi.fn();
diff(element, rrCanvas, replayer);
expect(replayer.applyCanvas).toHaveBeenCalledTimes(MutationNumber);
});
@@ -274,7 +269,9 @@ describe('diff algorithm for rrdom', () => {
expect(element.playbackRate).toEqual(1);
const rrDocument = new RRDocument();
const rrMedia = rrDocument.createElement(tagName) as RRMediaElement;
const rrMedia = rrDocument.createElement(
tagName,
) as unknown as RRMediaElement;
rrMedia.volume = 0.5;
rrMedia.currentTime = 100;
rrMedia.muted = true;
@@ -424,7 +421,7 @@ describe('diff algorithm for rrdom', () => {
const value = 'http://www.w3.org/2000/svg';
node.attributes.xmlns = value;
jest.spyOn(Element.prototype, 'setAttributeNS');
vi.spyOn(Element.prototype, 'setAttributeNS');
diff(element, node, replayer);
expect((element as Node as SVGElement).getAttribute('xmlns')).toBe(value);
expect(SVGElement.prototype.setAttributeNS).toHaveBeenCalledWith(
@@ -432,7 +429,7 @@ describe('diff algorithm for rrdom', () => {
'xmlns',
value,
);
jest.restoreAllMocks();
vi.restoreAllMocks();
});
it('can diff properties for canvas', async () => {
@@ -443,11 +440,11 @@ describe('diff algorithm for rrdom', () => {
rrDocument.mirror.add(rrCanvas, sn);
rrCanvas.attributes['rr_dataURL'] = 'data:image/png;base64,';
jest.spyOn(document, 'createElement');
vi.spyOn(document, 'createElement');
diff(element, rrCanvas, replayer);
expect(document.createElement).toHaveBeenCalledWith('img');
jest.restoreAllMocks();
vi.restoreAllMocks();
});
it('can omit srcdoc attribute of iframe element', () => {
@@ -1169,7 +1166,7 @@ describe('diff algorithm for rrdom', () => {
});
describe('diff iframe elements', () => {
jest.setTimeout(60_000);
vi.setConfig({ testTimeout: 60_000 });
it('should add an element to the contentDocument of an iframe element', () => {
document.write('<html></html>');
@@ -1329,7 +1326,7 @@ describe('diff algorithm for rrdom', () => {
* If not, the diff function will throw errors or warnings.
*/
// Mock the original console.warn function to make the test fail once console.warn is called.
const warn = jest.spyOn(global.console, 'warn');
const warn = vi.spyOn(global.console, 'warn');
document.write('<!DOCTYPE html><html><body></body></html>');
const rrdom = new RRDocument();
@@ -1392,8 +1389,9 @@ describe('diff algorithm for rrdom', () => {
await page.goto('about:blank');
try {
const code = await compileTSCode(
path.resolve(__dirname, '../src/index.ts'),
const code = fs.readFileSync(
path.resolve(__dirname, '../dist/rrdom.umd.cjs'),
'utf8',
);
await page.evaluate(code);
@@ -1473,7 +1471,7 @@ describe('diff algorithm for rrdom', () => {
describe('afterAppend callback', () => {
it('should call afterAppend callback', () => {
const afterAppendFn = jest.spyOn(replayer, 'afterAppend');
const afterAppendFn = vi.spyOn(replayer, 'afterAppend');
const node = createTree(
{
tagName: 'div',
@@ -1512,7 +1510,7 @@ describe('diff algorithm for rrdom', () => {
});
it('should call afterAppend callback in the post traversal order', () => {
const afterAppendFn = jest.spyOn(replayer, 'afterAppend');
const afterAppendFn = vi.spyOn(replayer, 'afterAppend');
document.open();
const rrdom = new RRDocument();
@@ -1585,7 +1583,7 @@ describe('diff algorithm for rrdom', () => {
});
it('should only call afterAppend for newly created nodes', () => {
const afterAppendFn = jest.spyOn(replayer, 'afterAppend');
const afterAppendFn = vi.spyOn(replayer, 'afterAppend');
const rrdom = buildFromDom(document, replayer.mirror) as RRDocument;
// Append 3 nodes to rrdom.
@@ -1663,9 +1661,7 @@ describe('diff algorithm for rrdom', () => {
rrDocument.mirror.add(rrNode, getDefaultSN(rrNode, 3));
expect(() =>
createOrGetNode(rrNode, mirror, rrDocument.mirror),
).toThrowErrorMatchingInlineSnapshot(
`"Cannot create CDATA sections in HTML documents"`,
);
).toThrowErrorMatchingInlineSnapshot(`DOMException {}`);
});
it('create a DocumentType from RRDocumentType', () => {
@@ -1705,164 +1701,6 @@ describe('diff algorithm for rrdom', () => {
});
});
describe('apply virtual style rules to node', () => {
beforeEach(() => {
const dummyReplayer = new Replayer([
{
type: EventType.DomContentLoaded,
timestamp: 0,
},
{
type: EventType.Meta,
data: {
with: 1920,
height: 1080,
},
timestamp: 0,
},
] as unknown as eventWithTime[]);
replayer.applyStyleSheetMutation = (
data: styleDeclarationData | styleSheetRuleData,
styleSheet: CSSStyleSheet,
) => {
if (data.source === IncrementalSource.StyleSheetRule)
// Disable the ts check here because these two functions are private methods.
// @ts-ignore
dummyReplayer.applyStyleSheetRule(data, styleSheet);
else if (data.source === IncrementalSource.StyleDeclaration)
// @ts-ignore
dummyReplayer.applyStyleDeclaration(data, styleSheet);
};
});
it('should insert rule at index 0 in empty sheet', () => {
document.write('<style></style>');
const styleEl = document.getElementsByTagName('style')[0];
const cssText = '.added-rule {border: 1px solid yellow;}';
const styleRuleData: styleSheetRuleData = {
source: IncrementalSource.StyleSheetRule,
adds: [
{
rule: cssText,
index: 0,
},
],
};
replayer.applyStyleSheetMutation(styleRuleData, styleEl.sheet!);
expect(styleEl.sheet?.cssRules?.length).toEqual(1);
expect(styleEl.sheet?.cssRules[0].cssText).toEqual(cssText);
});
it('should insert rule at index 0 and keep exsisting rules', () => {
document.write(`
<style>
a {color: blue}
div {color: black}
</style>
`);
const styleEl = document.getElementsByTagName('style')[0];
const cssText = '.added-rule {border: 1px solid yellow;}';
const styleRuleData: styleSheetRuleData = {
source: IncrementalSource.StyleSheetRule,
adds: [
{
rule: cssText,
index: 0,
},
],
};
replayer.applyStyleSheetMutation(styleRuleData, styleEl.sheet!);
expect(styleEl.sheet?.cssRules?.length).toEqual(3);
expect(styleEl.sheet?.cssRules[0].cssText).toEqual(cssText);
});
it('should delete rule at index 0', () => {
document.write(`
<style>
a {color: blue;}
div {color: black;}
</style>
`);
const styleEl = document.getElementsByTagName('style')[0];
const styleRuleData: styleSheetRuleData = {
source: IncrementalSource.StyleSheetRule,
removes: [
{
index: 0,
},
],
};
replayer.applyStyleSheetMutation(styleRuleData, styleEl.sheet!);
expect(styleEl.sheet?.cssRules?.length).toEqual(1);
expect(styleEl.sheet?.cssRules[0].cssText).toEqual('div {color: black;}');
});
it('should insert rule at index [0,0] and keep existing rules', () => {
document.write(`
<style>
@media {
a {color: blue}
div {color: black}
}
</style>
`);
const styleEl = document.getElementsByTagName('style')[0];
const cssText = '.added-rule {border: 1px solid yellow;}';
const styleRuleData: styleSheetRuleData = {
source: IncrementalSource.StyleSheetRule,
adds: [
{
rule: cssText,
index: [0, 0],
},
],
};
replayer.applyStyleSheetMutation(styleRuleData, styleEl.sheet!);
expect(
(styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules?.length,
).toEqual(3);
expect(
(styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules[0].cssText,
).toEqual(cssText);
});
it('should delete rule at index [0,1]', () => {
document.write(`
<style>
@media {
a {color: blue;}
div {color: black;}
}
</style>
`);
const styleEl = document.getElementsByTagName('style')[0];
const styleRuleData: styleSheetRuleData = {
source: IncrementalSource.StyleSheetRule,
removes: [
{
index: [0, 1],
},
],
};
replayer.applyStyleSheetMutation(styleRuleData, styleEl.sheet!);
expect(
(styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules?.length,
).toEqual(1);
expect(
(styleEl.sheet?.cssRules[0] as CSSMediaRule).cssRules[0].cssText,
).toEqual('a {color: blue;}');
});
});
describe('test sameNodeType function', () => {
const rrdom = new RRDocument();
it('should return true when two elements have same tagNames', () => {

View File

@@ -3,21 +3,21 @@
*/
import { NodeType as RRNodeType } from 'rrweb-snapshot';
import {
BaseRRDocumentImpl,
BaseRRDocumentTypeImpl,
BaseRRElementImpl,
BaseRRMediaElementImpl,
BaseRRDocument,
BaseRRDocumentType,
BaseRRMediaElement,
BaseRRNode,
IRRDocumentType,
IRRNode,
} from '../src/document';
describe('Basic RRDocument implementation', () => {
const RRNode = BaseRRNode;
const RRDocument = BaseRRDocumentImpl(RRNode);
const RRDocumentType = BaseRRDocumentTypeImpl(RRNode);
const RRElement = BaseRRElementImpl(RRNode);
class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
const RRNode = class extends BaseRRNode {
public textContent: string | null;
};
const RRDocument = BaseRRDocument;
const RRDocumentType = BaseRRDocumentType;
class RRMediaElement extends BaseRRMediaElement {}
describe('Basic RRNode implementation', () => {
it('should have basic properties', () => {
@@ -146,30 +146,24 @@ describe('Basic RRDocument implementation', () => {
it('should not implement appendChild', () => {
const parentNode = new RRNode();
const childNode = new RRNode();
expect(() =>
parentNode.appendChild(childNode),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method."`,
expect(() => parentNode.appendChild(childNode)).toThrowError(
`RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.`,
);
});
it('should not implement insertBefore', () => {
const parentNode = new RRNode();
const childNode = new RRNode();
expect(() =>
parentNode.insertBefore(childNode, null),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method."`,
expect(() => parentNode.insertBefore(childNode, null)).toThrowError(
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.`,
);
});
it('should not implement removeChild', () => {
const parentNode = new RRNode();
const childNode = new RRNode();
expect(() =>
parentNode.removeChild(childNode),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method."`,
expect(() => parentNode.removeChild(childNode)).toThrowError(
`RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.`,
);
});
});
@@ -266,10 +260,8 @@ describe('Basic RRDocument implementation', () => {
expect(node.childNodes[0]).toEqual(documentType);
expect(documentType.parentElement).toBeNull();
expect(documentType.parentNode).toBe(node);
expect(() =>
node.appendChild(documentType),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one RRDoctype on RRDocument allowed."`,
expect(() => node.appendChild(documentType)).toThrowError(
`RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one RRDoctype on RRDocument allowed.`,
);
const element = node.createElement('html');
@@ -278,38 +270,30 @@ describe('Basic RRDocument implementation', () => {
expect(element.parentElement).toBeNull();
expect(element.parentNode).toBe(node);
const div = node.createElement('div');
expect(() => node.appendChild(div)).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one RRElement on RRDocument allowed."`,
expect(() => node.appendChild(div)).toThrowError(
`RRDomException: Failed to execute 'appendChild' on 'RRNode': Only one RRElement on RRDocument allowed.`,
);
});
it('can insert new child before an existing child', () => {
const node = new RRDocument();
const docType = node.createDocumentType('', '', '');
expect(() =>
node.insertBefore(node, docType),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode."`,
expect(() => node.insertBefore(node, docType)).toThrowError(
`Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.`,
);
expect(node.insertBefore(docType, null)).toBe(docType);
expect(() =>
node.insertBefore(docType, null),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRDoctype on RRDocument allowed."`,
expect(() => node.insertBefore(docType, null)).toThrowError(
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRDoctype on RRDocument allowed.`,
);
node.removeChild(docType);
const documentElement = node.createElement('html');
expect(() =>
node.insertBefore(documentElement, docType),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode."`,
expect(() => node.insertBefore(documentElement, docType)).toThrowError(
`Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.`,
);
expect(node.insertBefore(documentElement, null)).toBe(documentElement);
expect(() =>
node.insertBefore(documentElement, null),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRElement on RRDocument allowed."`,
expect(() => node.insertBefore(documentElement, null)).toThrowError(
`RRDomException: Failed to execute 'insertBefore' on 'RRNode': Only one RRElement on RRDocument allowed.`,
);
expect(node.insertBefore(docType, documentElement)).toBe(docType);
expect(node.childNodes[0]).toBe(docType);
@@ -332,7 +316,7 @@ describe('Basic RRDocument implementation', () => {
expect(() =>
node.removeChild(node.createElement('div')),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode."`,
`[Error: Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode.]`,
);
expect(node.removeChild(documentType)).toBe(documentType);
expect(documentType.parentNode).toBeNull();
@@ -790,7 +774,7 @@ describe('Basic RRDocument implementation', () => {
expect(() =>
node.insertBefore(node, child1),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode."`,
`[Error: Failed to execute 'insertBefore' on 'RRNode': The RRNode before which the new node is to be inserted is not a child of this RRNode.]`,
);
expect(node.insertBefore(child1, null)).toBe(child1);
expect(node.childNodes[0]).toBe(child1);
@@ -868,7 +852,7 @@ describe('Basic RRDocument implementation', () => {
expect(() =>
node.removeChild(document.createElement('div')),
).toThrowErrorMatchingInlineSnapshot(
`"Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode."`,
`[Error: Failed to execute 'removeChild' on 'RRNode': The RRNode to be removed is not a child of this RRNode.]`,
);
// Remove the middle child.
expect(node.removeChild(child2)).toBe(child2);
@@ -1098,10 +1082,8 @@ describe('Basic RRDocument implementation', () => {
it('should not support attachShadow function', () => {
const node = new RRMediaElement('video');
expect(() =>
node.attachShadow({ mode: 'open' }),
).toThrowErrorMatchingInlineSnapshot(
`"RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow"`,
expect(() => node.attachShadow({ mode: 'open' })).toThrowError(
`RRDomException: Failed to execute 'attachShadow' on 'RRElement': This RRElement does not support attachShadow`,
);
});
});

View File

@@ -1,27 +0,0 @@
import * as rollup from 'rollup';
import * as typescript from 'rollup-plugin-typescript2';
import resolve from '@rollup/plugin-node-resolve';
const _typescript = typescript as unknown as typeof typescript.default;
/**
* Use rollup to compile an input TS script into JS code string.
*/
export async function compileTSCode(inputFilePath: string) {
const bundle = await rollup.rollup({
input: inputFilePath,
plugins: [
resolve() as unknown as rollup.Plugin,
_typescript({
tsconfigOverride: { compilerOptions: { module: 'ESNext' } },
clean: true,
}) as unknown as rollup.Plugin,
],
});
const {
output: [{ code: _code }],
} = await bundle.generate({
name: 'rrdom',
format: 'iife',
});
return _code;
}

View File

@@ -4,6 +4,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as puppeteer from 'puppeteer';
import { vi } from 'vitest';
import { JSDOM } from 'jsdom';
import {
cdataNode,
@@ -26,7 +27,6 @@ import {
RRElement,
BaseRRNode as RRNode,
} from '../src';
import { compileTSCode } from './utils';
const printRRDomCode = `
/**
@@ -50,7 +50,7 @@ function walk(node, mirror, blankSpace) {
`;
describe('RRDocument for browser environment', () => {
jest.setTimeout(60_000);
vi.setConfig({ testTimeout: 60_000 });
let mirror: Mirror;
beforeEach(() => {
mirror = new Mirror();
@@ -216,7 +216,10 @@ describe('RRDocument for browser environment', () => {
beforeAll(async () => {
browser = await puppeteer.launch();
code = await compileTSCode(path.resolve(__dirname, '../src/index.ts'));
code = fs.readFileSync(
path.resolve(__dirname, '../dist/rrdom.umd.cjs'),
'utf8',
);
});
afterAll(async () => {
await browser.close();

View File

@@ -1,19 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src"],
"compilerOptions": {
"composite": true,
"target": "ES6",
"module": "commonjs",
"noImplicitAny": true,
"strictNullChecks": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"rootDir": "src",
"outDir": "build",
"lib": ["es6", "dom"],
"skipLibCheck": true,
"declaration": true,
"importsNotUsedAsValues": "error"
"tsBuildInfoFile": "./tsconfig.tsbuildinfo"
},
"references": [
{
@@ -22,12 +12,5 @@
{
"path": "../types"
}
],
"compileOnSave": true,
"exclude": ["test"],
"include": [
"src",
"../rrweb/src/record/workers/workers.d.ts",
"../rrweb/src/record/constructable-stylesheets.d.ts"
]
}

View File

@@ -0,0 +1,4 @@
import path from 'path';
import config from '../../vite.config.default';
export default config(path.resolve(__dirname, 'src/index.ts'), 'rrdom');

View File

@@ -0,0 +1,12 @@
/// <reference types="vitest" />
import { defineProject, mergeConfig } from 'vitest/config';
import configShared from '../../vitest.config';
export default mergeConfig(
configShared,
defineProject({
test: {
globals: true,
},
}),
);