@b9g/libuild 0.1.10 → 0.1.11

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.1.11] - 2025-11-02
6
+
7
+ ### Fixed
8
+ - **CRITICAL**: Workspace dependencies (workspace:*) are now properly resolved to actual version numbers during build
9
+ - Validation warnings for valid libuild output paths (dist/src/ files that libuild creates)
10
+
5
11
  ## [0.1.10] - 2025-10-29
6
12
 
7
13
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/libuild",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Zero-config library builds",
5
5
  "keywords": [
6
6
  "build",
package/src/libuild.cjs CHANGED
@@ -215463,67 +215463,70 @@ function transformSrcToDist(value) {
215463
215463
  }
215464
215464
  return value;
215465
215465
  }
215466
- async function validateBinPaths(value, srcDir, fieldName = "bin") {
215466
+ async function validateBinPaths(value, cwd, save, fieldName = "bin") {
215467
215467
  if (typeof value === "string") {
215468
- await validateSingleBinPath(value, fieldName, srcDir);
215468
+ await validateSingleBinPath(value, fieldName, cwd, save);
215469
215469
  } else if (typeof value === "object" && value !== null) {
215470
215470
  for (const [key, val] of Object.entries(value)) {
215471
215471
  if (typeof val === "string") {
215472
- await validateSingleBinPath(val, `${fieldName}.${key}`, srcDir);
215472
+ await validateSingleBinPath(val, `${fieldName}.${key}`, cwd, save);
215473
215473
  }
215474
215474
  }
215475
215475
  }
215476
215476
  }
215477
- async function validateSingleBinPath(binPath, fieldName, srcDir) {
215478
- if (binPath.startsWith("dist/") || binPath.startsWith("./dist/")) {
215479
- let srcPath;
215480
- if (binPath.startsWith("./dist/src/")) {
215481
- srcPath = binPath.replace("./dist/src/", "src/");
215482
- } else if (binPath.startsWith("dist/src/")) {
215483
- srcPath = binPath.replace("dist/src/", "src/");
215484
- } else if (binPath.startsWith("./dist/")) {
215485
- srcPath = binPath.replace("./dist/", "src/");
215486
- } else if (binPath.startsWith("dist/")) {
215487
- srcPath = binPath.replace("dist/", "src/");
215488
- } else {
215489
- srcPath = "";
215477
+ async function validateSingleBinPath(binPath, fieldName, cwd, save) {
215478
+ if (save) {
215479
+ return;
215480
+ }
215481
+ if (binPath.startsWith("dist/src/") || binPath.startsWith("./dist/src/")) {
215482
+ const fullPath2 = Path3.join(cwd, binPath);
215483
+ const distExists = await fileExists(fullPath2);
215484
+ if (distExists) {
215485
+ return;
215490
215486
  }
215491
- const basePath = srcPath.replace(/\.(js|cjs)$/, "");
215492
- const tsPath = Path3.join(srcDir, "..", basePath + ".ts");
215493
- const jsPath = Path3.join(srcDir, "..", basePath + ".js");
215487
+ const srcPath = binPath.startsWith("./dist/src/") ? binPath.replace("./dist/src/", "src/") : binPath.replace("dist/src/", "src/");
215488
+ const basePath = srcPath.replace(/\.(js|cjs|mjs)$/, "");
215489
+ const tsPath = Path3.join(cwd, basePath + ".ts");
215490
+ const jsPath = Path3.join(cwd, basePath + ".js");
215494
215491
  const srcExists = await fileExists(tsPath) || await fileExists(jsPath);
215495
215492
  if (!srcExists) {
215496
- throw new Error(`${fieldName} field points to dist/ directory: "${binPath}"
215497
-
215498
- The bin field should reference source files that libuild will build and transform:
215499
- CORRECT: "bin": {"tool": "src/cli.js"}
215500
- INCORRECT: "bin": {"tool": "dist/cli.js"}
215501
-
215502
- Libuild workflow:
215503
- 1. Point bin entries to src/ files
215504
- 2. Run 'libuild build --save' to update package.json with dist/ paths
215505
- 3. Set "private": true in your development package.json
215506
-
215507
- If you need to include pre-built executable files, use the files field instead:
215508
- "files": ["scripts/my-tool.js"]`);
215493
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" but neither the dist file nor corresponding src file exists.
215494
+
215495
+ Create "${srcPath}" and run 'libuild build --save' to update paths correctly.`);
215496
+ return;
215509
215497
  }
215510
- console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to dist/ directory: "${binPath}"
215498
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which doesn't exist yet.
215511
215499
 
215512
- libuild is ZERO-CONFIG - there is no libuild.config.js file!
215513
- Configuration comes from your package.json fields.
215500
+ Run 'libuild build' to create the dist files, or use 'libuild build --save' to update the path to "${srcPath}".`);
215501
+ return;
215502
+ }
215503
+ if (binPath.startsWith("dist/") || binPath.startsWith("./dist/")) {
215504
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" in dist/ directory.
215514
215505
 
215515
- The libuild workflow is to point bin entries to src/ files and use --save to update paths.
215516
- Consider changing to: "${srcPath}" and running 'libuild build --save'`);
215506
+ libuild expects bin entries to point to src/ files. Consider changing to the corresponding src/ path and using --save.`);
215517
215507
  return;
215518
215508
  }
215519
- if (binPath.startsWith("src/") || binPath.startsWith("./src/")) {
215509
+ const fullPath = Path3.join(cwd, binPath);
215510
+ const pathExists = await fileExists(fullPath);
215511
+ if (pathExists) {
215520
215512
  return;
215521
215513
  }
215522
- if (!binPath.startsWith("src/") && !binPath.startsWith("./src/")) {
215523
- console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which is not in src/ directory.
215524
- Consider moving executable to src/ or using the files field for pre-built scripts.
215525
- Run 'libuild build --save' after fixing to update paths correctly.`);
215514
+ if (binPath.startsWith("src/") || binPath.startsWith("./src/")) {
215515
+ const basePath = fullPath.replace(/\.(js|cjs|mjs)$/, "");
215516
+ const tsPath = basePath + ".ts";
215517
+ const jsPath = basePath + ".js";
215518
+ const tsExists = await fileExists(tsPath);
215519
+ const jsExists = await fileExists(jsPath);
215520
+ if (tsExists || jsExists) {
215521
+ return;
215522
+ }
215526
215523
  }
215524
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which doesn't exist
215525
+
215526
+ libuild is ZERO-CONFIG - there is no libuild.config.js file!
215527
+ Configuration comes from your package.json fields.
215528
+
215529
+ Use 'libuild build --save' to update package.json with correct dist/ paths automatically.`);
215527
215530
  }
215528
215531
  function transformBinPaths(value) {
215529
215532
  if (typeof value === "string") {
@@ -215581,7 +215584,66 @@ function fixExportsForDist(obj) {
215581
215584
  }
215582
215585
  return obj;
215583
215586
  }
215584
- function cleanPackageJSON(pkg, mainEntry, options) {
215587
+ async function resolveWorkspaceDependencies(dependencies, cwd) {
215588
+ if (!dependencies)
215589
+ return void 0;
215590
+ const resolved = {};
215591
+ for (const [depName, depVersion] of Object.entries(dependencies)) {
215592
+ if (depVersion.startsWith("workspace:")) {
215593
+ const resolvedVersion = await resolveWorkspaceVersion(depName, depVersion, cwd);
215594
+ resolved[depName] = resolvedVersion;
215595
+ } else {
215596
+ resolved[depName] = depVersion;
215597
+ }
215598
+ }
215599
+ return resolved;
215600
+ }
215601
+ async function resolveWorkspaceVersion(packageName, workspaceSpec, cwd) {
215602
+ try {
215603
+ if (workspaceSpec === "workspace:*") {
215604
+ const packageNameParts = packageName.replace("@", "").replace("/", "-");
215605
+ const packageBaseName = packageName.split("/").pop() || packageName;
215606
+ const possiblePaths = [
215607
+ `../packages/${packageNameParts}/package.json`,
215608
+ `../packages/${packageBaseName}/package.json`,
215609
+ `../${packageNameParts}/package.json`,
215610
+ `../${packageBaseName}/package.json`,
215611
+ `./packages/${packageNameParts}/package.json`,
215612
+ `./packages/${packageBaseName}/package.json`,
215613
+ `./${packageNameParts}/package.json`,
215614
+ `./${packageBaseName}/package.json`
215615
+ ];
215616
+ for (const pkgPath of possiblePaths) {
215617
+ try {
215618
+ const resolvedPath = Path3.resolve(cwd, pkgPath);
215619
+ if (await fileExists(resolvedPath)) {
215620
+ const pkgContent = await FS3.readFile(resolvedPath, "utf-8");
215621
+ const pkgJson = JSON.parse(pkgContent);
215622
+ if (pkgJson.name === packageName) {
215623
+ console.info(` Resolved workspace:* dependency "${packageName}" to ^${pkgJson.version}`);
215624
+ return `^${pkgJson.version}`;
215625
+ }
215626
+ }
215627
+ } catch {
215628
+ continue;
215629
+ }
215630
+ }
215631
+ console.warn(`\u26A0\uFE0F WARNING: Could not resolve workspace dependency "${packageName}" - keeping as workspace:*`);
215632
+ console.warn(` Searched in: ${possiblePaths.map((p) => Path3.resolve(cwd, p)).join(", ")}`);
215633
+ return workspaceSpec;
215634
+ }
215635
+ const versionPart = workspaceSpec.replace("workspace:", "");
215636
+ if (versionPart !== "*") {
215637
+ console.info(` Resolved workspace:${versionPart} dependency "${packageName}" to ${versionPart}`);
215638
+ return versionPart;
215639
+ }
215640
+ return workspaceSpec;
215641
+ } catch (error) {
215642
+ console.warn(`\u26A0\uFE0F WARNING: Error resolving workspace dependency "${packageName}": ${error.message}`);
215643
+ return workspaceSpec;
215644
+ }
215645
+ }
215646
+ async function cleanPackageJSON(pkg, mainEntry, options, cwd) {
215585
215647
  const cleaned = {
215586
215648
  name: pkg.name,
215587
215649
  version: pkg.version
@@ -215632,6 +215694,8 @@ function cleanPackageJSON(pkg, mainEntry, options) {
215632
215694
  cleaned[field] = transformBinPaths(pkg[field]);
215633
215695
  } else if (pathFields.includes(field)) {
215634
215696
  cleaned[field] = transformSrcToDist(pkg[field]);
215697
+ } else if (field === "dependencies" || field === "devDependencies" || field === "peerDependencies" || field === "optionalDependencies") {
215698
+ cleaned[field] = await resolveWorkspaceDependencies(pkg[field], cwd);
215635
215699
  } else {
215636
215700
  cleaned[field] = pkg[field];
215637
215701
  }
@@ -215681,7 +215745,7 @@ async function build2(cwd, save = false) {
215681
215745
  const pkgPath = Path3.join(cwd, "package.json");
215682
215746
  const pkg = JSON.parse(await FS3.readFile(pkgPath, "utf-8"));
215683
215747
  if (pkg.bin) {
215684
- await validateBinPaths(pkg.bin, srcDir, "bin");
215748
+ await validateBinPaths(pkg.bin, cwd, save, "bin");
215685
215749
  }
215686
215750
  if (!pkg.private) {
215687
215751
  console.warn("\u26A0\uFE0F WARNING: Root package.json is not private - this could lead to accidental publishing of development package.json");
@@ -215826,7 +215890,7 @@ async function build2(cwd, save = false) {
215826
215890
  }
215827
215891
  const autoDiscoveredFiles = [];
215828
215892
  console.info(" Generating package.json...");
215829
- const cleanedPkg = cleanPackageJSON(pkg, mainEntry, options);
215893
+ const cleanedPkg = await cleanPackageJSON(pkg, mainEntry, options, cwd);
215830
215894
  const exportsResult = generateExports(entries, mainEntry, options, pkg.exports);
215831
215895
  cleanedPkg.exports = fixExportsForDist(exportsResult.exports);
215832
215896
  if (exportsResult.staleExports.length > 0) {
package/src/libuild.js CHANGED
@@ -442,67 +442,70 @@ function transformSrcToDist(value) {
442
442
  }
443
443
  return value;
444
444
  }
445
- async function validateBinPaths(value, srcDir, fieldName = "bin") {
445
+ async function validateBinPaths(value, cwd, save, fieldName = "bin") {
446
446
  if (typeof value === "string") {
447
- await validateSingleBinPath(value, fieldName, srcDir);
447
+ await validateSingleBinPath(value, fieldName, cwd, save);
448
448
  } else if (typeof value === "object" && value !== null) {
449
449
  for (const [key, val] of Object.entries(value)) {
450
450
  if (typeof val === "string") {
451
- await validateSingleBinPath(val, `${fieldName}.${key}`, srcDir);
451
+ await validateSingleBinPath(val, `${fieldName}.${key}`, cwd, save);
452
452
  }
453
453
  }
454
454
  }
455
455
  }
456
- async function validateSingleBinPath(binPath, fieldName, srcDir) {
457
- if (binPath.startsWith("dist/") || binPath.startsWith("./dist/")) {
458
- let srcPath;
459
- if (binPath.startsWith("./dist/src/")) {
460
- srcPath = binPath.replace("./dist/src/", "src/");
461
- } else if (binPath.startsWith("dist/src/")) {
462
- srcPath = binPath.replace("dist/src/", "src/");
463
- } else if (binPath.startsWith("./dist/")) {
464
- srcPath = binPath.replace("./dist/", "src/");
465
- } else if (binPath.startsWith("dist/")) {
466
- srcPath = binPath.replace("dist/", "src/");
467
- } else {
468
- srcPath = "";
456
+ async function validateSingleBinPath(binPath, fieldName, cwd, save) {
457
+ if (save) {
458
+ return;
459
+ }
460
+ if (binPath.startsWith("dist/src/") || binPath.startsWith("./dist/src/")) {
461
+ const fullPath2 = Path3.join(cwd, binPath);
462
+ const distExists = await fileExists(fullPath2);
463
+ if (distExists) {
464
+ return;
469
465
  }
470
- const basePath = srcPath.replace(/\.(js|cjs)$/, "");
471
- const tsPath = Path3.join(srcDir, "..", basePath + ".ts");
472
- const jsPath = Path3.join(srcDir, "..", basePath + ".js");
466
+ const srcPath = binPath.startsWith("./dist/src/") ? binPath.replace("./dist/src/", "src/") : binPath.replace("dist/src/", "src/");
467
+ const basePath = srcPath.replace(/\.(js|cjs|mjs)$/, "");
468
+ const tsPath = Path3.join(cwd, basePath + ".ts");
469
+ const jsPath = Path3.join(cwd, basePath + ".js");
473
470
  const srcExists = await fileExists(tsPath) || await fileExists(jsPath);
474
471
  if (!srcExists) {
475
- throw new Error(`${fieldName} field points to dist/ directory: "${binPath}"
476
-
477
- The bin field should reference source files that libuild will build and transform:
478
- CORRECT: "bin": {"tool": "src/cli.js"}
479
- INCORRECT: "bin": {"tool": "dist/cli.js"}
480
-
481
- Libuild workflow:
482
- 1. Point bin entries to src/ files
483
- 2. Run 'libuild build --save' to update package.json with dist/ paths
484
- 3. Set "private": true in your development package.json
485
-
486
- If you need to include pre-built executable files, use the files field instead:
487
- "files": ["scripts/my-tool.js"]`);
472
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" but neither the dist file nor corresponding src file exists.
473
+
474
+ Create "${srcPath}" and run 'libuild build --save' to update paths correctly.`);
475
+ return;
488
476
  }
489
- console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to dist/ directory: "${binPath}"
477
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which doesn't exist yet.
490
478
 
491
- libuild is ZERO-CONFIG - there is no libuild.config.js file!
492
- Configuration comes from your package.json fields.
479
+ Run 'libuild build' to create the dist files, or use 'libuild build --save' to update the path to "${srcPath}".`);
480
+ return;
481
+ }
482
+ if (binPath.startsWith("dist/") || binPath.startsWith("./dist/")) {
483
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" in dist/ directory.
493
484
 
494
- The libuild workflow is to point bin entries to src/ files and use --save to update paths.
495
- Consider changing to: "${srcPath}" and running 'libuild build --save'`);
485
+ libuild expects bin entries to point to src/ files. Consider changing to the corresponding src/ path and using --save.`);
496
486
  return;
497
487
  }
498
- if (binPath.startsWith("src/") || binPath.startsWith("./src/")) {
488
+ const fullPath = Path3.join(cwd, binPath);
489
+ const pathExists = await fileExists(fullPath);
490
+ if (pathExists) {
499
491
  return;
500
492
  }
501
- if (!binPath.startsWith("src/") && !binPath.startsWith("./src/")) {
502
- console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which is not in src/ directory.
503
- Consider moving executable to src/ or using the files field for pre-built scripts.
504
- Run 'libuild build --save' after fixing to update paths correctly.`);
493
+ if (binPath.startsWith("src/") || binPath.startsWith("./src/")) {
494
+ const basePath = fullPath.replace(/\.(js|cjs|mjs)$/, "");
495
+ const tsPath = basePath + ".ts";
496
+ const jsPath = basePath + ".js";
497
+ const tsExists = await fileExists(tsPath);
498
+ const jsExists = await fileExists(jsPath);
499
+ if (tsExists || jsExists) {
500
+ return;
501
+ }
505
502
  }
503
+ console.warn(`\u26A0\uFE0F WARNING: ${fieldName} field points to "${binPath}" which doesn't exist
504
+
505
+ libuild is ZERO-CONFIG - there is no libuild.config.js file!
506
+ Configuration comes from your package.json fields.
507
+
508
+ Use 'libuild build --save' to update package.json with correct dist/ paths automatically.`);
506
509
  }
507
510
  function transformBinPaths(value) {
508
511
  if (typeof value === "string") {
@@ -560,7 +563,66 @@ function fixExportsForDist(obj) {
560
563
  }
561
564
  return obj;
562
565
  }
563
- function cleanPackageJSON(pkg, mainEntry, options) {
566
+ async function resolveWorkspaceDependencies(dependencies, cwd) {
567
+ if (!dependencies)
568
+ return void 0;
569
+ const resolved = {};
570
+ for (const [depName, depVersion] of Object.entries(dependencies)) {
571
+ if (depVersion.startsWith("workspace:")) {
572
+ const resolvedVersion = await resolveWorkspaceVersion(depName, depVersion, cwd);
573
+ resolved[depName] = resolvedVersion;
574
+ } else {
575
+ resolved[depName] = depVersion;
576
+ }
577
+ }
578
+ return resolved;
579
+ }
580
+ async function resolveWorkspaceVersion(packageName, workspaceSpec, cwd) {
581
+ try {
582
+ if (workspaceSpec === "workspace:*") {
583
+ const packageNameParts = packageName.replace("@", "").replace("/", "-");
584
+ const packageBaseName = packageName.split("/").pop() || packageName;
585
+ const possiblePaths = [
586
+ `../packages/${packageNameParts}/package.json`,
587
+ `../packages/${packageBaseName}/package.json`,
588
+ `../${packageNameParts}/package.json`,
589
+ `../${packageBaseName}/package.json`,
590
+ `./packages/${packageNameParts}/package.json`,
591
+ `./packages/${packageBaseName}/package.json`,
592
+ `./${packageNameParts}/package.json`,
593
+ `./${packageBaseName}/package.json`
594
+ ];
595
+ for (const pkgPath of possiblePaths) {
596
+ try {
597
+ const resolvedPath = Path3.resolve(cwd, pkgPath);
598
+ if (await fileExists(resolvedPath)) {
599
+ const pkgContent = await FS3.readFile(resolvedPath, "utf-8");
600
+ const pkgJson = JSON.parse(pkgContent);
601
+ if (pkgJson.name === packageName) {
602
+ console.info(` Resolved workspace:* dependency "${packageName}" to ^${pkgJson.version}`);
603
+ return `^${pkgJson.version}`;
604
+ }
605
+ }
606
+ } catch {
607
+ continue;
608
+ }
609
+ }
610
+ console.warn(`\u26A0\uFE0F WARNING: Could not resolve workspace dependency "${packageName}" - keeping as workspace:*`);
611
+ console.warn(` Searched in: ${possiblePaths.map((p) => Path3.resolve(cwd, p)).join(", ")}`);
612
+ return workspaceSpec;
613
+ }
614
+ const versionPart = workspaceSpec.replace("workspace:", "");
615
+ if (versionPart !== "*") {
616
+ console.info(` Resolved workspace:${versionPart} dependency "${packageName}" to ${versionPart}`);
617
+ return versionPart;
618
+ }
619
+ return workspaceSpec;
620
+ } catch (error) {
621
+ console.warn(`\u26A0\uFE0F WARNING: Error resolving workspace dependency "${packageName}": ${error.message}`);
622
+ return workspaceSpec;
623
+ }
624
+ }
625
+ async function cleanPackageJSON(pkg, mainEntry, options, cwd) {
564
626
  const cleaned = {
565
627
  name: pkg.name,
566
628
  version: pkg.version
@@ -611,6 +673,8 @@ function cleanPackageJSON(pkg, mainEntry, options) {
611
673
  cleaned[field] = transformBinPaths(pkg[field]);
612
674
  } else if (pathFields.includes(field)) {
613
675
  cleaned[field] = transformSrcToDist(pkg[field]);
676
+ } else if (field === "dependencies" || field === "devDependencies" || field === "peerDependencies" || field === "optionalDependencies") {
677
+ cleaned[field] = await resolveWorkspaceDependencies(pkg[field], cwd);
614
678
  } else {
615
679
  cleaned[field] = pkg[field];
616
680
  }
@@ -660,7 +724,7 @@ async function build2(cwd, save = false) {
660
724
  const pkgPath = Path3.join(cwd, "package.json");
661
725
  const pkg = JSON.parse(await FS3.readFile(pkgPath, "utf-8"));
662
726
  if (pkg.bin) {
663
- await validateBinPaths(pkg.bin, srcDir, "bin");
727
+ await validateBinPaths(pkg.bin, cwd, save, "bin");
664
728
  }
665
729
  if (!pkg.private) {
666
730
  console.warn("\u26A0\uFE0F WARNING: Root package.json is not private - this could lead to accidental publishing of development package.json");
@@ -805,7 +869,7 @@ async function build2(cwd, save = false) {
805
869
  }
806
870
  const autoDiscoveredFiles = [];
807
871
  console.info(" Generating package.json...");
808
- const cleanedPkg = cleanPackageJSON(pkg, mainEntry, options);
872
+ const cleanedPkg = await cleanPackageJSON(pkg, mainEntry, options, cwd);
809
873
  const exportsResult = generateExports(entries, mainEntry, options, pkg.exports);
810
874
  cleanedPkg.exports = fixExportsForDist(exportsResult.exports);
811
875
  if (exportsResult.staleExports.length > 0) {