move browser-only rrdom features to the new rrdom package (#913)
This commit is contained in:
4
.vscode/rrweb-monorepo.code-workspace
vendored
4
.vscode/rrweb-monorepo.code-workspace
vendored
@@ -8,6 +8,10 @@
|
|||||||
"name": "rrdom (package)",
|
"name": "rrdom (package)",
|
||||||
"path": "../packages/rrdom"
|
"path": "../packages/rrdom"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "rrdom-nodejs (package)",
|
||||||
|
"path": "../packages/rrdom-nodejs"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "rrweb (package)",
|
"name": "rrweb (package)",
|
||||||
"path": "../packages/rrweb"
|
"path": "../packages/rrweb"
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
"packages/rrweb",
|
"packages/rrweb",
|
||||||
"packages/rrweb-snapshot",
|
"packages/rrweb-snapshot",
|
||||||
"packages/rrweb-player",
|
"packages/rrweb-player",
|
||||||
"packages/rrdom"
|
"packages/rrdom",
|
||||||
|
"packages/rrdom-nodejs"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
"@typescript-eslint/eslint-plugin": "^5.25.0",
|
||||||
|
|||||||
4
packages/rrdom-nodejs/.gitignore
vendored
Normal file
4
packages/rrdom-nodejs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
es
|
||||||
|
lib
|
||||||
|
typings
|
||||||
5
packages/rrdom-nodejs/jest.config.js
Normal file
5
packages/rrdom-nodejs/jest.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
};
|
||||||
55
packages/rrdom-nodejs/package.json
Normal file
55
packages/rrdom-nodejs/package.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"name": "rrdom-nodejs",
|
||||||
|
"version": "0.1.2",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "rollup -c -w",
|
||||||
|
"bundle": "rollup --config",
|
||||||
|
"bundle:es-only": "cross-env ES_ONLY=true rollup --config",
|
||||||
|
"check-types": "tsc -noEmit",
|
||||||
|
"test": "jest",
|
||||||
|
"prepublish": "npm run bundle",
|
||||||
|
"lint": "yarn eslint src/**/*.ts"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"rrweb",
|
||||||
|
"rrdom-nodejs"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "lib/rrdom-nodejs.js",
|
||||||
|
"module": "es/rrdom-nodejs.js",
|
||||||
|
"typings": "es",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"lib",
|
||||||
|
"es",
|
||||||
|
"typings"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^20.0.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^13.0.4",
|
||||||
|
"@types/cssom": "^0.4.1",
|
||||||
|
"@types/cssstyle": "^2.2.1",
|
||||||
|
"@types/jest": "^27.4.1",
|
||||||
|
"@types/nwsapi": "^2.2.2",
|
||||||
|
"@types/puppeteer": "^5.4.4",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
|
"@typescript-eslint/parser": "^5.23.0",
|
||||||
|
"compare-versions": "^4.1.3",
|
||||||
|
"eslint": "^8.15.0",
|
||||||
|
"jest": "^27.5.1",
|
||||||
|
"puppeteer": "^9.1.1",
|
||||||
|
"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.6.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cssom": "^0.5.0",
|
||||||
|
"cssstyle": "^2.3.0",
|
||||||
|
"nwsapi": "^2.2.0",
|
||||||
|
"rrweb-snapshot": "^1.1.14",
|
||||||
|
"rrdom": "^0.1.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
84
packages/rrdom-nodejs/rollup.config.js
Normal file
84
packages/rrdom-nodejs/rollup.config.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: './src/document-nodejs.ts',
|
||||||
|
name: 'RRDocument',
|
||||||
|
path: 'document-nodejs',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
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(
|
||||||
|
// 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;
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
ClassList,
|
ClassList,
|
||||||
IRRDocument,
|
IRRDocument,
|
||||||
CSSStyleDeclaration,
|
CSSStyleDeclaration,
|
||||||
} from './document';
|
} from 'rrdom';
|
||||||
const nwsapi = require('nwsapi');
|
const nwsapi = require('nwsapi');
|
||||||
const cssom = require('cssom');
|
const cssom = require('cssom');
|
||||||
const cssstyle = require('cssstyle');
|
const cssstyle = require('cssstyle');
|
||||||
@@ -53,22 +53,27 @@ export class RRDocument
|
|||||||
return this._nwsapi;
|
return this._nwsapi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get documentElement(): RRElement | null {
|
get documentElement(): RRElement | null {
|
||||||
return super.documentElement as RRElement | null;
|
return super.documentElement as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get body(): RRElement | null {
|
get body(): RRElement | null {
|
||||||
return super.body as RRElement | null;
|
return super.body as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get head() {
|
get head() {
|
||||||
return super.head as RRElement | null;
|
return super.head as RRElement | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get implementation(): RRDocument {
|
get implementation(): RRDocument {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get firstElementChild(): RRElement | null {
|
get firstElementChild(): RRElement | null {
|
||||||
return this.documentElement;
|
return this.documentElement;
|
||||||
}
|
}
|
||||||
@@ -198,6 +203,7 @@ export class RRElement extends BaseRRElementImpl(RRNode) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
get style() {
|
get style() {
|
||||||
return (this._style as unknown) as CSSStyleDeclaration;
|
return (this._style as unknown) as CSSStyleDeclaration;
|
||||||
}
|
}
|
||||||
13
packages/rrdom-nodejs/src/index.ts
Normal file
13
packages/rrdom-nodejs/src/index.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {
|
||||||
|
polyfillPerformance,
|
||||||
|
polyfillRAF,
|
||||||
|
polyfillEvent,
|
||||||
|
polyfillNode,
|
||||||
|
polyfillDocument,
|
||||||
|
} from './polyfill';
|
||||||
|
polyfillPerformance();
|
||||||
|
polyfillRAF();
|
||||||
|
polyfillEvent();
|
||||||
|
polyfillNode();
|
||||||
|
polyfillDocument();
|
||||||
|
export * from './document-nodejs';
|
||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
RRStyleElement,
|
RRStyleElement,
|
||||||
RRText,
|
RRText,
|
||||||
} from '../src/document-nodejs';
|
} from '../src/document-nodejs';
|
||||||
import { buildFromDom } from '../src/virtual-dom';
|
import { buildFromDom } from 'rrdom';
|
||||||
|
|
||||||
describe('RRDocument for nodejs environment', () => {
|
describe('RRDocument for nodejs environment', () => {
|
||||||
describe('RRDocument API', () => {
|
describe('RRDocument API', () => {
|
||||||
@@ -542,6 +542,6 @@ describe('RRDocument for nodejs environment', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function getHtml(fileName: string) {
|
function getHtml(fileName: string) {
|
||||||
const filePath = path.resolve(__dirname, `./html/${fileName}`);
|
const filePath = path.resolve(__dirname, `../../rrdom/test/html/${fileName}`);
|
||||||
return fs.readFileSync(filePath, 'utf8');
|
return fs.readFileSync(filePath, 'utf8');
|
||||||
}
|
}
|
||||||
20
packages/rrdom-nodejs/tsconfig.json
Normal file
20
packages/rrdom-nodejs/tsconfig.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"compileOnSave": true,
|
||||||
|
"exclude": ["test"],
|
||||||
|
"include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"]
|
||||||
|
}
|
||||||
@@ -1,19 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "rrdom",
|
"name": "rrdom",
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"scripts": {
|
"homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/rrdom#readme",
|
||||||
"dev": "rollup -c -w",
|
|
||||||
"bundle": "rollup --config",
|
|
||||||
"bundle:es-only": "cross-env ES_ONLY=true rollup --config",
|
|
||||||
"check-types": "tsc -noEmit",
|
|
||||||
"test": "jest",
|
|
||||||
"prepublish": "npm run bundle",
|
|
||||||
"lint": "yarn eslint src/**/*.ts"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"rrweb",
|
|
||||||
"rrdom"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "lib/rrdom.js",
|
"main": "lib/rrdom.js",
|
||||||
"module": "es/rrdom.js",
|
"module": "es/rrdom.js",
|
||||||
@@ -25,17 +13,28 @@
|
|||||||
"es",
|
"es",
|
||||||
"typings"
|
"typings"
|
||||||
],
|
],
|
||||||
|
"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",
|
||||||
|
"check-types": "tsc -noEmit",
|
||||||
|
"test": "jest",
|
||||||
|
"prepublish": "npm run bundle",
|
||||||
|
"lint": "yarn eslint src/**/*.ts"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/rrweb-io/rrweb/issues"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^20.0.0",
|
"@rollup/plugin-commonjs": "^20.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.0.4",
|
|
||||||
"@types/cssom": "^0.4.1",
|
|
||||||
"@types/cssstyle": "^2.2.1",
|
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/nwsapi": "^2.2.2",
|
|
||||||
"@types/puppeteer": "^5.4.4",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
"@typescript-eslint/parser": "^5.23.0",
|
"@typescript-eslint/parser": "^5.23.0",
|
||||||
"compare-versions": "^4.1.3",
|
"@types/puppeteer": "^5.4.4",
|
||||||
"eslint": "^8.15.0",
|
"eslint": "^8.15.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"puppeteer": "^9.1.1",
|
"puppeteer": "^9.1.1",
|
||||||
@@ -43,13 +42,10 @@
|
|||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"rollup-plugin-typescript2": "^0.31.2",
|
"rollup-plugin-typescript2": "^0.31.2",
|
||||||
"rollup-plugin-web-worker-loader": "^1.6.1",
|
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||||
"rrweb-snapshot": "^1.1.14",
|
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.3",
|
||||||
"typescript": "^4.6.2"
|
"typescript": "^4.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssom": "^0.5.0",
|
"rrweb-snapshot": "^1.1.14"
|
||||||
"cssstyle": "^2.3.0",
|
|
||||||
"nwsapi": "^2.2.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,6 @@ const baseConfigs = [
|
|||||||
name: pkg.name,
|
name: pkg.name,
|
||||||
path: pkg.name,
|
path: pkg.name,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
input: './src/document-nodejs.ts',
|
|
||||||
name: 'RRDocument',
|
|
||||||
path: 'document-nodejs',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: './src/virtual-dom.ts',
|
|
||||||
name: 'RRDocument',
|
|
||||||
path: 'virtual-dom',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let configs = [];
|
let configs = [];
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type {
|
|||||||
RRStyleElement,
|
RRStyleElement,
|
||||||
RRDocument,
|
RRDocument,
|
||||||
Mirror,
|
Mirror,
|
||||||
} from './virtual-dom';
|
} from '.';
|
||||||
|
|
||||||
const NAMESPACES: Record<string, string> = {
|
const NAMESPACES: Record<string, string> = {
|
||||||
svg: 'http://www.w3.org/2000/svg',
|
svg: 'http://www.w3.org/2000/svg',
|
||||||
@@ -113,7 +113,7 @@ export function diff(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RRNodeType.Element: {
|
case RRNodeType.Element: {
|
||||||
const oldElement = (oldTree ) as HTMLElement;
|
const oldElement = oldTree as HTMLElement;
|
||||||
const newRRElement = newTree as IRRElement;
|
const newRRElement = newTree as IRRElement;
|
||||||
diffProps(oldElement, newRRElement, rrnodeMirror);
|
diffProps(oldElement, newRRElement, rrnodeMirror);
|
||||||
scrollDataToApply = (newRRElement as RRElement).scrollData;
|
scrollDataToApply = (newRRElement as RRElement).scrollData;
|
||||||
@@ -121,7 +121,7 @@ export function diff(
|
|||||||
switch (newRRElement.tagName) {
|
switch (newRRElement.tagName) {
|
||||||
case 'AUDIO':
|
case 'AUDIO':
|
||||||
case 'VIDEO': {
|
case 'VIDEO': {
|
||||||
const oldMediaElement = (oldTree ) as HTMLMediaElement;
|
const oldMediaElement = oldTree as HTMLMediaElement;
|
||||||
const newMediaRRElement = newRRElement as RRMediaElement;
|
const newMediaRRElement = newRRElement as RRMediaElement;
|
||||||
if (newMediaRRElement.paused !== undefined)
|
if (newMediaRRElement.paused !== undefined)
|
||||||
newMediaRRElement.paused
|
newMediaRRElement.paused
|
||||||
@@ -141,7 +141,7 @@ export function diff(
|
|||||||
replayer.applyCanvas(
|
replayer.applyCanvas(
|
||||||
canvasMutation.event,
|
canvasMutation.event,
|
||||||
canvasMutation.mutation,
|
canvasMutation.mutation,
|
||||||
(oldTree ) as HTMLCanvasElement,
|
oldTree as HTMLCanvasElement,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@@ -191,8 +191,7 @@ export function diff(
|
|||||||
|
|
||||||
// IFrame element doesn't have child nodes.
|
// IFrame element doesn't have child nodes.
|
||||||
if (newTree.nodeName === 'IFRAME') {
|
if (newTree.nodeName === 'IFRAME') {
|
||||||
const oldContentDocument = ((oldTree ) as HTMLIFrameElement)
|
const oldContentDocument = (oldTree as HTMLIFrameElement).contentDocument;
|
||||||
.contentDocument;
|
|
||||||
const newIFrameElement = newTree as RRIFrameElement;
|
const newIFrameElement = newTree as RRIFrameElement;
|
||||||
// If the iframe is cross-origin, the contentDocument will be null.
|
// If the iframe is cross-origin, the contentDocument will be null.
|
||||||
if (oldContentDocument) {
|
if (oldContentDocument) {
|
||||||
@@ -319,11 +318,9 @@ function diffChildren(
|
|||||||
if (
|
if (
|
||||||
replayer.mirror.getMeta(parentNode)?.type === RRNodeType.Document &&
|
replayer.mirror.getMeta(parentNode)?.type === RRNodeType.Document &&
|
||||||
replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element &&
|
replayer.mirror.getMeta(newNode)?.type === RRNodeType.Element &&
|
||||||
((parentNode ) as Document).documentElement
|
(parentNode as Document).documentElement
|
||||||
) {
|
) {
|
||||||
parentNode.removeChild(
|
parentNode.removeChild((parentNode as Document).documentElement);
|
||||||
((parentNode ) as Document).documentElement,
|
|
||||||
);
|
|
||||||
oldChildren[oldStartIndex] = undefined;
|
oldChildren[oldStartIndex] = undefined;
|
||||||
oldStartNode = undefined;
|
oldStartNode = undefined;
|
||||||
}
|
}
|
||||||
@@ -417,8 +414,7 @@ export function getNestedRule(
|
|||||||
return rule;
|
return rule;
|
||||||
} else {
|
} else {
|
||||||
return getNestedRule(
|
return getNestedRule(
|
||||||
((rule ).cssRules[position[1]] as CSSGroupingRule)
|
(rule.cssRules[position[1]] as CSSGroupingRule).cssRules,
|
||||||
.cssRules,
|
|
||||||
position.slice(2),
|
position.slice(2),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,450 @@
|
|||||||
import {
|
import {
|
||||||
polyfillPerformance,
|
NodeType as RRNodeType,
|
||||||
polyfillRAF,
|
createMirror as createNodeMirror,
|
||||||
polyfillEvent,
|
} from 'rrweb-snapshot';
|
||||||
polyfillNode,
|
import type {
|
||||||
polyfillDocument,
|
Mirror as NodeMirror,
|
||||||
} from './polyfill';
|
IMirror,
|
||||||
polyfillPerformance();
|
serializedNodeWithId,
|
||||||
polyfillRAF();
|
} from 'rrweb-snapshot';
|
||||||
polyfillEvent();
|
import type {
|
||||||
polyfillNode();
|
canvasMutationData,
|
||||||
polyfillDocument();
|
canvasEventWithTime,
|
||||||
export * from './document-nodejs';
|
inputData,
|
||||||
|
scrollData,
|
||||||
|
} from 'rrweb/src/types';
|
||||||
|
import type { VirtualStyleRules } from './diff';
|
||||||
|
import {
|
||||||
|
BaseRRNode as RRNode,
|
||||||
|
BaseRRCDATASectionImpl,
|
||||||
|
BaseRRCommentImpl,
|
||||||
|
BaseRRDocumentImpl,
|
||||||
|
BaseRRDocumentTypeImpl,
|
||||||
|
BaseRRElementImpl,
|
||||||
|
BaseRRMediaElementImpl,
|
||||||
|
BaseRRTextImpl,
|
||||||
|
IRRDocument,
|
||||||
|
IRRElement,
|
||||||
|
IRRNode,
|
||||||
|
NodeType,
|
||||||
|
IRRDocumentType,
|
||||||
|
IRRText,
|
||||||
|
IRRComment,
|
||||||
|
} from './document';
|
||||||
|
|
||||||
|
export class RRDocument extends BaseRRDocumentImpl(RRNode) {
|
||||||
|
// 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.
|
||||||
|
// The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes.
|
||||||
|
private _unserializedId = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every time the id is used, it will minus 1 automatically to avoid collisions.
|
||||||
|
*/
|
||||||
|
public get unserializedId(): number {
|
||||||
|
return this._unserializedId--;
|
||||||
|
}
|
||||||
|
|
||||||
|
public mirror: Mirror = createMirror();
|
||||||
|
|
||||||
|
public scrollData: scrollData | null = null;
|
||||||
|
|
||||||
|
constructor(mirror?: Mirror) {
|
||||||
|
super();
|
||||||
|
if (mirror) {
|
||||||
|
this.mirror = mirror;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createDocument(
|
||||||
|
_namespace: string | null,
|
||||||
|
_qualifiedName: string | null,
|
||||||
|
_doctype?: DocumentType | null,
|
||||||
|
) {
|
||||||
|
return new RRDocument();
|
||||||
|
}
|
||||||
|
|
||||||
|
createDocumentType(
|
||||||
|
qualifiedName: string,
|
||||||
|
publicId: string,
|
||||||
|
systemId: string,
|
||||||
|
) {
|
||||||
|
const documentTypeNode = new RRDocumentType(
|
||||||
|
qualifiedName,
|
||||||
|
publicId,
|
||||||
|
systemId,
|
||||||
|
);
|
||||||
|
documentTypeNode.ownerDocument = this;
|
||||||
|
return documentTypeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
createElement<K extends keyof HTMLElementTagNameMap>(
|
||||||
|
tagName: K,
|
||||||
|
): RRElementType<K>;
|
||||||
|
createElement(tagName: string): RRElement;
|
||||||
|
createElement(tagName: string) {
|
||||||
|
const upperTagName = tagName.toUpperCase();
|
||||||
|
let element;
|
||||||
|
switch (upperTagName) {
|
||||||
|
case 'AUDIO':
|
||||||
|
case 'VIDEO':
|
||||||
|
element = new RRMediaElement(upperTagName);
|
||||||
|
break;
|
||||||
|
case 'IFRAME':
|
||||||
|
element = new RRIFrameElement(upperTagName, this.mirror);
|
||||||
|
break;
|
||||||
|
case 'CANVAS':
|
||||||
|
element = new RRCanvasElement(upperTagName);
|
||||||
|
break;
|
||||||
|
case 'STYLE':
|
||||||
|
element = new RRStyleElement(upperTagName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
element = new RRElement(upperTagName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
element.ownerDocument = this;
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
createComment(data: string) {
|
||||||
|
const commentNode = new RRComment(data);
|
||||||
|
commentNode.ownerDocument = this;
|
||||||
|
return commentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
createCDATASection(data: string) {
|
||||||
|
const sectionNode = new RRCDATASection(data);
|
||||||
|
sectionNode.ownerDocument = this;
|
||||||
|
return sectionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTextNode(data: string) {
|
||||||
|
const textNode = new RRText(data);
|
||||||
|
textNode.ownerDocument = this;
|
||||||
|
return textNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyTree() {
|
||||||
|
this.childNodes = [];
|
||||||
|
this.mirror.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
open() {
|
||||||
|
super.open();
|
||||||
|
this._unserializedId = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode);
|
||||||
|
|
||||||
|
export class RRElement extends BaseRRElementImpl(RRNode) {
|
||||||
|
inputData: inputData | null = null;
|
||||||
|
scrollData: scrollData | null = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
|
||||||
|
|
||||||
|
export class RRCanvasElement extends RRElement implements IRRElement {
|
||||||
|
public canvasMutations: {
|
||||||
|
event: canvasEventWithTime;
|
||||||
|
mutation: canvasMutationData;
|
||||||
|
}[] = [];
|
||||||
|
/**
|
||||||
|
* This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement.
|
||||||
|
*/
|
||||||
|
getContext(): RenderingContext | null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RRStyleElement extends RRElement {
|
||||||
|
public rules: VirtualStyleRules = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RRIFrameElement extends RRElement {
|
||||||
|
contentDocument: RRDocument = new RRDocument();
|
||||||
|
constructor(upperTagName: string, mirror: Mirror) {
|
||||||
|
super(upperTagName);
|
||||||
|
this.contentDocument.mirror = mirror;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RRText = BaseRRTextImpl(RRNode);
|
||||||
|
export type RRText = typeof RRText;
|
||||||
|
|
||||||
|
export const RRComment = BaseRRCommentImpl(RRNode);
|
||||||
|
export type RRComment = typeof RRComment;
|
||||||
|
|
||||||
|
export const RRCDATASection = BaseRRCDATASectionImpl(RRNode);
|
||||||
|
export type RRCDATASection = typeof RRCDATASection;
|
||||||
|
|
||||||
|
interface RRElementTagNameMap {
|
||||||
|
audio: RRMediaElement;
|
||||||
|
canvas: RRCanvasElement;
|
||||||
|
iframe: RRIFrameElement;
|
||||||
|
style: RRStyleElement;
|
||||||
|
video: RRMediaElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
type RRElementType<
|
||||||
|
K extends keyof HTMLElementTagNameMap
|
||||||
|
> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement;
|
||||||
|
|
||||||
|
function getValidTagName(element: HTMLElement): string {
|
||||||
|
// https://github.com/rrweb-io/rrweb-snapshot/issues/56
|
||||||
|
if (element instanceof HTMLFormElement) {
|
||||||
|
return 'FORM';
|
||||||
|
}
|
||||||
|
return element.tagName.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a RRNode from a real Node.
|
||||||
|
* @param node the real Node
|
||||||
|
* @param rrdom the RRDocument
|
||||||
|
* @param domMirror the NodeMirror that records the real document tree
|
||||||
|
* @returns the built RRNode
|
||||||
|
*/
|
||||||
|
export function buildFromNode(
|
||||||
|
node: Node,
|
||||||
|
rrdom: IRRDocument,
|
||||||
|
domMirror: NodeMirror,
|
||||||
|
parentRRNode?: IRRNode | null,
|
||||||
|
): IRRNode | null {
|
||||||
|
let rrNode: IRRNode;
|
||||||
|
|
||||||
|
switch (node.nodeType) {
|
||||||
|
case NodeType.DOCUMENT_NODE:
|
||||||
|
if (parentRRNode && parentRRNode.nodeName === 'IFRAME')
|
||||||
|
rrNode = (parentRRNode as RRIFrameElement).contentDocument;
|
||||||
|
else {
|
||||||
|
rrNode = rrdom;
|
||||||
|
(rrNode as IRRDocument).compatMode = (node as Document).compatMode as
|
||||||
|
| 'BackCompat'
|
||||||
|
| 'CSS1Compat';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NodeType.DOCUMENT_TYPE_NODE:
|
||||||
|
const documentType = node as DocumentType;
|
||||||
|
rrNode = rrdom.createDocumentType(
|
||||||
|
documentType.name,
|
||||||
|
documentType.publicId,
|
||||||
|
documentType.systemId,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case NodeType.ELEMENT_NODE:
|
||||||
|
const elementNode = node as HTMLElement;
|
||||||
|
const tagName = getValidTagName(elementNode);
|
||||||
|
rrNode = rrdom.createElement(tagName);
|
||||||
|
const rrElement = rrNode as IRRElement;
|
||||||
|
for (const { name, value } of Array.from(elementNode.attributes)) {
|
||||||
|
rrElement.attributes[name] = value;
|
||||||
|
}
|
||||||
|
elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft);
|
||||||
|
elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop);
|
||||||
|
/**
|
||||||
|
* We don't have to record special values of input elements at the beginning.
|
||||||
|
* Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case NodeType.TEXT_NODE:
|
||||||
|
rrNode = rrdom.createTextNode((node as Text).textContent || '');
|
||||||
|
break;
|
||||||
|
case NodeType.CDATA_SECTION_NODE:
|
||||||
|
rrNode = rrdom.createCDATASection((node as CDATASection).data);
|
||||||
|
break;
|
||||||
|
case NodeType.COMMENT_NODE:
|
||||||
|
rrNode = rrdom.createComment((node as Comment).textContent || '');
|
||||||
|
break;
|
||||||
|
// if node is a shadow root
|
||||||
|
case NodeType.DOCUMENT_FRAGMENT_NODE:
|
||||||
|
rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sn: serializedNodeWithId | null = domMirror.getMeta(node);
|
||||||
|
|
||||||
|
if (rrdom instanceof RRDocument) {
|
||||||
|
if (!sn) {
|
||||||
|
sn = getDefaultSN(rrNode, rrdom.unserializedId);
|
||||||
|
domMirror.add(node, sn);
|
||||||
|
}
|
||||||
|
rrdom.mirror.add(rrNode, { ...sn });
|
||||||
|
}
|
||||||
|
|
||||||
|
return rrNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a RRDocument from a real document tree.
|
||||||
|
* @param dom the real document tree
|
||||||
|
* @param domMirror the NodeMirror that records the real document tree
|
||||||
|
* @param rrdom the rrdom object to be constructed
|
||||||
|
* @returns the build rrdom
|
||||||
|
*/
|
||||||
|
export function buildFromDom(
|
||||||
|
dom: Document,
|
||||||
|
domMirror: NodeMirror = createNodeMirror(),
|
||||||
|
rrdom: IRRDocument = new RRDocument(),
|
||||||
|
) {
|
||||||
|
function walk(node: Node, parentRRNode: IRRNode | null) {
|
||||||
|
const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode);
|
||||||
|
if (rrNode === null) return;
|
||||||
|
if (
|
||||||
|
// if the parentRRNode isn't a RRIFrameElement
|
||||||
|
parentRRNode?.nodeName !== 'IFRAME' &&
|
||||||
|
// if node isn't a shadow root
|
||||||
|
node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE
|
||||||
|
) {
|
||||||
|
parentRRNode?.appendChild(rrNode);
|
||||||
|
rrNode.parentNode = parentRRNode;
|
||||||
|
rrNode.parentElement = parentRRNode as RRElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.nodeName === 'IFRAME') {
|
||||||
|
walk((node as HTMLIFrameElement).contentDocument!, rrNode);
|
||||||
|
} else if (
|
||||||
|
node.nodeType === NodeType.DOCUMENT_NODE ||
|
||||||
|
node.nodeType === NodeType.ELEMENT_NODE ||
|
||||||
|
node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE
|
||||||
|
) {
|
||||||
|
// if the node is a shadow dom
|
||||||
|
if (
|
||||||
|
node.nodeType === NodeType.ELEMENT_NODE &&
|
||||||
|
(node as HTMLElement).shadowRoot
|
||||||
|
)
|
||||||
|
walk((node as HTMLElement).shadowRoot!, rrNode);
|
||||||
|
node.childNodes.forEach((childNode) => walk(childNode, rrNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk(dom, null);
|
||||||
|
return rrdom;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMirror(): Mirror {
|
||||||
|
return new Mirror();
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on Mirror from rrweb-snapshots
|
||||||
|
export class Mirror implements IMirror<RRNode> {
|
||||||
|
private idNodeMap: Map<number, RRNode> = new Map();
|
||||||
|
private nodeMetaMap: WeakMap<RRNode, serializedNodeWithId> = new WeakMap();
|
||||||
|
|
||||||
|
getId(n: RRNode | undefined | null): number {
|
||||||
|
if (!n) return -1;
|
||||||
|
|
||||||
|
const id = this.getMeta(n)?.id;
|
||||||
|
|
||||||
|
// if n is not a serialized Node, use -1 as its id.
|
||||||
|
return id ?? -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
getNode(id: number): RRNode | null {
|
||||||
|
return this.idNodeMap.get(id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIds(): number[] {
|
||||||
|
return Array.from(this.idNodeMap.keys());
|
||||||
|
}
|
||||||
|
|
||||||
|
getMeta(n: RRNode): serializedNodeWithId | null {
|
||||||
|
return this.nodeMetaMap.get(n) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes the node from idNodeMap
|
||||||
|
// doesn't remove the node from nodeMetaMap
|
||||||
|
removeNodeFromMap(n: RRNode) {
|
||||||
|
const id = this.getId(n);
|
||||||
|
this.idNodeMap.delete(id);
|
||||||
|
|
||||||
|
if (n.childNodes) {
|
||||||
|
n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has(id: number): boolean {
|
||||||
|
return this.idNodeMap.has(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNode(node: RRNode): boolean {
|
||||||
|
return this.nodeMetaMap.has(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
add(n: RRNode, meta: serializedNodeWithId) {
|
||||||
|
const id = meta.id;
|
||||||
|
this.idNodeMap.set(id, n);
|
||||||
|
this.nodeMetaMap.set(n, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
replace(id: number, n: RRNode) {
|
||||||
|
this.idNodeMap.set(id, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.idNodeMap = new Map();
|
||||||
|
this.nodeMetaMap = new WeakMap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a default serializedNodeWithId value for a RRNode.
|
||||||
|
* @param id the serialized id to assign
|
||||||
|
*/
|
||||||
|
export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
||||||
|
switch (node.RRNodeType) {
|
||||||
|
case RRNodeType.Document:
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
childNodes: [],
|
||||||
|
};
|
||||||
|
case RRNodeType.DocumentType:
|
||||||
|
const doctype = node as IRRDocumentType;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
name: doctype.name,
|
||||||
|
publicId: doctype.publicId,
|
||||||
|
systemId: doctype.systemId,
|
||||||
|
};
|
||||||
|
case RRNodeType.Element:
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase.
|
||||||
|
attributes: {},
|
||||||
|
childNodes: [],
|
||||||
|
};
|
||||||
|
case RRNodeType.Text:
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
textContent: (node as IRRText).textContent || '',
|
||||||
|
};
|
||||||
|
case RRNodeType.Comment:
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
textContent: (node as IRRComment).textContent || '',
|
||||||
|
};
|
||||||
|
case RRNodeType.CDATA:
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
type: node.RRNodeType,
|
||||||
|
textContent: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RRNode };
|
||||||
|
|
||||||
|
export {
|
||||||
|
diff,
|
||||||
|
createOrGetNode,
|
||||||
|
StyleRuleType,
|
||||||
|
ReplayerHandler,
|
||||||
|
VirtualStyleRules,
|
||||||
|
} from './diff';
|
||||||
|
export * from './document';
|
||||||
|
|||||||
@@ -1,450 +0,0 @@
|
|||||||
import {
|
|
||||||
NodeType as RRNodeType,
|
|
||||||
createMirror as createNodeMirror,
|
|
||||||
} from 'rrweb-snapshot';
|
|
||||||
import type {
|
|
||||||
Mirror as NodeMirror,
|
|
||||||
IMirror,
|
|
||||||
serializedNodeWithId,
|
|
||||||
} from 'rrweb-snapshot';
|
|
||||||
import type {
|
|
||||||
canvasMutationData,
|
|
||||||
canvasEventWithTime,
|
|
||||||
inputData,
|
|
||||||
scrollData,
|
|
||||||
} from 'rrweb/src/types';
|
|
||||||
import {
|
|
||||||
BaseRRNode as RRNode,
|
|
||||||
BaseRRCDATASectionImpl,
|
|
||||||
BaseRRCommentImpl,
|
|
||||||
BaseRRDocumentImpl,
|
|
||||||
BaseRRDocumentTypeImpl,
|
|
||||||
BaseRRElementImpl,
|
|
||||||
BaseRRMediaElementImpl,
|
|
||||||
BaseRRTextImpl,
|
|
||||||
IRRDocument,
|
|
||||||
IRRElement,
|
|
||||||
IRRNode,
|
|
||||||
NodeType,
|
|
||||||
IRRDocumentType,
|
|
||||||
IRRText,
|
|
||||||
IRRComment,
|
|
||||||
} from './document';
|
|
||||||
import type { VirtualStyleRules } from './diff';
|
|
||||||
|
|
||||||
export class RRDocument extends BaseRRDocumentImpl(RRNode) {
|
|
||||||
// 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.
|
|
||||||
// The id of serialized node is larger than 0. So this value less than 0 is used as id for these unserialized nodes.
|
|
||||||
private _unserializedId = -1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Every time the id is used, it will minus 1 automatically to avoid collisions.
|
|
||||||
*/
|
|
||||||
public get unserializedId(): number {
|
|
||||||
return this._unserializedId--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public mirror: Mirror = createMirror();
|
|
||||||
|
|
||||||
public scrollData: scrollData | null = null;
|
|
||||||
|
|
||||||
constructor(mirror?: Mirror) {
|
|
||||||
super();
|
|
||||||
if (mirror) {
|
|
||||||
this.mirror = mirror;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createDocument(
|
|
||||||
_namespace: string | null,
|
|
||||||
_qualifiedName: string | null,
|
|
||||||
_doctype?: DocumentType | null,
|
|
||||||
) {
|
|
||||||
return new RRDocument();
|
|
||||||
}
|
|
||||||
|
|
||||||
createDocumentType(
|
|
||||||
qualifiedName: string,
|
|
||||||
publicId: string,
|
|
||||||
systemId: string,
|
|
||||||
) {
|
|
||||||
const documentTypeNode = new RRDocumentType(
|
|
||||||
qualifiedName,
|
|
||||||
publicId,
|
|
||||||
systemId,
|
|
||||||
);
|
|
||||||
documentTypeNode.ownerDocument = this;
|
|
||||||
return documentTypeNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
createElement<K extends keyof HTMLElementTagNameMap>(
|
|
||||||
tagName: K,
|
|
||||||
): RRElementType<K>;
|
|
||||||
createElement(tagName: string): RRElement;
|
|
||||||
createElement(tagName: string) {
|
|
||||||
const upperTagName = tagName.toUpperCase();
|
|
||||||
let element;
|
|
||||||
switch (upperTagName) {
|
|
||||||
case 'AUDIO':
|
|
||||||
case 'VIDEO':
|
|
||||||
element = new RRMediaElement(upperTagName);
|
|
||||||
break;
|
|
||||||
case 'IFRAME':
|
|
||||||
element = new RRIFrameElement(upperTagName, this.mirror);
|
|
||||||
break;
|
|
||||||
case 'CANVAS':
|
|
||||||
element = new RRCanvasElement(upperTagName);
|
|
||||||
break;
|
|
||||||
case 'STYLE':
|
|
||||||
element = new RRStyleElement(upperTagName);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
element = new RRElement(upperTagName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
element.ownerDocument = this;
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
createComment(data: string) {
|
|
||||||
const commentNode = new RRComment(data);
|
|
||||||
commentNode.ownerDocument = this;
|
|
||||||
return commentNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
createCDATASection(data: string) {
|
|
||||||
const sectionNode = new RRCDATASection(data);
|
|
||||||
sectionNode.ownerDocument = this;
|
|
||||||
return sectionNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
createTextNode(data: string) {
|
|
||||||
const textNode = new RRText(data);
|
|
||||||
textNode.ownerDocument = this;
|
|
||||||
return textNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyTree() {
|
|
||||||
this.childNodes = [];
|
|
||||||
this.mirror.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
open() {
|
|
||||||
super.open();
|
|
||||||
this._unserializedId = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RRDocumentType = BaseRRDocumentTypeImpl(RRNode);
|
|
||||||
|
|
||||||
export class RRElement extends BaseRRElementImpl(RRNode) {
|
|
||||||
inputData: inputData | null = null;
|
|
||||||
scrollData: scrollData | null = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RRMediaElement extends BaseRRMediaElementImpl(RRElement) {}
|
|
||||||
|
|
||||||
export class RRCanvasElement extends RRElement implements IRRElement {
|
|
||||||
public canvasMutations: {
|
|
||||||
event: canvasEventWithTime;
|
|
||||||
mutation: canvasMutationData;
|
|
||||||
}[] = [];
|
|
||||||
/**
|
|
||||||
* This is a dummy implementation to distinguish RRCanvasElement from real HTMLCanvasElement.
|
|
||||||
*/
|
|
||||||
getContext(): RenderingContext | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RRStyleElement extends RRElement {
|
|
||||||
public rules: VirtualStyleRules = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RRIFrameElement extends RRElement {
|
|
||||||
contentDocument: RRDocument = new RRDocument();
|
|
||||||
constructor(upperTagName: string, mirror: Mirror) {
|
|
||||||
super(upperTagName);
|
|
||||||
this.contentDocument.mirror = mirror;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RRText = BaseRRTextImpl(RRNode);
|
|
||||||
export type RRText = typeof RRText;
|
|
||||||
|
|
||||||
export const RRComment = BaseRRCommentImpl(RRNode);
|
|
||||||
export type RRComment = typeof RRComment;
|
|
||||||
|
|
||||||
export const RRCDATASection = BaseRRCDATASectionImpl(RRNode);
|
|
||||||
export type RRCDATASection = typeof RRCDATASection;
|
|
||||||
|
|
||||||
interface RRElementTagNameMap {
|
|
||||||
audio: RRMediaElement;
|
|
||||||
canvas: RRCanvasElement;
|
|
||||||
iframe: RRIFrameElement;
|
|
||||||
style: RRStyleElement;
|
|
||||||
video: RRMediaElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RRElementType<
|
|
||||||
K extends keyof HTMLElementTagNameMap
|
|
||||||
> = K extends keyof RRElementTagNameMap ? RRElementTagNameMap[K] : RRElement;
|
|
||||||
|
|
||||||
function getValidTagName(element: HTMLElement): string {
|
|
||||||
// https://github.com/rrweb-io/rrweb-snapshot/issues/56
|
|
||||||
if (element instanceof HTMLFormElement) {
|
|
||||||
return 'FORM';
|
|
||||||
}
|
|
||||||
return element.tagName.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a RRNode from a real Node.
|
|
||||||
* @param node the real Node
|
|
||||||
* @param rrdom the RRDocument
|
|
||||||
* @param domMirror the NodeMirror that records the real document tree
|
|
||||||
* @returns the built RRNode
|
|
||||||
*/
|
|
||||||
export function buildFromNode(
|
|
||||||
node: Node,
|
|
||||||
rrdom: IRRDocument,
|
|
||||||
domMirror: NodeMirror,
|
|
||||||
parentRRNode?: IRRNode | null,
|
|
||||||
): IRRNode | null {
|
|
||||||
let rrNode: IRRNode;
|
|
||||||
|
|
||||||
switch (node.nodeType) {
|
|
||||||
case NodeType.DOCUMENT_NODE:
|
|
||||||
if (parentRRNode && parentRRNode.nodeName === 'IFRAME')
|
|
||||||
rrNode = (parentRRNode as RRIFrameElement).contentDocument;
|
|
||||||
else {
|
|
||||||
rrNode = rrdom;
|
|
||||||
(rrNode as IRRDocument).compatMode = (node as Document).compatMode as
|
|
||||||
| 'BackCompat'
|
|
||||||
| 'CSS1Compat';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NodeType.DOCUMENT_TYPE_NODE:
|
|
||||||
const documentType = (node ) as DocumentType;
|
|
||||||
rrNode = rrdom.createDocumentType(
|
|
||||||
documentType.name,
|
|
||||||
documentType.publicId,
|
|
||||||
documentType.systemId,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case NodeType.ELEMENT_NODE:
|
|
||||||
const elementNode = (node ) as HTMLElement;
|
|
||||||
const tagName = getValidTagName(elementNode);
|
|
||||||
rrNode = rrdom.createElement(tagName);
|
|
||||||
const rrElement = rrNode as IRRElement;
|
|
||||||
for (const { name, value } of Array.from(elementNode.attributes)) {
|
|
||||||
rrElement.attributes[name] = value;
|
|
||||||
}
|
|
||||||
elementNode.scrollLeft && (rrElement.scrollLeft = elementNode.scrollLeft);
|
|
||||||
elementNode.scrollTop && (rrElement.scrollTop = elementNode.scrollTop);
|
|
||||||
/**
|
|
||||||
* We don't have to record special values of input elements at the beginning.
|
|
||||||
* Because if these values are changed later, the mutation will be applied through the batched input events on its RRElement after the diff algorithm is executed.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
case NodeType.TEXT_NODE:
|
|
||||||
rrNode = rrdom.createTextNode(((node ) as Text).textContent || '');
|
|
||||||
break;
|
|
||||||
case NodeType.CDATA_SECTION_NODE:
|
|
||||||
rrNode = rrdom.createCDATASection(((node ) as CDATASection).data);
|
|
||||||
break;
|
|
||||||
case NodeType.COMMENT_NODE:
|
|
||||||
rrNode = rrdom.createComment(
|
|
||||||
((node ) as Comment).textContent || '',
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
// if node is a shadow root
|
|
||||||
case NodeType.DOCUMENT_FRAGMENT_NODE:
|
|
||||||
rrNode = (parentRRNode as IRRElement).attachShadow({ mode: 'open' });
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sn: serializedNodeWithId | null = domMirror.getMeta(node);
|
|
||||||
|
|
||||||
if (rrdom instanceof RRDocument) {
|
|
||||||
if (!sn) {
|
|
||||||
sn = getDefaultSN(rrNode, rrdom.unserializedId);
|
|
||||||
domMirror.add(node, sn);
|
|
||||||
}
|
|
||||||
rrdom.mirror.add(rrNode, { ...sn });
|
|
||||||
}
|
|
||||||
|
|
||||||
return rrNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a RRDocument from a real document tree.
|
|
||||||
* @param dom the real document tree
|
|
||||||
* @param domMirror the NodeMirror that records the real document tree
|
|
||||||
* @param rrdom the rrdom object to be constructed
|
|
||||||
* @returns the build rrdom
|
|
||||||
*/
|
|
||||||
export function buildFromDom(
|
|
||||||
dom: Document,
|
|
||||||
domMirror: NodeMirror = createNodeMirror(),
|
|
||||||
rrdom: IRRDocument = new RRDocument(),
|
|
||||||
) {
|
|
||||||
function walk(node: Node, parentRRNode: IRRNode | null) {
|
|
||||||
const rrNode = buildFromNode(node, rrdom, domMirror, parentRRNode);
|
|
||||||
if (rrNode === null) return;
|
|
||||||
if (
|
|
||||||
// if the parentRRNode isn't a RRIFrameElement
|
|
||||||
parentRRNode?.nodeName !== 'IFRAME' &&
|
|
||||||
// if node isn't a shadow root
|
|
||||||
node.nodeType !== NodeType.DOCUMENT_FRAGMENT_NODE
|
|
||||||
) {
|
|
||||||
parentRRNode?.appendChild(rrNode);
|
|
||||||
rrNode.parentNode = parentRRNode;
|
|
||||||
rrNode.parentElement = parentRRNode as RRElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.nodeName === 'IFRAME') {
|
|
||||||
walk((node as HTMLIFrameElement).contentDocument!, rrNode);
|
|
||||||
} else if (
|
|
||||||
node.nodeType === NodeType.DOCUMENT_NODE ||
|
|
||||||
node.nodeType === NodeType.ELEMENT_NODE ||
|
|
||||||
node.nodeType === NodeType.DOCUMENT_FRAGMENT_NODE
|
|
||||||
) {
|
|
||||||
// if the node is a shadow dom
|
|
||||||
if (
|
|
||||||
node.nodeType === NodeType.ELEMENT_NODE &&
|
|
||||||
((node ) as HTMLElement).shadowRoot
|
|
||||||
)
|
|
||||||
walk(((node ) as HTMLElement).shadowRoot!, rrNode);
|
|
||||||
node.childNodes.forEach((childNode) => walk(childNode, rrNode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
walk(dom, null);
|
|
||||||
return rrdom;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createMirror(): Mirror {
|
|
||||||
return new Mirror();
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on Mirror from rrweb-snapshots
|
|
||||||
export class Mirror implements IMirror<RRNode> {
|
|
||||||
private idNodeMap: Map<number, RRNode> = new Map();
|
|
||||||
private nodeMetaMap: WeakMap<RRNode, serializedNodeWithId> = new WeakMap();
|
|
||||||
|
|
||||||
getId(n: RRNode | undefined | null): number {
|
|
||||||
if (!n) return -1;
|
|
||||||
|
|
||||||
const id = this.getMeta(n)?.id;
|
|
||||||
|
|
||||||
// if n is not a serialized Node, use -1 as its id.
|
|
||||||
return id ?? -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
getNode(id: number): RRNode | null {
|
|
||||||
return this.idNodeMap.get(id) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIds(): number[] {
|
|
||||||
return Array.from(this.idNodeMap.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
getMeta(n: RRNode): serializedNodeWithId | null {
|
|
||||||
return this.nodeMetaMap.get(n) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// removes the node from idNodeMap
|
|
||||||
// doesn't remove the node from nodeMetaMap
|
|
||||||
removeNodeFromMap(n: RRNode) {
|
|
||||||
const id = this.getId(n);
|
|
||||||
this.idNodeMap.delete(id);
|
|
||||||
|
|
||||||
if (n.childNodes) {
|
|
||||||
n.childNodes.forEach((childNode) => this.removeNodeFromMap(childNode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
has(id: number): boolean {
|
|
||||||
return this.idNodeMap.has(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasNode(node: RRNode): boolean {
|
|
||||||
return this.nodeMetaMap.has(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(n: RRNode, meta: serializedNodeWithId) {
|
|
||||||
const id = meta.id;
|
|
||||||
this.idNodeMap.set(id, n);
|
|
||||||
this.nodeMetaMap.set(n, meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
replace(id: number, n: RRNode) {
|
|
||||||
this.idNodeMap.set(id, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
this.idNodeMap = new Map();
|
|
||||||
this.nodeMetaMap = new WeakMap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a default serializedNodeWithId value for a RRNode.
|
|
||||||
* @param id the serialized id to assign
|
|
||||||
*/
|
|
||||||
export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId {
|
|
||||||
switch (node.RRNodeType) {
|
|
||||||
case RRNodeType.Document:
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
childNodes: [],
|
|
||||||
};
|
|
||||||
case RRNodeType.DocumentType:
|
|
||||||
const doctype = node as IRRDocumentType;
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
name: doctype.name,
|
|
||||||
publicId: doctype.publicId,
|
|
||||||
systemId: doctype.systemId,
|
|
||||||
};
|
|
||||||
case RRNodeType.Element:
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
tagName: (node as IRRElement).tagName.toLowerCase(), // In rrweb data, all tagNames are lowercase.
|
|
||||||
attributes: {},
|
|
||||||
childNodes: [],
|
|
||||||
};
|
|
||||||
case RRNodeType.Text:
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
textContent: (node as IRRText).textContent || '',
|
|
||||||
};
|
|
||||||
case RRNodeType.Comment:
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
textContent: (node as IRRComment).textContent || '',
|
|
||||||
};
|
|
||||||
case RRNodeType.CDATA:
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
type: node.RRNodeType,
|
|
||||||
textContent: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { RRNode };
|
|
||||||
export {
|
|
||||||
diff,
|
|
||||||
createOrGetNode,
|
|
||||||
StyleRuleType,
|
|
||||||
VirtualStyleRules,
|
|
||||||
ReplayerHandler,
|
|
||||||
} from './diff';
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @jest-environment jsdom
|
* @jest-environment jsdom
|
||||||
*/
|
*/
|
||||||
import { getDefaultSN, RRDocument, RRMediaElement } from '../src/virtual-dom';
|
import { getDefaultSN, RRDocument, RRMediaElement } from '../src';
|
||||||
import {
|
import {
|
||||||
applyVirtualStyleRulesToNode,
|
applyVirtualStyleRulesToNode,
|
||||||
createOrGetNode,
|
createOrGetNode,
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import {
|
|||||||
RRCanvasElement,
|
RRCanvasElement,
|
||||||
RRDocument,
|
RRDocument,
|
||||||
RRElement,
|
RRElement,
|
||||||
RRNode,
|
BaseRRNode as RRNode,
|
||||||
} from '../src/virtual-dom';
|
} from '../src';
|
||||||
|
|
||||||
const _typescript = (typescript as unknown) as typeof typescript.default;
|
const _typescript = (typescript as unknown) as typeof typescript.default;
|
||||||
const printRRDomCode = `
|
const printRRDomCode = `
|
||||||
@@ -219,9 +219,9 @@ describe('RRDocument for browser environment', () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
browser = await puppeteer.launch();
|
browser = await puppeteer.launch();
|
||||||
const bundle = await rollup.rollup({
|
const bundle = await rollup.rollup({
|
||||||
input: path.resolve(__dirname, '../src/virtual-dom.ts'),
|
input: path.resolve(__dirname, '../src/index.ts'),
|
||||||
plugins: [
|
plugins: [
|
||||||
resolve(),
|
(resolve() as unknown) as rollup.Plugin,
|
||||||
(_typescript({
|
(_typescript({
|
||||||
tsconfigOverride: { compilerOptions: { module: 'ESNext' } },
|
tsconfigOverride: { compilerOptions: { module: 'ESNext' } },
|
||||||
}) as unknown) as rollup.Plugin,
|
}) as unknown) as rollup.Plugin,
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
},
|
},
|
||||||
"compileOnSave": true,
|
"compileOnSave": true,
|
||||||
"exclude": ["test"],
|
"exclude": ["test"],
|
||||||
"include": ["src", "test.d.ts", "../rrweb/src/record/workers/workers.d.ts"]
|
"include": ["src", "../rrweb/src/record/workers/workers.d.ts"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,5 @@ module.exports = {
|
|||||||
testMatch: ['**/**.test.ts'],
|
testMatch: ['**/**.test.ts'],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.css$': 'identity-obj-proxy',
|
'\\.css$': 'identity-obj-proxy',
|
||||||
'rrdom/es/(.*)': 'rrdom/lib/$1',
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
buildFromDom,
|
buildFromDom,
|
||||||
diff,
|
diff,
|
||||||
getDefaultSN,
|
getDefaultSN,
|
||||||
} from 'rrdom/es/virtual-dom';
|
} from 'rrdom';
|
||||||
import type {
|
import type {
|
||||||
RRNode,
|
RRNode,
|
||||||
RRElement,
|
RRElement,
|
||||||
@@ -26,7 +26,7 @@ import type {
|
|||||||
ReplayerHandler,
|
ReplayerHandler,
|
||||||
Mirror as RRDOMMirror,
|
Mirror as RRDOMMirror,
|
||||||
VirtualStyleRules,
|
VirtualStyleRules,
|
||||||
} from 'rrdom/es/virtual-dom';
|
} from 'rrdom';
|
||||||
import * as mittProxy from 'mitt';
|
import * as mittProxy from 'mitt';
|
||||||
import { polyfill as smoothscrollPolyfill } from './smoothscroll';
|
import { polyfill as smoothscrollPolyfill } from './smoothscroll';
|
||||||
import { Timer } from './timer';
|
import { Timer } from './timer';
|
||||||
@@ -731,7 +731,7 @@ export class Replayer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (this.usingVirtualDom) {
|
if (this.usingVirtualDom) {
|
||||||
const styleEl = this.virtualDom.createElement('style') ;
|
const styleEl = this.virtualDom.createElement('style');
|
||||||
this.virtualDom.mirror.add(
|
this.virtualDom.mirror.add(
|
||||||
styleEl,
|
styleEl,
|
||||||
getDefaultSN(styleEl, this.virtualDom.unserializedId),
|
getDefaultSN(styleEl, this.virtualDom.unserializedId),
|
||||||
@@ -752,10 +752,7 @@ export class Replayer {
|
|||||||
head as HTMLHeadElement,
|
head as HTMLHeadElement,
|
||||||
);
|
);
|
||||||
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
for (let idx = 0; idx < injectStylesRules.length; idx++) {
|
||||||
(styleEl.sheet! ).insertRule(
|
styleEl.sheet!.insertRule(injectStylesRules[idx], idx);
|
||||||
injectStylesRules[idx],
|
|
||||||
idx,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1210,7 +1207,7 @@ export class Replayer {
|
|||||||
if (!target) {
|
if (!target) {
|
||||||
return this.debugNodeNotFound(d, d.id);
|
return this.debugNodeNotFound(d, d.id);
|
||||||
}
|
}
|
||||||
const styleSheet = ((target ) as HTMLStyleElement).sheet!;
|
const styleSheet = (target as HTMLStyleElement).sheet!;
|
||||||
d.adds?.forEach(({ rule, index: nestedIndex }) => {
|
d.adds?.forEach(({ rule, index: nestedIndex }) => {
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(nestedIndex)) {
|
if (Array.isArray(nestedIndex)) {
|
||||||
@@ -1692,7 +1689,7 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (attributeName === 'style') {
|
} else if (attributeName === 'style') {
|
||||||
const styleValues = value ;
|
const styleValues = value;
|
||||||
const targetEl = target as HTMLElement | RRElement;
|
const targetEl = target as HTMLElement | RRElement;
|
||||||
for (const s in styleValues) {
|
for (const s in styleValues) {
|
||||||
if (styleValues[s] === false) {
|
if (styleValues[s] === false) {
|
||||||
@@ -1772,7 +1769,7 @@ export class Replayer {
|
|||||||
const previousInMap = previousId && map[previousId];
|
const previousInMap = previousId && map[previousId];
|
||||||
const nextInMap = nextId && map[nextId];
|
const nextInMap = nextId && map[nextId];
|
||||||
if (previousInMap) {
|
if (previousInMap) {
|
||||||
const { node, mutation } = previousInMap ;
|
const { node, mutation } = previousInMap;
|
||||||
parent.insertBefore(node as Node & RRNode, target as Node & RRNode);
|
parent.insertBefore(node as Node & RRNode, target as Node & RRNode);
|
||||||
delete map[mutation.node.id];
|
delete map[mutation.node.id];
|
||||||
delete this.legacy_missingNodeRetryMap[mutation.node.id];
|
delete this.legacy_missingNodeRetryMap[mutation.node.id];
|
||||||
@@ -1781,7 +1778,7 @@ export class Replayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nextInMap) {
|
if (nextInMap) {
|
||||||
const { node, mutation } = nextInMap ;
|
const { node, mutation } = nextInMap;
|
||||||
parent.insertBefore(
|
parent.insertBefore(
|
||||||
node as Node & RRNode,
|
node as Node & RRNode,
|
||||||
target.nextSibling as Node & RRNode,
|
target.nextSibling as Node & RRNode,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type { PackFn, UnpackFn } from './packer/base';
|
|||||||
import type { IframeManager } from './record/iframe-manager';
|
import type { IframeManager } from './record/iframe-manager';
|
||||||
import type { ShadowDomManager } from './record/shadow-dom-manager';
|
import type { ShadowDomManager } from './record/shadow-dom-manager';
|
||||||
import type { Replayer } from './replay';
|
import type { Replayer } from './replay';
|
||||||
import type { RRNode } from 'rrdom/es/virtual-dom';
|
import type { RRNode } from 'rrdom';
|
||||||
import type { CanvasManager } from './record/observers/canvas/canvas-manager';
|
import type { CanvasManager } from './record/observers/canvas/canvas-manager';
|
||||||
|
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type {
|
|||||||
} from './types';
|
} from './types';
|
||||||
import type { IMirror, Mirror } from 'rrweb-snapshot';
|
import type { IMirror, Mirror } from 'rrweb-snapshot';
|
||||||
import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot';
|
import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot';
|
||||||
import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom';
|
import type { RRNode, RRIFrameElement } from 'rrdom';
|
||||||
|
|
||||||
export function on(
|
export function on(
|
||||||
type: string,
|
type: string,
|
||||||
|
|||||||
2
packages/rrweb/typings/replay/index.d.ts
vendored
2
packages/rrweb/typings/replay/index.d.ts
vendored
@@ -1,5 +1,5 @@
|
|||||||
import { Mirror } from 'rrweb-snapshot';
|
import { Mirror } from 'rrweb-snapshot';
|
||||||
import { RRDocument } from 'rrdom/es/virtual-dom';
|
import { RRDocument } from 'rrdom';
|
||||||
import { Timer } from './timer';
|
import { Timer } from './timer';
|
||||||
import { createPlayerService, createSpeedService } from './machine';
|
import { createPlayerService, createSpeedService } from './machine';
|
||||||
import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types';
|
import { eventWithTime, playerConfig, playerMetaData, Handler } from '../types';
|
||||||
|
|||||||
2
packages/rrweb/typings/types.d.ts
vendored
2
packages/rrweb/typings/types.d.ts
vendored
@@ -3,7 +3,7 @@ import type { PackFn, UnpackFn } from './packer/base';
|
|||||||
import type { IframeManager } from './record/iframe-manager';
|
import type { IframeManager } from './record/iframe-manager';
|
||||||
import type { ShadowDomManager } from './record/shadow-dom-manager';
|
import type { ShadowDomManager } from './record/shadow-dom-manager';
|
||||||
import type { Replayer } from './replay';
|
import type { Replayer } from './replay';
|
||||||
import type { RRNode } from 'rrdom/es/virtual-dom';
|
import type { RRNode } from 'rrdom';
|
||||||
import type { CanvasManager } from './record/observers/canvas/canvas-manager';
|
import type { CanvasManager } from './record/observers/canvas/canvas-manager';
|
||||||
export declare enum EventType {
|
export declare enum EventType {
|
||||||
DomContentLoaded = 0,
|
DomContentLoaded = 0,
|
||||||
|
|||||||
2
packages/rrweb/typings/utils.d.ts
vendored
2
packages/rrweb/typings/utils.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types';
|
import type { throttleOptions, listenerHandler, hookResetter, blockClass, addedNodeMutation, DocumentDimension, IWindow, DeprecatedMirror, textMutation } from './types';
|
||||||
import type { IMirror, Mirror } from 'rrweb-snapshot';
|
import type { IMirror, Mirror } from 'rrweb-snapshot';
|
||||||
import type { RRNode, RRIFrameElement } from 'rrdom/es/virtual-dom';
|
import type { RRNode, RRIFrameElement } from 'rrdom';
|
||||||
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler;
|
export declare function on(type: string, fn: EventListenerOrEventListenerObject, target?: Document | IWindow): listenerHandler;
|
||||||
export declare let _mirror: DeprecatedMirror;
|
export declare let _mirror: DeprecatedMirror;
|
||||||
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void;
|
export declare function throttle<T>(func: (arg: T) => void, wait: number, options?: throttleOptions): (arg: T) => void;
|
||||||
|
|||||||
36
yarn.lock
36
yarn.lock
@@ -1791,10 +1791,10 @@
|
|||||||
magic-string "^0.25.7"
|
magic-string "^0.25.7"
|
||||||
resolve "^1.17.0"
|
resolve "^1.17.0"
|
||||||
|
|
||||||
"@rollup/plugin-node-resolve@^13.0.4":
|
"@rollup/plugin-node-resolve@^13.0.4", "@rollup/plugin-node-resolve@^13.2.1":
|
||||||
version "13.0.6"
|
version "13.2.1"
|
||||||
resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.0.6.tgz"
|
resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz"
|
||||||
integrity sha512-sFsPDMPd4gMqnh2gS0uIxELnoRUp5kBl5knxD2EO0778G1oOJv4G1vyT2cpWz75OU2jDVcXhjVUuTAczGyFNKA==
|
integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@rollup/pluginutils" "^3.1.0"
|
"@rollup/pluginutils" "^3.1.0"
|
||||||
"@types/resolve" "1.17.1"
|
"@types/resolve" "1.17.1"
|
||||||
@@ -1815,18 +1815,6 @@
|
|||||||
is-module "^1.0.0"
|
is-module "^1.0.0"
|
||||||
resolve "^1.19.0"
|
resolve "^1.19.0"
|
||||||
|
|
||||||
"@rollup/plugin-node-resolve@^13.2.1":
|
|
||||||
version "13.2.1"
|
|
||||||
resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.2.1.tgz"
|
|
||||||
integrity sha512-btX7kzGvp1JwShQI9V6IM841YKNPYjKCvUbNrQ2EcVYbULtUd/GH6wZ/qdqH13j9pOHBER+EZXNN2L8RSJhVRA==
|
|
||||||
dependencies:
|
|
||||||
"@rollup/pluginutils" "^3.1.0"
|
|
||||||
"@types/resolve" "1.17.1"
|
|
||||||
builtin-modules "^3.1.0"
|
|
||||||
deepmerge "^4.2.2"
|
|
||||||
is-module "^1.0.0"
|
|
||||||
resolve "^1.19.0"
|
|
||||||
|
|
||||||
"@rollup/plugin-typescript@^8.2.5":
|
"@rollup/plugin-typescript@^8.2.5":
|
||||||
version "8.2.5"
|
version "8.2.5"
|
||||||
resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz"
|
resolved "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz"
|
||||||
@@ -1965,12 +1953,12 @@
|
|||||||
|
|
||||||
"@types/cssom@^0.4.1":
|
"@types/cssom@^0.4.1":
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz"
|
resolved "https://registry.npmjs.org/@types/cssom/-/cssom-0.4.1.tgz#fb64e145b425bd6c1b0ed78ebd66ba43b6e088ab"
|
||||||
integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ==
|
integrity sha512-hHGVfUuGZe5FpgCxpTJccH0gD1bui5gWceW0We0TyAzUr6wBaqDnSLG9Yr3xqS4AkGhnclNOwRSXH/LIfki3fQ==
|
||||||
|
|
||||||
"@types/cssstyle@^2.2.1":
|
"@types/cssstyle@^2.2.1":
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz"
|
resolved "https://registry.npmjs.org/@types/cssstyle/-/cssstyle-2.2.1.tgz#fa010824006ff47af94a6b9baf9759e031815347"
|
||||||
integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ==
|
integrity sha512-CSQFKdZc3dmWoZXLAM0pPL6XiYLG8hMGzImM2MwQ9kavB5LnbeMGan94CCj4oxY65xMl5mRMwrFUfKPOWO4WpQ==
|
||||||
|
|
||||||
"@types/estree@*":
|
"@types/estree@*":
|
||||||
@@ -2088,7 +2076,7 @@
|
|||||||
|
|
||||||
"@types/nwsapi@^2.2.2":
|
"@types/nwsapi@^2.2.2":
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz"
|
resolved "https://registry.npmjs.org/@types/nwsapi/-/nwsapi-2.2.2.tgz#1b1dccfc38b2b7e1b9ea71d5285796878375e862"
|
||||||
integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g==
|
integrity sha512-C4G47l3cAra4729xbhL9y3PjTpO7LJwXd47Fn1mbnZ6WcTkFPo8iDJPyMGCIudxpc7aeM8K1Fmw+lZfOb5ya9g==
|
||||||
|
|
||||||
"@types/offscreencanvas@^2019.6.4":
|
"@types/offscreencanvas@^2019.6.4":
|
||||||
@@ -3499,7 +3487,7 @@ compare-func@^2.0.0:
|
|||||||
|
|
||||||
compare-versions@^4.1.3:
|
compare-versions@^4.1.3:
|
||||||
version "4.1.3"
|
version "4.1.3"
|
||||||
resolved "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.3.tgz"
|
resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4"
|
||||||
integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==
|
integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
@@ -9676,6 +9664,14 @@ resolve@^1.1.7, resolve@^1.10.0, resolve@^1.16.1, resolve@^1.17.0, resolve@^1.19
|
|||||||
is-core-module "^2.2.0"
|
is-core-module "^2.2.0"
|
||||||
path-parse "^1.0.6"
|
path-parse "^1.0.6"
|
||||||
|
|
||||||
|
resolve@~1.19.0:
|
||||||
|
version "1.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c"
|
||||||
|
integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==
|
||||||
|
dependencies:
|
||||||
|
is-core-module "^2.1.0"
|
||||||
|
path-parse "^1.0.6"
|
||||||
|
|
||||||
restore-cursor@^2.0.0:
|
restore-cursor@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user