@zgeoff/format-codemod 0.0.1 → 0.0.2

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.
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * Deduped: overlapping patterns (a `src/**` glob plus a file inside src/) must
3
- * not process — or report — the same file twice.
3
+ * not process — or report — the same file twice. Ignore globs filter the final
4
+ * list uniformly, so a file is skipped whether it arrived via a directory,
5
+ * a glob, or an explicit path argument.
4
6
  */
5
- export declare function expandInputs(patterns: readonly string[]): Promise<string[]>;
7
+ export declare function expandInputs(patterns: readonly string[], ignore?: readonly string[]): Promise<string[]>;
@@ -5,6 +5,7 @@ export interface CLIArgs {
5
5
  readonly bench: boolean;
6
6
  readonly help: boolean;
7
7
  readonly version: boolean;
8
+ readonly ignore: readonly string[];
8
9
  readonly inputs: readonly string[];
9
10
  }
10
11
  /**
package/dist/cli.js CHANGED
@@ -20,9 +20,9 @@ function buildBenchStatsFromReports(reports, ms) {
20
20
  // src/cli/expand-inputs.ts
21
21
  import fs from "node:fs";
22
22
  import path from "node:path";
23
- async function expandInputs(patterns) {
23
+ async function expandInputs(patterns, ignore = []) {
24
24
  const lists = await Promise.all(patterns.map((p) => expandPattern(p)));
25
- return [...new Set(lists.flat())];
25
+ return [...new Set(lists.flat())].filter((file) => !isIgnored(file, ignore));
26
26
  }
27
27
  function expandPattern(p) {
28
28
  if (fs.existsSync(p)) {
@@ -41,6 +41,13 @@ function isExcluded(entry) {
41
41
  const p = typeof entry === "string" ? entry : path.join(entry.parentPath ?? "", entry.name);
42
42
  return p.split(/[\\/]/u).some((seg) => seg === "node_modules" || seg === ".git");
43
43
  }
44
+ function isIgnored(file, patterns) {
45
+ if (patterns.length === 0) {
46
+ return false;
47
+ }
48
+ const relative = path.relative(process.cwd(), file);
49
+ return patterns.some((pattern) => path.matchesGlob(file, pattern) || path.matchesGlob(relative, pattern));
50
+ }
44
51
 
45
52
  // src/cli/parse-cli-args.ts
46
53
  import { parseArgs } from "node:util";
@@ -54,7 +61,8 @@ function parseCLIArgs(argv) {
54
61
  bench: { type: "boolean", default: false },
55
62
  quiet: { type: "boolean", default: false },
56
63
  version: { type: "boolean", default: false },
57
- help: { type: "boolean", default: false }
64
+ help: { type: "boolean", default: false },
65
+ ignore: { type: "string", multiple: true, default: [] }
58
66
  },
59
67
  allowPositionals: true,
60
68
  strict: true
@@ -65,6 +73,7 @@ function parseCLIArgs(argv) {
65
73
  bench: values.bench,
66
74
  help: values.help,
67
75
  version: values.version,
76
+ ignore: values.ignore,
68
77
  inputs: positionals
69
78
  };
70
79
  } catch (error) {
@@ -86,10 +95,11 @@ usage:
86
95
  format-codemod [options] <file|dir|glob> ...
87
96
 
88
97
  options:
89
- --check exit 1 if any file would change (no writes)
90
- --dry print unified diff to stdout (no writes)
91
- --bench print parse stats as JSON to stderr
92
- --quiet only print files that would change
98
+ --check exit 1 if any file would change (no writes)
99
+ --dry print unified diff to stdout (no writes)
100
+ --bench print parse stats as JSON to stderr
101
+ --quiet only print files that would change
102
+ --ignore <glob> skip files matching the glob (repeatable)
93
103
  --version
94
104
  --help
95
105
 
@@ -102,6 +112,7 @@ examples:
102
112
  format-codemod 'src/**/*.ts'
103
113
  format-codemod --check 'src/**/*.{ts,tsx}'
104
114
  format-codemod --dry path/to/file.ts
