@b9g/libuild 0.1.22 → 0.1.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b9g/libuild",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "Zero-config library builds",
5
5
  "keywords": [
6
6
  "build",
@@ -22,18 +22,25 @@
22
22
  "libuild": "src/cli.js"
23
23
  },
24
24
  "dependencies": {
25
- "esbuild": "^0.19.0"
25
+ "commander": "^14.0.2",
26
+ "esbuild": "^0.19.0",
27
+ "expect": "^30.2.0",
28
+ "glob": "^13.0.0"
26
29
  },
27
30
  "devDependencies": {
28
31
  "@types/bun": "latest",
29
32
  "typescript": "^5.0.0"
30
33
  },
31
34
  "peerDependencies": {
32
- "typescript": "^5.0.0"
35
+ "typescript": "^5.0.0",
36
+ "playwright": "^1.40.0"
33
37
  },
34
38
  "peerDependenciesMeta": {
35
39
  "typescript": {
36
40
  "optional": true
41
+ },
42
+ "playwright": {
43
+ "optional": true
37
44
  }
38
45
  },
39
46
  "engines": {
@@ -47,34 +54,68 @@
47
54
  "CHANGELOG.md",
48
55
  "src/"
49
56
  ],
50
- "main": "src/libuild.cjs",
51
57
  "module": "src/libuild.js",
52
58
  "exports": {
53
59
  ".": {
54
60
  "types": "./src/libuild.d.ts",
55
- "import": "./src/libuild.js",
56
- "require": "./src/libuild.cjs"
61
+ "import": "./src/libuild.js"
57
62
  },
58
63
  "./cli": {
59
64
  "types": "./src/cli.d.ts",
60
- "import": "./src/cli.js",
61
- "require": "./src/cli.cjs"
65
+ "import": "./src/cli.js"
62
66
  },
63
67
  "./cli.js": {
64
68
  "types": "./src/cli.d.ts",
65
- "import": "./src/cli.js",
66
- "require": "./src/cli.cjs"
69
+ "import": "./src/cli.js"
67
70
  },
68
71
  "./libuild": {
69
72
  "types": "./src/libuild.d.ts",
70
- "import": "./src/libuild.js",
71
- "require": "./src/libuild.cjs"
73
+ "import": "./src/libuild.js"
72
74
  },
73
75
  "./libuild.js": {
74
76
  "types": "./src/libuild.d.ts",
75
- "import": "./src/libuild.js",
76
- "require": "./src/libuild.cjs"
77
+ "import": "./src/libuild.js"
78
+ },
79
+ "./package.json": "./package.json",
80
+ "./test": {
81
+ "types": "./src/test.d.ts",
82
+ "import": "./src/test.js"
83
+ },
84
+ "./test.js": {
85
+ "types": "./src/test.d.ts",
86
+ "import": "./src/test.js"
87
+ },
88
+ "./test-bun": {
89
+ "types": "./src/test-bun.d.ts",
90
+ "import": "./src/test-bun.js"
91
+ },
92
+ "./test-bun.js": {
93
+ "types": "./src/test-bun.d.ts",
94
+ "import": "./src/test-bun.js"
77
95
  },
78
- "./package.json": "./package.json"
96
+ "./test-node": {
97
+ "types": "./src/test-node.d.ts",
98
+ "import": "./src/test-node.js"
99
+ },
100
+ "./test-node.js": {
101
+ "types": "./src/test-node.d.ts",
102
+ "import": "./src/test-node.js"
103
+ },
104
+ "./test-browser": {
105
+ "types": "./src/test-browser.d.ts",
106
+ "import": "./src/test-browser.js"
107
+ },
108
+ "./test-browser.js": {
109
+ "types": "./src/test-browser.d.ts",
110
+ "import": "./src/test-browser.js"
111
+ },
112
+ "./test-runner": {
113
+ "types": "./src/test-runner.d.ts",
114
+ "import": "./src/test-runner.js"
115
+ },
116
+ "./test-runner.js": {
117
+ "types": "./src/test-runner.d.ts",
118
+ "import": "./src/test-runner.js"
119
+ }
79
120
  }
80
121
  }
@@ -0,0 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
package/src/cli.js CHANGED
@@ -3,160 +3,45 @@
3
3
  /// <reference types="./cli.d.ts" />
4
4
 
5
5
  // src/cli.ts
6
- import { parseArgs } from "util";
6
+ import { Command } from "commander";
7
7
  import * as Path from "path";
8
8
  import { build, publish } from "./libuild.js";
