@@ -8,18 +8,57 @@ type Knot = {
88 level ?: number
99}
1010
11+ const acceptedAttrNames = new Set ( [ 'role' , 'name' , 'aria-label' , 'rel' , 'href' ] )
12+
13+ /** Check if attribute name and value are word-like. */
14+ export function attr ( name : string , value : string ) : boolean {
15+ let nameIsOk = acceptedAttrNames . has ( name )
16+ nameIsOk ||= name . startsWith ( 'data-' ) && wordLike ( name )
17+
18+ let valueIsOk = wordLike ( value ) && value . length < 100
19+ valueIsOk ||= value . startsWith ( '#' ) && wordLike ( value . slice ( 1 ) )
20+
21+ return nameIsOk && valueIsOk
22+ }
23+
24+ /** Check if id name is word-like. */
25+ export function idName ( name : string ) : boolean {
26+ return wordLike ( name )
27+ }
28+
29+ /** Check if class name is word-like. */
30+ export function className ( name : string ) : boolean {
31+ return wordLike ( name )
32+ }
33+
34+ /** Check if tag name is word-like. */
35+ export function tagName ( name : string ) : boolean {
36+ return true
37+ }
38+
39+ /** Configuration options for the finder. */
1140export type Options = {
41+ /** The root element to start the search from. */
1242 root : Element
43+ /** Function that determines if an id name may be used in a selector. */
1344 idName : ( name : string ) => boolean
45+ /** Function that determines if a class name may be used in a selector. */
1446 className : ( name : string ) => boolean
47+ /** Function that determines if a tag name may be used in a selector. */
1548 tagName : ( name : string ) => boolean
49+ /** Function that determines if an attribute may be used in a selector. */
1650 attr : ( name : string , value : string ) => boolean
51+ /** Timeout to search for a selector. */
1752 timeoutMs : number
53+ /** Minimum length of levels in fining selector. */
1854 seedMinLength : number
55+ /** Minimum length for optimising selector. */
1956 optimizedMinLength : number
57+ /** Maximum number of path checks. */
2058 maxNumberOfPathChecks : number
2159}
2260
61+ /** Finds unique CSS selectors for the given element. */
2362export function finder ( input : Element , options ?: Partial < Options > ) : string {
2463 if ( input . nodeType !== Node . ELEMENT_NODE ) {
2564 throw new Error ( `Can't generate CSS selector for non-element node type.` )
@@ -29,10 +68,10 @@ export function finder(input: Element, options?: Partial<Options>): string {
2968 }
3069 const defaults : Options = {
3170 root : document . body ,
32- idName : wordLike ,
33- className : wordLike ,
34- tagName : ( name : string ) => true ,
35- attr : useAttr ,
71+ idName : idName ,
72+ className : className ,
73+ tagName : tagName ,
74+ attr : attr ,
3675 timeoutMs : 1000 ,
3776 seedMinLength : 3 ,
3877 optimizedMinLength : 2 ,
@@ -115,7 +154,7 @@ function* search(
115154 }
116155}
117156
118- export function wordLike ( name : string ) : boolean {
157+ function wordLike ( name : string ) : boolean {
119158 if ( / ^ [ a - z 0 - 9 \- ] { 3 , } $ / i. test ( name ) ) {
120159 const words = name . split ( / - | [ A - Z ] / )
121160 for ( const word of words ) {
@@ -131,16 +170,6 @@ export function wordLike(name: string): boolean {
131170 return false
132171}
133172
134- const acceptedAttrNames = new Set ( [ 'role' , 'name' , 'aria-label' , 'rel' , 'href' ] )
135-
136- export function useAttr ( name : string , value : string ) {
137- let nameIsOk = acceptedAttrNames . has ( name )
138- nameIsOk ||= name . startsWith ( 'data-' ) && wordLike ( name )
139- let valueIsOk = wordLike ( value ) && value . length < 100
140- valueIsOk ||= value . startsWith ( '#' ) && wordLike ( value . slice ( 1 ) )
141- return nameIsOk && valueIsOk
142- }
143-
144173function tie ( element : Element , config : Options ) : Knot [ ] {
145174 const level : Knot [ ] = [ ]
146175
0 commit comments