@dereekb/dbx-cli 14.0.0 → 14.1.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.
@@ -3,7 +3,7 @@ import { createRequire as __createRequire } from 'node:module';
3
3
  const require = __createRequire(import.meta.url);
4
4
 
5
5
  // packages/dbx-cli/lint-cache/src/main.ts
6
- import { resolve } from "node:path";
6
+ import { resolve as resolve2 } from "node:path";
7
7
  import yargs from "yargs";
8
8
  import { hideBin } from "yargs/helpers";
9
9
 
@@ -18,9 +18,55 @@ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, wr
18
18
  import { join as join2 } from "node:path";
19
19
 
20
20
  // packages/dbx-cli/lint-cache/src/project-lookup.ts
21
+ import { execFileSync } from "node:child_process";
21
22
  import { existsSync, readdirSync, readFileSync } from "node:fs";
22
23
  import { join, relative } from "node:path";
24
+
25
+ // packages/dbx-cli/lint-cache/src/types.ts
26
+ var LINT_CACHE_LINTERS = ["eslint", "oxlint"];
27
+ var DEFAULT_LINT_CACHE_LINTER = "eslint";
28
+ var LINT_CACHE_LINTER_TARGET_NAMES = {
29
+ eslint: "lint",
30
+ oxlint: "oxlint"
31
+ };
32
+ var LINT_CACHE_LINTER_TARGET_IS_INFERRED = {
33
+ eslint: false,
34
+ oxlint: true
35
+ };
36
+ function cacheFileName(projectName, linter = DEFAULT_LINT_CACHE_LINTER) {
37
+ const stem = projectName.replaceAll(/[^A-Za-z0-9._-]/g, "_");
38
+ return linter === DEFAULT_LINT_CACHE_LINTER ? `${stem}.json` : `${stem}.${linter}.json`;
39
+ }
40
+ function indexFileName(linter = DEFAULT_LINT_CACHE_LINTER) {
41
+ return linter === DEFAULT_LINT_CACHE_LINTER ? "index.json" : `index.${linter}.json`;
42
+ }
43
+
44
+ // packages/dbx-cli/lint-cache/src/project-lookup.ts
45
+ var DEFAULT_TARGET_NAME = LINT_CACHE_LINTER_TARGET_NAMES[DEFAULT_LINT_CACHE_LINTER];
23
46
  var SKIP_DIR_NAMES = /* @__PURE__ */ new Set(["node_modules", "dist", "coverage", ".nx", ".angular", ".next"]);
