@doccov/sdk 0.24.0 → 0.24.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.
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ import {
51
51
  saveSnapshot,
52
52
  serializeJSDoc,
53
53
  ts
54
- } from "./shared/chunk-c1f9mytc.js";
54
+ } from "./shared/chunk-p1stkhse.js";
55
55
  import {
56
56
  mergeFilters,
57
57
  parseListFlag
@@ -67,6 +67,140 @@ import {
67
67
  getReportPath
68
68
  } from "./shared/chunk-esptwrfq.js";
69
69
 
70
+ // src/analysis/doccov-builder.ts
71
+ import { DRIFT_CATEGORIES as DRIFT_CATEGORIES2 } from "@doccov/spec";
72
+ function buildDocCovSpec(options) {
73
+ const { openpkg, openpkgPath } = options;
74
+ const registry = buildExportRegistry(openpkg);
75
+ const exports = {};
76
+ let totalScore = 0;
77
+ let documentedCount = 0;
78
+ const missingByRule = {
79
+ description: 0,
80
+ params: 0,
81
+ returns: 0,
82
+ examples: 0,
83
+ throws: 0
84
+ };
85
+ const driftByCategory = {
86
+ structural: 0,
87
+ semantic: 0,
88
+ example: 0
89
+ };
90
+ let totalDrift = 0;
91
+ let fixableDrift = 0;
92
+ for (const exp of openpkg.exports ?? []) {
93
+ const coverage = computeExportCoverage(exp);
94
+ const rawDrifts = computeExportDrift(exp, registry);
95
+ const categorizedDrifts = rawDrifts.map((d) => toCategorizedDrift(d));
96
+ const exportId = exp.id ?? exp.name;
97
+ exports[exportId] = {
98
+ coverageScore: coverage.score,
99
+ missing: coverage.missing.length > 0 ? coverage.missing : undefined,
100
+ drift: categorizedDrifts.length > 0 ? categorizedDrifts : undefined
101
+ };
102
+ totalScore += coverage.score;
103
+ if (coverage.score === 100)
104
+ documentedCount++;
105
+ for (const rule of coverage.missing) {
106
+ missingByRule[rule]++;
107
+ }
108
+ for (const d of categorizedDrifts) {
109
+ driftByCategory[d.category]++;
110
+ totalDrift++;
111
+ if (d.fixable)
112
+ fixableDrift++;
113
+ }
114
+ }
115
+ const exportCount = openpkg.exports?.length ?? 0;
116
+ const summary = {
117
+ score: exportCount > 0 ? Math.round(totalScore / exportCount) : 100,
118
+ totalExports: exportCount,
119
+ documentedExports: documentedCount,
120
+ missingByRule,
121
+ drift: {
122
+ total: totalDrift,
123
+ fixable: fixableDrift,
124
+ byCategory: driftByCategory
125
+ }
126
+ };
127
+ return {
128
+ doccov: "1.0.0",
129
+ source: {
130
+ file: openpkgPath,
131
+ specVersion: openpkg.openpkg,
132
+ packageName: openpkg.meta.name,
133
+ packageVersion: openpkg.meta.version
134
+ },
135
+ generatedAt: new Date().toISOString(),
136
+ summary,
137
+ exports
138
+ };
139
+ }
140
+ function computeExportCoverage(exp) {
141
+ const missing = [];
142
+ let points = 0;
143
+ let maxPoints = 0;
144
+ maxPoints += 30;
145
+ if (exp.description && exp.description.trim().length > 0) {
146
+ points += 30;
147
+ } else {
148
+ missing.push("description");
149
+ }
150
+ const isCallable = exp.kind === "function" || exp.kind === "class";
151
+ if (isCallable && exp.signatures?.length) {
152
+ const sig = exp.signatures[0];
153
+ const params = sig.parameters ?? [];
154
+ if (params.length > 0) {
155
+ maxPoints += 25;
156
+ const documentedParams = params.filter((p) => p.description && p.description.trim().length > 0);
157
+ if (documentedParams.length === params.length) {
158
+ points += 25;
159
+ } else if (documentedParams.length > 0) {
160
+ points += Math.round(documentedParams.length / params.length * 25);
161
+ missing.push("params");
162
+ } else {
163
+ missing.push("params");
164
+ }
165
+ }
166
+ if (exp.kind === "function" && sig.returns) {
167
+ maxPoints += 20;
168
+ if (sig.returns.description && sig.returns.description.trim().length > 0) {
169
+ points += 20;
170
+ } else {
171
+ missing.push("returns");
172
+ }
173
+ }
174
+ if (sig.throws && sig.throws.length > 0) {
175
+ maxPoints += 10;
176
+ const documentedThrows = sig.throws.filter((t) => t.description);
177
+ if (documentedThrows.length === sig.throws.length) {
178
+ points += 10;
179
+ } else {
180
+ missing.push("throws");
181
+ }
182
+ }
183
+ }
184
+ maxPoints += 15;
185
+ if (exp.examples && exp.examples.length > 0) {
186
+ points += 15;
187
+ } else {
188
+ missing.push("examples");
189
+ }
190
+ const score = maxPoints > 0 ? Math.round(points / maxPoints * 100) : 100;
191
+ return { score, missing };
192
+ }
193
+ function toCategorizedDrift(drift) {
194
+ const driftType = drift.type;
195
+ return {
196
+ type: driftType,
197
+ target: drift.target,
198
+ issue: drift.issue,
199
+ suggestion: drift.suggestion,
200
+ category: DRIFT_CATEGORIES2[driftType],
201
+ fixable: isFixableDrift(drift)
202
+ };
203
+ }
70
204
  // src/openpkg.ts
71
205
  import * as fsSync from "node:fs";
72
206
  import * as fs5 from "node:fs/promises";
@@ -4191,163 +4325,130 @@ function resolvePackageDir(entryFile) {
4191
4325
  currentDir = parentDir;
4192
4326
  }
4193
4327
  }