115
+ format-codemod --ignore 'src/generated/**' .
105
116
  `);
106
117
  }
107
118
 
@@ -149,6 +160,36 @@ function applyEdits(src, edits) {
149
160
  return segments.toReversed().join("");
150
161
  }
151
162
 
163
+ // src/transform/is-ast-node.ts
164
+ function isASTNode(value) {
165
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
166
+ }
167
+
168
+ // src/transform/collect-ast-nodes.ts
169
+ function collectASTNodes(value) {
170
+ if (!Array.isArray(value)) {
171
+ return isASTNode(value) ? [value] : [];
172
+ }
173
+ const nodes = [];
174
+ for (const item of value) {
175
+ if (isASTNode(item)) {
176
+ nodes.push(item);
177
+ }
178
+ }
179
+ return nodes;
180
+ }
181
+
182
+ // src/transform/collect-child-nodes.ts
183
+ function collectChildNodes(node) {
184
+ const children = [];
185
+ for (const key of Object.keys(node)) {
186
+ if (key !== "loc" && key !== "parent") {
187
+ children.push(...collectASTNodes(node[key]));
188
+ }
189
+ }
190
+ return children;
191
+ }
192
+
152
193
  // src/transform/plan-gap-edit.ts
153
194
  function planGapEdit(file, prev, next) {
154
195
  const gap = buildGap(file, prev, next);
@@ -269,7 +310,7 @@ function buildPairEdits(file, container, body) {
269
310
  for (let i = 0;i < body.length - 1; i++) {
270
311
  const prev = body[i];
271
312
  const next = body[i + 1];
272
- if (prev !== undefined && next !== undefined && (needsBlankLine(container, prev, next) || isMultiline(file.src, prev) || isMultiline(file.src, next))) {
313
+ if (prev !== undefined && next !== undefined && !isImportPair(prev, next) && (needsBlankLine(container, prev, next) || isMultiline(file.src, prev) || isMultiline(file.src, next))) {
273
314
  const edit = planGapEdit(file, prev, next);
274
315
  if (edit !== null) {
275
316
  edits.push(edit);
@@ -278,6 +319,9 @@ function buildPairEdits(file, container, body) {
278
319
  }
279
320
  return edits;
280
321
  }
322
+ function isImportPair(prev, next) {
323
+ return prev.type === "ImportDeclaration" && next.type === "ImportDeclaration";
324
+ }
281
325
  function isMultiline(src, node) {
282
326
  return typeof node.start === "number" && typeof node.end === "number" && src.slice(node.start, node.end).includes(`
