fix(tracker): put vendor lib inside src for the cjs compilation

This commit is contained in:
ShiKhu 2021-08-13 21:37:47 +08:00
parent 0920f17009
commit 57ff3574b5
9 changed files with 999 additions and 3 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openreplay/tracker",
"version": "3.1.0",
"version": "3.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
"version": "3.2.0",
"version": "3.2.1",
"keywords": [
"logging",
"replay"

View file

@ -1,4 +1,4 @@
import { finder } from '@medv/finder';
import { finder } from '../vendors/finder/finder';
import { normSpaces, hasOpenreplayAttribute, getLabelAttribute } from '../utils';
import App from '../app';
import { MouseMove, MouseClick } from '../../messages';

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 20182020 Anton Medvedev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,132 @@
![finder](https://medv.io/assets/finder.png)
# finder
[![npm](https://img.shields.io/npm/v/@medv/finder?color=grightgreen)](https://www.npmjs.com/package/@medv/finder)
[![Build status](https://img.shields.io/travis/antonmedv/finder)](https://travis-ci.org/antonmedv/finder)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@medv/finder?label=size)](https://bundlephobia.com/result?p=@medv/finder)
> CSS Selector Generator
## Features
* Generates **shortest** selectors
* **Unique** selectors per page
* Stable and **robust** selectors
* **2.1 kB** gzip and minify size
## Install
```bash
npm install @medv/finder
```
Finder can be used via modules:
```html
<script type="module">
import {finder} from 'https://medv.io/finder/finder.js'
</script>
```
## Usage
```js
import {finder} from '@medv/finder'
document.addEventListener('click', event => {
const selector = finder(event.target)
console.log(selector)
})
```
## Example
Example of generated selector:
```css
.blog > article:nth-child(3) .add-comment
```
## Configuration
`finder` takes configuration object as second parameters. Here is example of all params with default values:
```js
const selector = finder(event.target, {
root: document.body,
className: (name) => true,
tagName: (name) => true,
attr: (name, value) => false,
seedMinLength: 1,
optimizedMinLength: 2,
threshold: 1000,
maxNumberOfTries: 10_000,
})
```
#### `root: Element`
Root of search, defaults to `document.body`.
#### `idName: (name: string) => boolean`
Check if this ID can be used. For example you can restrict using framework specific IDs:
```js
const selector = finder(event.target, {
idName: name => !name.startsWith('ember')
})
```
#### `className: (name: string) => boolean`
Check if this class name can be used. For example you can restrict using _is-*_ class names:
```js
const selector = finder(event.target, {
className: name => !name.startsWith('is-')
})
```
#### `tagName: (name: string) => boolean`
Check if tag name can be used, same as `className`.
#### `attr: (name: string, value: string) => boolean`
Check if attr name can be used.
#### `seedMinLength: number`
Minimum length of levels in fining selector. Starts from `1`.
For more robust selectors give this param value around 4-5 depending on depth of you DOM tree.
If `finder` hits `root` this param is ignored.
#### `optimizedMinLength: number`
Minimum length for optimising selector. Starts from `2`.
For example selector `body > div > div > p` can be optimized to `body p`.
#### `threshold: number`
Max number of selectors to check before falling into `nth-child` usage.
Checking for uniqueness of selector is very costs operation, if you have DOM tree depth of 5, with 5 classes on each level,
that gives you more than 3k selectors to check.
`finder` uses two step approach so it's reaching this threshold in some cases twice.
Default `1000` is good enough in most cases.
#### `maxNumberOfTries: number`
Max number of tries when we do the optimization. It is a trade-off between optimization and efficiency.
Default `10_000` is good enough in most cases.
### Google Chrome Extension
![Chrome Extension](https://user-images.githubusercontent.com/141232/36737287-4a999d84-1c0d-11e8-8a14-43bcf9baf7ca.png)
Generate the unique selectors in your browser by using [Chrome Extension](https://chrome.google.com/webstore/detail/get-unique-css-selector/lkfaghhbdebclkklgjhhonadomejckai)
## License
[MIT](LICENSE)

View file

@ -0,0 +1,12 @@
export declare type Options = {
root: Element;
idName: (name: string) => boolean;
className: (name: string) => boolean;
tagName: (name: string) => boolean;
attr: (name: string, value: string) => boolean;
seedMinLength: number;
optimizedMinLength: number;
threshold: number;
maxNumberOfTries: number;
};
export declare function finder(input: Element, options?: Partial<Options>): string;

View file

@ -0,0 +1,339 @@
var Limit;
(function (Limit) {
Limit[Limit["All"] = 0] = "All";
Limit[Limit["Two"] = 1] = "Two";
Limit[Limit["One"] = 2] = "One";
})(Limit || (Limit = {}));
let config;
let rootDocument;
export function finder(input, options) {
if (input.nodeType !== Node.ELEMENT_NODE) {
throw new Error(`Can't generate CSS selector for non-element node type.`);
}
if ("html" === input.tagName.toLowerCase()) {
return "html";
}
const defaults = {
root: document.body,
idName: (name) => true,
className: (name) => true,
tagName: (name) => true,
attr: (name, value) => false,
seedMinLength: 1,
optimizedMinLength: 2,
threshold: 1000,
maxNumberOfTries: 10000,
};
config = Object.assign(Object.assign({}, defaults), options);
rootDocument = findRootDocument(config.root, defaults);
let path = bottomUpSearch(input, Limit.All, () => bottomUpSearch(input, Limit.Two, () => bottomUpSearch(input, Limit.One)));
if (path) {
const optimized = sort(optimize(path, input));
if (optimized.length > 0) {
path = optimized[0];
}
return selector(path);
}
else {
throw new Error(`Selector was not found.`);
}
}
function findRootDocument(rootNode, defaults) {
if (rootNode.nodeType === Node.DOCUMENT_NODE) {
return rootNode;
}
if (rootNode === defaults.root) {
return rootNode.ownerDocument;
}
return rootNode;
}
function bottomUpSearch(input, limit, fallback) {
let path = null;
let stack = [];
let current = input;
let i = 0;
while (current && current !== config.root.parentElement) {
let level = maybe(id(current)) || maybe(...attr(current)) || maybe(...classNames(current)) || maybe(tagName(current)) || [any()];
const nth = index(current);
if (limit === Limit.All) {
if (nth) {
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth)));
}
}
else if (limit === Limit.Two) {
level = level.slice(0, 1);
if (nth) {
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth)));
}
}
else if (limit === Limit.One) {
const [node] = level = level.slice(0, 1);
if (nth && dispensableNth(node)) {
level = [nthChild(node, nth)];
}
}
for (let node of level) {
node.level = i;
}
stack.push(level);
if (stack.length >= config.seedMinLength) {
path = findUniquePath(stack, fallback);
if (path) {
break;
}
}
current = current.parentElement;
i++;
}
if (!path) {
path = findUniquePath(stack, fallback);
}
return path;
}
function findUniquePath(stack, fallback) {
const paths = sort(combinations(stack));
if (paths.length > config.threshold) {
return fallback ? fallback() : null;
}
for (let candidate of paths) {
if (unique(candidate)) {
return candidate;
}
}
return null;
}
function selector(path) {
let node = path[0];
let query = node.name;
for (let i = 1; i < path.length; i++) {
const level = path[i].level || 0;
if (node.level === level - 1) {
query = `${path[i].name} > ${query}`;
}
else {
query = `${path[i].name} ${query}`;
}
node = path[i];
}
return query;
}
function penalty(path) {
return path.map(node => node.penalty).reduce((acc, i) => acc + i, 0);
}
function unique(path) {
switch (rootDocument.querySelectorAll(selector(path)).length) {
case 0:
throw new Error(`Can't select any node with this selector: ${selector(path)}`);
case 1:
return true;
default:
return false;
}
}
function id(input) {
const elementId = input.getAttribute("id");
if (elementId && config.idName(elementId)) {
return {
name: "#" + cssesc(elementId, { isIdentifier: true }),
penalty: 0,
};
}
return null;
}
function attr(input) {
const attrs = Array.from(input.attributes).filter((attr) => config.attr(attr.name, attr.value));
return attrs.map((attr) => ({
name: "[" + cssesc(attr.name, { isIdentifier: true }) + "=\"" + cssesc(attr.value) + "\"]",
penalty: 0.5
}));
}
function classNames(input) {
const names = Array.from(input.classList)
.filter(config.className);
return names.map((name) => ({
name: "." + cssesc(name, { isIdentifier: true }),
penalty: 1
}));
}
function tagName(input) {
const name = input.tagName.toLowerCase();
if (config.tagName(name)) {
return {
name,
penalty: 2
};
}
return null;
}
function any() {
return {
name: "*",
penalty: 3
};
}
function index(input) {
const parent = input.parentNode;
if (!parent) {
return null;
}
let child = parent.firstChild;
if (!child) {
return null;
}
let i = 0;
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) {
i++;
}
if (child === input) {
break;
}
child = child.nextSibling;
}
return i;
}
function nthChild(node, i) {
return {
name: node.name + `:nth-child(${i})`,
penalty: node.penalty + 1
};
}
function dispensableNth(node) {
return node.name !== "html" && !node.name.startsWith("#");
}
function maybe(...level) {
const list = level.filter(notEmpty);
if (list.length > 0) {
return list;
}
return null;
}
function notEmpty(value) {
return value !== null && value !== undefined;
}
function* combinations(stack, path = []) {
if (stack.length > 0) {
for (let node of stack[0]) {
yield* combinations(stack.slice(1, stack.length), path.concat(node));
}
}
else {
yield path;
}
}
function sort(paths) {
return Array.from(paths).sort((a, b) => penalty(a) - penalty(b));
}
function* optimize(path, input, scope = {
counter: 0,
visited: new Map()
}) {
if (path.length > 2 && path.length > config.optimizedMinLength) {
for (let i = 1; i < path.length - 1; i++) {
if (scope.counter > config.maxNumberOfTries) {
return; // Okay At least I tried!
}
scope.counter += 1;
const newPath = [...path];
newPath.splice(i, 1);
const newPathKey = selector(newPath);
if (scope.visited.has(newPathKey)) {
return;
}
if (unique(newPath) && same(newPath, input)) {
yield newPath;
scope.visited.set(newPathKey, true);
yield* optimize(newPath, input, scope);
}
}
}
}
function same(path, input) {
return rootDocument.querySelector(selector(path)) === input;
}
const regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/;
const regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/;
const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;
const defaultOptions = {
"escapeEverything": false,
"isIdentifier": false,
"quotes": "single",
"wrap": false
};
function cssesc(string, opt = {}) {
const options = Object.assign(Object.assign({}, defaultOptions), opt);
if (options.quotes != "single" && options.quotes != "double") {
options.quotes = "single";
}
const quote = options.quotes == "double" ? "\"" : "'";
const isIdentifier = options.isIdentifier;
const firstChar = string.charAt(0);
let output = "";
let counter = 0;
const length = string.length;
while (counter < length) {
const character = string.charAt(counter++);
let codePoint = character.charCodeAt(0);
let value = void 0;
// If its not a printable ASCII character…
if (codePoint < 0x20 || codePoint > 0x7E) {
if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {
// Its a high surrogate, and there is a next character.
const extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) {
// next character is low surrogate
codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
}
else {
// Its an unmatched surrogate; only append this code unit, in case
// the next code unit is the high surrogate of a surrogate pair.
counter--;
}
}
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
}
else {
if (options.escapeEverything) {
if (regexAnySingleEscape.test(character)) {
value = "\\" + character;
}
else {
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
}
}
else if (/[\t\n\f\r\x0B]/.test(character)) {
value = "\\" + codePoint.toString(16).toUpperCase() + " ";
}
else if (character == "\\" || !isIdentifier && (character == "\"" && quote == character || character == "'" && quote == character) || isIdentifier && regexSingleEscape.test(character)) {
value = "\\" + character;
}
else {
value = character;
}
}
output += value;
}
if (isIdentifier) {
if (/^-[-\d]/.test(output)) {
output = "\\-" + output.slice(1);
}
else if (/\d/.test(firstChar)) {
output = "\\3" + firstChar + " " + output.slice(1);
}
}
// Remove spaces after `\HEX` escapes that are not followed by a hex digit,
// since theyre redundant. Note that this is only possible if the escape
// sequence isnt preceded by an odd number of backslashes.
output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {
if ($1 && $1.length % 2) {
// Its not safe to remove the space, so dont.
return $0;
}
// Strip the space.
return ($1 || "") + $2;
});
if (!isIdentifier && options.wrap) {
return quote + output + quote;
}
return output;
}

