cssgrep 1.3.0 → 1.5.0
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/CHANGELOG.md +28 -1
- package/README.md +7 -6
- package/cli.js +62 -16
- package/lib.js +23 -7
- package/man/cssgrep.1 +11 -8
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.5.0] - 2026-07-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `--json` records now include `attribs`, the matched element's attribute
|
|
14
|
+
object (names lowercased by the parser) — the same field the library's
|
|
15
|
+
`Match` exposes, so scraping pipelines get attribute data without a second
|
|
16
|
+
pass (e.g. `jq -r .attribs.href`).
|
|
17
|
+
|
|
18
|
+
## [1.4.0] - 2026-07-07
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Large result sets no longer crash with `RangeError: Invalid string length`
|
|
22
|
+
(exit 1, zero output): output is written in byte-bounded chunks instead of
|
|
23
|
+
one giant join. Observed with 40k matches on an 8 MB minified line.
|
|
24
|
+
- Emitting many matches on one physical line was quadratic (two O(line)
|
|
25
|
+
scans per match); the same 40k-locator case dropped from 9.4 s to 0.4 s.
|
|
26
|
+
Found by the new `npm run bench` harness.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- grep parity: without `-n`, a matching line now prints once, however many
|
|
30
|
+
matches sit on it. With `-n` there is still one record per match, each
|
|
31
|
+
with its own `line:col` locator.
|
|
32
|
+
- Startup no longer loads the pretty-printing dependencies unless `-p` is
|
|
33
|
+
used (43.4 → 37.9 ms measured), and zero-match files skip the line index.
|
|
34
|
+
|
|
10
35
|
## [1.3.0] - 2026-07-07
|
|
11
36
|
|
|
12
37
|
### Added
|
|
@@ -108,7 +133,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
108
133
|
- `-0`/`--null`, `--color`, `-w`/`--max-width`, `-V`/`--version`.
|
|
109
134
|
- Standalone binaries (Bun, Node SEA), shell completions, and a man page.
|
|
110
135
|
|
|
111
|
-
[Unreleased]: https://github.com/msbatarce/cssgrep/compare/v1.
|
|
136
|
+
[Unreleased]: https://github.com/msbatarce/cssgrep/compare/v1.5.0...HEAD
|
|
137
|
+
[1.5.0]: https://github.com/msbatarce/cssgrep/compare/v1.4.0...v1.5.0
|
|
138
|
+
[1.4.0]: https://github.com/msbatarce/cssgrep/compare/v1.3.0...v1.4.0
|
|
112
139
|
[1.3.0]: https://github.com/msbatarce/cssgrep/compare/v1.2.0...v1.3.0
|
|
113
140
|
[1.2.0]: https://github.com/msbatarce/cssgrep/compare/v1.1.0...v1.2.0
|
|
114
141
|
[1.1.0]: https://github.com/msbatarce/cssgrep/compare/v1.0.0...v1.1.0
|
package/README.md
CHANGED
|
@@ -61,9 +61,10 @@ cssgrep <selector> -r <dir ...> # recurse directories
|
|
|
61
61
|
cat page.html | cssgrep <selector> # read from stdin
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
locator
|
|
64
|
+
Like `grep`, each matching line is printed on its own, once — however many
|
|
65
|
+
matches sit on it. With `-n` there is one record *per match*, each with its
|
|
66
|
+
own `line:col` locator (that per-match precision is the point of the tool). A
|
|
67
|
+
`file:` prefix is added when searching multiple files:
|
|
67
68
|
|
|
68
69
|
```
|
|
69
70
|
{line contents} # default; stdin or single file
|
|
@@ -89,7 +90,7 @@ locator appears only with `-n`:
|
|
|
89
90
|
| `-p`, `--print` | Pretty-print the matched node's HTML, re-indented from scratch (works on minified input). No `line:col` locator is shown. |
|
|
90
91
|
| `--attr <name>` | Print the value of attribute `<name>` for each match (nodes without it are skipped; if every match is skipped the exit status is 1). The name is matched case-insensitively. Honors `-n` and `-w`. |
|
|
91
92
|
| `--text` | Print the matched node's text content, whitespace collapsed. Honors `-n` and `-w`. |
|
|
92
|
-
| `--json` | Print one JSON object per match (NDJSON), with `file`, `line`, `col`, `html`, `text` — plus `label` when `-e` is used. |
|
|
93
|
+
| `--json` | Print one JSON object per match (NDJSON), with `file`, `line`, `col`, `attribs` (the element's attributes, names lowercased), `html`, `text` — plus `label` when `-e` is used. |
|
|
93
94
|
| `--parent <n>` | Report the `n`-th element ancestor of each match instead of the match itself (de-duplicated). Pairs well with `-p`. |
|
|
94
95
|
| `-w`, `--max-width <n>` | Truncate the shown line to `n` columns (adds `…`). Value attaches or follows: `-w100`, `-w 100`, `--max-width=100`. |
|
|
95
96
|
| `-A`, `--after-context <n>` | Print `n` source lines after each match. |
|
|
@@ -186,8 +187,8 @@ $ cssgrep -n -e 'title=h1' -e 'price=.card .price' page.html
|
|
|
186
187
|
9:12 [price] <span class="price">$4.99</span>
|
|
187
188
|
|
|
188
189
|
$ cssgrep --json -e 'title=h1' -e 'price=.card .price' page.html
|
|
189
|
-
{"file":"page.html","line":3,"col":5,"label":"title","html":"<h1>Widget</h1>","text":"Widget"}
|
|
190
|
-
{"file":"page.html","line":9,"col":12,"label":"price","html":"...","text":"$4.99"}
|
|
190
|
+
{"file":"page.html","line":3,"col":5,"label":"title","attribs":{},"html":"<h1>Widget</h1>","text":"Widget"}
|
|
191
|
+
{"file":"page.html","line":9,"col":12,"label":"price","attribs":{"class":"price"},"html":"...","text":"$4.99"}
|
|
191
192
|
```
|
|
192
193
|
|
|
193
194
|
The label is anything matching `[A-Za-z_][A-Za-z0-9_-]*` before a `=`; since a
|
package/cli.js
CHANGED
|
@@ -3,17 +3,27 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const render = require('dom-serializer').default;
|
|
7
|
-
const { html: beautify } = require('js-beautify');
|
|
8
6
|
const {
|
|
9
7
|
parse, offsetToPosition, lineTextAt, textOf, collapseWs, ancestor,
|
|
10
8
|
} = require('./lib.js');
|
|
11
9
|
|
|
10
|
+
// dom-serializer + js-beautify are only needed by -p, and cost ~13 ms of a
|
|
11
|
+
// ~43 ms startup (measured, ROADMAP Phase 11) — loaded on first use so the
|
|
12
|
+
// grepprg-style hot path never pays for them.
|
|
13
|
+
let render = null;
|
|
14
|
+
let beautify = null;
|
|
15
|
+
function loadPrettyPrinter() {
|
|
16
|
+
if (!render) {
|
|
17
|
+
render = require('dom-serializer').default;
|
|
18
|
+
beautify = require('js-beautify').html;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
// Single source of truth for the version. A constant rather than a read of
|
|
13
23
|
// package.json, so it survives compilation into a standalone binary (Bun
|
|
14
24
|
// --compile / Node SEA), where package.json won't sit next to the executable.
|
|
15
25
|
// Keep in sync with package.json on release.
|
|
16
|
-
const VERSION = '1.
|
|
26
|
+
const VERSION = '1.5.0';
|
|
17
27
|
|
|
18
28
|
const USAGE = `cssgrep - search HTML by CSS selector, grep-style.
|
|
19
29
|
|
|
@@ -23,7 +33,7 @@ Usage:
|
|
|
23
33
|
cssgrep -e '[label=]<sel>' [-e ...] [file ...]
|
|
24
34
|
cat file.html | cssgrep <selector>
|
|
25
35
|
|
|
26
|
-
Output (
|
|
36
|
+
Output (each matching line once, like grep; with -n, one record per match):
|
|
27
37
|
{line contents} (default; stdin or single file)
|
|
28
38
|
{file}:{line contents} (default; multiple files)
|
|
29
39
|
{line}:{col} {line contents} (with -n; stdin or single file)
|
|
@@ -48,7 +58,7 @@ Options:
|
|
|
48
58
|
--attr <name> Print the value of attribute <name> (skips nodes without it).
|
|
49
59
|
--text Print the matched node's text content (whitespace collapsed).
|
|
50
60
|
--json Print one JSON record per match (NDJSON: file,line,col,
|
|
51
|
-
html,text; plus label with -e).
|
|
61
|
+
attribs,html,text; plus label with -e).
|
|
52
62
|
--parent <n> Report the n-th ancestor of each match instead (dedup'd).
|
|
53
63
|
-w, --max-width <n> Truncate the shown line to <n> columns (ellipsis added).
|
|
54
64
|
-A, --after-context <n> Print <n> source lines after each match.
|
|
@@ -452,6 +462,7 @@ const HL_END = '';
|
|
|
452
462
|
// highlight) is given and coloring is on, those nodes are wrapped in the match
|
|
453
463
|
// color within the printed block.
|
|
454
464
|
function prettyPrint(el, origins, opts) {
|
|
465
|
+
loadPrettyPrinter();
|
|
455
466
|
const highlight = origins && origins.length && opts && opts.colorOn;
|
|
456
467
|
const inserted = [];
|
|
457
468
|
if (highlight) {
|
|
@@ -529,9 +540,10 @@ function emitContext(src, starts, name, showLabel, targets, opts, out) {
|
|
|
529
540
|
// Map each match line to a representative node span (the first match on it),
|
|
530
541
|
// which drives the in-line highlight — and the [label] tag — when emitting.
|
|
531
542
|
const info = new Map();
|
|
543
|
+
const posState = {};
|
|
532
544
|
for (const { el, label: selLabel } of targets) {
|
|
533
545
|
const off = el.startIndex == null ? 0 : el.startIndex;
|
|
534
|
-
const pos = offsetToPosition(starts, src, off);
|
|
546
|
+
const pos = offsetToPosition(starts, src, off, posState);
|
|
535
547
|
if (info.has(pos.line)) continue;
|
|
536
548
|
const nodeEnd = (el.endIndex == null ? off : el.endIndex) + 1;
|
|
537
549
|
info.set(pos.line, { off, nodeEnd, pos, selLabel });
|
|
@@ -615,6 +627,9 @@ function searchSource(src, name, showLabel, opts, out, limit = Infinity) {
|
|
|
615
627
|
out.push(label ? `${label}${fileSep}${limited.length}` : String(limited.length));
|
|
616
628
|
return limited.length;
|
|
617
629
|
}
|
|
630
|
+
// Nothing to emit: return before building the line index — a zero-match
|
|
631
|
+
// pass over a large file shouldn't pay a full line scan for nothing.
|
|
632
|
+
if (limited.length === 0) return 0;
|
|
618
633
|
const starts = opts.print ? null : doc.lineStarts();
|
|
619
634
|
// --parent re-targets matches to ancestors (no-op without it). Dedup is per
|
|
620
635
|
// (ancestor, label), so two -e selectors sharing a container still report it
|
|
@@ -653,19 +668,22 @@ function searchSource(src, name, showLabel, opts, out, limit = Infinity) {
|
|
|
653
668
|
// matches lacking the attribute, and --parent dedup can merge several
|
|
654
669
|
// matches into one printed ancestor.
|
|
655
670
|
if (opts.json) {
|
|
656
|
-
// NDJSON: one self-contained record per match. `
|
|
657
|
-
//
|
|
658
|
-
//
|
|
659
|
-
//
|
|
671
|
+
// NDJSON: one self-contained record per match. `attribs` mirrors the lib
|
|
672
|
+
// Match's field (names lowercased by the parser) so scraping never needs
|
|
673
|
+
// a second pass; `html` is the exact source slice; newlines are escaped
|
|
674
|
+
// by JSON.stringify, so each record stays on one line. Ignores --color
|
|
675
|
+
// and -n (line/col are always present). `label` appears only with -e.
|
|
676
|
+
const posState = {};
|
|
660
677
|
for (const { el, label: selLabel } of targets) {
|
|
661
678
|
const off = el.startIndex == null ? 0 : el.startIndex;
|
|
662
|
-
const pos = offsetToPosition(starts, src, off);
|
|
679
|
+
const pos = offsetToPosition(starts, src, off, posState);
|
|
663
680
|
const nodeEnd = (el.endIndex == null ? off : el.endIndex) + 1;
|
|
664
681
|
out.push(JSON.stringify({
|
|
665
682
|
file: name,
|
|
666
683
|
line: pos.line,
|
|
667
684
|
col: pos.bcol,
|
|
668
685
|
...(selLabel !== null && { label: selLabel }),
|
|
686
|
+
attribs: el.attribs || {},
|
|
669
687
|
html: src.slice(off, nodeEnd),
|
|
670
688
|
text: collapseWs(textOf(el)),
|
|
671
689
|
}));
|
|
@@ -673,6 +691,12 @@ function searchSource(src, name, showLabel, opts, out, limit = Infinity) {
|
|
|
673
691
|
return targets.length;
|
|
674
692
|
}
|
|
675
693
|
let emitted = 0;
|
|
694
|
+
// grep parity: without -n, a physical line prints once no matter how many
|
|
695
|
+
// matches sit on it (grep never repeats a line). With -n each match keeps
|
|
696
|
+
// its own line:col record — that per-match locator is the tool's point.
|
|
697
|
+
// Extraction modes print per-match values, so they never dedup.
|
|
698
|
+
const seenLines = (opts.lineNumber || opts.attr != null || opts.text) ? null : new Set();
|
|
699
|
+
const posState = {};
|
|
676
700
|
for (const { el, label: selLabel } of targets) {
|
|
677
701
|
const c = opts.colorOn ? paint : (_, s) => s;
|
|
678
702
|
// The [label] tag from -e; a null label (positional selector) prints none.
|
|
@@ -687,7 +711,7 @@ function searchSource(src, name, showLabel, opts, out, limit = Infinity) {
|
|
|
687
711
|
continue;
|
|
688
712
|
}
|
|
689
713
|
const off = el.startIndex == null ? 0 : el.startIndex;
|
|
690
|
-
const pos = offsetToPosition(starts, src, off);
|
|
714
|
+
const pos = offsetToPosition(starts, src, off, posState);
|
|
691
715
|
|
|
692
716
|
// Choose the content printed for this match. --attr/--text replace the
|
|
693
717
|
// source line with the extracted value (whole value highlighted as the
|
|
@@ -699,6 +723,10 @@ function searchSource(src, name, showLabel, opts, out, limit = Infinity) {
|
|
|
699
723
|
} else if (opts.text) {
|
|
700
724
|
text = c(COLORS.match, truncate(collapseWs(textOf(el)), opts.maxWidth));
|
|
701
725
|
} else {
|
|
726
|
+
if (seenLines) {
|
|
727
|
+
if (seenLines.has(pos.line)) continue; // this line already printed
|
|
728
|
+
seenLines.add(pos.line);
|
|
729
|
+
}
|
|
702
730
|
const nodeEnd = (el.endIndex == null ? off : el.endIndex) + 1; // exclusive
|
|
703
731
|
text = renderText(pos, off, nodeEnd, opts);
|
|
704
732
|
}
|
|
@@ -1225,11 +1253,29 @@ function main() {
|
|
|
1225
1253
|
// For -l/-L, -0 NUL-terminates each file name (no newline) so the list is
|
|
1226
1254
|
// safe for `xargs -0`. Other modes keep newline-separated records (with -0
|
|
1227
1255
|
// the NUL appears only as the in-record file-name separator).
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1256
|
+
//
|
|
1257
|
+
// Written in byte-bounded chunks, never as one join of everything: with
|
|
1258
|
+
// enough (or long enough) result lines a single joined string exceeds
|
|
1259
|
+
// V8's maximum string length and crashes — observed with 40k matches on
|
|
1260
|
+
// an 8 MB minified line. Writes queue; process.exitCode (not exit())
|
|
1261
|
+
// keeps them flush-safe.
|
|
1262
|
+
const nulList = opts.nul && (opts.filesWithMatches || opts.filesWithoutMatch);
|
|
1263
|
+
const CHUNK_BYTES = 32 << 20; // ~32 MB per write
|
|
1264
|
+
let batch = [];
|
|
1265
|
+
let bytes = 0;
|
|
1266
|
+
const flush = () => {
|
|
1267
|
+
if (!batch.length) return;
|
|
1268
|
+
process.stdout.write(nulList ? batch.map(s => s + '\0').join('')
|
|
1269
|
+
: batch.join('\n') + '\n');
|
|
1270
|
+
batch = [];
|
|
1271
|
+
bytes = 0;
|
|
1272
|
+
};
|
|
1273
|
+
for (const line of out) {
|
|
1274
|
+
batch.push(line);
|
|
1275
|
+
bytes += line.length + 1;
|
|
1276
|
+
if (bytes >= CHUNK_BYTES) flush();
|
|
1232
1277
|
}
|
|
1278
|
+
flush();
|
|
1233
1279
|
}
|
|
1234
1280
|
// Normally success means "a match was found". With -L it means "a file
|
|
1235
1281
|
// without a match was printed", which is decoupled from the match total.
|
package/lib.js
CHANGED
|
@@ -26,7 +26,7 @@ function lineIndex(src) {
|
|
|
26
26
|
return starts;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function offsetToPosition(starts, src, offset) {
|
|
29
|
+
function offsetToPosition(starts, src, offset, state) {
|
|
30
30
|
// binary search for the greatest line start <= offset
|
|
31
31
|
let lo = 0, hi = starts.length - 1;
|
|
32
32
|
while (lo < hi) {
|
|
@@ -35,11 +35,27 @@ function offsetToPosition(starts, src, offset) {
|
|
|
35
35
|
else hi = mid - 1;
|
|
36
36
|
}
|
|
37
37
|
const lineStart = starts[lo];
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// The next line's start bounds this line (O(1)); an indexOf scan here is
|
|
39
|
+
// O(line length) *per match* — quadratic on minified single-line files.
|
|
40
|
+
const lineEnd = lo + 1 < starts.length ? starts[lo + 1] - 1 : src.length;
|
|
40
41
|
// strip a trailing \r so CRLF files render cleanly
|
|
41
42
|
let text = src.slice(lineStart, lineEnd);
|
|
42
43
|
if (text.endsWith('\r')) text = text.slice(0, -1);
|
|
44
|
+
// bcol counts bytes from the line start — also quadratic if recomputed per
|
|
45
|
+
// match. `state` (one mutable object per source, supplied by the caller)
|
|
46
|
+
// remembers the previous match's byte count, so in-order matches on the
|
|
47
|
+
// same line pay only the delta; out-of-order falls back to a full count.
|
|
48
|
+
let bytes;
|
|
49
|
+
if (state && state.lineStart === lineStart && offset >= state.offset) {
|
|
50
|
+
bytes = state.bytes + Buffer.byteLength(src.slice(state.offset, offset), 'utf8');
|
|
51
|
+
} else {
|
|
52
|
+
bytes = Buffer.byteLength(src.slice(lineStart, offset), 'utf8');
|
|
53
|
+
}
|
|
54
|
+
if (state) {
|
|
55
|
+
state.lineStart = lineStart;
|
|
56
|
+
state.offset = offset;
|
|
57
|
+
state.bytes = bytes;
|
|
58
|
+
}
|
|
43
59
|
return {
|
|
44
60
|
line: lo + 1, // 1-based
|
|
45
61
|
// col in UTF-16 code units: a JS string index into `text`, used by the
|
|
@@ -47,7 +63,7 @@ function offsetToPosition(starts, src, offset) {
|
|
|
47
63
|
// and terminals count bytes, so non-ASCII text before the match would
|
|
48
64
|
// otherwise land the cursor short.
|
|
49
65
|
col: offset - lineStart + 1, // 1-based
|
|
50
|
-
bcol:
|
|
66
|
+
bcol: bytes + 1, // 1-based
|
|
51
67
|
text,
|
|
52
68
|
};
|
|
53
69
|
}
|
|
@@ -56,8 +72,7 @@ function offsetToPosition(starts, src, offset) {
|
|
|
56
72
|
// line's byte start (so a node's offset maps to a column within it).
|
|
57
73
|
function lineTextAt(starts, src, lineNo) {
|
|
58
74
|
const lineStart = starts[lineNo - 1];
|
|
59
|
-
|
|
60
|
-
if (lineEnd === -1) lineEnd = src.length;
|
|
75
|
+
const lineEnd = lineNo < starts.length ? starts[lineNo] - 1 : src.length;
|
|
61
76
|
let text = src.slice(lineStart, lineEnd);
|
|
62
77
|
if (text.endsWith('\r')) text = text.slice(0, -1);
|
|
63
78
|
return { lineStart, text };
|
|
@@ -296,7 +311,8 @@ function parse(html) {
|
|
|
296
311
|
});
|
|
297
312
|
let starts = null;
|
|
298
313
|
const lineStarts = () => starts || (starts = lineIndex(html));
|
|
299
|
-
const
|
|
314
|
+
const posState = {}; // incremental byte-column cache, scoped to this doc
|
|
315
|
+
const position = offset => offsetToPosition(lineStarts(), html, offset, posState);
|
|
300
316
|
return {
|
|
301
317
|
html,
|
|
302
318
|
// Internal (unstable): the raw htmlparser2 document and the cached
|
package/man/cssgrep.1
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.\" Man page for cssgrep. Keep the OPTIONS section in sync with the USAGE
|
|
2
2
|
.\" string in cli.js.
|
|
3
|
-
.TH CSSGREP 1 "2026-07-
|
|
3
|
+
.TH CSSGREP 1 "2026-07-12" "cssgrep 1.5.0" "User Commands"
|
|
4
4
|
.SH NAME
|
|
5
5
|
cssgrep \- search HTML by CSS selector, grep-style
|
|
6
6
|
.SH SYNOPSIS
|
|
@@ -46,15 +46,16 @@ skipped with a note on standard error, suppressible with
|
|
|
46
46
|
.B \-s
|
|
47
47
|
or
|
|
48
48
|
.BR \-q .
|
|
49
|
-
One line is printed per match.
|
|
50
49
|
As with
|
|
51
50
|
.BR grep ,
|
|
52
|
-
|
|
51
|
+
each matching line is printed on its own, once \(em however many matches sit
|
|
52
|
+
on it. With
|
|
53
|
+
.B \-n
|
|
54
|
+
there is one record per
|
|
55
|
+
.IR match ,
|
|
56
|
+
each with its own locator. A
|
|
53
57
|
.RI " file :"
|
|
54
|
-
prefix is added when searching multiple files
|
|
55
|
-
.IR line : col
|
|
56
|
-
locator appears only with
|
|
57
|
-
.BR \-n .
|
|
58
|
+
prefix is added when searching multiple files.
|
|
58
59
|
.SH OPTIONS
|
|
59
60
|
.TP
|
|
60
61
|
.BR \-e ", " \-\-selector " [\fIlabel\fR=]\fIsel\fR"
|
|
@@ -158,11 +159,13 @@ Print the matched node's text content, whitespace collapsed.
|
|
|
158
159
|
.TP
|
|
159
160
|
.B \-\-json
|
|
160
161
|
Print one JSON object per match (NDJSON) with
|
|
161
|
-
.IR file ", " line ", " col ", " html ", " text
|
|
162
|
+
.IR file ", " line ", " col ", " attribs ", " html ", " text
|
|
162
163
|
(plus
|
|
163
164
|
.I label
|
|
164
165
|
with
|
|
165
166
|
.BR \-e ).
|
|
167
|
+
.I attribs
|
|
168
|
+
is the element's attribute object, names lowercased by the parser.
|
|
166
169
|
.TP
|
|
167
170
|
.BI \-\-parent " n"
|
|
168
171
|
Report the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cssgrep",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Search HTML by CSS selector and print matches grep-style (file:line:col with -n).",
|
|
5
5
|
"main": "lib.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"build:darwin-arm64": "bun build ./cli.js --compile --target=bun-darwin-arm64 --outfile dist/cssgrep-darwin-arm64",
|
|
38
38
|
"build:windows-x64": "bun build ./cli.js --compile --target=bun-windows-x64 --outfile dist/cssgrep-windows-x64.exe",
|
|
39
39
|
"build:binaries": "npm run build:linux-x64 && npm run build:linux-arm64 && npm run build:darwin-x64 && npm run build:darwin-arm64 && npm run build:windows-x64",
|
|
40
|
-
"build:sea": "node scripts/build-sea.js"
|
|
40
|
+
"build:sea": "node scripts/build-sea.js",
|
|
41
|
+
"bench": "node bench/bench.js"
|
|
41
42
|
},
|
|
42
43
|
"keywords": [
|
|
43
44
|
"css",
|