4194
- // src/analysis/doccov-builder.ts
4195
- import { DRIFT_CATEGORIES as DRIFT_CATEGORIES2 } from "@doccov/spec";
4196
- function buildDocCovSpec(options) {
4197
- const { openpkg, openpkgPath } = options;
4198
- const registry = buildExportRegistry(openpkg);
4199
- const exports = {};
4200
- let totalScore = 0;
4201
- let documentedCount = 0;
4202
- const missingByRule = {
4203
- description: 0,
4204
- params: 0,
4205
- returns: 0,
4206
- examples: 0,
4207
- throws: 0
4208
- };
4209
- const driftByCategory = {
4210
- structural: 0,
4211
- semantic: 0,
4212
- example: 0
4213
- };
4214
- let totalDrift = 0;
4215
- let fixableDrift = 0;
4216
- for (const exp of openpkg.exports ?? []) {
4217
- const coverage = computeExportCoverage(exp);
4218
- const rawDrifts = computeExportDrift(exp, registry);
4219
- const categorizedDrifts = rawDrifts.map((d) => toCategorizedDrift(d));
4220
- const exportId = exp.id ?? exp.name;
4221
- exports[exportId] = {
4222
- coverageScore: coverage.score,
4223
- missing: coverage.missing.length > 0 ? coverage.missing : undefined,
4224
- drift: categorizedDrifts.length > 0 ? categorizedDrifts : undefined
4225
- };
4226
- totalScore += coverage.score;
4227
- if (coverage.score === 100)
4228
- documentedCount++;
4229
- for (const rule of coverage.missing) {
4230
- missingByRule[rule]++;
4231
- }
4232
- for (const d of categorizedDrifts) {
4233
- driftByCategory[d.category]++;
4234
- totalDrift++;
4235
- if (d.fixable)
4236
- fixableDrift++;
4237
- }
4238
- }
4239
- const exportCount = openpkg.exports?.length ?? 0;
4240
- const summary = {
4241
- score: exportCount > 0 ? Math.round(totalScore / exportCount) : 100,
4242
- totalExports: exportCount,
4243
- documentedExports: documentedCount,
4244
- missingByRule,
4245
- drift: {
4246
- total: totalDrift,
4247
- fixable: fixableDrift,
4248
- byCategory: driftByCategory
4249
- }
4250
- };
4251
- return {
4252
- doccov: "1.0.0",
4253
- source: {
4254
- file: openpkgPath,
4255
- specVersion: openpkg.openpkg,
4256
- packageName: openpkg.meta.name,
4257
- packageVersion: openpkg.meta.version
4258
- },
4259
- generatedAt: new Date().toISOString(),
4260
- summary,
4261
- exports
4262
- };
4328
+ // src/config/types.ts
4329
+ function defineConfig(config) {
4330
+ return config;
4263
4331
  }
4264
- function computeExportCoverage(exp) {
4265
- const missing = [];
4266
- let points = 0;
4267
- let maxPoints = 0;
4268
- maxPoints += 30;
4269
- if (exp.description && exp.description.trim().length > 0) {
4270
- points += 30;
4271
- } else {
4272
- missing.push("description");
4332
+ // src/detect/utils.ts
4333
+ async function safeParseJson(fs6, path9) {
4334
+ try {
4335
+ if (!await fs6.exists(path9))
4336
+ return null;
4337
+ const content = await fs6.readFile(path9);
4338
+ return JSON.parse(content);
4339
+ } catch {
4340
+ return null;
4273
4341
  }
4274
- const isCallable = exp.kind === "function" || exp.kind === "class";
4275
- if (isCallable && exp.signatures?.length) {
4276
- const sig = exp.signatures[0];
4277
- const params = sig.parameters ?? [];
4278
- if (params.length > 0) {
4279
- maxPoints += 25;
4280
- const documentedParams = params.filter((p) => p.description && p.description.trim().length > 0);
4281
- if (documentedParams.length === params.length) {
4282
- points += 25;
4283
- } else if (documentedParams.length > 0) {
4284
- points += Math.round(documentedParams.length / params.length * 25);
4285
- missing.push("params");
4286
- } else {
4287
- missing.push("params");
4288
- }
4289
- }
4290
- if (exp.kind === "function" && sig.returns) {
4291
- maxPoints += 20;
4292
- if (sig.returns.description && sig.returns.description.trim().length > 0) {
4293
- points += 20;
4294
- } else {
4295
- missing.push("returns");
4296
- }
4297
- }
4298
- if (sig.throws && sig.throws.length > 0) {
4299
- maxPoints += 10;
4300
- const documentedThrows = sig.throws.filter((t) => t.description);
4301
- if (documentedThrows.length === sig.throws.length) {
4302
- points += 10;
4303
- } else {
4304
- missing.push("throws");
4305
- }
4306
- }
4307
- }
4308
- maxPoints += 15;
4309
- if (exp.examples && exp.examples.length > 0) {
4310
- points += 15;
4311
- } else {
4312
- missing.push("examples");
4313
- }
4314
- const score = maxPoints > 0 ? Math.round(points / maxPoints * 100) : 100;
4315
- return { score, missing };
4316
4342
  }
4317
- function toCategorizedDrift(drift) {
4318
- const driftType = drift.type;
4343
+ async function readPackageJson(fs6, dir) {
4344
+ const path9 = dir === "." ? "package.json" : `${dir}/package.json`;
4345
+ return safeParseJson(fs6, path9);
4346
+ }
4347
+
4348
+ // src/detect/build.ts
4349
+ var BUILD_SCRIPT_NAMES = new Set([
4350
+ "build",
4351
+ "compile",
4352
+ "tsc",
4353
+ "bundle",
4354
+ "prepare",
4355
+ "prepublish",
4356
+ "prepublishOnly"
4357
+ ]);
4358
+ var BUILD_SCRIPT_PREFIXES = ["build:", "compile:", "bundle:"];
4359
+ var BUILD_TOOL_PATTERNS = [
4360
+ "tsc",
4361
+ "esbuild",
4362
+ "rollup",
4363
+ "webpack",
4364
+ "vite build",
4365
+ "parcel build",
4366
+ "swc",
4367
+ "tsup",
4368
+ "unbuild",
4369
+ "bunup",
4370
+ "pkgroll",
4371
+ "microbundle",
4372
+ "babel",
4373
+ "ncc build"
4374
+ ];
4375
+ async function detectBuildInfo(fs6, packagePath = ".") {
4376
+ const pkgJson = await readPackageJson(fs6, packagePath);
4377
+ const scripts = pkgJson?.scripts ?? {};
4378
+ const scriptNames = Object.keys(scripts);
4379
+ const buildScriptsByName = scriptNames.filter((name) => BUILD_SCRIPT_NAMES.has(name) || BUILD_SCRIPT_PREFIXES.some((prefix) => name.startsWith(prefix)));
4380
+ const buildScriptsByContent = scriptNames.filter((name) => {
4381
+ if (buildScriptsByName.includes(name))
4382
+ return false;
4383
+ const content = scripts[name] ?? "";
4384
+ return BUILD_TOOL_PATTERNS.some((tool) => content.includes(tool) && !content.includes(`${tool}-`));
4385
+ });
4386
+ const buildScripts = [...new Set([...buildScriptsByName, ...buildScriptsByContent])];
4387
+ const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
4388
+ const hasTsConfig = await fs6.exists(tsconfigPath);
4389
+ const hasTsDep = pkgJson?.devDependencies?.typescript !== undefined || pkgJson?.dependencies?.typescript !== undefined;
4390
+ const hasTypeScript = hasTsConfig || hasTsDep;
4391
+ const wasm = await detectWasmProject(fs6, packagePath, pkgJson);
4392
+ const napi = detectNapiProject(pkgJson);
4319
4393
  return {
4320
- type: driftType,
4321
- target: drift.target,
4322
- issue: drift.issue,
4323
- suggestion: drift.suggestion,
4324
- category: DRIFT_CATEGORIES2[driftType],
4325
- fixable: isFixableDrift(drift)
4394
+ scripts: buildScripts,
4395
+ hasBuildScript: buildScripts.length > 0,
4396
+ hasTypeScript,
4397
+ exoticIndicators: { wasm, napi }
4326
4398
  };
4327
4399
  }
4328
- // src/config/types.ts
4329
- function defineConfig(config) {
4330
- return config;
4331
- }
4332
- // src/resolve/index.ts
4333
- import * as path9 from "node:path";
4334
-
4335
- // src/detect/utils.ts
4336
- async function safeParseJson(fs6, path9) {
4337
- try {
4338
- if (!await fs6.exists(path9))
4339
- return null;
4340
- const content = await fs6.readFile(path9);
4341
- return JSON.parse(content);
4342
- } catch {
4343
- return null;
4400
+ var WASM_PACKAGES = new Set([
4401
+ "wasm-pack",
4402
+ "@aspect/wasm-pack",
4403
+ "@aspect-build/wasm-pack",
4404
+ "@aspect/rules_js",
4405
+ "wasm-bindgen",
4406
+ "@aspect/aspect-cli"
4407
+ ]);
4408
+ async function detectWasmProject(fs6, packagePath, pkgJson) {
4409
+ const pkgCargoPath = packagePath === "." ? "Cargo.toml" : `${packagePath}/Cargo.toml`;
4410
+ if (await fs6.exists(pkgCargoPath))
4411
+ return true;
4412
+ if (packagePath !== "." && await fs6.exists("Cargo.toml"))
4413
+ return true;
4414
+ if (pkgJson) {
4415
+ const deps = Object.keys({
4416
+ ...pkgJson.dependencies ?? {},
4417
+ ...pkgJson.devDependencies ?? {}
4418
+ });
4419
+ if (deps.some((dep) => WASM_PACKAGES.has(dep))) {
4420
+ return true;
4421
+ }
4344
4422
  }
4423
+ return false;
4345
4424
  }
4346
- async function readPackageJson(fs6, dir) {
4347
- const path9 = dir === "." ? "package.json" : `${dir}/package.json`;
4348
- return safeParseJson(fs6, path9);
4425
+ var NAPI_PACKAGES = new Set([
4426
+ "@napi-rs/cli",
4427
+ "@aspect/napi-cli",
4428
+ "napi",
4429
+ "napi-build",
4430
+ "napi-rs",
4431
+ "neon",
4432
+ "@aspect/neon-cli"
4433
+ ]);
4434
+ function detectNapiProject(pkgJson) {
4435
+ if (!pkgJson)
4436
+ return false;
4437
+ const deps = Object.keys({
4438
+ ...pkgJson.dependencies ?? {},
4439
+ ...pkgJson.devDependencies ?? {}
4440
+ });
4441
+ return deps.some((dep) => NAPI_PACKAGES.has(dep) || dep.includes("@aspect/napi"));
4442
+ }
4443
+ function getPrimaryBuildScript(buildInfo) {
4444
+ if (buildInfo.scripts.includes("build"))
4445
+ return "build";
4446
+ if (buildInfo.scripts.includes("compile"))
4447
+ return "compile";
4448
+ if (buildInfo.scripts.includes("tsc"))
4449
+ return "tsc";
4450
+ return buildInfo.scripts[0] ?? null;
4349
4451
  }
4350
-
4351
4452
  // src/detect/entry-point.ts
4352
4453
  async function detectEntryPoint(fs6, packagePath = ".") {
4353
4454
  const pkgJson = await readPackageJson(fs6, packagePath);
@@ -4472,13 +4573,110 @@ async function resolveToSource(fs6, basePath, filePath, tsConfig) {
4472
4573
  }
4473
4574
  return null;
4474
4575
  }
4576
+ // src/detect/filesystem.ts
4577
+ import * as fs6 from "node:fs";
4578
+ import * as nodePath from "node:path";
4579
+ import { Writable } from "node:stream";
4580
+
4581
+ class NodeFileSystem {
4582
+ basePath;
4583
+ constructor(basePath) {
4584
+ this.basePath = basePath;
4585
+ }
4586
+ resolve(relativePath) {
4587
+ return nodePath.join(this.basePath, relativePath);
4588
+ }
4589
+ async exists(relativePath) {
4590
+ return fs6.existsSync(this.resolve(relativePath));
4591
+ }
4592
+ async readFile(relativePath) {
4593
+ return fs6.readFileSync(this.resolve(relativePath), "utf-8");
4594
+ }
4595
+ async readDir(relativePath) {
4596
+ return fs6.readdirSync(this.resolve(relativePath));
4597
+ }
4598
+ async isDirectory(relativePath) {
4599
+ const fullPath = this.resolve(relativePath);
4600
+ if (!fs6.existsSync(fullPath))
4601
+ return false;
4602
+ return fs6.statSync(fullPath).isDirectory();
4603
+ }
4604
+ }
4605
+ function createCaptureStream() {
4606
+ let output = "";
4607
+ const stream = new Writable({
4608
+ write(chunk, _encoding, callback) {
4609
+ output += chunk.toString();
4610
+ callback();
4611
+ }
4612
+ });
4613
+ return { stream, getOutput: () => output };
4614
+ }
4615
+
4616
+ class FileNotFoundError extends Error {
4617
+ path;
4618
+ constructor(path9, message) {
4619
+ super(message ?? `File not found: ${path9}`);
4620
+ this.path = path9;
4621
+ this.name = "FileNotFoundError";
4622
+ }
4623
+ }
4475
4624
 
4625
+ class SandboxFileSystem {
4626
+ sandbox;
4627
+ constructor(sandbox) {
4628
+ this.sandbox = sandbox;
4629
+ }
4630
+ async exists(path9) {
4631
+ const result = await this.sandbox.runCommand({
4632
+ cmd: "test",
4633
+ args: ["-e", path9]
4634
+ });
4635
+ return result.exitCode === 0;
4636
+ }
4637
+ async readFile(path9) {
4638
+ const exists = await this.exists(path9);
4639
+ if (!exists) {
4640
+ throw new FileNotFoundError(path9);
4641
+ }
4642
+ const capture = createCaptureStream();
4643
+ const result = await this.sandbox.runCommand({
4644
+ cmd: "cat",
4645
+ args: [path9],
4646
+ stdout: capture.stream
4647
+ });
4648
+ if (result.exitCode !== 0) {
4649
+ throw new FileNotFoundError(path9, `Failed to read file: ${path9}`);
4650
+ }
4651
+ return capture.getOutput();
4652
+ }
4653
+ async readDir(path9) {
4654
+ const capture = createCaptureStream();
4655
+ const result = await this.sandbox.runCommand({
4656
+ cmd: "ls",
4657
+ args: ["-1", path9],
4658
+ stdout: capture.stream
4659
+ });
4660
+ if (result.exitCode !== 0) {
4661
+ return [];
4662
+ }
4663
+ return capture.getOutput().split(`
4664
+ `).filter(Boolean);
4665
+ }
4666
+ async isDirectory(path9) {
4667
+ const result = await this.sandbox.runCommand({
4668
+ cmd: "test",
4669
+ args: ["-d", path9]
4670
+ });
4671
+ return result.exitCode === 0;
4672
+ }
4673
+ }
4476
4674
  // src/detect/monorepo.ts
4477
- async function detectMonorepo(fs6) {
4478
- const pkgJson = await readPackageJson(fs6, ".");
4675
+ async function detectMonorepo(fs7) {
4676
+ const pkgJson = await readPackageJson(fs7, ".");
4479
4677
  if (pkgJson?.workspaces) {
4480
4678
  const patterns = extractWorkspacePatterns(pkgJson.workspaces);
4481
- const packages = await resolveWorkspacePackages(fs6, patterns, pkgJson.name, pkgJson.private);
4679
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson.name, pkgJson.private);
4482
4680
  return {
4483
4681
  isMonorepo: packages.length > 0,
4484
4682
  type: "npm-workspaces",
@@ -4486,10 +4684,10 @@ async function detectMonorepo(fs6) {
4486
4684
  packages
4487
4685
  };
4488
4686
  }
4489
- if (await fs6.exists("pnpm-workspace.yaml")) {
4490
- const content = await fs6.readFile("pnpm-workspace.yaml");
4687
+ if (await fs7.exists("pnpm-workspace.yaml")) {
4688
+ const content = await fs7.readFile("pnpm-workspace.yaml");
4491
4689
  const patterns = parsePnpmWorkspace(content);
4492
- const packages = await resolveWorkspacePackages(fs6, patterns, pkgJson?.name, pkgJson?.private);
4690
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson?.name, pkgJson?.private);
4493
4691
  return {
4494
4692
  isMonorepo: packages.length > 0,
4495
4693
  type: "pnpm-workspaces",
@@ -4497,10 +4695,10 @@ async function detectMonorepo(fs6) {
4497
4695
  packages
4498
4696
  };
4499
4697
  }
4500
- if (await fs6.exists("lerna.json")) {
4501
- const lerna = await safeParseJson(fs6, "lerna.json");
4698
+ if (await fs7.exists("lerna.json")) {
4699
+ const lerna = await safeParseJson(fs7, "lerna.json");
4502
4700
  const patterns = lerna?.packages ?? ["packages/*"];
4503
- const packages = await resolveWorkspacePackages(fs6, patterns, pkgJson?.name, pkgJson?.private);
4701
+ const packages = await resolveWorkspacePackages(fs7, patterns, pkgJson?.name, pkgJson?.private);
4504
4702
  return {
4505
4703
  isMonorepo: packages.length > 0,
4506
4704
  type: "lerna",
@@ -4574,7 +4772,7 @@ function parsePnpmWorkspace(content) {
4574
4772
  }
4575
4773
  return patterns.length > 0 ? patterns : ["packages/*"];
4576
4774
  }
4577
- async function resolveWorkspacePackages(fs6, patterns, rootPackageName, rootIsPrivate) {
4775
+ async function resolveWorkspacePackages(fs7, patterns, rootPackageName, rootIsPrivate) {
4578
4776
  const packages = [];
4579
4777
  const seen = new Set;
4580
4778
  if (rootPackageName && !rootIsPrivate && rootPackageName !== "root") {
@@ -4596,18 +4794,18 @@ async function resolveWorkspacePackages(fs6, patterns, rootPackageName, rootIsPr
4596
4794
  }
4597
4795
  dirsToScan.add("packages");
4598
4796
  for (const dir of dirsToScan) {
4599
- if (!await fs6.exists(dir))
4797
+ if (!await fs7.exists(dir))
4600
4798
  continue;
4601
- if (!await fs6.isDirectory(dir))
4799
+ if (!await fs7.isDirectory(dir))
4602
4800
  continue;
4603
- const subdirs = await fs6.readDir(dir);
4801
+ const subdirs = await fs7.readDir(dir);
4604
4802
  for (const subdir of subdirs) {
4605
4803
  const pkgPath = `${dir}/${subdir}`;
4606
4804
  const pkgJsonPath = `${pkgPath}/package.json`;
4607
- if (!await fs6.exists(pkgJsonPath))
4805
+ if (!await fs7.exists(pkgJsonPath))
4608
4806
  continue;
4609
4807
  try {
4610
- const content = await fs6.readFile(pkgJsonPath);
4808
+ const content = await fs7.readFile(pkgJsonPath);
4611
4809
  const pkg = JSON.parse(content);
4612
4810
  if (pkg.name && !seen.has(pkg.name)) {
4613
4811
  seen.add(pkg.name);
@@ -4634,290 +4832,24 @@ function formatPackageList(packages, limit = 10) {
4634
4832
  return lines.join(`
4635
4833
  `);
4636
4834
  }
4637
-
4638
- // src/resolve/index.ts
4639
- async function resolveTarget(fs6, options) {
4640
- let targetDir = options.cwd;
4641
- let packageInfo;
4642
- if (options.package) {
4643
- const mono = await detectMonorepo(fs6);
4644
- if (!mono.isMonorepo) {
4645
- throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
4835
+ // src/detect/package-manager.ts
4836
+ var PM_CONFIGS = {
4837
+ pnpm: {
4838
+ lockfiles: ["pnpm-lock.yaml"],
4839
+ info: {
4840
+ name: "pnpm",
4841
+ lockfile: "pnpm-lock.yaml",
4842
+ installArgs: ["install", "--frozen-lockfile"],
4843
+ runPrefix: ["pnpm"]
4646
4844
  }
4647
- const pkg = findPackageByName(mono.packages, options.package);
4648
- if (!pkg) {
4649
- const available = mono.packages.map((p) => p.name).join(", ");
4650
- throw new Error(`Package "${options.package}" not found. Available: ${available}`);
4651
- }
4652
- targetDir = path9.join(options.cwd, pkg.path);
4653
- packageInfo = pkg;
4654
- }
4655
- let entryFile;
4656
- let entryPointInfo;
4657
- if (!options.entry) {
4658
- entryPointInfo = await detectEntryPoint(fs6, getRelativePath(options.cwd, targetDir));
4659
- entryFile = path9.join(targetDir, entryPointInfo.path);
4660
- } else {
4661
- const explicitPath = path9.resolve(targetDir, options.entry);
4662
- const isDirectory = await isDir(fs6, getRelativePath(options.cwd, explicitPath));
4663
- if (isDirectory) {
4664
- targetDir = explicitPath;
4665
- entryPointInfo = await detectEntryPoint(fs6, getRelativePath(options.cwd, explicitPath));
4666
- entryFile = path9.join(explicitPath, entryPointInfo.path);
4667
- } else {
4668
- entryFile = explicitPath;
4669
- entryPointInfo = {
4670
- path: options.entry,
4671
- source: "explicit",
4672
- isDeclarationOnly: options.entry.endsWith(".d.ts")
4673
- };
4674
- }
4675
- }
4676
- return {
4677
- targetDir,
4678
- entryFile,
4679
- packageInfo,
4680
- entryPointInfo
4681
- };
4682
- }
4683
- function getRelativePath(base, target) {
4684
- if (base === target)
4685
- return ".";
4686
- const rel = path9.relative(base, target);
4687
- return rel || ".";
4688
- }
4689
- async function isDir(fs6, relativePath) {
4690
- const hasPackageJson = await fs6.exists(path9.join(relativePath, "package.json"));
4691
- if (hasPackageJson)
4692
- return true;
4693
- const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
4694
- for (const entry of commonEntryFiles) {
4695
- if (await fs6.exists(path9.join(relativePath, entry))) {
4696
- return true;
4697
- }
4698
- }
4699
- return !path9.extname(relativePath);
4700
- }
4701
- // src/detect/build.ts
4702
- var BUILD_SCRIPT_NAMES = new Set([
4703
- "build",
4704
- "compile",
4705
- "tsc",
4706
- "bundle",
4707
- "prepare",
4708
- "prepublish",
4709
- "prepublishOnly"
4710
- ]);
4711
- var BUILD_SCRIPT_PREFIXES = ["build:", "compile:", "bundle:"];
4712
- var BUILD_TOOL_PATTERNS = [
4713
- "tsc",
4714
- "esbuild",
4715
- "rollup",
4716
- "webpack",
4717
- "vite build",
4718
- "parcel build",
4719
- "swc",
4720
- "tsup",
4721
- "unbuild",
4722
- "bunup",
4723
- "pkgroll",
4724
- "microbundle",
4725
- "babel",
4726
- "ncc build"
4727
- ];
4728
- async function detectBuildInfo(fs6, packagePath = ".") {
4729
- const pkgJson = await readPackageJson(fs6, packagePath);
4730
- const scripts = pkgJson?.scripts ?? {};
4731
- const scriptNames = Object.keys(scripts);
4732
- const buildScriptsByName = scriptNames.filter((name) => BUILD_SCRIPT_NAMES.has(name) || BUILD_SCRIPT_PREFIXES.some((prefix) => name.startsWith(prefix)));
4733
- const buildScriptsByContent = scriptNames.filter((name) => {
4734
- if (buildScriptsByName.includes(name))
4735
- return false;
4736
- const content = scripts[name] ?? "";
4737
- return BUILD_TOOL_PATTERNS.some((tool) => content.includes(tool) && !content.includes(`${tool}-`));
4738
- });
4739
- const buildScripts = [...new Set([...buildScriptsByName, ...buildScriptsByContent])];
4740
- const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
4741
- const hasTsConfig = await fs6.exists(tsconfigPath);
4742
- const hasTsDep = pkgJson?.devDependencies?.typescript !== undefined || pkgJson?.dependencies?.typescript !== undefined;
4743
- const hasTypeScript = hasTsConfig || hasTsDep;
4744
- const wasm = await detectWasmProject(fs6, packagePath, pkgJson);
4745
- const napi = detectNapiProject(pkgJson);
4746
- return {
4747
- scripts: buildScripts,
4748
- hasBuildScript: buildScripts.length > 0,
4749
- hasTypeScript,
4750
- exoticIndicators: { wasm, napi }
4751
- };
4752
- }
4753
- var WASM_PACKAGES = new Set([
4754
- "wasm-pack",
4755
- "@aspect/wasm-pack",
4756
- "@aspect-build/wasm-pack",
4757
- "@aspect/rules_js",
4758
- "wasm-bindgen",
4759
- "@aspect/aspect-cli"
4760
- ]);
4761
- async function detectWasmProject(fs6, packagePath, pkgJson) {
4762
- const pkgCargoPath = packagePath === "." ? "Cargo.toml" : `${packagePath}/Cargo.toml`;
4763
- if (await fs6.exists(pkgCargoPath))
4764
- return true;
4765
- if (packagePath !== "." && await fs6.exists("Cargo.toml"))
4766
- return true;
4767
- if (pkgJson) {
4768
- const deps = Object.keys({
4769
- ...pkgJson.dependencies ?? {},
4770
- ...pkgJson.devDependencies ?? {}
4771
- });
4772
- if (deps.some((dep) => WASM_PACKAGES.has(dep))) {
4773
- return true;
4774
- }
4775
- }
4776
- return false;
4777
- }
4778
- var NAPI_PACKAGES = new Set([
4779
- "@napi-rs/cli",
4780
- "@aspect/napi-cli",
4781
- "napi",
4782
- "napi-build",
4783
- "napi-rs",
4784
- "neon",
4785
- "@aspect/neon-cli"
4786
- ]);
4787
- function detectNapiProject(pkgJson) {
4788
- if (!pkgJson)
4789
- return false;
4790
- const deps = Object.keys({
4791
- ...pkgJson.dependencies ?? {},
4792
- ...pkgJson.devDependencies ?? {}
4793
- });
4794
- return deps.some((dep) => NAPI_PACKAGES.has(dep) || dep.includes("@aspect/napi"));
4795
- }
4796
- function getPrimaryBuildScript(buildInfo) {
4797
- if (buildInfo.scripts.includes("build"))
4798
- return "build";
4799
- if (buildInfo.scripts.includes("compile"))
4800
- return "compile";
4801
- if (buildInfo.scripts.includes("tsc"))
4802
- return "tsc";
4803
- return buildInfo.scripts[0] ?? null;
4804
- }
4805
- // src/detect/filesystem.ts
4806
- import * as fs6 from "node:fs";
4807
- import * as nodePath from "node:path";
4808
- import { Writable } from "node:stream";
4809
-
4810
- class NodeFileSystem {
4811
- basePath;
4812
- constructor(basePath) {
4813
- this.basePath = basePath;
4814
- }
4815
- resolve(relativePath) {
4816
- return nodePath.join(this.basePath, relativePath);
4817
- }
4818
- async exists(relativePath) {
4819
- return fs6.existsSync(this.resolve(relativePath));
4820
- }
4821
- async readFile(relativePath) {
4822
- return fs6.readFileSync(this.resolve(relativePath), "utf-8");
4823
- }
4824
- async readDir(relativePath) {
4825
- return fs6.readdirSync(this.resolve(relativePath));
4826
- }
4827
- async isDirectory(relativePath) {
4828
- const fullPath = this.resolve(relativePath);
4829
- if (!fs6.existsSync(fullPath))
4830
- return false;
4831
- return fs6.statSync(fullPath).isDirectory();
4832
- }
4833
- }
4834
- function createCaptureStream() {
4835
- let output = "";
4836
- const stream = new Writable({
4837
- write(chunk, _encoding, callback) {
4838
- output += chunk.toString();
4839
- callback();
4840
- }
4841
- });
4842
- return { stream, getOutput: () => output };
4843
- }
4844
-
4845
- class FileNotFoundError extends Error {
4846
- path;
4847
- constructor(path10, message) {
4848
- super(message ?? `File not found: ${path10}`);
4849
- this.path = path10;
4850
- this.name = "FileNotFoundError";
4851
- }
4852
- }
4853
-
4854
- class SandboxFileSystem {
4855
- sandbox;
4856
- constructor(sandbox) {
4857
- this.sandbox = sandbox;
4858
- }
4859
- async exists(path10) {
4860
- const result = await this.sandbox.runCommand({
4861
- cmd: "test",
4862
- args: ["-e", path10]
4863
- });
4864
- return result.exitCode === 0;
4865
- }
4866
- async readFile(path10) {
4867
- const exists = await this.exists(path10);
4868
- if (!exists) {
4869
- throw new FileNotFoundError(path10);
4870
- }
4871
- const capture = createCaptureStream();
4872
- const result = await this.sandbox.runCommand({
4873
- cmd: "cat",
4874
- args: [path10],
4875
- stdout: capture.stream
4876
- });
4877
- if (result.exitCode !== 0) {
4878
- throw new FileNotFoundError(path10, `Failed to read file: ${path10}`);
4879
- }
4880
- return capture.getOutput();
4881
- }
4882
- async readDir(path10) {
4883
- const capture = createCaptureStream();
4884
- const result = await this.sandbox.runCommand({
4885
- cmd: "ls",
4886
- args: ["-1", path10],
4887
- stdout: capture.stream
4888
- });
4889
- if (result.exitCode !== 0) {
4890
- return [];
4891
- }
4892
- return capture.getOutput().split(`
4893
- `).filter(Boolean);
4894
- }
4895
- async isDirectory(path10) {
4896
- const result = await this.sandbox.runCommand({
4897
- cmd: "test",
4898
- args: ["-d", path10]
4899
- });
4900
- return result.exitCode === 0;
4901
- }
4902
- }
4903
- // src/detect/package-manager.ts
4904
- var PM_CONFIGS = {
4905
- pnpm: {
4906
- lockfiles: ["pnpm-lock.yaml"],
4907
- info: {
4908
- name: "pnpm",
4909
- lockfile: "pnpm-lock.yaml",
4910
- installArgs: ["install", "--frozen-lockfile"],
4911
- runPrefix: ["pnpm"]
4912
- }
4913
- },
4914
- bun: {
4915
- lockfiles: ["bun.lock", "bun.lockb"],
4916
- info: {
4917
- name: "bun",
4918
- lockfile: "bun.lock",
4919
- installArgs: ["install", "--frozen-lockfile"],
4920
- runPrefix: ["bun"]
4845
+ },
4846
+ bun: {
4847
+ lockfiles: ["bun.lock", "bun.lockb"],
4848
+ info: {
4849
+ name: "bun",
4850
+ lockfile: "bun.lock",
4851
+ installArgs: ["install", "--frozen-lockfile"],
4852
+ runPrefix: ["bun"]
4921
4853
  }
4922
4854
  },
4923
4855
  yarn: {
@@ -5019,6 +4951,70 @@ async function analyzeProject(fs7, options = {}) {
5019
4951
  ]);
5020
4952
  return { packageManager, monorepo, entryPoint, build };
5021
4953
  }
4954
+ // src/resolve/index.ts
4955
+ import * as path9 from "node:path";
4956
+ async function resolveTarget(fs7, options) {
4957
+ let targetDir = options.cwd;
4958
+ let packageInfo;
4959
+ if (options.package) {
4960
+ const mono = await detectMonorepo(fs7);
4961
+ if (!mono.isMonorepo) {
4962
+ throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
4963
+ }
4964
+ const pkg = findPackageByName(mono.packages, options.package);
4965
+ if (!pkg) {
4966
+ const available = mono.packages.map((p) => p.name).join(", ");
4967
+ throw new Error(`Package "${options.package}" not found. Available: ${available}`);
4968
+ }
4969
+ targetDir = path9.join(options.cwd, pkg.path);
4970
+ packageInfo = pkg;
4971
+ }
4972
+ let entryFile;
4973
+ let entryPointInfo;
4974
+ if (!options.entry) {
4975
+ entryPointInfo = await detectEntryPoint(fs7, getRelativePath(options.cwd, targetDir));
4976
+ entryFile = path9.join(targetDir, entryPointInfo.path);
4977
+ } else {
4978
+ const explicitPath = path9.resolve(targetDir, options.entry);
4979
+ const isDirectory = await isDir(fs7, getRelativePath(options.cwd, explicitPath));
4980
+ if (isDirectory) {
4981
+ targetDir = explicitPath;
4982
+ entryPointInfo = await detectEntryPoint(fs7, getRelativePath(options.cwd, explicitPath));
4983
+ entryFile = path9.join(explicitPath, entryPointInfo.path);
4984
+ } else {
4985
+ entryFile = explicitPath;
4986
+ entryPointInfo = {
4987
+ path: options.entry,
4988
+ source: "explicit",
4989
+ isDeclarationOnly: options.entry.endsWith(".d.ts")
4990
+ };
4991
+ }
4992
+ }
4993
+ return {
4994
+ targetDir,
4995
+ entryFile,
4996
+ packageInfo,
4997
+ entryPointInfo
4998
+ };
4999
+ }
5000
+ function getRelativePath(base, target) {
5001
+ if (base === target)
5002
+ return ".";
5003
+ const rel = path9.relative(base, target);
5004
+ return rel || ".";
5005
+ }
5006
+ async function isDir(fs7, relativePath) {
5007
+ const hasPackageJson = await fs7.exists(path9.join(relativePath, "package.json"));
5008
+ if (hasPackageJson)
5009
+ return true;
5010
+ const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
5011
+ for (const entry of commonEntryFiles) {
5012
+ if (await fs7.exists(path9.join(relativePath, entry))) {
5013
+ return true;
5014
+ }
5015
+ }
5016
+ return !path9.extname(relativePath);
5017
+ }
5022
5018
  // src/examples/types.ts
5023
5019
  var ALL_VALIDATIONS = ["presence", "typecheck", "run"];
5024
5020
  var VALIDATION_INFO = {