@zgeoff/format-codemod 0.0.3 → 0.0.5

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 CHANGED
@@ -50,10 +50,11 @@ no "never" rules.
50
50
  ```bash
51
51
  format-codemod [options] <file|dir|glob> ...
52
52
 
53
- --check exit 1 if any file would change; do not write
54
- --dry print unified diff to stdout; do not write
55
- --bench print parse stats as JSON to stderr
56
- --quiet only print files that would change
53
+ --check exit 1 if any file would change; do not write
54
+ --dry print unified diff to stdout; do not write
55
+ --bench print parse stats as JSON to stderr
56
+ --quiet only print files that would change
57
+ --ignore <glob> skip files matching the glob (repeatable)
57
58
  --version
58
59
  --help
59
60
  ```
@@ -79,6 +80,24 @@ file is processed once.
79
80
  `--dry` prints a real unified diff — `@@` hunk headers with three lines of context — that applies
80
81
  cleanly with `patch(1)`.
81
82
 
83
+ ### Ignoring files
84
+
85
+ A `.formatignore` file in the working directory supplies ignore globs without bloating the command
86
+ line — one per line, with blank lines and `#`-prefixed comment lines skipped:
87
+
88
+ ```gitignore
89
+ # committed codegen
90
+ src/generated/**
91
+
92
+ # build output
93
+ dist/**
94
+ ```
95
+
96
+ Each line uses the same glob semantics as `--ignore` (Node's `path.matchesGlob`, tried against both
97
+ the expanded path and the path relative to the working directory). It is not a gitignore dialect:
98
+ `!` negation and directory-anchoring rules are not supported. `--ignore` flags remain available and
99
+ merge additively with the file's globs.
100
+
82
101
  ## Dialect handling
83
102
 
84
103
  The filename picks the parse dialect: `.tsx` enables JSX, everything else parses as plain
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads `.formatignore` from the given directory: one glob per line, blank
3
+ * lines and `#`-prefixed comment lines skipped. Lines share --ignore's glob
4
+ * semantics — this is not a gitignore dialect, so `!` negation and directory
5
+ * anchoring don't apply. A missing file yields an empty list, so callers
6
+ * merge unconditionally.
7
+ */
8
+ export declare function loadFormatIgnore(dir: string): readonly string[];
package/dist/cli.js CHANGED
@@ -49,11 +49,23 @@ function isIgnored(file, patterns) {
49
49
  return patterns.some((pattern) => path.matchesGlob(file, pattern) || path.matchesGlob(relative, pattern));
50
50
  }
51
51
 
52
+ // src/cli/load-format-ignore.ts
53
+ import fs2 from "node:fs";
54
+ import path2 from "node:path";
55
+ function loadFormatIgnore(dir) {
56
+ const file = path2.join(dir, ".formatignore");
57
+ if (!fs2.existsSync(file)) {
58
+ return [];
59
+ }
60
+ return fs2.readFileSync(file, "utf8").split(`
61
+ `).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
62
+ }
63
+
52
64
  // src/cli/parse-cli-args.ts
53
65
  import { parseArgs } from "node:util";
