cssgrep 1.2.0 → 1.4.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/man/cssgrep.1 CHANGED
@@ -1,6 +1,6 @@
1
1
  .\" Man page for cssgrep. Keep the OPTIONS section in sync with the USAGE
2
- .\" string in index.js.
3
- .TH CSSGREP 1 "2026-07-02" "cssgrep 1.2.0" "User Commands"
2
+ .\" string in cli.js.
3
+ .TH CSSGREP 1 "2026-07-07" "cssgrep 1.4.0" "User Commands"
4
4
  .SH NAME
5
5
  cssgrep \- search HTML by CSS selector, grep-style
6
6
  .SH SYNOPSIS
@@ -13,6 +13,13 @@ cssgrep \- search HTML by CSS selector, grep-style
13
13
  .B \-r
14
14
  .IR dir ...
15
15
  .br
16
+ .B cssgrep
17
+ .B \-e
18
+ .RI \(aq[ label =] sel \(aq
19
+ .RB [ \-e
20
+ \&...]
21
+ .RI [ file ...]
22
+ .br
16
23
  .B cat
17
24
  .I file.html
18
25
  | \fBcssgrep\fR \fIselector\fR
@@ -39,17 +46,43 @@ skipped with a note on standard error, suppressible with
39
46
  .B \-s
40
47
  or
41
48
  .BR \-q .
42
- One line is printed per match.
43
49
  As with
44
50
  .BR grep ,
45
- the matched line is printed on its own; a
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
46
57
  .RI " file :"
47
- prefix is added when searching multiple files, and the
48
- .IR line : col
49
- locator appears only with
50
- .BR \-n .
58
+ prefix is added when searching multiple files.
51
59
  .SH OPTIONS
52
60
  .TP
61
+ .BR \-e ", " \-\-selector " [\fIlabel\fR=]\fIsel\fR"
62
+ Add a selector (repeatable). Matches from all
63
+ .B \-e
64
+ selectors merge into one document-order stream, each tagged
65
+ .RI [ label ]
66
+ in the output (and as a
67
+ .I label
68
+ field with
69
+ .BR \-\-json ).
70
+ An unlabeled selector is tagged with its own text. A label is an identifier
71
+ before a
72
+ .BR = ;
73
+ a
74
+ .B =
75
+ outside brackets is never valid CSS, so attribute selectors like
76
+ .I [href=x]
77
+ are never mistaken for labels.
78
+ .B \-m
79
+ and
80
+ .B \-M
81
+ cap the merged stream, and print modes apply to every selector alike. With
82
+ .BR \-e ,
83
+ every positional argument is a file path, as with
84
+ .BR "grep \-e" .
85
+ .TP
53
86
  .BR \-r ", " \-\-recursive
54
87
  Recurse into directory arguments (defaults to the current directory if none
55
88
  are given).
@@ -126,7 +159,11 @@ Print the matched node's text content, whitespace collapsed.
126
159
  .TP
127
160
  .B \-\-json
128
161
  Print one JSON object per match (NDJSON) with
129
- .IR file ", " line ", " col ", " html ", " text .
162
+ .IR file ", " line ", " col ", " html ", " text
163
+ (plus
164
+ .I label
165
+ with
166
+ .BR \-e ).
130
167
  .TP
131
168
  .BI \-\-parent " n"
132
169
  Report the
@@ -232,10 +269,94 @@ Globs for
232
269
  support
233
270
  .BR * " (within a path segment), " ** " (across " / ),
234
271
  .BR ? " (one non-slash char), and brace alternation like " *.{html,htm} .
272
+ .SH WATCH MODE
273
+ .TP
274
+ .B \-\-watch
275
+ Keep running and repeat the search whenever a watched file changes. Requires
276
+ file or directory arguments (standard input cannot be watched); cannot be
277
+ combined with
278
+ .B \-q
279
+ or the rewrite operations. Runs until interrupted; Ctrl-C exits with
280
+ status 0, like
281
+ .BR watch (1).
282
+ .TP
283
+ .B \-\-no\-clear
284
+ With
285
+ .B \-\-watch
286
+ on a terminal: append run results instead of clearing the screen.
287
+ .PP
288
+ Output adapts to its destination, like
289
+ .BR \-\-color=auto :
290
+ on a terminal each run clears the screen and reprints; piped output contains
291
+ no escape codes and appends each run after a
292
+ .RI \(dq== " HH:MM:SS changed-file " ==\(dq
293
+ separator; with
294
+ .B \-\-json
295
+ each rerun emits an NDJSON
296
+ .I {"event":"run","changed":...,"matches":n}
297
+ record followed by the match records.
298
+ .PP
299
+ Every rerun repeats the full directory walk, so new files are discovered (and
300
+ deleted ones dropped) under the same
301
+ .BR \-\-include / \-\-ignore / \-\-ext
302
+ rules as a fresh invocation; change bursts are debounced. Watching uses the
303
+ operating system's native file events (no polling), which may be unreliable
304
+ on network or virtual filesystems.
305
+ .SH REWRITING
306
+ Five repeatable, freely combinable operations edit the matched elements
307
+ instead of reporting them:
308
+ .TP
309
+ .BI \-\-add\-class " c"
310
+ Add a class to each matched element (deduplicated; attribute created if
311
+ missing).
312
+ .TP
313
+ .BI \-\-remove\-class " c"
314
+ Remove a class; the attribute is dropped when emptied.
315
+ .TP
316
+ .BI \-\-set\-attr " k=v"
317
+ Set attribute
318
+ .I k
319
+ to
320
+ .IR v ,
321
+ adding it if missing. The value is escaped.
322
+ .TP
323
+ .BI \-\-remove\-attr " k"
324
+ Remove attribute
325
+ .I k
326
+ (all source occurrences).
327
+ .TP
328
+ .BI \-\-rename\-tag " t"
329
+ Rename the element \(em its closing tag too, when one explicitly exists
330
+ (voids like
331
+ .IR <img> ,
332
+ self-closing and implied closes are left without one).
333
+ .TP
334
+ .B \-\-diff
335
+ Emit a unified diff instead of the rewritten document. Required for multiple
336
+ files; apply with
337
+ .BR git-apply (1)
338
+ or
339
+ .BR patch (1).
340
+ .PP
341
+ Only the matched tags' bytes change: matching runs once against the original
342
+ document and edits are byte-splices, so quoting, entities and formatting
343
+ everywhere else pass through untouched. Operations compose in a fixed order
344
+ \(em rename \(-> remove-attr \(-> set-attr \(-> remove-class \(-> add-class
345
+ \(em regardless of argument order.
346
+ .B \-\-parent
347
+ composes with rewriting. A single input prints the rewritten document to
348
+ standard output;
349
+ .B cssgrep
350
+ never writes a file. Input that is not valid UTF-8 is refused (exit 2).
351
+ Rewriting cannot be combined with
352
+ .BR \-n ", " \-p ", " \-\-attr ", " \-\-text ", " \-\-json ", " \-c ", "
353
+ .BR \-l ", " \-L ", " \-q ", " \-A / \-B / \-C ", " \-m / \-M ", " \-w ", "
354
+ .BR \-0 " or " \-e .
355
+ Exit status: 0 if anything was edited, 1 if nothing matched, 2 on error.
235
356
  .SH EXIT STATUS
236
357
  .TP
237
358
  .B 0
238
- One or more matches were found.
359
+ One or more matches were found (or, when rewriting, edits were made).
239
360
  .TP
240
361
  .B 1
241
362
  No matches were found.
@@ -255,6 +376,31 @@ Recurse a tree, skipping vendored files:
255
376
  .TP
256
377
  Extract attribute values for scripting:
257
378
  .B cssgrep \-\-attr href "a.external" page.html
379
+ .TP
380
+ Scrape several labeled fields in one pass:
381
+ .B cssgrep \-e "title=h1" \-e "price=.card .price" \-\-json page.html
382
+ .TP
383
+ Rename every <b> to <strong> across a tree, review, then apply:
384
+ .B cssgrep b \-\-rename\-tag strong \-r src/ \-\-diff | git apply
385
+ .SH INVERTING MATCHES
386
+ There is no
387
+ .B \-v
388
+ flag: CSS expresses inversion in the selector itself via
389
+ .BR :not() " (complex selectors and selector lists included) and " :has() ,
390
+ scoped to the elements of interest \(em a flag would have to invent its own
391
+ answer to \(lqwhich non-matching elements?\(rq.
392
+ .TP
393
+ Images missing alt text:
394
+ .B cssgrep \(dqimg:not([alt])\(dq \-rn .
395
+ .TP
396
+ Cards containing no link:
397
+ .B cssgrep \(dqdiv.card:not(:has(a))\(dq page.html
398
+ .TP
399
+ Links outside nav and footer:
400
+ .B cssgrep \(dqa:not(nav a, footer a)\(dq page.html
401
+ .PP
402
+ For whole files without any match, use
403
+ .BR \-L .
258
404
  .SH VIM INTEGRATION
259
405
  Wire
260
406
  .B cssgrep
package/package.json CHANGED
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "cssgrep",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Search HTML by CSS selector and print matches grep-style (file:line:col with -n).",
5
- "main": "index.js",
5
+ "main": "lib.js",
6
+ "types": "index.d.ts",
6
7
  "bin": {
7
- "cssgrep": "index.js"
8
+ "cssgrep": "cli.js"
8
9
  },
9
10
  "man": "./man/cssgrep.1",
10
11
  "files": [
11
- "index.js",
12
+ "lib.js",
13
+ "cli.js",
14
+ "index.d.ts",
12
15
  "README.md",
13
16
  "CHANGELOG.md",
14
17
  "LICENSE",
@@ -28,13 +31,14 @@
28
31
  "homepage": "https://github.com/msbatarce/cssgrep#readme",
29
32
  "scripts": {
30
33
  "test": "node test.js",
31
- "build:linux-x64": "bun build ./index.js --compile --target=bun-linux-x64 --outfile dist/cssgrep-linux-x64",
32
- "build:linux-arm64": "bun build ./index.js --compile --target=bun-linux-arm64 --outfile dist/cssgrep-linux-arm64",
33
- "build:darwin-x64": "bun build ./index.js --compile --target=bun-darwin-x64 --outfile dist/cssgrep-darwin-x64",
34
- "build:darwin-arm64": "bun build ./index.js --compile --target=bun-darwin-arm64 --outfile dist/cssgrep-darwin-arm64",
35
- "build:windows-x64": "bun build ./index.js --compile --target=bun-windows-x64 --outfile dist/cssgrep-windows-x64.exe",
34
+ "build:linux-x64": "bun build ./cli.js --compile --target=bun-linux-x64 --outfile dist/cssgrep-linux-x64",
35
+ "build:linux-arm64": "bun build ./cli.js --compile --target=bun-linux-arm64 --outfile dist/cssgrep-linux-arm64",
36
+ "build:darwin-x64": "bun build ./cli.js --compile --target=bun-darwin-x64 --outfile dist/cssgrep-darwin-x64",
37
+ "build:darwin-arm64": "bun build ./cli.js --compile --target=bun-darwin-arm64 --outfile dist/cssgrep-darwin-arm64",
38
+ "build:windows-x64": "bun build ./cli.js --compile --target=bun-windows-x64 --outfile dist/cssgrep-windows-x64.exe",
36
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",
37
- "build:sea": "node scripts/build-sea.js"
40
+ "build:sea": "node scripts/build-sea.js",
41
+ "bench": "node bench/bench.js"
38
42
  },
39
43
  "keywords": [
40
44
  "css",