283
327
  `);
@@ -374,30 +418,6 @@ function isVarDecl(node) {
374
418
  function isFnOrClassDecl(node) {
375
419
  return node.type === "FunctionDeclaration" || node.type === "ClassDeclaration";
376
420
  }
377
- function collectChildNodes(node) {
378
- const children = [];
379
- for (const key of Object.keys(node)) {
380
- if (key !== "loc" && key !== "parent") {
381
- children.push(...collectASTNodes(node[key]));
382
- }
383
- }
384
- return children;
385
- }
386
- function collectASTNodes(value) {
387
- if (!Array.isArray(value)) {
388
- return isASTNode(value) ? [value] : [];
389
- }
390
- const nodes = [];
391
- for (const item of value) {
392
- if (isASTNode(item)) {
393
- nodes.push(item);
394
- }
395
- }
396
- return nodes;
397
- }
398
- function isASTNode(value) {
399
- return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
400
- }
401
421
 
402
422
  // src/transform/parse-source.ts
403
423
  import { parseSync } from "oxc-parser";
@@ -633,7 +653,7 @@ if (typeof parsedArgs === "string") {
633
653
  console.error(parsedArgs);
634
654
  process.exit(2);
635
655
  }
636
- var { mode, quiet, bench, help, version, inputs } = parsedArgs;
656
+ var { mode, quiet, bench, help, version, ignore, inputs } = parsedArgs;
637
657
  if (help || inputs.length === 0 && !version) {
638
658
  const exitCode = inputs.length === 0 ? 2 : 0;
639
659
  printHelp();
@@ -646,7 +666,7 @@ if (version) {
646
666
  process.exit(0);
647
667
  }
648
668
  var t0 = Date.now();
649
- var files = await expandInputs(inputs);
669
+ var files = await expandInputs(inputs, ignore);
650
670
  if (files.length === 0) {
651
671
  console.error("no files matched");
652
672
  process.exit(2);
package/dist/index.js CHANGED
@@ -10,6 +10,36 @@ function applyEdits(src, edits) {
10
10
  return segments.toReversed().join("");
11
11
  }
12
12
 
13
+ // src/transform/is-ast-node.ts
14
+ function isASTNode(value) {
15
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
16
+ }
17
+
18
+ // src/transform/collect-ast-nodes.ts
19
+ function collectASTNodes(value) {
20
+ if (!Array.isArray(value)) {
21
+ return isASTNode(value) ? [value] : [];
22
+ }
23
+ const nodes = [];
24
+ for (const item of value) {
25
+ if (isASTNode(item)) {
26
+ nodes.push(item);
27
+ }
28
+ }
29
+ return nodes;
30
+ }
31
+
32
+ // src/transform/collect-child-nodes.ts
33
+ function collectChildNodes(node) {
34
+ const children = [];
35
+ for (const key of Object.keys(node)) {
36
+ if (key !== "loc" && key !== "parent") {
37
+ children.push(...collectASTNodes(node[key]));
38
+ }
39
+ }
40
+ return children;
41
+ }
42
+
13
43
  // src/transform/plan-gap-edit.ts
14
44
  function planGapEdit(file, prev, next) {
15
45
  const gap = buildGap(file, prev, next);
@@ -130,7 +160,7 @@ function buildPairEdits(file, container, body) {
130
160
  for (let i = 0;i < body.length - 1; i++) {
131
161
  const prev = body[i];
132
162
  const next = body[i + 1];
133
- if (prev !== undefined && next !== undefined && (needsBlankLine(container, prev, next) || isMultiline(file.src, prev) || isMultiline(file.src, next))) {
163
+ if (prev !== undefined && next !== undefined && !isImportPair(prev, next) && (needsBlankLine(container, prev, next) || isMultiline(file.src, prev) || isMultiline(file.src, next))) {
134
164
  const edit = planGapEdit(file, prev, next);
135
165
  if (edit !== null) {
136
166
  edits.push(edit);
@@ -139,6 +169,9 @@ function buildPairEdits(file, container, body) {
139
169
  }
140
170
  return edits;
141
171
  }
172
+ function isImportPair(prev, next) {
173
+ return prev.type === "ImportDeclaration" && next.type === "ImportDeclaration";
174
+ }
142
175
  function isMultiline(src, node) {
143
176
  return typeof node.start === "number" && typeof node.end === "number" && src.slice(node.start, node.end).includes(`
144
177
  `);
@@ -235,30 +268,6 @@ function isVarDecl(node) {
235
268
  function isFnOrClassDecl(node) {
236
269
  return node.type === "FunctionDeclaration" || node.type === "ClassDeclaration";
237
270
  }
238
- function collectChildNodes(node) {
239
- const children = [];
240
- for (const key of Object.keys(node)) {
241
- if (key !== "loc" && key !== "parent") {
242
- children.push(...collectASTNodes(node[key]));
243
- }
244
- }
245
- return children;
246
- }
247
- function collectASTNodes(value) {
248
- if (!Array.isArray(value)) {
249
- return isASTNode(value) ? [value] : [];
250
- }
251
- const nodes = [];
252
- for (const item of value) {
253
- if (isASTNode(item)) {
254
- nodes.push(item);
255
- }
256
- }
257
- return nodes;
258
- }
259
- function isASTNode(value) {
260
- return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
261
- }
262
271
 
263
272
  // src/transform/parse-source.ts
264
273
  import { parseSync } from "oxc-parser";
@@ -0,0 +1,6 @@
1
+ import type { ASTNode } from '../types.ts';
2
+ /**
3
+ * The AST nodes in a property value: the node itself, an array's node
4
+ * elements, or nothing for non-node values.
5
+ */
6
+ export declare function collectASTNodes(value: unknown): ASTNode[];
@@ -0,0 +1,6 @@
1
+ import type { ASTNode } from '../types.ts';
2
+ /**
3
+ * A node's direct child nodes across all properties, skipping the location
4
+ * and parent back-references a traversal must not follow.
5
+ */
6
+ export declare function collectChildNodes(node: ASTNode): ASTNode[];
@@ -0,0 +1,2 @@
1
+ import type { ASTNode } from '../types.ts';
2
+ export declare function isASTNode(value: unknown): value is ASTNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zgeoff/format-codemod",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Enforce blank-line padding conventions in TypeScript via a fast text-splice codemod",
5
5
  "license": "MIT",
6
6
  "repository": {