View file

@ -0,0 +1,414 @@
type Node = {
name: string
penalty: number
level?: number
}
type Path = Node[]
enum Limit {
All,
Two,
One,
}
export type Options = {
root: Element
idName: (name: string) => boolean
className: (name: string) => boolean
tagName: (name: string) => boolean
attr: (name: string, value: string) => boolean
seedMinLength: number
optimizedMinLength: number
threshold: number
maxNumberOfTries: number
}
let config: Options
let rootDocument: Document | Element
export function finder(input: Element, options?: Partial<Options>) {
if (input.nodeType !== Node.ELEMENT_NODE) {
throw new Error(`Can't generate CSS selector for non-element node type.`)
}
if ("html" === input.tagName.toLowerCase()) {
return "html"
}
const defaults: Options = {
root: document.body,
idName: (name: string) => true,
className: (name: string) => true,
tagName: (name: string) => true,
attr: (name: string, value: string) => false,
seedMinLength: 1,
optimizedMinLength: 2,
threshold: 1000,
maxNumberOfTries: 10000,
}
config = {...defaults, ...options}
rootDocument = findRootDocument(config.root, defaults)
let path =
bottomUpSearch(input, Limit.All, () =>
bottomUpSearch(input, Limit.Two, () =>
bottomUpSearch(input, Limit.One)))
if (path) {
const optimized = sort(optimize(path, input))
if (optimized.length > 0) {
path = optimized[0]
}
return selector(path)
} else {
throw new Error(`Selector was not found.`)
}
}
function findRootDocument(rootNode: Element | Document, defaults: Options) {
if (rootNode.nodeType === Node.DOCUMENT_NODE) {
return rootNode
}
if (rootNode === defaults.root) {
return rootNode.ownerDocument as Document
}
return rootNode
}
function bottomUpSearch(input: Element, limit: Limit, fallback?: () => Path | null): Path | null {
let path: Path | null = null
let stack: Node[][] = []
let current: Element | null = input
let i = 0
while (current && current !== config.root.parentElement) {
let level: Node[] = maybe(id(current)) || maybe(...attr(current)) || maybe(...classNames(current)) || maybe(tagName(current)) || [any()]
const nth = index(current)
if (limit === Limit.All) {
if (nth) {
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth)))
}
} else if (limit === Limit.Two) {
level = level.slice(0, 1)
if (nth) {
level = level.concat(level.filter(dispensableNth).map(node => nthChild(node, nth)))
}
} else if (limit === Limit.One) {
const [node] = level = level.slice(0, 1)
if (nth && dispensableNth(node)) {
level = [nthChild(node, nth)]
}
}
for (let node of level) {
node.level = i
}
stack.push(level)
if (stack.length >= config.seedMinLength) {
path = findUniquePath(stack, fallback)
if (path) {
break
}
}
current = current.parentElement
i++
}
if (!path) {
path = findUniquePath(stack, fallback)
}
return path
}
function findUniquePath(stack: Node[][], fallback?: () => Path | null): Path | null {
const paths = sort(combinations(stack))
if (paths.length > config.threshold) {
return fallback ? fallback() : null
}
for (let candidate of paths) {
if (unique(candidate)) {
return candidate
}
}
return null
}
function selector(path: Path): string {
let node = path[0]
let query = node.name
for (let i = 1; i < path.length; i++) {
const level = path[i].level || 0
if (node.level === level - 1) {
query = `${path[i].name} > ${query}`
} else {
query = `${path[i].name} ${query}`
}
node = path[i]
}
return query
}
function penalty(path: Path): number {
return path.map(node => node.penalty).reduce((acc, i) => acc + i, 0)
}
function unique(path: Path) {
switch (rootDocument.querySelectorAll(selector(path)).length) {
case 0:
throw new Error(`Can't select any node with this selector: ${selector(path)}`)
case 1:
return true
default:
return false
}
}
function id(input: Element): Node | null {
const elementId = input.getAttribute("id")
if (elementId && config.idName(elementId)) {
return {
name: "#" + cssesc(elementId, {isIdentifier: true}),
penalty: 0,
}
}
return null
}
function attr(input: Element): Node[] {
const attrs = Array.from(input.attributes).filter((attr) => config.attr(attr.name, attr.value))
return attrs.map((attr): Node => ({
name: "[" + cssesc(attr.name, {isIdentifier: true}) + "=\"" + cssesc(attr.value) + "\"]",
penalty: 0.5
}))
}
function classNames(input: Element): Node[] {
const names = Array.from(input.classList)
.filter(config.className)
return names.map((name): Node => ({
name: "." + cssesc(name, {isIdentifier: true}),
penalty: 1
}))
}
function tagName(input: Element): Node | null {
const name = input.tagName.toLowerCase()
if (config.tagName(name)) {
return {
name,
penalty: 2
}
}
return null
}
function any(): Node {
return {
name: "*",
penalty: 3
}
}
function index(input: Element): number | null {
const parent = input.parentNode
if (!parent) {
return null
}
let child = parent.firstChild
if (!child) {
return null
}
let i = 0
while (child) {
if (child.nodeType === Node.ELEMENT_NODE) {
i++
}
if (child === input) {
break
}
child = child.nextSibling
}
return i
}
function nthChild(node: Node, i: number): Node {
return {
name: node.name + `:nth-child(${i})`,
penalty: node.penalty + 1
}
}
function dispensableNth(node: Node) {
return node.name !== "html" && !node.name.startsWith("#")
}
function maybe(...level: (Node | null)[]): Node[] | null {
const list = level.filter(notEmpty)
if (list.length > 0) {
return list
}
return null
}
function notEmpty<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined
}
function* combinations(stack: Node[][], path: Node[] = []): Generator<Node[]> {
if (stack.length > 0) {
for (let node of stack[0]) {
yield* combinations(stack.slice(1, stack.length), path.concat(node))
}
} else {
yield path
}
}
function sort(paths: Iterable<Path>): Path[] {
return Array.from(paths).sort((a, b) => penalty(a) - penalty(b))
}
type Scope = {
counter: number
visited: Map<string, boolean>
}
function* optimize(path: Path, input: Element, scope: Scope = {
counter: 0,
visited: new Map<string, boolean>()
}): Generator<Node[]> {
if (path.length > 2 && path.length > config.optimizedMinLength) {
for (let i = 1; i < path.length - 1; i++) {
if (scope.counter > config.maxNumberOfTries) {
return // Okay At least I tried!
}
scope.counter += 1
const newPath = [...path]
newPath.splice(i, 1)
const newPathKey = selector(newPath)
if (scope.visited.has(newPathKey)) {
return
}
if (unique(newPath) && same(newPath, input)) {
yield newPath
scope.visited.set(newPathKey, true)
yield* optimize(newPath, input, scope)
}
}
}
}
function same(path: Path, input: Element) {
return rootDocument.querySelector(selector(path)) === input
}
const regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/
const regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/
const regexExcessiveSpaces = /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g
const defaultOptions = {
"escapeEverything": false,
"isIdentifier": false,
"quotes": "single",
"wrap": false
}
function cssesc(string: string, opt: Partial<typeof defaultOptions> = {}) {
const options = {...defaultOptions, ...opt}
if (options.quotes != "single" && options.quotes != "double") {
options.quotes = "single"
}
const quote = options.quotes == "double" ? "\"" : "'"
const isIdentifier = options.isIdentifier
const firstChar = string.charAt(0)
let output = ""
let counter = 0
const length = string.length
while (counter < length) {
const character = string.charAt(counter++)
let codePoint = character.charCodeAt(0)
let value: string | undefined = void 0
// If its not a printable ASCII character…
if (codePoint < 0x20 || codePoint > 0x7E) {
if (codePoint >= 0xD800 && codePoint <= 0xDBFF && counter < length) {
// Its a high surrogate, and there is a next character.
const extra = string.charCodeAt(counter++)
if ((extra & 0xFC00) == 0xDC00) {
// next character is low surrogate
codePoint = ((codePoint & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000
} else {
// Its an unmatched surrogate; only append this code unit, in case
// the next code unit is the high surrogate of a surrogate pair.
counter--
}
}
value = "\\" + codePoint.toString(16).toUpperCase() + " "
} else {
if (options.escapeEverything) {
if (regexAnySingleEscape.test(character)) {
value = "\\" + character
} else {
value = "\\" + codePoint.toString(16).toUpperCase() + " "
}
} else if (/[\t\n\f\r\x0B]/.test(character)) {
value = "\\" + codePoint.toString(16).toUpperCase() + " "
} else if (character == "\\" || !isIdentifier && (character == "\"" && quote == character || character == "'" && quote == character) || isIdentifier && regexSingleEscape.test(character)) {
value = "\\" + character
} else {
value = character
}
}
output += value
}
if (isIdentifier) {
if (/^-[-\d]/.test(output)) {
output = "\\-" + output.slice(1)
} else if (/\d/.test(firstChar)) {
output = "\\3" + firstChar + " " + output.slice(1)
}
}
// Remove spaces after `\HEX` escapes that are not followed by a hex digit,
// since theyre redundant. Note that this is only possible if the escape
// sequence isnt preceded by an odd number of backslashes.
output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {
if ($1 && $1.length % 2) {
// Its not safe to remove the space, so dont.
return $0
}
// Strip the space.
return ($1 || "") + $2
})
if (!isIdentifier && options.wrap) {
return quote + output + quote
}
return output
}

View file

@ -0,0 +1,78 @@
{
"_from": "@medv/finder",
"_id": "@medv/finder@2.0.0",
"_inBundle": false,
"_integrity": "sha512-gV4jOsGpiWNDGd8Dw7tod1Fc9Gc7StaOT4oZ/6srHRWtsHU+HYWzmkYsa3Qy/z0e9tY1WpJ9wWdBFGskfbzoug==",
"_location": "/@medv/finder",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "@medv/finder",
"name": "@medv/finder",
"escapedName": "@medv%2ffinder",
"scope": "@medv",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/@medv/finder/-/finder-2.0.0.tgz",
"_shasum": "699b7141393aa815f120b38f54f92ad212225902",
"_spec": "@medv/finder",
"_where": "/Users/shikhu/work/openreplay/tracker/tracker",
"author": {
"name": "Anton Medvedev",
"email": "anton@medv.io"
},
"ava": {
"require": [
"esm",
"./test/helpers/setup-browser-env.js"
]
},
"bugs": {
"url": "https://github.com/antonmedv/finder/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "CSS Selector Generator",
"devDependencies": {
"ava": "^3.8.2",
"babel-minify": "*",
"browser-env": "^3.3.0",
"esm": "^3.2.25",
"gzip-size-cli": "*",
"release-it": "^13.6.1",
"typescript": "3.9.3"
},
"files": [
"*.ts",
"*.js"
],
"homepage": "https://github.com/antonmedv/finder",
"keywords": [
"css",
"selector",
"generator"
],
"license": "MIT",
"main": "finder.js",
"name": "@medv/finder",
"repository": {
"type": "git",
"url": "git+https://github.com/antonmedv/finder.git"
},
"scripts": {
"prepare": "tsc",
"release": "release-it --access public",
"size": "minify finder.js --sourceType module | gzip-size",
"start": "tsc -w",
"test": "tsc && ava"
},
"types": "finder.d.ts",
"version": "2.0.0"
}