@geoql/doctor-core 1.3.0 → 1.3.1

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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
2
2
  import { performance } from "node:perf_hooks";
3
- import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
3
+ import { access, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
4
4
  import { createRequire } from "node:module";
5
5
  import { execFile, spawn } from "node:child_process";
6
6
  import { parseSync } from "oxc-parser";
@@ -227,7 +227,84 @@ async function checkBuildQuality(projectInfo) {
227
227
  //#endregion
228
228
  //#region src/dead-code/build-knip-config.ts
229
229
  const DEMO_WORKSPACES = ["example", "playground"];
230
- function buildKnipConfig(projectInfo, doctorConfig) {
230
+ async function findPnpmWorkspaceYaml(startDir) {
231
+ let dir = startDir;
232
+ for (let i = 0; i < 5; i++) {
233
+ const candidate = join(dir, "pnpm-workspace.yaml");
234
+ try {
235
+ if ((await stat(candidate)).isFile()) return candidate;
236
+ } catch {}
237
+ const parent = dirname(dir);
238
+ if (parent === dir) break;
239
+ dir = parent;
240
+ }
241
+ return null;
242
+ }
243
+ function collectCatalogNames(packageJson) {
244
+ const names = /* @__PURE__ */ new Set();
245
+ const fields = [
246
+ packageJson.dependencies,
247
+ packageJson.devDependencies,
248
+ packageJson.peerDependencies,
249
+ packageJson.optionalDependencies
250
+ ];
251
+ for (const field of fields) {
252
+ if (!field) continue;
253
+ for (const version of Object.values(field)) if (typeof version === "string" && version.startsWith("catalog:")) names.add(version.slice(8));
254
+ }
255
+ return names;
256
+ }
257
+ function extractCatalogDependencies(workspaceYaml, catalogName) {
258
+ const lines = workspaceYaml.split(/\r?\n/);
259
+ const deps = [];
260
+ let inCatalogs = false;
261
+ let inTargetBlock = false;
262
+ let blockIndent = -1;
263
+ const escapedCatalogName = catalogName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
264
+ const catalogPattern = new RegExp(`^${escapedCatalogName}\\s*:\\s*$`);
265
+ for (const line of lines) {
266
+ const trimmed = line.trim();
267
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
268
+ if (!inCatalogs) {
269
+ if (/^catalogs\s*:/.test(trimmed)) inCatalogs = true;
270
+ continue;
271
+ }
272
+ const indent = line.length - line.trimStart().length;
273
+ if (inTargetBlock) {
274
+ if (indent <= blockIndent) break;
275
+ const match = line.match(/^[ \t]*["']?([@a-zA-Z0-9_./-]+)["']?[ \t]*:/);
276
+ if (match) deps.push(match[1]);
277
+ continue;
278
+ }
279
+ if (catalogPattern.test(trimmed)) {
280
+ inTargetBlock = true;
281
+ blockIndent = indent;
282
+ } else if (indent === 0 && /^\w+/.test(trimmed) && !/^catalogs\b/.test(trimmed)) break;
283
+ }
284
+ return deps;
285
+ }
286
+ async function resolveCatalogDependencies(rootDirectory) {
287
+ const workspacePath = await findPnpmWorkspaceYaml(rootDirectory);
288
+ if (!workspacePath) return [];
289
+ let packageJson;
290
+ try {
291
+ packageJson = JSON.parse(await readFile(join(rootDirectory, "package.json"), "utf8"));
292
+ } catch {
293
+ return [];
294
+ }
295
+ const catalogNames = collectCatalogNames(packageJson);
296
+ if (catalogNames.size === 0) return [];
297
+ let workspaceYaml;
298
+ try {
299
+ workspaceYaml = await readFile(workspacePath, "utf8");
300
+ } catch {
301
+ return [];
302
+ }
303
+ const resolved = /* @__PURE__ */ new Set();
304
+ for (const catalogName of catalogNames) for (const dep of extractCatalogDependencies(workspaceYaml, catalogName)) resolved.add(dep);
305
+ return [...resolved];
306
+ }
307
+ async function buildKnipConfig(projectInfo, doctorConfig) {
231
308
  const isNuxt = projectInfo.framework === "nuxt";
232
309
  const usesConventionResolution = projectInfo.hasAutoImports || projectInfo.hasComponentsAutoImport || projectInfo.hasVueRouter;
233
310
  const entry = isNuxt ? [
@@ -248,6 +325,7 @@ function buildKnipConfig(projectInfo, doctorConfig) {
248
325
  "src/stores/**/*.ts"
249
326
  ] : []
250
327
  ];
328
+ const catalogDependencies = await resolveCatalogDependencies(projectInfo.rootDirectory);
251
329
  const config = {
252
330
  cwd: projectInfo.rootDirectory,
253
331
  entry,
@@ -259,11 +337,12 @@ function buildKnipConfig(projectInfo, doctorConfig) {
259
337
  "!**/knip.config.mjs"
260
338
  ],
261
339
  ignoreFiles: [...doctorConfig.exclude, "knip.config.mjs"],
262
- ignoreDependencies: [
340
+ ignoreDependencies: [...new Set([
263
341
  "vite-plus",
264
342
  "@geoql/vue-doctor",
265
- "@geoql/nuxt-doctor"
266
- ],
343
+ "@geoql/nuxt-doctor",
344
+ ...catalogDependencies
345
+ ])],
267
346
  ignoreWorkspaces: [...DEMO_WORKSPACES]
268
347
  };
269
348
  if (isNuxt) config.compilers = { nuxt: true };
@@ -421,7 +500,7 @@ async function writeKnipConfig(rootDir, config) {
421
500
  }
422
501
  async function checkDeadCode(options) {
423
502
  if (!options.enabled) return [];
424
- const config = buildKnipConfig(options.projectInfo, options.doctorConfig);
503
+ const config = await buildKnipConfig(options.projectInfo, options.doctorConfig);
425
504
  const timeoutMs = options.timeoutMs ?? 3e4;
426
505
  let createOptions;
427
506
  let run;
@@ -1611,6 +1690,7 @@ async function generateOxlintConfig(input) {
1611
1690
  ...userConfig ? { extends: [userConfig] } : {},
1612
1691
  plugins: ["vue"],
1613
1692
  jsPlugins: input.pluginPaths,
1693
+ ...input.exclude && input.exclude.length > 0 ? { ignorePatterns: input.exclude } : {},
1614
1694
  rules
1615
1695
  };
1616
1696
  const configPath = join(dir, ".oxlintrc.json");
@@ -1816,7 +1896,8 @@ async function runScriptPass(opts) {
1816
1896
  pluginPaths,
1817
1897
  ruleOverrides: opts.ruleOverrides,
1818
1898
  rootDir: opts.rootDir,
1819
- framework: opts.framework
1899
+ framework: opts.framework,
1900
+ exclude: opts.exclude
1820
1901
  });
1821
1902
  try {
1822
1903
  const raw = await runOxlint({
@@ -4127,7 +4208,8 @@ async function audit(config = {}) {
4127
4208
  ruleOverrides: config.rules,
4128
4209
  framework: project.framework === "nuxt" ? "nuxt" : "vue",
4129
4210
  fix: config.fix === true && config.scopeFiles === void 0,
4130
- fixExcludes: config.fixExcludes
4211
+ fixExcludes: config.fixExcludes,
4212
+ exclude
4131
4213
  });
4132
4214
  scriptDiagnostics = result.diagnostics;
4133
4215
  oxlintStderr = result.stderr;