Reverse monkey patch built in methods to support LWC (#1509)
* Get around monkey patched Nodes
* inlineImages: Setting of `image.crossOrigin` is not always necessary (#1468)
Setting of the `crossorigin` attribute is not necessary for same-origin images, and causes an immediate image reload (albeit from cache) necessitating the use of a load event listener which subsequently mutates the snapshot. This change allows us to avoid the mutation of the snapshot for the same-origin case.
* Modify inlineImages test to remove delay and show that we can inline images without mutation
* Add an explicit test for when the `image.crossOrigin = 'anonymous';` method is necessary. Uses a combination of about:blank and our test server to simulate a cross-origin context
* Other test changes: there were some spurious rrweb mutations being generated by the addition of the crossorigin attribute that are now elimnated from the rrweb/__snapshots__/integration.test.ts.snap after this PR - this is good
* Move `childNodes` to @rrweb/utils
* Use non-monkey patched versions of the `childNodes`, `parentNode` `parentElement` `textContent` accessors
* Add getRootNode and contains, and add comprehensive todo list
* chore: Update turbo.json tasks for better build process
* Update caniuse-lite
* chore: Update eslint-plugin-compat to version 5.0.0
* chore: Bump @rrweb/utils version to 2.0.0-alpha.15
* delete unused yarn.lock files
* Set correct @rrweb/utils version in package.json
* Migrate over some accessors to reverse-monkey-patched version
* Add missing functions
* Fix illegal invocation error
* Revert closer to what it was.
This feels incorrect to me (Justin Halsall), but some of the tests break without it so I'm restoring this to be closer to its original here:
cfd686d488/packages/rrweb-snapshot/src/snapshot.ts (L1011)
* Reverse monkey patch all methods LWC hijacks
* Make tests more stable
* Safely handle rrdom nodes in hasShadowRoot
* Remove duplicated test
* Use variable `serverURL` in test
* Use monorepo default browserlist
* Fix typing issue for new typescript
* Remove unused package
* Remove unused code
* Add prefix to reverse-monkey-patched methods to make them more explicit
* Add default exports to @rrweb/utils
---------
Co-authored-by: Eoghan Murray <eoghan@getthere.ie>
This commit is contained in:
178
packages/utils/Readme.md
Normal file
178
packages/utils/Readme.md
Normal file
@@ -0,0 +1,178 @@
|
||||
# @rrweb/utils
|
||||
|
||||
This package contains the shared utility functions used across rrweb packages.
|
||||
See the [guide](../../guide.md) for more info on rrweb.
|
||||
|
||||
## Sponsors
|
||||
|
||||
[Become a sponsor](https://opencollective.com/rrweb#sponsor) and get your logo on our README on Github with a link to your site.
|
||||
|
||||
### Gold Sponsors 🥇
|
||||
|
||||
<div dir="auto">
|
||||
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/0/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/1/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/2/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/3/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/4/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/5/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/gold-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/gold-sponsor/6/avatar.svg?requireActive=false&avatarHeight=225" alt="sponsor"></a>
|
||||
|
||||
</div>
|
||||
|
||||
### Silver Sponsors 🥈
|
||||
|
||||
<div dir="auto">
|
||||
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/0/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/1/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/2/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/3/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/4/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/5/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/silver-sponsor/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/silver-sponsor/6/avatar.svg?requireActive=false&avatarHeight=158" alt="sponsor"></a>
|
||||
|
||||
</div>
|
||||
|
||||
### Bronze Sponsors 🥉
|
||||
|
||||
<div dir="auto">
|
||||
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/0/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/0/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/1/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/1/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/2/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/2/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/3/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/3/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/4/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/4/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/5/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/5/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/6/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/6/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/7/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/7/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
<a href="https://opencollective.com/rrweb/tiers/sponsors/8/website?requireActive=false" target="_blank"><img src="https://opencollective.com/rrweb/tiers/sponsors/8/avatar.svg?requireActive=false&avatarHeight=70" alt="sponsor"></a>
|
||||
|
||||
</div>
|
||||
|
||||
### Backers
|
||||
|
||||
<a href="https://opencollective.com/rrweb#sponsor" rel="nofollow"><img src="https://opencollective.com/rrweb/tiers/backers.svg?avatarHeight=36"></a>
|
||||
|
||||
## Core Team Members
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Yuyz0112">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/13651389?s=100"
|
||||
width="100px;"
|
||||
alt=""
|
||||
/>
|
||||
<br /><sub><b>Yuyz0112</b></sub>
|
||||
<br /><br />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/YunFeng0817">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/27533910?s=100"
|
||||
width="100px;"
|
||||
alt=""
|
||||
/>
|
||||
<br /><sub><b>Yun Feng</b></sub>
|
||||
<br /><br />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/eoghanmurray">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/156780?s=100"
|
||||
width="100px;"
|
||||
alt=""
|
||||
/>
|
||||
<br /><sub><b>eoghanmurray</b></sub>
|
||||
<br /><br />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Juice10">
|
||||
<img
|
||||
src="https://avatars.githubusercontent.com/u/4106?s=100"
|
||||
width="100px;"
|
||||
alt=""
|
||||
/>
|
||||
<br /><sub><b>Juice10</b></sub>
|
||||
<br /><sub>open for rrweb consulting</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Who's using rrweb?
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="http://www.smartx.com/" target="_blank">
|
||||
<img width="195px" src="https://www.rrweb.io/logos/smartx.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://posthog.com?utm_source=rrweb&utm_medium=sponsorship&utm_campaign=open-source-sponsorship" target="_blank">
|
||||
<img width="195px" src="https://www.rrweb.io/logos/posthog.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://statcounter.com/session-replay/" target="_blank">
|
||||
<img width="195px" src="https://statcounter.com/images/logo-statcounter-arc-blue.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://recordonce.com/" target="_blank">
|
||||
<img width="195px" alt="Smart screen recording for SaaS" src="https://uploads-ssl.webflow.com/5f3d133183156245630d4446/5f3d1940abe8db8612c23521_Record-Once-logo-554x80px.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://cux.io" target="_blank">
|
||||
<img style="padding: 8px" alt="The first ever UX automation tool" width="195px" src="https://cux.io/cux-logo.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://remsupp.com" target="_blank">
|
||||
<img style="padding: 8px" alt="Remote Access & Co-Browsing" width="195px" src="https://remsupp.com/images/logo.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://highlight.io" target="_blank">
|
||||
<img style="padding: 8px" alt="The open source, fullstack Monitoring Platform." width="195px" src="https://github.com/highlight/highlight/raw/main/highlight.io/public/images/logo.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://analyzee.io" target="_blank">
|
||||
<img style="padding: 8px" alt="Comprehensive data analytics platform that empowers businesses to gain valuable insights and make data-driven decisions." width="195px" src="https://cdn.analyzee.io/assets/analyzee-logo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://requestly.io" target="_blank">
|
||||
<img style="padding: 8px" alt="Intercept, Modify, Record & Replay HTTP Requests." width="195px" src="https://github.com/requestly/requestly/assets/16779465/652552db-c867-44cb-9bb5-94a2026e04ca">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://gleap.io" target="_blank">
|
||||
<img style="padding: 8px" alt="In-app bug reporting & customer feedback platform." width="195px" src="https://assets-global.website-files.com/6506f3f29c68b1724807619d/6506f56010237164c6306591_GleapLogo.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://uxwizz.com" target="_blank">
|
||||
<img style="padding: 8px" alt="Self-hosted website analytics with heatmaps and session recordings." width="195px" src="https://github.com/UXWizz/public-files/raw/main/assets/logo.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://www.howdygo.com" target="_blank">
|
||||
<img style="padding: 8px" alt="Interactive product demos for small marketing teams" width="195px" src="https://assets-global.website-files.com/650afb446f1dd5bd410f00cc/650b2cec6188ff54dd9b01e1_Logo.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
53
packages/utils/package.json
Normal file
53
packages/utils/package.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "@rrweb/utils",
|
||||
"version": "2.0.0-alpha.16",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"rrweb",
|
||||
"@rrweb/utils"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "tsc -noEmit && vite build",
|
||||
"check-types": "tsc -noEmit",
|
||||
"prepublish": "npm run build",
|
||||
"lint": "yarn eslint src/**/*.ts"
|
||||
},
|
||||
"homepage": "https://github.com/rrweb-io/rrweb/tree/main/packages/@rrweb/utils#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/rrweb-io/rrweb/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rrweb-io/rrweb.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./dist/utils.umd.cjs",
|
||||
"module": "./dist/utils.js",
|
||||
"unpkg": "./dist/utils.umd.cjs",
|
||||
"typings": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/utils.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/utils.umd.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"package.json"
|
||||
],
|
||||
"devDependencies": {
|
||||
"vite": "^5.2.8",
|
||||
"vite-plugin-dts": "^3.8.1"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
221
packages/utils/src/index.ts
Normal file
221
packages/utils/src/index.ts
Normal file
@@ -0,0 +1,221 @@
|
||||
type PrototypeOwner = Node | ShadowRoot | MutationObserver | Element;
|
||||
type TypeofPrototypeOwner =
|
||||
| typeof Node
|
||||
| typeof ShadowRoot
|
||||
| typeof MutationObserver
|
||||
| typeof Element;
|
||||
|
||||
type BasePrototypeCache = {
|
||||
Node: typeof Node.prototype;
|
||||
ShadowRoot: typeof ShadowRoot.prototype;
|
||||
MutationObserver: typeof MutationObserver.prototype;
|
||||
Element: typeof Element.prototype;
|
||||
};
|
||||
|
||||
const testableAccessors = {
|
||||
Node: ['childNodes', 'parentNode', 'parentElement', 'textContent'] as const,
|
||||
ShadowRoot: ['host', 'styleSheets'] as const,
|
||||
Element: ['shadowRoot', 'querySelector', 'querySelectorAll'] as const,
|
||||
MutationObserver: [] as const,
|
||||
} as const;
|
||||
|
||||
const testableMethods = {
|
||||
Node: ['contains', 'getRootNode'] as const,
|
||||
ShadowRoot: ['getSelection'],
|
||||
Element: [],
|
||||
MutationObserver: ['constructor'],
|
||||
} as const;
|
||||
|
||||
const untaintedBasePrototype: Partial<BasePrototypeCache> = {};
|
||||
|
||||
export function getUntaintedPrototype<T extends keyof BasePrototypeCache>(
|
||||
key: T,
|
||||
): BasePrototypeCache[T] {
|
||||
if (untaintedBasePrototype[key])
|
||||
return untaintedBasePrototype[key] as BasePrototypeCache[T];
|
||||
|
||||
const defaultObj = globalThis[key] as TypeofPrototypeOwner;
|
||||
const defaultPrototype = defaultObj.prototype as BasePrototypeCache[T];
|
||||
|
||||
// use list of testable accessors to check if the prototype is tainted
|
||||
const accessorNames =
|
||||
key in testableAccessors ? testableAccessors[key] : undefined;
|
||||
const isUntaintedAccessors = Boolean(
|
||||
accessorNames &&
|
||||
// @ts-expect-error 2345
|
||||
accessorNames.every((accessor: keyof typeof defaultPrototype) =>
|
||||
Boolean(
|
||||
Object.getOwnPropertyDescriptor(defaultPrototype, accessor)
|
||||
?.get?.toString()
|
||||
.includes('[native code]'),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const methodNames = key in testableMethods ? testableMethods[key] : undefined;
|
||||
const isUntaintedMethods = Boolean(
|
||||
methodNames &&
|
||||
methodNames.every(
|
||||
// @ts-expect-error 2345
|
||||
(method: keyof typeof defaultPrototype) =>
|
||||
typeof defaultPrototype[method] === 'function' &&
|
||||
defaultPrototype[method]?.toString().includes('[native code]'),
|
||||
),
|
||||
);
|
||||
|
||||
if (isUntaintedAccessors && isUntaintedMethods) {
|
||||
untaintedBasePrototype[key] = defaultObj.prototype as BasePrototypeCache[T];
|
||||
return defaultObj.prototype as BasePrototypeCache[T];
|
||||
}
|
||||
|
||||
try {
|
||||
const iframeEl = document.createElement('iframe');
|
||||
document.body.appendChild(iframeEl);
|
||||
const win = iframeEl.contentWindow;
|
||||
if (!win) return defaultObj.prototype as BasePrototypeCache[T];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
||||
const untaintedObject = (win as any)[key]
|
||||
.prototype as BasePrototypeCache[T];
|
||||
// cleanup
|
||||
document.body.removeChild(iframeEl);
|
||||
|
||||
if (!untaintedObject) return defaultPrototype;
|
||||
|
||||
return (untaintedBasePrototype[key] = untaintedObject);
|
||||
} catch {
|
||||
return defaultPrototype;
|
||||
}
|
||||
}
|
||||
|
||||
const untaintedAccessorCache: Record<
|
||||
string,
|
||||
(this: PrototypeOwner, ...args: unknown[]) => unknown
|
||||
> = {};
|
||||
|
||||
export function getUntaintedAccessor<
|
||||
K extends keyof BasePrototypeCache,
|
||||
T extends keyof BasePrototypeCache[K],
|
||||
>(
|
||||
key: K,
|
||||
instance: BasePrototypeCache[K],
|
||||
accessor: T,
|
||||
): BasePrototypeCache[K][T] {
|
||||
const cacheKey = `${key}.${String(accessor)}`;
|
||||
if (untaintedAccessorCache[cacheKey])
|
||||
return untaintedAccessorCache[cacheKey].call(
|
||||
instance,
|
||||
) as BasePrototypeCache[K][T];
|
||||
|
||||
const untaintedPrototype = getUntaintedPrototype(key);
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const untaintedAccessor = Object.getOwnPropertyDescriptor(
|
||||
untaintedPrototype,
|
||||
accessor,
|
||||
)?.get;
|
||||
|
||||
if (!untaintedAccessor) return instance[accessor];
|
||||
|
||||
untaintedAccessorCache[cacheKey] = untaintedAccessor;
|
||||
|
||||
return untaintedAccessor.call(instance) as BasePrototypeCache[K][T];
|
||||
}
|
||||
|
||||
type BaseMethod<K extends keyof BasePrototypeCache> = (
|
||||
this: BasePrototypeCache[K],
|
||||
...args: unknown[]
|
||||
) => unknown;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const untaintedMethodCache: Record<string, BaseMethod<any>> = {};
|
||||
export function getUntaintedMethod<
|
||||
K extends keyof BasePrototypeCache,
|
||||
T extends keyof BasePrototypeCache[K],
|
||||
>(
|
||||
key: K,
|
||||
instance: BasePrototypeCache[K],
|
||||
method: T,
|
||||
): BasePrototypeCache[K][T] {
|
||||
const cacheKey = `${key}.${String(method)}`;
|
||||
if (untaintedMethodCache[cacheKey])
|
||||
return untaintedMethodCache[cacheKey].bind(
|
||||
instance,
|
||||
) as BasePrototypeCache[K][T];
|
||||
|
||||
const untaintedPrototype = getUntaintedPrototype(key);
|
||||
const untaintedMethod = untaintedPrototype[method];
|
||||
|
||||
if (typeof untaintedMethod !== 'function') return instance[method];
|
||||
|
||||
untaintedMethodCache[cacheKey] = untaintedMethod as BaseMethod<K>;
|
||||
|
||||
return untaintedMethod.bind(instance) as BasePrototypeCache[K][T];
|
||||
}
|
||||
|
||||
export function childNodes(n: Node): NodeListOf<Node> {
|
||||
return getUntaintedAccessor('Node', n, 'childNodes');
|
||||
}
|
||||
|
||||
export function parentNode(n: Node): ParentNode | null {
|
||||
return getUntaintedAccessor('Node', n, 'parentNode');
|
||||
}
|
||||
|
||||
export function parentElement(n: Node): HTMLElement | null {
|
||||
return getUntaintedAccessor('Node', n, 'parentElement');
|
||||
}
|
||||
|
||||
export function textContent(n: Node): string | null {
|
||||
return getUntaintedAccessor('Node', n, 'textContent');
|
||||
}
|
||||
|
||||
export function contains(n: Node, other: Node): boolean {
|
||||
return getUntaintedMethod('Node', n, 'contains')(other);
|
||||
}
|
||||
|
||||
export function getRootNode(n: Node): Node {
|
||||
return getUntaintedMethod('Node', n, 'getRootNode')();
|
||||
}
|
||||
|
||||
export function host(n: ShadowRoot): Element | null {
|
||||
if (!n || !('host' in n)) return null;
|
||||
return getUntaintedAccessor('ShadowRoot', n, 'host');
|
||||
}
|
||||
|
||||
export function styleSheets(n: ShadowRoot): StyleSheetList {
|
||||
return n.styleSheets;
|
||||
}
|
||||
|
||||
export function shadowRoot(n: Node): ShadowRoot | null {
|
||||
if (!n || !('shadowRoot' in n)) return null;
|
||||
return getUntaintedAccessor('Element', n as Element, 'shadowRoot');
|
||||
}
|
||||
|
||||
export function querySelector(n: Element, selectors: string): Element | null {
|
||||
return getUntaintedAccessor('Element', n, 'querySelector')(selectors);
|
||||
}
|
||||
|
||||
export function querySelectorAll(
|
||||
n: Element,
|
||||
selectors: string,
|
||||
): NodeListOf<Element> {
|
||||
return getUntaintedAccessor('Element', n, 'querySelectorAll')(selectors);
|
||||
}
|
||||
|
||||
export function mutationObserverCtor(): (typeof MutationObserver)['prototype']['constructor'] {
|
||||
return getUntaintedPrototype('MutationObserver').constructor;
|
||||
}
|
||||
|
||||
export default {
|
||||
childNodes,
|
||||
parentNode,
|
||||
parentElement,
|
||||
textContent,
|
||||
contains,
|
||||
getRootNode,
|
||||
host,
|
||||
styleSheets,
|
||||
shadowRoot,
|
||||
querySelector,
|
||||
querySelectorAll,
|
||||
mutationObserver: mutationObserverCtor,
|
||||
};
|
||||
10
packages/utils/tsconfig.json
Normal file
10
packages/utils/tsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["vite.config.ts"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo"
|
||||
},
|
||||
"references": []
|
||||
}
|
||||
4
packages/utils/vite.config.js
Normal file
4
packages/utils/vite.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import path from 'path';
|
||||
import config from '../../vite.config.default';
|
||||
|
||||
export default config(path.resolve(__dirname, 'src/index.ts'), 'rrwebUtils');
|
||||
Reference in New Issue
Block a user