54
66
  function parseCLIArgs(argv) {
55
67
  try {
56
- const { values, positionals } = parseArgs({
68
+ const parsed = parseArgs({
57
69
  args: [...argv],
58
70
  options: {
59
71
  check: { type: "boolean", default: false },
@@ -68,13 +80,13 @@ function parseCLIArgs(argv) {
68
80
  strict: true
69
81
  });
70
82
  return {
71
- mode: pickMode(values.check, values.dry),
72
- quiet: values.quiet,
73
- bench: values.bench,
74
- help: values.help,
75
- version: values.version,
76
- ignore: values.ignore,
77
- inputs: positionals
83
+ mode: pickMode(parsed.values.check, parsed.values.dry),
84
+ quiet: parsed.values.quiet,
85
+ bench: parsed.values.bench,
86
+ help: parsed.values.help,
87
+ version: parsed.values.version,
88
+ ignore: parsed.values.ignore,
89
+ inputs: parsed.positionals
78
90
  };
79
91
  } catch (error) {
80
92
  return error instanceof Error ? error.message : String(error);
@@ -103,6 +115,11 @@ options:
103
115
  --version
104
116
  --help
105
117
 
118
+ files:
119
+ .formatignore read from the working directory when present: one glob per
120
+ line, blank lines and # comments skipped, merged with
121
+ --ignore
122
+
106
123
  exit codes:
107
124
  0 clean
108
125
  1 files would change (--check)
@@ -133,9 +150,9 @@ function printReport(file, report, quiet) {
133
150
  }
134
151
 
135
152
  // src/cli/read-package-version.ts
136
- import fs2 from "node:fs";
153
+ import fs3 from "node:fs";
137
154
  function readPackageVersion(pkgPath) {
138
- const parsed = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
155
+ const parsed = JSON.parse(fs3.readFileSync(pkgPath, "utf8"));
139
156
  if (!isPackageJSON(parsed)) {
140
157
  throw new TypeError("Invalid package.json");
141
158
  }
@@ -146,7 +163,7 @@ function isPackageJSON(value) {
146
163
  }
147
164
 
148
165
  // src/cli/check-file.ts
149
- import fs4 from "node:fs";
166
+ import fs5 from "node:fs";
150
167
 
151
168
  // src/transform/apply-edits.ts
152
169
  function applyEdits(src, edits) {
@@ -222,11 +239,11 @@ function getStatementValues(node) {
222
239
  if (node.type === "VariableDeclaration") {
223
240
  return collectASTNodes(node["declarations"]).map((declarator) => declarator["init"]).filter((init) => isASTNode(init));
224
241
  }
225
- const { expression } = node;
242
+ const expression = node["expression"];
226
243
  if (node.type !== "ExpressionStatement" || !isASTNode(expression)) {
227
244
  return [];
228
245
  }
229
- const { right } = expression;
246
+ const right = expression["right"];
230
247
  if (expression.type === "AssignmentExpression" && isASTNode(right)) {
231
248
  return [right];
232
249
  }
@@ -291,7 +308,7 @@ function getStatementKind(node) {
291
308
  return isExpressionStatementOf(node, MUTATION_TYPES) ? "mutation" : null;
292
309
  }
293
310
  function isExpressionStatementOf(node, types) {
294
- const { expression } = node;
311
+ const expression = node["expression"];
295
312
  return node.type === "ExpressionStatement" && isASTNode(expression) && types.includes(expression.type);
296
313
  }
297
314
  var VAR_DECL_KINDS = new Set(["const", "let", "var"]);
@@ -478,7 +495,7 @@ function transform(src, options) {
478
495
  }
479
496
 
480
497
  // src/cli/apply-diff-mode.ts
481
- import fs3 from "node:fs";
498
+ import fs4 from "node:fs";
482
499
 
483
500
  // src/cli/build-unified-diff.ts
484
501
  function buildUnifiedDiff(before, after, label) {
@@ -553,10 +570,11 @@ class MyersDiff {
553
570
  return ops.toReversed();
554
571
  }
555
572
  unwindRound(pos, d) {
556
- const { prev, moveDown } = this.findPrevious(pos, d);
573
+ const previous = this.findPrevious(pos, d);
574
+ const prev = previous.prev;
557
575
  const ops = this.buildEqualOps(pos, prev);
558
576
  if (d > 0) {
559
- const changeOp = moveDown ? { kind: "+", text: this.b[prev.y] ?? "" } : { kind: "-", text: this.a[prev.x] ?? "" };
577
+ const changeOp = previous.moveDown ? { kind: "+", text: this.b[prev.y] ?? "" } : { kind: "-", text: this.a[prev.x] ?? "" };
560
578
  ops.push(changeOp);
561
579
  }
562
580
  return { ops, pos: prev };
@@ -571,7 +589,8 @@ class MyersDiff {
571
589
  }
572
590
  buildEqualOps(pos, prev) {
573
591
  const ops = [];
574
- let { x, y } = pos;
592
+ let x = pos.x;
593
+ let y = pos.y;
575
594
  while (x > prev.x && y > prev.y) {
576
595
  ops.push({ kind: " ", text: this.a[x - 1] ?? "" });
577
596
  x--;
@@ -601,12 +620,12 @@ function buildWindows(ops) {
601
620
  return windows;
602
621
  }
603
622
  function buildHunk(ops, w) {
604
- const { aLine, bLine } = countPrecedingLines(ops, w.from);
623
+ const preceding = countPrecedingLines(ops, w.from);
605
624
  const slice = ops.slice(w.from, w.to);
606
625
  const aCount = slice.filter((o) => o.kind !== "+").length;
607
626
  const bCount = slice.filter((o) => o.kind !== "-").length;
608
627
  return {
609
- header: `@@ -${formatRange(aLine, aCount)} +${formatRange(bLine, bCount)} @@
628
+ header: `@@ -${formatRange(preceding.aLine, aCount)} +${formatRange(preceding.bLine, bCount)} @@
610
629
  `,
611
630
  lines: slice.map((o) => `${o.kind}${o.text}
612
631
  `)
@@ -640,13 +659,13 @@ function applyDiffMode(edit, mode) {
640
659
  if (mode === "dry") {
641
660
  return { message: null, stdout: buildUnifiedDiff(edit.src, edit.result.output, edit.file) };
642
661
  }
643
- fs3.writeFileSync(edit.file, edit.result.output);
662
+ fs4.writeFileSync(edit.file, edit.result.output);
644
663
  return { message: `WROTE ${edit.file} ${edit.result.edits} edit(s)`, stdout: null };
645
664
  }
646
665
 
647
666
  // src/cli/check-file.ts
648
667
  function checkFile(file, mode) {
649
- const src = fs4.readFileSync(file, "utf8");
668
+ const src = fs5.readFileSync(file, "utf8");
650
669
  const bytes = Buffer.byteLength(src);
651
670
  const result = transform(src, { filename: file });
652
671
  if (result.parseError !== null) {
@@ -683,19 +702,23 @@ if (typeof parsedArgs === "string") {
683
702
  console.error(parsedArgs);
684
703
  process.exit(2);
685
704
  }
686
- var { mode, quiet, bench, help, version, ignore, inputs } = parsedArgs;
687
- if (help || inputs.length === 0 && !version) {
705
+ var mode = parsedArgs.mode;
706
+ var quiet = parsedArgs.quiet;
707
+ var bench = parsedArgs.bench;
708
+ var inputs = parsedArgs.inputs;
709
+ if (parsedArgs.help || inputs.length === 0 && !parsedArgs.version) {
688
710
  const exitCode = inputs.length === 0 ? 2 : 0;
689
711
  printHelp();
690
712
  process.exit(exitCode);
691
713
  }
692
- if (version) {
714
+ if (parsedArgs.version) {
693
715
  const pkgPath = fileURLToPath(new URL("../package.json", import.meta.url));
694
716
  process.stdout.write(`${readPackageVersion(pkgPath)}
695
717
  `);
696
718
  process.exit(0);
697
719
  }
698
720
  var t0 = Date.now();
721
+ var ignore = [...loadFormatIgnore(process.cwd()), ...parsedArgs.ignore];
699
722
  var files = await expandInputs(inputs, ignore);
700
723
  if (files.length === 0) {
701
724
  console.error("no files matched");
package/dist/index.js CHANGED
@@ -72,11 +72,11 @@ function getStatementValues(node) {
72
72
  if (node.type === "VariableDeclaration") {
73
73
  return collectASTNodes(node["declarations"]).map((declarator) => declarator["init"]).filter((init) => isASTNode(init));
74
74
  }
75
- const { expression } = node;
75
+ const expression = node["expression"];
76
76
  if (node.type !== "ExpressionStatement" || !isASTNode(expression)) {
77
77
  return [];
78
78
  }
79
- const { right } = expression;
79
+ const right = expression["right"];
80
80
  if (expression.type === "AssignmentExpression" && isASTNode(right)) {
81
81
  return [right];
82
82
  }
@@ -141,7 +141,7 @@ function getStatementKind(node) {
141
141
  return isExpressionStatementOf(node, MUTATION_TYPES) ? "mutation" : null;
142
142
  }
143
143
  function isExpressionStatementOf(node, types) {
144
- const { expression } = node;
144
+ const expression = node["expression"];
145
145
  return node.type === "ExpressionStatement" && isASTNode(expression) && types.includes(expression.type);
146
146
  }
147
147
  var VAR_DECL_KINDS = new Set(["const", "let", "var"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/format-codemod",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Enforce blank-line padding conventions in TypeScript via a fast text-splice codemod",
5
5
  "license": "MIT",
6
6
  "repository": {