9
- var { values, positionals } = parseArgs({
10
- args: process.argv.slice(2),
11
- options: {
12
- help: { type: "boolean", short: "h" },
13
- version: { type: "boolean", short: "v" },
14
- save: { type: "boolean" },
15
- "no-save": { type: "boolean" }
16
- },
17
- allowPositionals: true,
18
- strict: false
19
- // Allow unknown options to be passed through
9
+ import { runTests } from "./test-runner.js";
10
+ var program = new Command();
11
+ program.name("libuild").description("Zero-config library builds").version("0.1.22");
12
+ program.command("build", { isDefault: true }).description("Build the library").argument("[directory]", "Directory to build", ".").option("--save", "Update root package.json to point to dist files").action(async (directory, options) => {
13
+ const cwd = Path.resolve(directory);
14
+ await build(cwd, options.save || false);
20
15
  });
21
- var HELP_TEXT = `
22
- libuild - Zero-config library builds
23
-
24
- Usage:
25
- libuild [command] [directory] [options]
26
-
27
- Commands:
28
- build Build the library (default command)
29
- publish Build and publish the library
30
-
31
- Arguments:
32
- directory Optional directory to build (defaults to current directory)
33
-
34
- Options:
35
- --save Update root package.json to point to dist files
36
- --no-save Skip package.json updates (for publish command)
37
-
38
- IMPORTANT:
39
- \u2022 libuild is zero-config - there is NO libuild.config.js file
40
- \u2022 Configuration comes from your package.json (main, module, exports, etc.)
41
- \u2022 Use --save to regenerate package.json fields based on built files
42
- \u2022 Invalid bin/exports paths are automatically cleaned up with --save
43
-
44
- For publish command, all additional flags are forwarded to npm publish.
45
-
46
- Examples:
47
- libuild # Build the library in current directory
48
- libuild build # Same as above
49
- libuild ../other-proj # Build library in ../other-proj
50
- libuild build --save # Build and update package.json for npm link
51
- libuild publish ../lib # Build and publish library in ../lib
52
- libuild publish --dry-run --tag beta # Build and publish with npm flags
53
- `;
54
- async function main() {
55
- if (values.help) {
56
- console.log(HELP_TEXT);
57
- process.exit(0);
58
- }
59
- if (values.version) {
60
- const pkg = await import("../package.json");
61
- console.log(pkg.version);
62
- process.exit(0);
63
- }
64
- const command = positionals[0] || "build";
65
- let targetDir;
66
- for (let i = 1; i < positionals.length; i++) {
67
- const arg = positionals[i];
68
- const flagValueFlags = ["--tag", "--access", "--registry", "--otp", "--workspace"];
69
- const isValueForFlag = flagValueFlags.some((flag) => {
70
- const flagIndex = process.argv.indexOf(flag);
71
- const argIndex = process.argv.indexOf(arg);
72
- return flagIndex !== -1 && argIndex === flagIndex + 1;
73
- });
74
- if (isValueForFlag) {
75
- continue;
76
- }
77
- if (!arg.startsWith("--")) {
78
- try {
79
- const isSystemPath = Path.isAbsolute(arg) && (arg.startsWith("/bin/") || arg.startsWith("/usr/") || arg.startsWith("/etc/") || arg.startsWith("/var/") || arg.startsWith("/opt/") || arg.startsWith("/tmp/") || arg.startsWith("/System/") || arg.startsWith("/Applications/"));
80
- if (isSystemPath) {
81
- continue;
82
- }
83
- const resolvedPath = Path.resolve(arg);
84
- if (arg.includes("/") || arg.includes("\\") || arg === "." || arg === ".." || arg.startsWith("./") || arg.startsWith("../")) {
85
- targetDir = arg;
86
- break;
87
- }
88
- const fs = await import("fs/promises");
89
- try {
90
- const stat = await fs.stat(resolvedPath);
91
- if (stat.isDirectory() && !Path.isAbsolute(arg)) {
92
- targetDir = arg;
93
- break;
94
- }
95
- } catch {
96
- }
97
- } catch {
98
- }
99
- }
100
- }
101
- const cwd = targetDir ? Path.resolve(targetDir) : process.cwd();
102
- const shouldSave = values.save || command === "publish" && !values["no-save"];
103
- const allowedNpmFlags = [
104
- "--dry-run",
105
- "--tag",
106
- "--access",
107
- "--registry",
108
- "--otp",
109
- "--provenance",
110
- "--workspace",
111
- "--workspaces",
112
- "--include-workspace-root"
113
- ];
114
- const rawExtraArgs = process.argv.slice(2).filter(
115
- (arg) => !["build", "publish"].includes(arg) && !["--save", "--no-save", "--help", "-h", "--version", "-v"].includes(arg) && arg !== targetDir
116
- // Don't filter out the target directory
117
- );
118
- const extraArgs = [];
119
- for (let i = 0; i < rawExtraArgs.length; i++) {
120
- const arg = rawExtraArgs[i];
121
- if (arg.startsWith("--")) {
122
- const flagName = arg.split("=")[0];
123
- if (allowedNpmFlags.includes(flagName)) {
124
- extraArgs.push(arg);
125
- if (!arg.includes("=") && ["--tag", "--access", "--registry", "--otp", "--workspace"].includes(flagName)) {
126
- if (i + 1 < rawExtraArgs.length && !rawExtraArgs[i + 1].startsWith("--")) {
127
- extraArgs.push(rawExtraArgs[i + 1]);
128
- i++;
129
- }
130
- }
131
- } else {
132
- console.warn(`Warning: Ignoring unknown/unsafe npm flag: ${arg}`);
133
- }
134
- } else {
135
- const prevArg = i > 0 ? rawExtraArgs[i - 1] : "";
136
- const isPrevArgValueFlag = ["--tag", "--access", "--registry", "--otp", "--workspace"].includes(prevArg) && !prevArg.includes("=");
137
- if (isPrevArgValueFlag && extraArgs.includes(prevArg)) {
138
- extraArgs.push(arg);
139
- } else {
140
- console.warn(`Warning: Ignoring unexpected argument: ${arg}`);
141
- }
142
- }
143
- }
144
- try {
145
- switch (command) {
146
- case "build":
147
- await build(cwd, shouldSave);
148
- break;
149
- case "publish":
150
- await publish(cwd, shouldSave, extraArgs);
151
- break;
152
- default:
153
- console.error(`Unknown command: ${command}`);
154
- console.log(HELP_TEXT);
155
- process.exit(1);
156
- }
157
- } catch (error) {
158
- console.error("Error:", error.message);
16
+ program.command("publish").description("Build and publish the library").argument("[directory]", "Directory to build and publish", ".").option("--no-save", "Skip package.json updates").option("--dry-run", "Perform a dry run").option("--tag <tag>", "Publish with a specific tag").option("--access <access>", "Set access level (public/restricted)").option("--registry <url>", "Use a specific registry").option("--otp <code>", "One-time password for 2FA").option("--provenance", "Generate provenance statement").action(async (directory, options) => {
17
+ const cwd = Path.resolve(directory);
18
+ const shouldSave = options.save !== false;
19
+ const npmArgs = [];
20
+ if (options.dryRun) npmArgs.push("--dry-run");
21
+ if (options.tag) npmArgs.push("--tag", options.tag);
22
+ if (options.access) npmArgs.push("--access", options.access);
23
+ if (options.registry) npmArgs.push("--registry", options.registry);
24
+ if (options.otp) npmArgs.push("--otp", options.otp);
25
+ if (options.provenance) npmArgs.push("--provenance");
26
+ await publish(cwd, shouldSave, npmArgs);
27
+ });
28
+ program.command("test").description("Run tests across platforms").argument("[directory]", "Directory containing tests", ".").option("-p, --platform <platforms...>", "Platforms to test on (bun, node, chromium, firefox, webkit)").option("--debug", "Keep browser open for debugging").option("-w, --watch", "Watch mode - re-run tests on file changes").option("--timeout <ms>", "Test timeout in milliseconds", "60000").action(async (directory, options) => {
29
+ const cwd = Path.resolve(directory);
30
+ const validPlatforms = ["bun", "node", "chromium", "firefox", "webkit"];
31
+ const platforms = options.platform?.length ? options.platform.filter((p) => validPlatforms.includes(p)) : ["bun"];
32
+ if (options.platform?.length && platforms.length !== options.platform.length) {
33
+ const invalid = options.platform.filter((p) => !validPlatforms.includes(p));
34
+ console.error(`Invalid platform(s): ${invalid.join(", ")}`);
35
+ console.error(`Valid platforms: ${validPlatforms.join(", ")}`);
159
36
  process.exit(1);
160
37
  }
161
- }
162
- main();
38
+ const success = await runTests({
39
+ cwd,
40
+ platforms,
41
+ debug: options.debug || false,
42
+ watch: options.watch || false,
43
+ timeout: parseInt(options.timeout || "60000", 10)
44
+ });
45
+ process.exit(success ? 0 : 1);
46
+ });
47
+ program.parse();
package/src/libuild.js CHANGED
@@ -1,4 +1,6 @@
1
1
  /// <reference types="./libuild.d.ts" />
2
+ import "./_chunks/chunk-CH5RFCXH.js";
3
+
2
4
  // src/libuild.ts
3
5
  import * as FS3 from "fs/promises";
4
6
  import * as Path3 from "path";
@@ -13,8 +15,7 @@ function umdPlugin(options) {
13
15
  name: "umd",
14
16
  setup(build3) {
15
17
  build3.onEnd(async (result) => {
16
- if (result.errors.length > 0)
17
- return;
18
+ if (result.errors.length > 0) return;
18
19
  const outputDir = build3.initialOptions.outdir;
19
20
  if (outputDir) {
20
21
  const files = await FS.readdir(outputDir);
@@ -183,8 +184,7 @@ async function addTripleSlashReferences(tsFiles, outDir, rootDir) {
183
184
  FS2.access(jsPath).then(() => true).catch(() => false),
184
185
  FS2.access(dtsPath).then(() => true).catch(() => false)
185
186
  ]);
186
- if (!jsExists || !dtsExists)
187
- continue;
187
+ if (!jsExists || !dtsExists) continue;
188
188
  const content = await FS2.readFile(jsPath, "utf-8");
189
189
  const referenceComment = `/// <reference types="./${baseName}.d.ts" />`;
190
190
  let modifiedContent;
@@ -212,8 +212,7 @@ async function addAmbientReferences(tsFiles, outDir, rootDir) {
212
212
  } catch {
213
213
  return;
214
214
  }
215
- if (ambientFiles.length === 0)
216
- return;
215
+ if (ambientFiles.length === 0) return;
217
216
  for (const tsFile of tsFiles) {
218
217
  const relativePath = Path2.relative(rootDir, tsFile);
219
218
  const relativeDir = Path2.dirname(relativePath);
@@ -221,8 +220,7 @@ async function addAmbientReferences(tsFiles, outDir, rootDir) {
221
220
  const outputSubDir = relativeDir === "." ? outDir : Path2.join(outDir, relativeDir);
222
221
  const dtsPath = Path2.join(outputSubDir, `${baseName}.d.ts`);
223
222
  const dtsExists = await FS2.access(dtsPath).then(() => true).catch(() => false);
224
- if (!dtsExists)
225
- continue;
223
+ if (!dtsExists) continue;
226
224
  const content = await FS2.readFile(dtsPath, "utf-8");
227
225
  const relativeToRoot = relativeDir === "." ? "./" : "../".repeat(relativeDir.split(Path2.sep).length);
228
226
  const references = ambientFiles.map((ambientFile) => `/// <reference path="${relativeToRoot}${ambientFile}" />`).join("\n");
@@ -266,26 +264,19 @@ async function processJavaScriptExecutable(filePath, runtimeBanner) {
266
264
  }
267
265
  }
268
266
  function isValidEntrypoint(filename) {
269
- if (!filename.endsWith(".ts") && !filename.endsWith(".js"))
270
- return false;
271
- if (filename.endsWith(".d.ts"))
272
- return false;
273
- if (filename.startsWith("_"))
274
- return false;
275
- if (filename.startsWith("."))
276
- return false;
277
- if (filename.includes(".test."))
278
- return false;
279
- if (filename.includes(".spec."))
280
- return false;
267
+ if (!filename.endsWith(".ts") && !filename.endsWith(".js")) return false;
268
+ if (filename.endsWith(".d.ts")) return false;
269
+ if (filename.startsWith("_")) return false;
270
+ if (filename.startsWith(".")) return false;
271
+ if (filename.includes(".test.")) return false;
272
+ if (filename.includes(".spec.")) return false;
281
273
  return true;
282
274
  }
283
275
  async function findEntrypoints(srcDir) {
284
276
  const files = await FS3.readdir(srcDir, { withFileTypes: true });
285
277
  return files.filter((dirent) => {
286
278
  if (dirent.isDirectory()) {
287
- if (dirent.name === "__tests__" || dirent.name === "test")
288
- return false;
279
+ if (dirent.name === "__tests__" || dirent.name === "test") return false;
289
280
  return false;
290
281
  }
291
282
  return isValidEntrypoint(dirent.name);
@@ -521,8 +512,7 @@ async function generateExports(entries, mainEntry, options, existingExports = {}
521
512
  }
522
513
  }
523
514
  for (const entry of entries) {
524
- if (entry === "umd")
525
- continue;
515
+ if (entry === "umd") continue;
526
516
  const key = `./${entry}`;
527
517
  if (!exports[key]) {
528
518
  exports[key] = await createExportEntry(entry);
@@ -698,6 +688,9 @@ function fixExportsForDist(obj) {
698
688
  if (obj.startsWith("./dist/src/")) {
699
689
  return obj.replace("./dist/src/", "./src/");
700
690
  }
691
+ if (obj.startsWith("./dist/bin/")) {
692
+ return obj.replace("./dist/bin/", "./bin/");
693
+ }
701
694
  if (obj.includes("/dist/") && obj.endsWith("/package.json")) {
702
695
  return "./package.json";
703
696
  }
@@ -774,8 +767,7 @@ async function makeFilesExecutable(pkg, cwd, allBinEntries, runtimeBanner) {
774
767
  }
775
768
  }
776
769
  async function resolveWorkspaceDependencies(dependencies, cwd) {
777
- if (!dependencies)
778
- return void 0;
770
+ if (!dependencies) return void 0;
779
771
  const resolved = {};
780
772
  for (const [depName, depVersion] of Object.entries(dependencies)) {
781
773
  if (depVersion.startsWith("workspace:")) {
@@ -952,11 +944,9 @@ async function build2(cwd, save = false) {
952
944
  let srcEntries;
953
945
  if (pkg.exports && typeof pkg.exports === "object") {
954
946
  const srcExportKeys = Object.keys(pkg.exports).filter((key) => {
955
- if (key === "." || key === "./package.json")
956
- return false;
947
+ if (key === "." || key === "./package.json") return false;
957
948
  const val = pkg.exports[key];
958
- if (typeof val === "string")
959
- return val.includes("/src/");
949
+ if (typeof val === "string") return val.includes("/src/");
960
950
  if (typeof val === "object" && val !== null) {
961
951
  return Object.values(val).some((v) => typeof v === "string" && v.includes("/src/"));
962
952
  }
@@ -1025,8 +1015,10 @@ async function build2(cwd, save = false) {
1025
1015
  const externalDeps = [
1026
1016
  "*.json",
1027
1017
  // Let Node.js handle JSON imports natively
1028
- "esbuild"
1018
+ "esbuild",
1029
1019
  // Explicit external to suppress require.resolve warning from esbuild's own code
1020
+ "bun:*"
1021
+ // Bun built-ins (bun:test, bun:sqlite, etc.)
1030
1022
  ];
1031
1023
  const entryPoints = [];
1032
1024
  const umdEntries = [];
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Browser test shim for @b9g/libuild/test
3
+ *
4
+ * Implements a minimal test runner that works in browsers.
5
+ * Results are exposed via globalThis.__LIBUILD_TEST__ for Playwright to poll.
6
+ */
7
+ interface Matchers {
8
+ toBe(expected: unknown): void;
9
+ toEqual(expected: unknown): void;
10
+ toStrictEqual(expected: unknown): void;
11
+ toBeTruthy(): void;
12
+ toBeFalsy(): void;
13
+ toBeNull(): void;
14
+ toBeUndefined(): void;
15
+ toBeDefined(): void;
16
+ toBeNaN(): void;
17
+ toBeGreaterThan(expected: number): void;
18
+ toBeGreaterThanOrEqual(expected: number): void;
19
+ toBeLessThan(expected: number): void;
20
+ toBeLessThanOrEqual(expected: number): void;
21
+ toContain(expected: unknown): void;
22
+ toHaveLength(expected: number): void;
23
+ toMatch(expected: RegExp | string): void;
24
+ toThrow(expected?: string | RegExp | Error): void;
25
+ toBeInstanceOf(expected: new (...args: any[]) => any): void;
26
+ not: Matchers;
27
+ }
28
+ export declare function expect(actual: unknown): Matchers;
29
+ declare global {
30
+ var __LIBUILD_TEST__: {
31
+ ended: boolean;
32
+ failed: number;
33
+ passed: number;
34
+ errors: Array<{
35
+ name: string;
36
+ error: string;
37
+ }>;
38
+ };
39
+ }
40
+ type TestFn = () => void | Promise<void>;
41
+ type HookFn = () => void | Promise<void>;
42
+ export declare function describe(name: string, fn: () => void): void;
43
+ export declare function test(name: string, fn: TestFn): void;
44
+ export declare const it: typeof test;
45
+ export declare function beforeEach(fn: HookFn): void;
46
+ export declare function afterEach(fn: HookFn): void;
47
+ export declare function beforeAll(fn: HookFn): void;
48
+ export declare function afterAll(fn: HookFn): void;
49
+ export {};