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