assign-gingerly 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -0
- package/buildCSSQuery.js +126 -0
- package/buildCSSQuery.ts +152 -0
- package/index.js +1 -0
- package/index.ts +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -2180,6 +2180,216 @@ assignGingerly(element, attrs);
|
|
|
2180
2180
|
|
|
2181
2181
|
</details>
|
|
2182
2182
|
|
|
2183
|
+
## Building CSS Queries with `buildCSSQuery`
|
|
2184
|
+
|
|
2185
|
+
The `buildCSSQuery` function generates CSS selector strings that match elements with attributes defined in an enhancement configuration's `withAttrs`. This is particularly useful for libraries like mount-observer that need to find elements that should be enhanced.
|
|
2186
|
+
|
|
2187
|
+
### Basic Usage
|
|
2188
|
+
|
|
2189
|
+
```TypeScript
|
|
2190
|
+
import { buildCSSQuery } from 'assign-gingerly';
|
|
2191
|
+
|
|
2192
|
+
const config = {
|
|
2193
|
+
spawn: MyEnhancement,
|
|
2194
|
+
withAttrs: {
|
|
2195
|
+
base: 'my-component',
|
|
2196
|
+
theme: '${base}-theme'
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
|
|
2200
|
+
const query = buildCSSQuery(config, 'div, span');
|
|
2201
|
+
console.log(query);
|
|
2202
|
+
// 'div[my-component], span[my-component], div[enh-my-component], span[enh-my-component],
|
|
2203
|
+
// div[my-component-theme], span[my-component-theme], div[enh-my-component-theme], span[enh-my-component-theme]'
|
|
2204
|
+
|
|
2205
|
+
// Use with querySelector
|
|
2206
|
+
const elements = document.querySelectorAll(query);
|
|
2207
|
+
```
|
|
2208
|
+
|
|
2209
|
+
**Without selectors (matches any element):**
|
|
2210
|
+
|
|
2211
|
+
```TypeScript
|
|
2212
|
+
const query = buildCSSQuery(config, '');
|
|
2213
|
+
console.log(query);
|
|
2214
|
+
// '[my-component], [enh-my-component], [my-component-theme], [enh-my-component-theme]'
|
|
2215
|
+
|
|
2216
|
+
// Matches any element with these attributes
|
|
2217
|
+
const elements = document.querySelectorAll(query);
|
|
2218
|
+
```
|
|
2219
|
+
|
|
2220
|
+
### How It Works
|
|
2221
|
+
|
|
2222
|
+
`buildCSSQuery` creates a cross-product of:
|
|
2223
|
+
1. **Selectors**: The CSS selectors you provide (e.g., `'div, span'`)
|
|
2224
|
+
2. **Attributes**: All attribute names from `withAttrs` (resolving template variables)
|
|
2225
|
+
3. **Prefixes**: Both unprefixed and `enh-` prefixed versions
|
|
2226
|
+
|
|
2227
|
+
This ensures you find all elements that might be enhanced, regardless of whether they use the `enh-` prefix or not.
|
|
2228
|
+
|
|
2229
|
+
### Template Variable Resolution
|
|
2230
|
+
|
|
2231
|
+
Template variables in `withAttrs` are automatically resolved:
|
|
2232
|
+
|
|
2233
|
+
```TypeScript
|
|
2234
|
+
const config = {
|
|
2235
|
+
spawn: BeABeacon,
|
|
2236
|
+
withAttrs: {
|
|
2237
|
+
base: 'be-a-beacon',
|
|
2238
|
+
theme: '${base}-theme',
|
|
2239
|
+
size: '${base}-size'
|
|
2240
|
+
}
|
|
2241
|
+
};
|
|
2242
|
+
|
|
2243
|
+
buildCSSQuery(config, 'template, script');
|
|
2244
|
+
// Returns selectors for: be-a-beacon, be-a-beacon-theme, be-a-beacon-size
|
|
2245
|
+
// Each with both prefixed and unprefixed versions
|
|
2246
|
+
```
|
|
2247
|
+
|
|
2248
|
+
### Complex Selectors
|
|
2249
|
+
|
|
2250
|
+
The function supports any valid CSS selector:
|
|
2251
|
+
|
|
2252
|
+
```TypeScript
|
|
2253
|
+
const config = {
|
|
2254
|
+
spawn: MyEnhancement,
|
|
2255
|
+
withAttrs: {
|
|
2256
|
+
base: 'data-enhanced'
|
|
2257
|
+
}
|
|
2258
|
+
};
|
|
2259
|
+
|
|
2260
|
+
// Classes and IDs
|
|
2261
|
+
buildCSSQuery(config, 'div.highlight, span#special');
|
|
2262
|
+
// 'div.highlight[data-enhanced], span#special[data-enhanced], ...'
|
|
2263
|
+
|
|
2264
|
+
// Combinators
|
|
2265
|
+
buildCSSQuery(config, 'div > span, ul li');
|
|
2266
|
+
// 'div > span[data-enhanced], ul li[data-enhanced], ...'
|
|
2267
|
+
|
|
2268
|
+
// Pseudo-classes
|
|
2269
|
+
buildCSSQuery(config, 'div:hover, span:first-child');
|
|
2270
|
+
// 'div:hover[data-enhanced], span:first-child[data-enhanced], ...'
|
|
2271
|
+
|
|
2272
|
+
// Attribute selectors
|
|
2273
|
+
buildCSSQuery(config, 'div[existing-attr]');
|
|
2274
|
+
// 'div[existing-attr][data-enhanced], ...'
|
|
2275
|
+
```
|
|
2276
|
+
|
|
2277
|
+
### Underscore-Prefixed Keys Excluded
|
|
2278
|
+
|
|
2279
|
+
Configuration keys starting with `_` are excluded from the query:
|
|
2280
|
+
|
|
2281
|
+
```TypeScript
|
|
2282
|
+
const config = {
|
|
2283
|
+
spawn: MyEnhancement,
|
|
2284
|
+
withAttrs: {
|
|
2285
|
+
base: 'my-attr',
|
|
2286
|
+
_base: {
|
|
2287
|
+
mapsTo: 'something' // Config only, not an attribute
|
|
2288
|
+
},
|
|
2289
|
+
theme: '${base}-theme',
|
|
2290
|
+
_theme: {
|
|
2291
|
+
instanceOf: 'String' // Config only
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
};
|
|
2295
|
+
|
|
2296
|
+
buildCSSQuery(config, 'div');
|
|
2297
|
+
// Only includes: my-attr and my-attr-theme
|
|
2298
|
+
// Does NOT include: _base or _theme
|
|
2299
|
+
```
|
|
2300
|
+
|
|
2301
|
+
### Edge Cases
|
|
2302
|
+
|
|
2303
|
+
**Empty selectors return attribute-only selectors:**
|
|
2304
|
+
```TypeScript
|
|
2305
|
+
const config = {
|
|
2306
|
+
spawn: MyClass,
|
|
2307
|
+
withAttrs: {
|
|
2308
|
+
base: 'my-attr',
|
|
2309
|
+
theme: '${base}-theme'
|
|
2310
|
+
}
|
|
2311
|
+
};
|
|
2312
|
+
|
|
2313
|
+
buildCSSQuery(config, '');
|
|
2314
|
+
// '[my-attr], [enh-my-attr], [my-attr-theme], [enh-my-attr-theme]'
|
|
2315
|
+
// Matches any element with these attributes
|
|
2316
|
+
```
|
|
2317
|
+
|
|
2318
|
+
**Empty withAttrs returns empty string:**
|
|
2319
|
+
```TypeScript
|
|
2320
|
+
buildCSSQuery({ spawn: MyClass }, 'div'); // '' (no withAttrs)
|
|
2321
|
+
buildCSSQuery({ spawn: MyClass, withAttrs: {} }, 'div'); // '' (empty withAttrs)
|
|
2322
|
+
```
|
|
2323
|
+
|
|
2324
|
+
**Deduplication:**
|
|
2325
|
+
```TypeScript
|
|
2326
|
+
buildCSSQuery(config, 'div, div, div');
|
|
2327
|
+
// Duplicates are removed automatically
|
|
2328
|
+
```
|
|
2329
|
+
|
|
2330
|
+
**Whitespace handling:**
|
|
2331
|
+
```TypeScript
|
|
2332
|
+
buildCSSQuery(config, ' div , span , p ');
|
|
2333
|
+
// Whitespace is trimmed automatically
|
|
2334
|
+
```
|
|
2335
|
+
|
|
2336
|
+
### Use Cases
|
|
2337
|
+
|
|
2338
|
+
1. **Mount Observer Integration**: Find elements that need enhancement
|
|
2339
|
+
```TypeScript
|
|
2340
|
+
// Match any element with the attributes
|
|
2341
|
+
const query = buildCSSQuery(enhancementConfig, '');
|
|
2342
|
+
const observer = new MutationObserver(() => {
|
|
2343
|
+
const elements = document.querySelectorAll(query);
|
|
2344
|
+
elements.forEach(el => enhance(el));
|
|
2345
|
+
});
|
|
2346
|
+
```
|
|
2347
|
+
|
|
2348
|
+
2. **Specific Element Types**: Enhance only certain element types
|
|
2349
|
+
```TypeScript
|
|
2350
|
+
const query = buildCSSQuery(config, 'template, script');
|
|
2351
|
+
document.querySelectorAll(query).forEach(el => {
|
|
2352
|
+
const instance = el.enh.get(config);
|
|
2353
|
+
});
|
|
2354
|
+
```
|
|
2355
|
+
|
|
2356
|
+
3. **Conditional Enhancement**: Find elements in specific contexts
|
|
2357
|
+
```TypeScript
|
|
2358
|
+
const query = buildCSSQuery(config, '.container > div');
|
|
2359
|
+
const elements = document.querySelectorAll(query);
|
|
2360
|
+
```
|
|
2361
|
+
|
|
2362
|
+
### API Reference
|
|
2363
|
+
|
|
2364
|
+
```TypeScript
|
|
2365
|
+
function buildCSSQuery(
|
|
2366
|
+
config: EnhancementConfig,
|
|
2367
|
+
selectors: string
|
|
2368
|
+
): string
|
|
2369
|
+
```
|
|
2370
|
+
|
|
2371
|
+
**Parameters:**
|
|
2372
|
+
- `config`: Enhancement configuration with `withAttrs` property
|
|
2373
|
+
- `selectors`: Comma-separated CSS selectors (e.g., `'div, span'`)
|
|
2374
|
+
- If empty string or whitespace only, returns attribute selectors without element prefix
|
|
2375
|
+
- This matches any element with the specified attributes
|
|
2376
|
+
|
|
2377
|
+
**Returns:**
|
|
2378
|
+
- CSS query string with cross-product of selectors and attributes
|
|
2379
|
+
- If selectors is empty: returns attribute-only selectors (e.g., `'[attr], [enh-attr]'`)
|
|
2380
|
+
- If withAttrs is missing or empty: returns empty string
|
|
2381
|
+
|
|
2382
|
+
**Throws:**
|
|
2383
|
+
- Error if template variables have circular references
|
|
2384
|
+
- Error if template variables reference undefined keys
|
|
2385
|
+
|
|
2386
|
+
### Performance Notes
|
|
2387
|
+
|
|
2388
|
+
- The function is synchronous and fast
|
|
2389
|
+
- Resulting queries can be long with many attributes, but CSS engines handle this efficiently
|
|
2390
|
+
- Queries are deduplicated automatically
|
|
2391
|
+
- Consider caching the result if calling repeatedly with the same config
|
|
2392
|
+
|
|
2183
2393
|
<!--
|
|
2184
2394
|
|
|
2185
2395
|
### Complete Example
|
package/buildCSSQuery.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves template variables in a string recursively
|
|
3
|
+
* @param template - Template string with ${var} placeholders
|
|
4
|
+
* @param patterns - The patterns object containing variable values
|
|
5
|
+
* @param resolvedCache - Cache of already resolved values
|
|
6
|
+
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
7
|
+
* @returns Resolved string
|
|
8
|
+
*/
|
|
9
|
+
function resolveTemplate(template, patterns, resolvedCache, visitedKeys = new Set()) {
|
|
10
|
+
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
11
|
+
// Check if already resolved
|
|
12
|
+
if (resolvedCache.has(varName)) {
|
|
13
|
+
return resolvedCache.get(varName);
|
|
14
|
+
}
|
|
15
|
+
// Check for circular reference
|
|
16
|
+
if (visitedKeys.has(varName)) {
|
|
17
|
+
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
18
|
+
}
|
|
19
|
+
const value = patterns[varName];
|
|
20
|
+
if (value === undefined) {
|
|
21
|
+
throw new Error(`Undefined template variable: ${varName}`);
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === 'string') {
|
|
24
|
+
// Recursively resolve
|
|
25
|
+
visitedKeys.add(varName);
|
|
26
|
+
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
27
|
+
visitedKeys.delete(varName);
|
|
28
|
+
resolvedCache.set(varName, resolved);
|
|
29
|
+
return resolved;
|
|
30
|
+
}
|
|
31
|
+
// Non-string value, return as-is
|
|
32
|
+
return String(value);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extracts attribute names from withAttrs configuration
|
|
37
|
+
* Resolves template variables and excludes underscore-prefixed config keys
|
|
38
|
+
* @param withAttrs - The attribute patterns configuration
|
|
39
|
+
* @returns Array of resolved attribute names
|
|
40
|
+
*/
|
|
41
|
+
function extractAttributeNames(withAttrs) {
|
|
42
|
+
const names = [];
|
|
43
|
+
const resolvedCache = new Map();
|
|
44
|
+
// Add base if present
|
|
45
|
+
if ('base' in withAttrs && typeof withAttrs.base === 'string') {
|
|
46
|
+
names.push(withAttrs.base);
|
|
47
|
+
}
|
|
48
|
+
// Add other attributes (skip underscore-prefixed config keys)
|
|
49
|
+
for (const key in withAttrs) {
|
|
50
|
+
if (key === 'base' || key.startsWith('_')) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const value = withAttrs[key];
|
|
54
|
+
if (typeof value === 'string') {
|
|
55
|
+
// Resolve template variables
|
|
56
|
+
const resolved = resolveTemplate(value, withAttrs, resolvedCache);
|
|
57
|
+
names.push(resolved);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return names;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Builds a CSS query selector that matches elements with attributes from withAttrs
|
|
64
|
+
* Creates a cross-product of selectors and attribute names (both prefixed and unprefixed)
|
|
65
|
+
*
|
|
66
|
+
* @param config - Enhancement configuration with withAttrs
|
|
67
|
+
* @param selectors - Comma-separated CSS selectors to match (e.g., 'template, script')
|
|
68
|
+
* If empty, returns just the attribute selectors without element prefix
|
|
69
|
+
* @returns CSS query string with cross-product of selectors and attributes
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* const config = {
|
|
73
|
+
* spawn: MyClass,
|
|
74
|
+
* withAttrs: {
|
|
75
|
+
* base: 'my-attr',
|
|
76
|
+
* theme: '${base}-theme'
|
|
77
|
+
* }
|
|
78
|
+
* };
|
|
79
|
+
*
|
|
80
|
+
* // With selectors
|
|
81
|
+
* buildCSSQuery(config, 'div, span');
|
|
82
|
+
* // Returns: 'div[my-attr], span[my-attr], div[enh-my-attr], span[enh-my-attr],
|
|
83
|
+
* // div[my-attr-theme], span[my-attr-theme], div[enh-my-attr-theme], span[enh-my-attr-theme]'
|
|
84
|
+
*
|
|
85
|
+
* // Without selectors (matches any element)
|
|
86
|
+
* buildCSSQuery(config, '');
|
|
87
|
+
* // Returns: '[my-attr], [enh-my-attr], [my-attr-theme], [enh-my-attr-theme]'
|
|
88
|
+
*/
|
|
89
|
+
export function buildCSSQuery(config, selectors) {
|
|
90
|
+
// Validate inputs
|
|
91
|
+
if (!config.withAttrs) {
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
// Extract and resolve attribute names first
|
|
95
|
+
const attrNames = extractAttributeNames(config.withAttrs);
|
|
96
|
+
if (attrNames.length === 0) {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
// Parse and normalize selectors
|
|
100
|
+
const selectorList = selectors
|
|
101
|
+
? selectors.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
102
|
+
: [];
|
|
103
|
+
// Build queries
|
|
104
|
+
const queries = [];
|
|
105
|
+
if (selectorList.length === 0) {
|
|
106
|
+
// No selectors provided - return just attribute selectors
|
|
107
|
+
for (const attrName of attrNames) {
|
|
108
|
+
queries.push(`[${attrName}]`);
|
|
109
|
+
queries.push(`[enh-${attrName}]`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Build cross-product of selectors × attributes × prefixes
|
|
114
|
+
for (const selector of selectorList) {
|
|
115
|
+
for (const attrName of attrNames) {
|
|
116
|
+
// Unprefixed version
|
|
117
|
+
queries.push(`${selector}[${attrName}]`);
|
|
118
|
+
// enh- prefixed version
|
|
119
|
+
queries.push(`${selector}[enh-${attrName}]`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Deduplicate and join
|
|
124
|
+
const uniqueQueries = [...new Set(queries)];
|
|
125
|
+
return uniqueQueries.join(', ');
|
|
126
|
+
}
|
package/buildCSSQuery.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { EnhancementConfig, AttrPatterns } from './types/assign-gingerly/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolves template variables in a string recursively
|
|
5
|
+
* @param template - Template string with ${var} placeholders
|
|
6
|
+
* @param patterns - The patterns object containing variable values
|
|
7
|
+
* @param resolvedCache - Cache of already resolved values
|
|
8
|
+
* @param visitedKeys - Set of keys being resolved (for cycle detection)
|
|
9
|
+
* @returns Resolved string
|
|
10
|
+
*/
|
|
11
|
+
function resolveTemplate(
|
|
12
|
+
template: string,
|
|
13
|
+
patterns: Record<string, any>,
|
|
14
|
+
resolvedCache: Map<string, string>,
|
|
15
|
+
visitedKeys: Set<string> = new Set()
|
|
16
|
+
): string {
|
|
17
|
+
return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
|
|
18
|
+
// Check if already resolved
|
|
19
|
+
if (resolvedCache.has(varName)) {
|
|
20
|
+
return resolvedCache.get(varName)!;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Check for circular reference
|
|
24
|
+
if (visitedKeys.has(varName)) {
|
|
25
|
+
throw new Error(`Circular reference detected in template variable: ${varName}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const value = patterns[varName];
|
|
29
|
+
|
|
30
|
+
if (value === undefined) {
|
|
31
|
+
throw new Error(`Undefined template variable: ${varName}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
// Recursively resolve
|
|
36
|
+
visitedKeys.add(varName);
|
|
37
|
+
const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
|
|
38
|
+
visitedKeys.delete(varName);
|
|
39
|
+
resolvedCache.set(varName, resolved);
|
|
40
|
+
return resolved;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Non-string value, return as-is
|
|
44
|
+
return String(value);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extracts attribute names from withAttrs configuration
|
|
50
|
+
* Resolves template variables and excludes underscore-prefixed config keys
|
|
51
|
+
* @param withAttrs - The attribute patterns configuration
|
|
52
|
+
* @returns Array of resolved attribute names
|
|
53
|
+
*/
|
|
54
|
+
function extractAttributeNames(withAttrs: AttrPatterns<any>): string[] {
|
|
55
|
+
const names: string[] = [];
|
|
56
|
+
const resolvedCache = new Map<string, string>();
|
|
57
|
+
|
|
58
|
+
// Add base if present
|
|
59
|
+
if ('base' in withAttrs && typeof withAttrs.base === 'string') {
|
|
60
|
+
names.push(withAttrs.base);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Add other attributes (skip underscore-prefixed config keys)
|
|
64
|
+
for (const key in withAttrs) {
|
|
65
|
+
if (key === 'base' || key.startsWith('_')) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const value = withAttrs[key];
|
|
70
|
+
if (typeof value === 'string') {
|
|
71
|
+
// Resolve template variables
|
|
72
|
+
const resolved = resolveTemplate(value, withAttrs, resolvedCache);
|
|
73
|
+
names.push(resolved);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return names;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Builds a CSS query selector that matches elements with attributes from withAttrs
|
|
82
|
+
* Creates a cross-product of selectors and attribute names (both prefixed and unprefixed)
|
|
83
|
+
*
|
|
84
|
+
* @param config - Enhancement configuration with withAttrs
|
|
85
|
+
* @param selectors - Comma-separated CSS selectors to match (e.g., 'template, script')
|
|
86
|
+
* If empty, returns just the attribute selectors without element prefix
|
|
87
|
+
* @returns CSS query string with cross-product of selectors and attributes
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const config = {
|
|
91
|
+
* spawn: MyClass,
|
|
92
|
+
* withAttrs: {
|
|
93
|
+
* base: 'my-attr',
|
|
94
|
+
* theme: '${base}-theme'
|
|
95
|
+
* }
|
|
96
|
+
* };
|
|
97
|
+
*
|
|
98
|
+
* // With selectors
|
|
99
|
+
* buildCSSQuery(config, 'div, span');
|
|
100
|
+
* // Returns: 'div[my-attr], span[my-attr], div[enh-my-attr], span[enh-my-attr],
|
|
101
|
+
* // div[my-attr-theme], span[my-attr-theme], div[enh-my-attr-theme], span[enh-my-attr-theme]'
|
|
102
|
+
*
|
|
103
|
+
* // Without selectors (matches any element)
|
|
104
|
+
* buildCSSQuery(config, '');
|
|
105
|
+
* // Returns: '[my-attr], [enh-my-attr], [my-attr-theme], [enh-my-attr-theme]'
|
|
106
|
+
*/
|
|
107
|
+
export function buildCSSQuery(
|
|
108
|
+
config: EnhancementConfig,
|
|
109
|
+
selectors: string
|
|
110
|
+
): string {
|
|
111
|
+
// Validate inputs
|
|
112
|
+
if (!config.withAttrs) {
|
|
113
|
+
return '';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Extract and resolve attribute names first
|
|
117
|
+
const attrNames = extractAttributeNames(config.withAttrs);
|
|
118
|
+
|
|
119
|
+
if (attrNames.length === 0) {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Parse and normalize selectors
|
|
124
|
+
const selectorList = selectors
|
|
125
|
+
? selectors.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
126
|
+
: [];
|
|
127
|
+
|
|
128
|
+
// Build queries
|
|
129
|
+
const queries: string[] = [];
|
|
130
|
+
|
|
131
|
+
if (selectorList.length === 0) {
|
|
132
|
+
// No selectors provided - return just attribute selectors
|
|
133
|
+
for (const attrName of attrNames) {
|
|
134
|
+
queries.push(`[${attrName}]`);
|
|
135
|
+
queries.push(`[enh-${attrName}]`);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// Build cross-product of selectors × attributes × prefixes
|
|
139
|
+
for (const selector of selectorList) {
|
|
140
|
+
for (const attrName of attrNames) {
|
|
141
|
+
// Unprefixed version
|
|
142
|
+
queries.push(`${selector}[${attrName}]`);
|
|
143
|
+
// enh- prefixed version
|
|
144
|
+
queries.push(`${selector}[enh-${attrName}]`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Deduplicate and join
|
|
150
|
+
const uniqueQueries = [...new Set(queries)];
|
|
151
|
+
return uniqueQueries.join(', ');
|
|
152
|
+
}
|
package/index.js
CHANGED
|
@@ -4,4 +4,5 @@ export { BaseRegistry } from './assignGingerly.js';
|
|
|
4
4
|
export { waitForEvent } from './waitForEvent.js';
|
|
5
5
|
export { ParserRegistry, globalParserRegistry } from './parserRegistry.js';
|
|
6
6
|
export { parseWithAttrs } from './parseWithAttrs.js';
|
|
7
|
+
export { buildCSSQuery } from './buildCSSQuery.js';
|
|
7
8
|
import './object-extension.js';
|
package/index.ts
CHANGED
|
@@ -4,4 +4,5 @@ export {BaseRegistry} from './assignGingerly.js';
|
|
|
4
4
|
export {waitForEvent} from './waitForEvent.js';
|
|
5
5
|
export {ParserRegistry, globalParserRegistry} from './parserRegistry.js';
|
|
6
6
|
export {parseWithAttrs} from './parseWithAttrs.js';
|
|
7
|
+
export {buildCSSQuery} from './buildCSSQuery.js';
|
|
7
8
|
import './object-extension.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assign-gingerly",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "This package provides a utility function for carefully merging one object into another.",
|
|
5
5
|
"homepage": "https://github.com/bahrus/assign-gingerly#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"./parseWithAttrs.js": {
|
|
44
44
|
"default": "./parseWithAttrs.js",
|
|
45
45
|
"types": "./parseWithAttrs.ts"
|
|
46
|
+
},
|
|
47
|
+
"./buildCSSQuery.js": {
|
|
48
|
+
"default": "./buildCSSQuery.js",
|
|
49
|
+
"types": "./buildCSSQuery.ts"
|
|
46
50
|
}
|
|
47
51
|
},
|
|
48
52
|
"main": "index.js",
|