Skip to content

Commit 8526326

Browse files
committed
refactoring public API
1 parent 555398d commit 8526326

File tree

5 files changed

+146
-70
lines changed

5 files changed

+146
-70
lines changed

src/rebuild.ts

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,12 @@ export function addHoverClass(cssText: string): string {
7777

7878
function buildNode(
7979
n: serializedNodeWithId,
80-
doc: Document,
81-
HACK_CSS: boolean,
80+
options: {
81+
doc: Document;
82+
hackCss: boolean;
83+
},
8284
): Node | null {
85+
const { doc, hackCss } = options;
8386
switch (n.type) {
8487
case NodeType.Document:
8588
return doc.implementation.createDocument(null, '', null);
@@ -109,7 +112,7 @@ function buildNode(
109112
const isTextarea = tagName === 'textarea' && name === 'value';
110113
const isRemoteOrDynamicCss =
111114
tagName === 'style' && name === '_cssText';
112-
if (isRemoteOrDynamicCss && HACK_CSS) {
115+
if (isRemoteOrDynamicCss && hackCss) {
113116
value = addHoverClass(value);
114117
}
115118
if (isTextarea || isRemoteOrDynamicCss) {
@@ -177,7 +180,7 @@ function buildNode(
177180
return node;
178181
case NodeType.Text:
179182
return doc.createTextNode(
180-
n.isStyle && HACK_CSS ? addHoverClass(n.textContent) : n.textContent,
183+
n.isStyle && hackCss ? addHoverClass(n.textContent) : n.textContent,
181184
);
182185
case NodeType.CDATA:
183186
return doc.createCDATASection(n.textContent);
@@ -190,12 +193,15 @@ function buildNode(
190193

191194
export function buildNodeWithSN(
192195
n: serializedNodeWithId,
193-
doc: Document,
194-
map: idNodeMap,
195-
skipChild = false,
196-
HACK_CSS = true,
196+
options: {
197+
doc: Document;
198+
map: idNodeMap;
199+
skipChild?: boolean;
200+
hackCss: boolean;
201+
},
197202
): INode | null {
198-
let node = buildNode(n, doc, HACK_CSS);
203+
const { doc, map, skipChild = false, hackCss = true } = options;
204+
let node = buildNode(n, { doc, hackCss });
199205
if (!node) {
200206
return null;
201207
}
@@ -214,7 +220,12 @@ export function buildNodeWithSN(
214220
!skipChild
215221
) {
216222
for (const childN of n.childNodes) {
217-
const childNode = buildNodeWithSN(childN, doc, map, false, HACK_CSS);
223+
const childNode = buildNodeWithSN(childN, {
224+
doc,
225+
map,
226+
skipChild: false,
227+
hackCss,
228+
});
218229
if (!childNode) {
219230
console.warn('Failed to rebuild', childN);
220231
} else {
@@ -259,15 +270,20 @@ function handleScroll(node: INode) {
259270

260271
function rebuild(
261272
n: serializedNodeWithId,
262-
doc: Document,
263-
onVisit?: (node: INode) => unknown,
264-
/**
265-
* This is not a public API yet, just for POC
266-
*/
267-
HACK_CSS: boolean = true,
273+
options: {
274+
doc: Document;
275+
onVisit?: (node: INode) => unknown;
276+
hackCss?: boolean;
277+
},
268278
): [Node | null, idNodeMap] {
279+
const { doc, onVisit, hackCss = true } = options;
269280
const idNodeMap: idNodeMap = {};
270-
const node = buildNodeWithSN(n, doc, idNodeMap, false, HACK_CSS);
281+
const node = buildNodeWithSN(n, {
282+
doc,
283+
map: idNodeMap,
284+
skipChild: false,
285+
hackCss,
286+
});
271287
visit(idNodeMap, (visitedNode) => {
272288
if (onVisit) {
273289
onVisit(visitedNode);

src/snapshot.ts

Lines changed: 73 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,23 @@ export function _isBlockedElement(
187187

188188
function serializeNode(
189189
n: Node,
190-
doc: Document,
191-
blockClass: string | RegExp,
192-
blockSelector: string | null,
193-
inlineStylesheet: boolean,
194-
maskInputOptions: MaskInputOptions = {},
195-
recordCanvas: boolean,
190+
options: {
191+
doc: Document;
192+
blockClass: string | RegExp;
193+
blockSelector: string | null;
194+
inlineStylesheet: boolean;
195+
maskInputOptions: MaskInputOptions;
196+
recordCanvas: boolean;
197+
},
196198
): serializedNode | false {
199+
const {
200+
doc,
201+
blockClass,
202+
blockSelector,
203+
inlineStylesheet,
204+
maskInputOptions = {},
205+
recordCanvas,
206+
} = options;
197207
switch (n.nodeType) {
198208
case n.DOCUMENT_NODE:
199209
return {
@@ -437,26 +447,39 @@ function slimDOMExcluded(
437447

438448
export function serializeNodeWithId(
439449
n: Node | INode,
440-
doc: Document,
441-
map: idNodeMap,
442-
blockClass: string | RegExp,
443-
blockSelector: string | null,
444-
skipChild = false,
445-
inlineStylesheet = true,
446-
maskInputOptions?: MaskInputOptions,
447-
slimDOMOptions: SlimDOMOptions = {},
448-
recordCanvas?: boolean,
449-
preserveWhiteSpace = true,
450+
options: {
451+
doc: Document;
452+
map: idNodeMap;
453+
blockClass: string | RegExp;
454+
blockSelector: string | null;
455+
skipChild: boolean;
456+
inlineStylesheet: boolean;
457+
maskInputOptions?: MaskInputOptions;
458+
slimDOMOptions: SlimDOMOptions;
459+
recordCanvas?: boolean;
460+
preserveWhiteSpace?: boolean;
461+
},
450462
): serializedNodeWithId | null {
451-
const _serializedNode = serializeNode(
452-
n,
463+
const {
464+
doc,
465+
map,
466+
blockClass,
467+
blockSelector,
468+
skipChild = false,
469+
inlineStylesheet = true,
470+
maskInputOptions = {},
471+
slimDOMOptions,
472+
recordCanvas = false,
473+
} = options;
474+
let { preserveWhiteSpace = true } = options;
475+
const _serializedNode = serializeNode(n, {
453476
doc,
454477
blockClass,
455478
blockSelector,
456479
inlineStylesheet,
457480
maskInputOptions,
458-
recordCanvas || false,
459-
);
481+
recordCanvas,
482+
});
460483
if (!_serializedNode) {
461484
// TODO: dev only
462485
console.warn(n, 'not serialized');
@@ -504,8 +527,7 @@ export function serializeNodeWithId(
504527
preserveWhiteSpace = false;
505528
}
506529
for (const childN of Array.from(n.childNodes)) {
507-
const serializedChildNode = serializeNodeWithId(
508-
childN,
530+
const serializedChildNode = serializeNodeWithId(childN, {
509531
doc,
510532
map,
511533
blockClass,
@@ -516,7 +538,7 @@ export function serializeNodeWithId(
516538
slimDOMOptions,
517539
recordCanvas,
518540
preserveWhiteSpace,
519-
);
541+
});
520542
if (serializedChildNode) {
521543
serializedNode.childNodes.push(serializedChildNode);
522544
}
@@ -527,16 +549,26 @@ export function serializeNodeWithId(
527549

528550
function snapshot(
529551
n: Document,
530-
blockClass: string | RegExp = 'rr-block',
531-
inlineStylesheet = true,
532-
maskAllInputsOrOptions: boolean | MaskInputOptions,
533-
slimDOMSensibleOrOptions: boolean | SlimDOMOptions,
534-
recordCanvas?: boolean,
535-
blockSelector: string | null = null,
552+
options?: {
553+
blockClass?: string | RegExp;
554+
inlineStylesheet?: boolean;
555+
maskAllInputs?: boolean | MaskInputOptions;
556+
slimDOM?: boolean | SlimDOMOptions;
557+
recordCanvas?: boolean;
558+
blockSelector?: string | null;
559+
},
536560
): [serializedNodeWithId | null, idNodeMap] {
561+
const {
562+
blockClass = 'rr-block',
563+
inlineStylesheet = true,
564+
recordCanvas = false,
565+
blockSelector = null,
566+
maskAllInputs = false,
567+
slimDOM = false,
568+
} = options || {};
537569
const idNodeMap: idNodeMap = {};
538570
const maskInputOptions: MaskInputOptions =
539-
maskAllInputsOrOptions === true
571+
maskAllInputs === true
540572
? {
541573
color: true,
542574
date: true,
@@ -554,40 +586,39 @@ function snapshot(
554586
textarea: true,
555587
select: true,
556588
}
557-
: maskAllInputsOrOptions === false
589+
: maskAllInputs === false
558590
? {}
559-
: maskAllInputsOrOptions;
591+
: maskAllInputs;
560592
const slimDOMOptions: SlimDOMOptions =
561-
slimDOMSensibleOrOptions === true || slimDOMSensibleOrOptions === 'all'
593+
slimDOM === true || slimDOM === 'all'
562594
? // if true: set of sensible options that should not throw away any information
563595
{
564596
script: true,
565597
comment: true,
566598
headFavicon: true,
567599
headWhitespace: true,
568-
headMetaDescKeywords: slimDOMSensibleOrOptions === 'all', // destructive
600+
headMetaDescKeywords: slimDOM === 'all', // destructive
569601
headMetaSocial: true,
570602
headMetaRobots: true,
571603
headMetaHttpEquiv: true,
572604
headMetaAuthorship: true,
573605
headMetaVerification: true,
574606
}
575-
: slimDOMSensibleOrOptions === false
607+
: slimDOM === false
576608
? {}
577-
: slimDOMSensibleOrOptions;
609+
: slimDOM;
578610
return [
579-
serializeNodeWithId(
580-
n,
581-
n,
582-
idNodeMap,
611+
serializeNodeWithId(n, {
612+
doc: n,
613+
map: idNodeMap,
583614
blockClass,
584615
blockSelector,
585-
false,
616+
skipChild: false,
586617
inlineStylesheet,
587618
maskInputOptions,
588619
slimDOMOptions,
589620
recordCanvas,
590-
),
621+
}),
591622
idNodeMap,
592623
];
593624
}

test/integration.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { SnapshotState, toMatchSnapshot } from 'jest-snapshot';
1111
import { Suite } from 'mocha';
1212

1313
const htmlFolder = path.join(__dirname, 'html');
14-
const htmls = fs.readdirSync(htmlFolder).map(filePath => {
14+
const htmls = fs.readdirSync(htmlFolder).map((filePath) => {
1515
const raw = fs.readFileSync(path.resolve(htmlFolder, filePath), 'utf-8');
1616
return {
1717
filePath,
@@ -24,7 +24,7 @@ interface IMimeType {
2424
}
2525

2626
const server = () =>
27-
new Promise<http.Server>(resolve => {
27+
new Promise<http.Server>((resolve) => {
2828
const mimeType: IMimeType = {
2929
'.html': 'text/html',
3030
'.js': 'text/javascript',
@@ -73,7 +73,7 @@ interface ISuite extends Suite {
7373
code: string;
7474
}
7575

76-
describe('integration tests', function(this: ISuite) {
76+
describe('integration tests', function (this: ISuite) {
7777
before(async () => {
7878
this.server = await server();
7979
this.browser = await puppeteer.launch({
@@ -102,16 +102,18 @@ describe('integration tests', function(this: ISuite) {
102102
const page: puppeteer.Page = await this.browser.newPage();
103103
// console for debug
104104
// tslint:disable-next-line: no-console
105-
page.on('console', msg => console.log(msg.text()));
105+
page.on('console', (msg) => console.log(msg.text()));
106106
await page.goto(`http://localhost:3030/html`);
107107
await page.setContent(html.src, {
108108
waitUntil: 'load',
109109
});
110-
const rebuildHtml = (await page.evaluate(`${this.code}
110+
const rebuildHtml = (
111+
await page.evaluate(`${this.code}
111112
const x = new XMLSerializer();
112113
const [snap] = rrweb.snapshot(document);
113-
x.serializeToString(rrweb.rebuild(snap, document)[0]);
114-
`)).replace(/\n\n/g, '');
114+
x.serializeToString(rrweb.rebuild(snap, { doc: document })[0]);
115+
`)
116+
).replace(/\n\n/g, '');
115117
const result = matchSnapshot(rebuildHtml, __filename, title);
116118
assert(result.pass, result.pass ? '' : result.report());
117119
}).timeout(5000);

typings/rebuild.d.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { serializedNodeWithId, idNodeMap, INode } from './types';
22
export declare function addHoverClass(cssText: string): string;
3-
export declare function buildNodeWithSN(n: serializedNodeWithId, doc: Document, map: idNodeMap, skipChild?: boolean, HACK_CSS?: boolean): INode | null;
4-
declare function rebuild(n: serializedNodeWithId, doc: Document, onVisit?: (node: INode) => unknown, HACK_CSS?: boolean): [Node | null, idNodeMap];
3+
export declare function buildNodeWithSN(n: serializedNodeWithId, options: {
4+
doc: Document;
5+
map: idNodeMap;
6+
skipChild?: boolean;
7+
hackCss: boolean;
8+
}): INode | null;
9+
declare function rebuild(n: serializedNodeWithId, options: {
10+
doc: Document;
11+
onVisit?: (node: INode) => unknown;
12+
hackCss?: boolean;
13+
}): [Node | null, idNodeMap];
514
export default rebuild;

typings/snapshot.d.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,26 @@ export declare function absoluteToStylesheet(cssText: string | null, href: strin
44
export declare function absoluteToDoc(doc: Document, attributeValue: string): string;
55
export declare function transformAttribute(doc: Document, name: string, value: string): string;
66
export declare function _isBlockedElement(element: HTMLElement, blockClass: string | RegExp, blockSelector: string | null): boolean;
7-
export declare function serializeNodeWithId(n: Node | INode, doc: Document, map: idNodeMap, blockClass: string | RegExp, blockSelector: string | null, skipChild?: boolean, inlineStylesheet?: boolean, maskInputOptions?: MaskInputOptions, slimDOMOptions?: SlimDOMOptions, recordCanvas?: boolean, preserveWhiteSpace?: boolean): serializedNodeWithId | null;
8-
declare function snapshot(n: Document, blockClass: string | RegExp | undefined, inlineStylesheet: boolean | undefined, maskAllInputsOrOptions: boolean | MaskInputOptions, slimDOMSensibleOrOptions: boolean | SlimDOMOptions, recordCanvas?: boolean, blockSelector?: string | null): [serializedNodeWithId | null, idNodeMap];
7+
export declare function serializeNodeWithId(n: Node | INode, options: {
8+
doc: Document;
9+
map: idNodeMap;
10+
blockClass: string | RegExp;
11+
blockSelector: string | null;
12+
skipChild: boolean;
13+
inlineStylesheet: boolean;
14+
maskInputOptions?: MaskInputOptions;
15+
slimDOMOptions: SlimDOMOptions;
16+
recordCanvas?: boolean;
17+
preserveWhiteSpace?: boolean;
18+
}): serializedNodeWithId | null;
19+
declare function snapshot(n: Document, options?: {
20+
blockClass?: string | RegExp;
21+
inlineStylesheet?: boolean;
22+
maskAllInputs?: boolean | MaskInputOptions;
23+
slimDOM?: boolean | SlimDOMOptions;
24+
recordCanvas?: boolean;
25+
blockSelector?: string | null;
26+
}): [serializedNodeWithId | null, idNodeMap];
927
export declare function visitSnapshot(node: serializedNodeWithId, onVisit: (node: serializedNodeWithId) => unknown): void;
1028
export declare function cleanupSnapshot(): void;
1129
export default snapshot;

0 commit comments

Comments
 (0)