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.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * Fix publint & attw issue. I was bumping into this issue:3729bc2a3c/docs/problems/NoResolution.mdAnd had to choose one of these three methods described here: https://github.com/andrewbranch/example-subpath-exports-ts-compat?tab=readme-ov-file#typescript-friendly-strategies-for-packagejson-subpath-exports-compatibility And I ended up going for the method described here:1ffe3425b0/examples/node_modules/package-json-redirects (package-json-redirects)The redirect method seemed the least invasive and most effective. * move some rrdom tests that require rrweb to rrweb package * Use pre-jest 29 syntax for snapshotting * get rrweb passing publint and attw * const enum does not work with isolated modules flag * Fix script tag type in webgl.test.ts.snap and update rrweb.umd.cjs path in webgl.test.ts * Fix paths * Move tests for console record plugin and fix bundle path * Fix tests for rrweb * pack integration tests were moved to @rrweb/all * Update rrweb bundle path in test files * Fix flaky scroll emit from test * Migrate rrweb's tests over to vitest and make them pass * Make sure benchmarks & updating tests work * Remove jest from rrweb * Fix paths * always use rrweb's own cssom * Update tsconfig.json for rrweb-plugin-sequential-id-record Fixes this error: Error: @rrweb/rrweb-plugin-sequential-id-record:prepublish: tsconfig.json(9,5): error TS6377: Cannot write file '/home/runner/work/rrweb/rrweb/tsconfig.tsbuildinfo' because it will overwrite '.tsbuildinfo' file generated by referenced project '/home/runner/work/rrweb/rrweb/packages/rrweb' * Add tsbuildinfo config to extended tsconfig files * Move rrdom over to vitest * Apply formatting changes * Update rrweb imports to use the new package structure * extend rrweb-snapshot's tsconfig from monorepo base config * extend @rrweb/types's tsconfig from monorepo base config * extend rrdom's tsconfig from monorepo base config * extend rrdom-nodejs's tsconfig from monorepo base config * extend web-extension's tsconfig from monorepo base config * unify tsconfigs * Continue when tests fail * Add stricter type checking * Add check-types global command * remove jest * Remove unused code * Add check-types command to build script * Fix linting issues * Add setup Chrome action for CI/CD workflow * Update puppeteer version in package.json for rrweb * Update Chrome setup in CI/CD workflow * Update Chrome setup in CI/CD workflow * Add Chrome setup and test cache location * Update CI/CD workflow to test chrome cache location * Add chrome installation step to CI/CD workflow * Update Puppeteer configuration for headless testing * Update dependencies and workflow configuration * Use same version of chrome on CI as is run locally * Use version of chrome that seems to work with rrdom tests * Try using puppeteerrc to define chrome version * Add .cache directory to .gitignore * Move global flag to vitest config * Update puppeteer version to 20.9.0 * Update console log messages in rrweb-plugin-console-record for new puppeteer version * Remove redundant Chrome setup from CI/CD workflow * Add minification and umd for all built files * Update import paths for rrweb dist files * Add @rrweb/replay and @rrweb/record * Add script to lint packages * Apply formatting changes * exclude styles export from typescript package type checking * WIP Move rrweb-player over to vite * Apply formatting changes * chore: Update rrweb plugin import paths * Remove rollup from rrweb-player * Fix typing issues * Fix typing issues * chore: Update rrweb-player to use vite for build process * Apply formatting changes * chore: Export Player class in rrweb-player/src/main.ts Makes attw happy * Apply formatting changes * Gets wiped by yarn workspaces-to-typescript-project-references * Add .eslintignore and .eslintrc.cjs files for rrweb-player package * Apply formatting changes * Update dependencies in rrweb-player/package.json * Apply formatting changes * chore: Update eslint configuration for rrweb-player package * Apply formatting changes * chore: Remove unused files from rrweb-player package * Apply formatting changes * chore: Update rrweb-player import path to use rrweb-player.cjs * chore: Update addEventListener signature in rrweb-player * Apply formatting changes * Add .eslintignore and update .gitignore files for to root * Apply formatting changes * Update documentation * Update @rrweb/types package description * Apply formatting changes * Update build and run commands in CONTRIBUTING.md * Apply formatting changes * Update package versions to 2.0.0-alpha.13 * Apply formatting changes * Apply formatting changes * Fix import statement in media/index.ts * Apply formatting changes * chore: Update .gitignore to exclude build and dist directories * Apply formatting changes * Apply formatting changes * Migrate setTimeout to vitest * Apply formatting changes * Apply formatting changes * Fix isNativeShadowDom function signature in utils.ts * try out jsr * Apply formatting changes * Update package versions to 2.0.0-alpha.14 * Apply formatting changes * Fix name of rrwebSnapshot object * Apply formatting changes * Remove unused lock files * Apply formatting changes * Update rrweb bundle path to use umd.cjs format * Apply formatting changes * Trigger tests to run again * Rename snapshots for vitest * Apply formatting changes * Ping CI * Apply formatting changes * Ping CI * Apply formatting changes * Ignore files generated by svelte-kit for prettier * Correct Player object
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user