47
+ var graphTargetCache = /* @__PURE__ */ new Map();
48
+ function projectNamesWithTarget(workspaceRoot, targetName) {
49
+ const key = `${workspaceRoot}\0${targetName}`;
50
+ let cached = graphTargetCache.get(key);
51
+ if (!cached) {
52
+ let names;
53
+ try {
54
+ const stdout = execFileSync("npx", ["nx", "show", "projects", `--with-target=${targetName}`, "--json"], {
55
+ cwd: workspaceRoot,
56
+ encoding: "utf8",
57
+ env: { ...process.env, FORCE_COLOR: "0" },
58
+ stdio: ["ignore", "pipe", "ignore"]
59
+ });
60
+ const parsed = JSON.parse(stdout);
61
+ names = Array.isArray(parsed) ? parsed.filter((n) => typeof n === "string") : [];
62
+ } catch {
63
+ names = [];
64
+ }
65
+ cached = new Set(names);
66
+ graphTargetCache.set(key, cached);
67
+ }
68
+ return cached;
69
+ }
24
70
  function discoverTopLevelDirs(workspaceRoot) {
25
71
  let dirs;
26
72
  try {
@@ -30,21 +76,22 @@ function discoverTopLevelDirs(workspaceRoot) {
30
76
  }
31
77
  return dirs;
32
78
  }
33
- function findProject(workspaceRoot, projectName) {
79
+ function findProject(workspaceRoot, projectName, targetName = DEFAULT_TARGET_NAME) {
34
80
  let result = null;
35
81
  for (const dir of discoverTopLevelDirs(workspaceRoot)) {
36
82
  if (result) break;
37
- result = walkForProject(workspaceRoot, join(workspaceRoot, dir), projectName);
83
+ result = walkForProject({ workspaceRoot, dir: join(workspaceRoot, dir), projectName, targetName });
38
84
  }
39
85
  if (!result) {
40
86
  const rootProject = readProjectJson(join(workspaceRoot, "project.json"));
41
87
  if (rootProject?.name === projectName) {
42
- result = toProjectInfo(workspaceRoot, workspaceRoot, rootProject);
88
+ result = toProjectInfo({ workspaceRoot, projectRoot: workspaceRoot, pj: rootProject, targetName });
43
89
  }
44
90
  }
45
91
  return result;
46
92
  }
47
- function walkForProject(workspaceRoot, dir, projectName) {
93
+ function walkForProject(input) {
94
+ const { workspaceRoot, dir, projectName, targetName } = input;
48
95
  let found = null;
49
96
  const entries = readdirSync(dir, { withFileTypes: true });
50
97
  for (const e of entries) {
@@ -56,25 +103,30 @@ function walkForProject(workspaceRoot, dir, projectName) {
56
103
  if (existsSync(pjPath)) {
57
104
  const pj = readProjectJson(pjPath);
58
105
  if (pj?.name === projectName) {
59
- found = toProjectInfo(workspaceRoot, childDir, pj);
106
+ found = toProjectInfo({ workspaceRoot, projectRoot: childDir, pj, targetName });
60
107
  continue;
61
108
  }
62
109
  }
63
- found = walkForProject(workspaceRoot, childDir, projectName);
110
+ found = walkForProject({ workspaceRoot, dir: childDir, projectName, targetName });
64
111
  }
65
112
  return found;
66
113
  }
67
- function listProjects(workspaceRoot) {
114
+ function listProjects(workspaceRoot, targetName = DEFAULT_TARGET_NAME, options = {}) {
68
115
  const out = [];
69
116
  for (const dir of discoverTopLevelDirs(workspaceRoot)) {
70
- collectProjects(workspaceRoot, join(workspaceRoot, dir), out);
117
+ collectProjects({ workspaceRoot, dir: join(workspaceRoot, dir), out, targetName });
71
118
  }
72
119
  const rootProject = readProjectJson(join(workspaceRoot, "project.json"));
73
- if (rootProject) out.push(toProjectInfo(workspaceRoot, workspaceRoot, rootProject));
120
+ if (rootProject) out.push(toProjectInfo({ workspaceRoot, projectRoot: workspaceRoot, pj: rootProject, targetName }));
74
121
  out.sort((a, b) => a.name.localeCompare(b.name));
75
- return out;
122
+ return options.resolveInferredTargets ? withInferredTarget(workspaceRoot, targetName, out) : out;
76
123
  }
77
- function collectProjects(workspaceRoot, dir, out) {
124
+ function withInferredTarget(workspaceRoot, targetName, projects) {
125
+ const names = projectNamesWithTarget(workspaceRoot, targetName);
126
+ return projects.map((p) => ({ ...p, hasLintTarget: names.has(p.name) }));
127
+ }
128
+ function collectProjects(input) {
129
+ const { workspaceRoot, dir, out, targetName } = input;
78
130
  const entries = readdirSync(dir, { withFileTypes: true });
79
131
  for (const e of entries) {
80
132
  if (!e.isDirectory()) continue;
@@ -83,9 +135,9 @@ function collectProjects(workspaceRoot, dir, out) {
83
135
  const pjPath = join(childDir, "project.json");
84
136
  if (existsSync(pjPath)) {
85
137
  const pj = readProjectJson(pjPath);
86
- if (pj) out.push(toProjectInfo(workspaceRoot, childDir, pj));
138
+ if (pj) out.push(toProjectInfo({ workspaceRoot, projectRoot: childDir, pj, targetName }));
87
139
  }
88
- collectProjects(workspaceRoot, childDir, out);
140
+ collectProjects({ workspaceRoot, dir: childDir, out, targetName });
89
141
  }
90
142
  }
91
143
  function readProjectJson(path) {
@@ -97,8 +149,9 @@ function readProjectJson(path) {
97
149
  }
98
150
  return parsed;
99
151
  }
100
- function toProjectInfo(workspaceRoot, projectRoot, pj) {
101
- const lintTarget = pj.targets?.["lint"];
152
+ function toProjectInfo(input) {
153
+ const { workspaceRoot, projectRoot, pj, targetName } = input;
154
+ const lintTarget = pj.targets?.[targetName];
102
155
  const lintPatterns = lintTarget?.options?.lintFilePatterns;
103
156
  return {
104
157
  name: pj.name ?? "",
@@ -111,7 +164,8 @@ function toProjectInfo(workspaceRoot, projectRoot, pj) {
111
164
 
112
165
  // packages/dbx-cli/lint-cache/src/build-many.ts
113
166
  async function runBuildMany(opts) {
114
- const lintable = listProjects(opts.workspaceRoot).filter((p) => p.hasLintTarget);
167
+ const linter = opts.linter ?? DEFAULT_LINT_CACHE_LINTER;
168
+ const lintable = listProjects(opts.workspaceRoot, LINT_CACHE_LINTER_TARGET_NAMES[linter], { resolveInferredTargets: LINT_CACHE_LINTER_TARGET_IS_INFERRED[linter] }).filter((p) => p.hasLintTarget);
115
169
  const targets = filterProjects({ projects: lintable, include: opts.include, exclude: opts.exclude });
116
170
  if (!existsSync2(opts.outputDir)) mkdirSync(opts.outputDir, { recursive: true });
117
171
  const results = [];
@@ -132,6 +186,7 @@ async function runBuildMany(opts) {
132
186
  outputDir: opts.outputDir,
133
187
  nxArgs: opts.nxArgs,
134
188
  fix: opts.fix,
189
+ linter,
135
190
  updateIndex: false
136
191
  });
137
192
  results.push(projectResultFromCache({ project: project.name, cachePath, cache }));
@@ -157,12 +212,13 @@ async function runBuildMany(opts) {
157
212
  results.sort((a, b) => a.project.localeCompare(b.project));
158
213
  const totalErrors = results.reduce((acc, r) => acc + (r.errorCount ?? 0), 0);
159
214
  const totalWarnings = results.reduce((acc, r) => acc + (r.warningCount ?? 0), 0);
160
- const indexPath = join2(opts.outputDir, "index.json");
215
+ const indexPath = join2(opts.outputDir, indexFileName(linter));
161
216
  writeFileSync(
162
217
  indexPath,
163
218
  JSON.stringify(
164
219
  {
165
220
  schemaVersion: 1,
221
+ linter,
166
222
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
167
223
  projectCount: results.length,
168
224
  succeeded: results.filter((r) => r.error == null).length,
@@ -230,7 +286,8 @@ function projectResultFromCache(input) {
230
286
  };
231
287
  }
232
288
  function patchIndexEntry(input) {
233
- const indexPath = join2(input.outputDir, "index.json");
289
+ const linter = input.linter ?? DEFAULT_LINT_CACHE_LINTER;
290
+ const indexPath = join2(input.outputDir, indexFileName(linter));
234
291
  let patched = false;
235
292
  if (existsSync2(indexPath)) {
236
293
  const index = JSON.parse(readFileSync2(indexPath, "utf8"));
@@ -244,6 +301,7 @@ function patchIndexEntry(input) {
244
301
  }
245
302
  const next = {
246
303
  schemaVersion: 1,
304
+ linter,
247
305
  generatedAt: index.generatedAt,
248
306
  projectCount: projects.length,
249
307
  succeeded: projects.filter((p) => p.error == null).length,
@@ -258,64 +316,161 @@ function patchIndexEntry(input) {
258
316
  return patched;
259
317
  }
260
318
 
261
- // packages/dbx-cli/lint-cache/src/types.ts
262
- function cacheFileName(projectName) {
263
- return `${projectName.replaceAll(/[^A-Za-z0-9._-]/g, "_")}.json`;
319
+ // packages/dbx-cli/lint-cache/src/oxlint-result.ts
320
+ import { resolve } from "node:path";
321
+ function extractJsonObject(stdout) {
322
+ const start = stdout.indexOf("{");
323
+ if (start < 0) {
324
+ throw new Error("no JSON object found in oxlint output");
325
+ }
326
+ let depth = 0;
327
+ let inString = false;
328
+ let escaped = false;
329
+ let end = -1;
330
+ for (let i = start; i < stdout.length && end < 0; i += 1) {
331
+ const ch = stdout[i];
332
+ if (escaped) {
333
+ escaped = false;
334
+ } else if (ch === "\\") {
335
+ if (inString) escaped = true;
336
+ } else if (ch === '"') {
337
+ inString = !inString;
338
+ } else if (!inString) {
339
+ if (ch === "{") depth += 1;
340
+ else if (ch === "}") {
341
+ depth -= 1;
342
+ if (depth === 0) end = i;
343
+ }
344
+ }
345
+ }
346
+ if (end < 0) {
347
+ throw new Error("unterminated JSON object in oxlint output");
348
+ }
349
+ return stdout.slice(start, end + 1);
350
+ }
351
+ function oxlintRuleId(code) {
352
+ let result = null;
353
+ if (code) {
354
+ const match = /^(?<plugin>[^()]+)\((?<rule>[^()]+)\)$/.exec(code);
355
+ if (match?.groups) {
356
+ const { plugin, rule } = match.groups;
357
+ result = plugin === "eslint" ? rule : `${plugin}/${rule}`;
358
+ } else {
359
+ result = code;
360
+ }
361
+ }
362
+ return result;
363
+ }
364
+ function parseOxlintResult(input) {
365
+ const raw = JSON.parse(extractJsonObject(input.stdout));
366
+ const byFile = /* @__PURE__ */ new Map();
367
+ for (const d of raw.diagnostics ?? []) {
368
+ const filePath = d.filename == null ? "<unknown>" : resolve(input.cwd, d.filename);
369
+ const span = d.labels?.[0]?.span;
370
+ const message = {
371
+ ruleId: oxlintRuleId(d.code),
372
+ severity: d.severity === "warning" ? "warning" : "error",
373
+ message: d.message ?? "",
374
+ line: span?.line ?? 0,
375
+ column: span?.column ?? 0,
376
+ endLine: null,
377
+ endColumn: null,
378
+ fixable: false
379
+ };
380
+ const existing = byFile.get(filePath);
381
+ if (existing) existing.push(message);
382
+ else byFile.set(filePath, [message]);
383
+ }
384
+ const files = Array.from(byFile.entries()).map(([filePath, messages]) => ({ filePath, messages })).sort((a, b) => a.filePath.localeCompare(b.filePath));
385
+ return { files, fileCount: raw.number_of_files ?? files.length };
264
386
  }
265
387
 
266
388
  // packages/dbx-cli/lint-cache/src/build.ts
267
389
  async function runBuild(opts) {
268
- const project = findProject(opts.workspaceRoot, opts.project);
390
+ const linter = opts.linter ?? DEFAULT_LINT_CACHE_LINTER;
391
+ const targetName = LINT_CACHE_LINTER_TARGET_NAMES[linter];
392
+ const project = findProject(opts.workspaceRoot, opts.project, targetName);
269
393
  if (!project) {
270
394
  throw new Error(`project not found in workspace: ${opts.project}`);
271
395
  }
272
396
  if (!existsSync3(opts.outputDir)) mkdirSync2(opts.outputDir, { recursive: true });
273
397
  const tmpFile = join3(opts.outputDir, `.tmp-${randomUUID()}.json`);
274
398
  const tmpFileRel = relative2(opts.workspaceRoot, tmpFile);
275
- let raw;
399
+ let normalized;
276
400
  try {
277
- await spawnNxLint({
401
+ const stdout = await spawnLintTarget({
278
402
  workspaceRoot: opts.workspaceRoot,
279
403
  project: opts.project,
280
- outputFile: tmpFileRel,
404
+ targetName,
405
+ outputFile: linter === "eslint" ? tmpFileRel : null,
406
+ silent: linter === "eslint",
281
407
  fix: opts.fix,
282
408
  extraArgs: opts.nxArgs ?? []
283
409
  });
284
- if (!existsSync3(tmpFile)) {
285
- throw new Error(`nx lint did not write the expected JSON output to ${tmpFile}`);
410
+ if (linter === "eslint") {
411
+ if (!existsSync3(tmpFile)) {
412
+ throw new Error(`nx run ${opts.project}:${targetName} did not write the expected JSON output to ${tmpFile}`);
413
+ }
414
+ normalized = normalizeEslintResult(JSON.parse(readFileSync3(tmpFile, "utf8")));
415
+ } else {
416
+ normalized = parseOxlintResult({ stdout, cwd: project.absoluteRoot });
286
417
  }
287
- raw = JSON.parse(readFileSync3(tmpFile, "utf8"));
288
418
  } finally {
289
419
  if (existsSync3(tmpFile)) rmSync(tmpFile, { force: true });
290
420
  }
291
421
  const cache = buildCache({
292
- raw,
422
+ normalized,
423
+ linter,
424
+ targetName,
293
425
  project: opts.project,
294
426
  projectRoot: project.projectRoot,
295
427
  workspaceRoot: opts.workspaceRoot
296
428
  });
297
- const cachePath = join3(opts.outputDir, cacheFileName(opts.project));
429
+ const cachePath = join3(opts.outputDir, cacheFileName(opts.project, linter));
298
430
  writeFileSync2(cachePath, JSON.stringify(cache, null, 2));
299
431
  if (opts.updateIndex !== false) {
300
432
  patchIndexEntry({
301
433
  outputDir: opts.outputDir,
434
+ linter,
302
435
  entry: projectResultFromCache({ project: opts.project, cachePath, cache })
303
436
  });
304
437
  }
305
438
  return { cachePath, cache };
306
439
  }
307
- function spawnNxLint(opts) {
440
+ function normalizeEslintResult(raw) {
441
+ const files = raw.map((r) => ({
442
+ filePath: r.filePath,
443
+ messages: r.messages.map((m) => ({
444
+ ruleId: m.ruleId ?? null,
445
+ severity: m.severity === 2 ? "error" : "warning",
446
+ message: m.message,
447
+ line: m.line ?? 0,
448
+ column: m.column ?? 0,
449
+ endLine: m.endLine ?? null,
450
+ endColumn: m.endColumn ?? null,
451
+ fixable: m.fix != null
452
+ }))
453
+ }));
454
+ return { files, fileCount: raw.length };
455
+ }
456
+ function spawnLintTarget(opts) {
308
457
  const fixArgs = opts.fix ? ["--fix"] : [];
309
- const args = ["nx", "run", `${opts.project}:lint`, "--format=json", `--output-file=${opts.outputFile}`, "--silent", "--no-cloud", ...fixArgs, ...opts.extraArgs];
458
+ const outputFileArgs = opts.outputFile ? [`--output-file=${opts.outputFile}`] : [];
459
+ const silentArgs = opts.silent ? ["--silent"] : [];
460
+ const args = ["nx", "run", `${opts.project}:${opts.targetName}`, "--format=json", ...outputFileArgs, ...silentArgs, "--no-cloud", ...fixArgs, ...opts.extraArgs];
310
461
  return new Promise((resolvePromise, rejectPromise) => {
311
462
  const child = spawn("npx", args, {
312
463
  cwd: opts.workspaceRoot,
313
464
  env: { ...process.env, FORCE_COLOR: "0" },
314
- stdio: ["ignore", "inherit", "inherit"]
465
+ stdio: ["ignore", "pipe", "inherit"]
466
+ });
467
+ let stdout = "";
468
+ child.stdout.on("data", (chunk) => {
469
+ stdout += chunk.toString("utf8");
315
470
  });
316
471
  child.on("error", rejectPromise);
317
472
  child.on("exit", () => {
318
- resolvePromise();
473
+ resolvePromise(stdout);
319
474
  });
320
475
  });
321
476
  }
@@ -328,27 +483,31 @@ function buildCache(input) {
328
483
  let fixableErrorCount = 0;
329
484
  let fixableWarningCount = 0;
330
485
  let filesWithIssues = 0;
331
- for (const r of input.raw) {
486
+ for (const r of input.normalized.files) {
332
487
  if (r.messages.length === 0) continue;
333
488
  const filePath = relative2(input.workspaceRoot, r.filePath) || r.filePath;
334
489
  filesWithIssues += 1;
335
- fileSummariesMap.set(filePath, { errors: r.errorCount, warnings: r.warningCount });
336
- errorCount += r.errorCount;
337
- warningCount += r.warningCount;
338
- fixableErrorCount += r.fixableErrorCount;
339
- fixableWarningCount += r.fixableWarningCount;
490
+ let fileErrors = 0;
491
+ let fileWarnings = 0;
340
492
  for (const m of r.messages) {
341
- const sev = m.severity === 2 ? "error" : "warning";
493
+ if (m.severity === "error") {
494
+ fileErrors += 1;
495
+ if (m.fixable) fixableErrorCount += 1;
496
+ } else {
497
+ fileWarnings += 1;
498
+ if (m.fixable) fixableWarningCount += 1;
499
+ }
342
500
  messages.push({
343
501
  filePath,
344
- line: m.line ?? 0,
345
- column: m.column ?? 0,
346
- endLine: m.endLine ?? null,
347
- endColumn: m.endColumn ?? null,
348
- ruleId: m.ruleId ?? null,
349
- severity: sev,
502
+ line: m.line,
503
+ column: m.column,
504
+ endLine: m.endLine,
505
+ endColumn: m.endColumn,
506
+ ruleId: m.ruleId,
507
+ severity: m.severity,
350
508
  message: m.message,
351
- fixable: m.fix != null
509
+ fixable: m.fixable,
510
+ linter: input.linter
352
511
  });
353
512
  const ruleKey = m.ruleId ?? "(no-rule)";
354
513
  let entry = ruleSummariesMap.get(ruleKey);
@@ -356,24 +515,31 @@ function buildCache(input) {
356
515
  entry = { errors: 0, warnings: 0, files: /* @__PURE__ */ new Set() };
357
516
  ruleSummariesMap.set(ruleKey, entry);
358
517
  }
359
- if (sev === "error") entry.errors += 1;
518
+ if (m.severity === "error") entry.errors += 1;
360
519
  else entry.warnings += 1;
361
520
  entry.files.add(filePath);
362
521
  }
522
+ fileSummariesMap.set(filePath, { errors: fileErrors, warnings: fileWarnings });
523
+ errorCount += fileErrors;
524
+ warningCount += fileWarnings;
363
525
  }
364
526
  const ruleSummaries = Array.from(ruleSummariesMap.entries()).map(([rule, v]) => ({ rule, errors: v.errors, warnings: v.warnings, files: v.files.size })).sort((a, b) => b.errors + b.warnings - (a.errors + a.warnings) || a.rule.localeCompare(b.rule));
365
527
  const fileSummaries = Array.from(fileSummariesMap.entries()).map(([filePath, v]) => ({ filePath, errors: v.errors, warnings: v.warnings })).sort((a, b) => b.errors + b.warnings - (a.errors + a.warnings) || a.filePath.localeCompare(b.filePath));
528
+ const linterVersion = input.linter === "eslint" ? "nx-lint-executor" : "nx-oxlint-target";
366
529
  return {
367
- schemaVersion: 1,
530
+ schemaVersion: 2,
368
531
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
369
532
  project: input.project,
370
533
  projectRoot: input.projectRoot,
371
- eslintVersion: "nx-lint-executor",
534
+ linter: input.linter,
535
+ linterVersion,
536
+ targetName: input.targetName,
537
+ eslintVersion: linterVersion,
372
538
  errorCount,
373
539
  warningCount,
374
540
  fixableErrorCount,
375
541
  fixableWarningCount,
376
- fileCount: input.raw.length,
542
+ fileCount: input.normalized.fileCount,
377
543
  filesWithIssues,
378
544
  ruleSummaries,
379
545
  fileSummaries,
@@ -555,33 +721,35 @@ function globToRegExp2(pattern) {
555
721
  // packages/dbx-cli/lint-cache/src/main.ts
556
722
  var FORMAT_CHOICES = ["summary", "rules", "files", "messages", "json"];
557
723
  var SEVERITY_CHOICES = ["error", "warning"];
724
+ var LINTER_OPTION = { choices: LINT_CACHE_LINTERS, default: DEFAULT_LINT_CACHE_LINTER, describe: "Which lint engine to use: `eslint` (the explicit `lint` target) or `oxlint` (the target inferred by @nx/oxlint)." };
558
725
  await yargs(hideBin(process.argv)).scriptName("dbx-cli-lint-cache").usage("$0 <command> [options]").command(
559
726
  "build <project>",
560
- "Run ESLint for a project and write the grouped result cache.",
561
- (y) => y.positional("project", { type: "string", describe: "Nx project name", demandOption: true }).option("output-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache file is written into (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("nx-arg", { type: "array", string: true, describe: "Extra argument passed through to `nx run <project>:lint` (repeatable)." }).option("fix", { type: "boolean", default: false, describe: "Run ESLint with --fix; remaining (non-fixable) issues are still cached." }).option("quiet", { type: "boolean", default: false, describe: "Suppress the post-build summary line." }),
727
+ "Run the linter for a project and write the grouped result cache.",
728
+ (y) => y.positional("project", { type: "string", describe: "Nx project name", demandOption: true }).option("output-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache file is written into (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("nx-arg", { type: "array", string: true, describe: "Extra argument passed through to `nx run <project>:lint` (repeatable)." }).option("fix", { type: "boolean", default: false, describe: "Run the linter with --fix; remaining (non-fixable) issues are still cached." }).option("linter", LINTER_OPTION).option("quiet", { type: "boolean", default: false, describe: "Suppress the post-build summary line." }),
562
729
  async (args) => {
563
730
  const workspaceRoot = resolveWorkspaceRoot(args);
564
- const outputDir = resolve(workspaceRoot, args["output-dir"]);
731
+ const outputDir = resolve2(workspaceRoot, args["output-dir"]);
565
732
  const { cachePath, cache } = await runBuild({
566
733
  project: args.project,
567
734
  workspaceRoot,
568
735
  outputDir,
569
736
  nxArgs: args["nx-arg"],
570
- fix: args.fix
737
+ fix: args.fix,
738
+ linter: args.linter
571
739
  });
572
740
  if (!args.quiet) {
573
741
  console.log(`[wrote] ${cachePath}`);
574
- console.log(`Summary: ${cache.errorCount} errors \xB7 ${cache.warningCount} warnings \xB7 ${cache.filesWithIssues}/${cache.fileCount} files with issues${args.fix ? " (after --fix)" : ""}`);
742
+ console.log(`Summary: [${cache.linter}] ${cache.errorCount} errors \xB7 ${cache.warningCount} warnings \xB7 ${cache.filesWithIssues}/${cache.fileCount} files with issues${args.fix ? " (after --fix)" : ""}`);
575
743
  }
576
744
  }
577
745
  ).command(
578
746
  "query <project>",
579
747
  "Filter and display messages from a project's cached lint result.",
580
- (y) => y.positional("project", { type: "string", describe: "Nx project name", demandOption: true }).option("cache-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache file lives in (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("rule", { type: "array", string: true, describe: "Filter to one or more rule IDs (repeatable). OR-ed." }).option("severity", { choices: SEVERITY_CHOICES, describe: "Filter to errors or warnings only." }).option("file", { type: "string", describe: "Substring filter against the file path; supports * and ** glob chars." }).option("message", { type: "string", describe: "Substring filter against the message text (case-insensitive)." }).option("limit", { type: "number", describe: "Limit the printed messages slice (totalMatched still reflects the full match count)." }).option("format", { choices: FORMAT_CHOICES, default: "summary", describe: "Output format." }),
748
+ (y) => y.positional("project", { type: "string", describe: "Nx project name", demandOption: true }).option("cache-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache file lives in (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("rule", { type: "array", string: true, describe: "Filter to one or more rule IDs (repeatable). OR-ed." }).option("severity", { choices: SEVERITY_CHOICES, describe: "Filter to errors or warnings only." }).option("file", { type: "string", describe: "Substring filter against the file path; supports * and ** glob chars." }).option("message", { type: "string", describe: "Substring filter against the message text (case-insensitive)." }).option("limit", { type: "number", describe: "Limit the printed messages slice (totalMatched still reflects the full match count)." }).option("linter", LINTER_OPTION).option("format", { choices: FORMAT_CHOICES, default: "summary", describe: "Output format." }),
581
749
  (args) => {
582
750
  const workspaceRoot = resolveWorkspaceRoot(args);
583
- const cacheDir = resolve(workspaceRoot, args["cache-dir"]);
584
- const cachePath = resolve(cacheDir, cacheFileName(args.project));
751
+ const cacheDir = resolve2(workspaceRoot, args["cache-dir"]);
752
+ const cachePath = resolve2(cacheDir, cacheFileName(args.project, args.linter));
585
753
  const result = runQuery(cachePath, {
586
754
  rule: args.rule,
587
755
  severity: args.severity,
@@ -593,11 +761,11 @@ await yargs(hideBin(process.argv)).scriptName("dbx-cli-lint-cache").usage("$0 <c
593
761
  }
594
762
  ).command(
595
763
  "build-many",
596
- "Run ESLint for every project with a lint target (with optional filters) and write per-project caches plus an aggregate index.json.",
597
- (y) => y.option("output-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache files are written into (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("include", { type: "array", string: true, default: [], describe: "Only build projects whose name matches one of these patterns (substring or *-glob). Repeatable." }).option("exclude", { type: "array", string: true, default: [], describe: "Skip projects whose name matches one of these patterns (substring or *-glob). Repeatable." }).option("concurrency", { type: "number", default: 4, describe: "Number of `nx run <p>:lint` processes to run in parallel." }).option("continue-on-error", { type: "boolean", default: true, describe: "Continue past per-project failures and record them in index.json." }).option("nx-arg", { type: "array", string: true, describe: "Extra argument passed through to each `nx run <project>:lint` (repeatable)." }).option("fix", { type: "boolean", default: false, describe: "Run ESLint with --fix on every targeted project; remaining (non-fixable) issues are still cached." }).option("quiet", { type: "boolean", default: false, describe: "Suppress per-project progress output." }),
764
+ "Run the linter for every project with that linter's target (with optional filters) and write per-project caches plus an aggregate index.",
765
+ (y) => y.option("output-dir", { type: "string", default: ".tmp/lint-cache", describe: "Directory the cache files are written into (workspace-relative)." }).option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("include", { type: "array", string: true, default: [], describe: "Only build projects whose name matches one of these patterns (substring or *-glob). Repeatable." }).option("exclude", { type: "array", string: true, default: [], describe: "Skip projects whose name matches one of these patterns (substring or *-glob). Repeatable." }).option("concurrency", { type: "number", default: 4, describe: "Number of `nx run <p>:lint` processes to run in parallel." }).option("continue-on-error", { type: "boolean", default: true, describe: "Continue past per-project failures and record them in index.json." }).option("nx-arg", { type: "array", string: true, describe: "Extra argument passed through to each `nx run <project>:lint` (repeatable)." }).option("fix", { type: "boolean", default: false, describe: "Run the linter with --fix on every targeted project; remaining (non-fixable) issues are still cached." }).option("linter", LINTER_OPTION).option("quiet", { type: "boolean", default: false, describe: "Suppress per-project progress output." }),
598
766
  async (args) => {
599
767
  const workspaceRoot = resolveWorkspaceRoot(args);
600
- const outputDir = resolve(workspaceRoot, args["output-dir"]);
768
+ const outputDir = resolve2(workspaceRoot, args["output-dir"]);
601
769
  const include = args.include ?? [];
602
770
  const exclude = args.exclude ?? [];
603
771
  const result = await runBuildMany({
@@ -609,28 +777,30 @@ await yargs(hideBin(process.argv)).scriptName("dbx-cli-lint-cache").usage("$0 <c
609
777
  continueOnError: args["continue-on-error"],
610
778
  nxArgs: args["nx-arg"],
611
779
  fix: args.fix,
780
+ linter: args.linter,
612
781
  onProgress: args.quiet ? void 0 : printProgress
613
782
  });
614
783
  if (!args.quiet) {
615
784
  const failed = result.projects.filter((p) => p.error != null).length;
616
785
  console.log("");
617
786
  console.log(`[wrote] ${result.indexPath}`);
618
- console.log(`Totals: ${result.totalErrors} errors \xB7 ${result.totalWarnings} warnings across ${result.projects.length} projects (${failed} failed).`);
787
+ console.log(`Totals: [${args.linter}] ${result.totalErrors} errors \xB7 ${result.totalWarnings} warnings across ${result.projects.length} projects (${failed} failed).`);
619
788
  }
620
789
  }
621
790
  ).command(
622
791
  "list-projects",
623
792
  "List Nx projects that build-many would target after applying include/exclude filters.",
624
- (y) => y.option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("include", { type: "array", string: true, default: [], describe: "Substring or *-glob filter to keep projects. Repeatable." }).option("exclude", { type: "array", string: true, default: [], describe: "Substring or *-glob filter to drop projects. Repeatable." }).option("all", { type: "boolean", default: false, describe: "Include projects that do not declare a lint target." }),
793
+ (y) => y.option("workspace", { type: "string", describe: "Workspace root (defaults to cwd)." }).option("include", { type: "array", string: true, default: [], describe: "Substring or *-glob filter to keep projects. Repeatable." }).option("exclude", { type: "array", string: true, default: [], describe: "Substring or *-glob filter to drop projects. Repeatable." }).option("linter", LINTER_OPTION).option("all", { type: "boolean", default: false, describe: "Include projects that do not have the selected linter's target." }),
625
794
  (args) => {
626
795
  const workspaceRoot = resolveWorkspaceRoot(args);
627
796
  const include = args.include ?? [];
628
797
  const exclude = args.exclude ?? [];
629
- const projects = listProjects(workspaceRoot);
798
+ const linter = args.linter;
799
+ const projects = listProjects(workspaceRoot, LINT_CACHE_LINTER_TARGET_NAMES[linter], { resolveInferredTargets: LINT_CACHE_LINTER_TARGET_IS_INFERRED[linter] });
630
800
  const lintable = args.all ? projects : projects.filter((p) => p.hasLintTarget);
631
801
  const filtered = filterProjects({ projects: lintable, include, exclude });
632
802
  for (const p of filtered) {
633
- const lintFlag = p.hasLintTarget ? "" : " (no lint target)";
803
+ const lintFlag = p.hasLintTarget ? "" : ` (no ${args.linter} target)`;
634
804
  console.log(`${p.name} ${p.projectRoot}${lintFlag}`);
635
805
  }
636
806
  console.log(`# ${filtered.length} project(s) (of ${lintable.length} lintable, ${projects.length} total)`);
@@ -641,7 +811,7 @@ await yargs(hideBin(process.argv)).scriptName("dbx-cli-lint-cache").usage("$0 <c
641
811
  process.exit(1);
642
812
  });
643
813
  function resolveWorkspaceRoot(args) {
644
- return resolve(args.workspace ?? process.cwd());
814
+ return resolve2(args.workspace ?? process.cwd());
645
815
  }
646
816
  function printProgress(event) {
647
817
  const tag = `[${event.index}/${event.total}]`;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-lint-cache",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
@@ -8,7 +8,7 @@
8
8
  "eslint": "10.9.1"
9
9
  },
10
10
  "peerDependencies": {
11
- "@dereekb/util": "14.0.0",
11
+ "@dereekb/util": "14.1.0",
12
12
  "yargs": "^18.0.0"
13
13
  }
14
14
  }
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/manifest-extract",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "@dereekb/date": "14.0.0",
8
- "@dereekb/dbx-cli": "14.0.0",
9
- "@dereekb/firebase": "14.0.0",
10
- "@dereekb/model": "14.0.0",
11
- "@dereekb/nestjs": "14.0.0",
12
- "@dereekb/rxjs": "14.0.0",
13
- "@dereekb/util": "14.0.0",
7
+ "@dereekb/date": "14.1.0",
8
+ "@dereekb/dbx-cli": "14.1.0",
9
+ "@dereekb/firebase": "14.1.0",
10
+ "@dereekb/model": "14.1.0",
11
+ "@dereekb/nestjs": "14.1.0",
12
+ "@dereekb/rxjs": "14.1.0",
13
+ "@dereekb/util": "14.1.0",
14
14
  "ts-morph": "^28.0.0"
15
15
  },
16
16
  "exports": {
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli/model-test",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "@dereekb/util": "14.0.0",
7
+ "@dereekb/util": "14.1.0",
8
8
  "ts-morph": "^28.0.0"
9
9
  },
10
10
  "exports": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli",
3
- "version": "14.0.0",
3
+ "version": "14.1.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "bin": {
@@ -66,16 +66,16 @@
66
66
  }
67
67
  },
68
68
  "peerDependencies": {
69
- "@dereekb/date": "14.0.0",
70
- "@dereekb/firebase": "14.0.0",
71
- "@dereekb/model": "14.0.0",
72
- "@dereekb/nestjs": "14.0.0",
73
- "@dereekb/util": "14.0.0",
69
+ "@dereekb/date": "14.1.0",
70
+ "@dereekb/firebase": "14.1.0",
71
+ "@dereekb/model": "14.1.0",
72
+ "@dereekb/nestjs": "14.1.0",
73
+ "@dereekb/util": "14.1.0",
74
74
  "@nestjs/common": "^12.0.1",
75
75
  "arktype": "^2.2.0",
76
76
  "firebase": "^12.18.0",
77
77
  "jiti": "2.7.0",
78
- "prettier": "3.9.6",
78
+ "oxfmt": "^0.66.0",
79
79
  "ts-morph": "^28.0.0",
80
80
  "vitest": "4.1.11",
81
81
  "yargs": "^18.0.0"