@b9g/libuild 0.1.21 → 0.1.23
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 +5 -0
- package/package.json +56 -15
- package/src/_chunks/chunk-IBOCQ33F.js +11 -0
- package/src/cli.js +43 -152
- package/src/libuild.js +17 -7
- package/src/test-browser.d.ts +49 -0
- package/src/test-browser.js +337 -0
- package/src/test-bun.d.ts +6 -0
- package/src/test-bun.js +24 -0
- package/src/test-node.d.ts +8 -0
- package/src/test-node.js +24 -0
- package/src/test-runner.d.ts +37 -0
- package/src/test-runner.js +406 -0
- package/src/test.d.ts +8 -0
- package/src/test.js +25 -0
- package/src/cli.cjs +0 -183
- package/src/libuild.cjs +0 -214116
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/// <reference types="./test-runner.d.ts" />
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "./_chunks/chunk-IBOCQ33F.js";
|
|
5
|
+
|
|
6
|
+
// src/test-runner.ts
|
|
7
|
+
import * as FS from "fs/promises";
|
|
8
|
+
import * as Path from "path";
|
|
9
|
+
import { createServer } from "http";
|
|
10
|
+
import * as ESBuild from "esbuild";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import { createRequire } from "module";
|
|
13
|
+
var __dirname = Path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
var DEFAULT_PATTERNS = [
|
|
15
|
+
"**/*.test.ts",
|
|
16
|
+
"**/*.test.tsx",
|
|
17
|
+
"**/*.test.js",
|
|
18
|
+
"**/*.test.jsx",
|
|
19
|
+
"**/*.spec.ts",
|
|
20
|
+
"**/*.spec.tsx",
|
|
21
|
+
"**/*.spec.js",
|
|
22
|
+
"**/*.spec.jsx",
|
|
23
|
+
"**/test/**/*.ts",
|
|
24
|
+
"**/test/**/*.tsx",
|
|
25
|
+
"**/test/**/*.js",
|
|
26
|
+
"**/test/**/*.jsx"
|
|
27
|
+
];
|
|
28
|
+
var IGNORE_PATTERNS = [
|
|
29
|
+
"**/node_modules/**",
|
|
30
|
+
"**/dist/**",
|
|
31
|
+
"**/.git/**",
|
|
32
|
+
"**/coverage/**"
|
|
33
|
+
];
|
|
34
|
+
async function findTestFiles(cwd, patterns) {
|
|
35
|
+
const { glob } = await import("glob");
|
|
36
|
+
const files = [];
|
|
37
|
+
for (const pattern of patterns) {
|
|
38
|
+
const matches = await glob(pattern, {
|
|
39
|
+
cwd,
|
|
40
|
+
ignore: IGNORE_PATTERNS,
|
|
41
|
+
absolute: true
|
|
42
|
+
});
|
|
43
|
+
files.push(...matches);
|
|
44
|
+
}
|
|
45
|
+
return [...new Set(files)];
|
|
46
|
+
}
|
|
47
|
+
function generateTestEntry(testFiles, platform) {
|
|
48
|
+
const imports = testFiles.map((file, i) => `import "${file.replace(/\\/g, "/")}";`).join("\n");
|
|
49
|
+
return `// Auto-generated test entry for ${platform}
|
|
50
|
+
${imports}
|
|
51
|
+
`;
|
|
52
|
+
}
|
|
53
|
+
function isBrowserPlatform(platform) {
|
|
54
|
+
return platform === "chromium" || platform === "firefox" || platform === "webkit";
|
|
55
|
+
}
|
|
56
|
+
async function bundleTests(testFiles, platform, outDir, cwd) {
|
|
57
|
+
const entryContent = generateTestEntry(testFiles, platform);
|
|
58
|
+
const entryPath = Path.join(outDir, `entry-${platform}.ts`);
|
|
59
|
+
const outPath = Path.join(outDir, `bundle-${platform}.js`);
|
|
60
|
+
await FS.writeFile(entryPath, entryContent);
|
|
61
|
+
const isBrowser = isBrowserPlatform(platform);
|
|
62
|
+
const shimName = isBrowser ? "test-browser" : `test-${platform}`;
|
|
63
|
+
let shimPath;
|
|
64
|
+
try {
|
|
65
|
+
shimPath = __require.resolve(`@b9g/libuild/${shimName}`);
|
|
66
|
+
} catch {
|
|
67
|
+
shimPath = Path.join(__dirname, `${shimName}.js`);
|
|
68
|
+
}
|
|
69
|
+
const requireShim = `
|
|
70
|
+
import { createRequire } from "module";
|
|
71
|
+
const require = createRequire(import.meta.url);
|
|
72
|
+
`;
|
|
73
|
+
const buildOptions = {
|
|
74
|
+
entryPoints: [entryPath],
|
|
75
|
+
bundle: true,
|
|
76
|
+
format: "esm",
|
|
77
|
+
outfile: outPath,
|
|
78
|
+
platform: isBrowser ? "browser" : "node",
|
|
79
|
+
target: isBrowser ? "es2020" : "node18",
|
|
80
|
+
// Replace @b9g/libuild/test with platform-specific shim
|
|
81
|
+
alias: {
|
|
82
|
+
"@b9g/libuild/test": shimPath
|
|
83
|
+
},
|
|
84
|
+
// External runtime-specific modules
|
|
85
|
+
external: platform === "bun" ? ["bun:test"] : [],
|
|
86
|
+
// Inject require shim for node/bun to handle CJS deps like expect/chalk
|
|
87
|
+
...isBrowser ? {} : { banner: { js: requireShim } },
|
|
88
|
+
// Define for dead code elimination
|
|
89
|
+
define: {
|
|
90
|
+
"process.env.NODE_ENV": '"test"'
|
|
91
|
+
},
|
|
92
|
+
logLevel: "warning"
|
|
93
|
+
};
|
|
94
|
+
await ESBuild.build(buildOptions);
|
|
95
|
+
return outPath;
|
|
96
|
+
}
|
|
97
|
+
function parseTapOutput(output) {
|
|
98
|
+
let passed = 0;
|
|
99
|
+
let failed = 0;
|
|
100
|
+
const errors = [];
|
|
101
|
+
const lines = output.split("\n");
|
|
102
|
+
let lastTestName = "";
|
|
103
|
+
let lastTestPassed = true;
|
|
104
|
+
for (let i = 0; i < lines.length; i++) {
|
|
105
|
+
const line = lines[i];
|
|
106
|
+
const okMatch = line.match(/^\s*ok \d+ - (.+)/);
|
|
107
|
+
const notOkMatch = line.match(/^\s*not ok \d+ - (.+)/);
|
|
108
|
+
if (okMatch) {
|
|
109
|
+
lastTestName = okMatch[1];
|
|
110
|
+
lastTestPassed = true;
|
|
111
|
+
} else if (notOkMatch) {
|
|
112
|
+
lastTestName = notOkMatch[1];
|
|
113
|
+
lastTestPassed = false;
|
|
114
|
+
}
|
|
115
|
+
if (line.includes("type: 'test'") || line.includes('type: "test"')) {
|
|
116
|
+
if (lastTestPassed) {
|
|
117
|
+
passed++;
|
|
118
|
+
} else {
|
|
119
|
+
failed++;
|
|
120
|
+
errors.push({ name: lastTestName, error: "Test failed" });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { passed, failed, errors };
|
|
125
|
+
}
|
|
126
|
+
async function runNodeTests(bundlePath, timeout) {
|
|
127
|
+
const { spawn } = await import("child_process");
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
const child = spawn("node", ["--test", "--test-reporter=tap", bundlePath], {
|
|
130
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
131
|
+
timeout
|
|
132
|
+
});
|
|
133
|
+
let stdout = "";
|
|
134
|
+
let stderr = "";
|
|
135
|
+
let pendingTest = null;
|
|
136
|
+
const printedTests = /* @__PURE__ */ new Set();
|
|
137
|
+
child.stdout?.on("data", (data) => {
|
|
138
|
+
const text = data.toString();
|
|
139
|
+
stdout += text;
|
|
140
|
+
for (const line of text.split("\n")) {
|
|
141
|
+
const okMatch = line.match(/^\s*ok \d+ - (.+)/);
|
|
142
|
+
const notOkMatch = line.match(/^\s*not ok \d+ - (.+)/);
|
|
143
|
+
if (okMatch) {
|
|
144
|
+
pendingTest = { name: okMatch[1], passed: true };
|
|
145
|
+
} else if (notOkMatch) {
|
|
146
|
+
pendingTest = { name: notOkMatch[1], passed: false };
|
|
147
|
+
}
|
|
148
|
+
if ((line.includes("type: 'test'") || line.includes('type: "test"')) && pendingTest) {
|
|
149
|
+
const key = `${pendingTest.name}-${pendingTest.passed}`;
|
|
150
|
+
if (!printedTests.has(key)) {
|
|
151
|
+
printedTests.add(key);
|
|
152
|
+
if (pendingTest.passed) {
|
|
153
|
+
console.log(`\u2713 ${pendingTest.name}`);
|
|
154
|
+
} else {
|
|
155
|
+
console.log(`\u2717 ${pendingTest.name}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
pendingTest = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
child.stderr?.on("data", (data) => {
|
|
163
|
+
stderr += data.toString();
|
|
164
|
+
process.stderr.write(data);
|
|
165
|
+
});
|
|
166
|
+
child.on("close", () => {
|
|
167
|
+
const { passed, failed, errors } = parseTapOutput(stdout);
|
|
168
|
+
resolve({
|
|
169
|
+
platform: "node",
|
|
170
|
+
passed,
|
|
171
|
+
failed,
|
|
172
|
+
errors
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
child.on("error", (err) => {
|
|
176
|
+
resolve({
|
|
177
|
+
platform: "node",
|
|
178
|
+
passed: 0,
|
|
179
|
+
failed: 1,
|
|
180
|
+
errors: [{ name: "spawn error", error: err.message }]
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function parseBunOutput(output) {
|
|
186
|
+
let passed = 0;
|
|
187
|
+
let failed = 0;
|
|
188
|
+
const errors = [];
|
|
189
|
+
const passMatch = output.match(/^\s*(\d+)\s+pass/m);
|
|
190
|
+
const failMatch = output.match(/^\s*(\d+)\s+fail/m);
|
|
191
|
+
if (passMatch)
|
|
192
|
+
passed = parseInt(passMatch[1], 10);
|
|
193
|
+
if (failMatch)
|
|
194
|
+
failed = parseInt(failMatch[1], 10);
|
|
195
|
+
return { passed, failed, errors };
|
|
196
|
+
}
|
|
197
|
+
async function runBunTests(bundlePath, timeout) {
|
|
198
|
+
const { spawn } = await import("child_process");
|
|
199
|
+
return new Promise((resolve) => {
|
|
200
|
+
const child = spawn("bun", ["test", bundlePath], {
|
|
201
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
202
|
+
timeout
|
|
203
|
+
});
|
|
204
|
+
let stdout = "";
|
|
205
|
+
let stderr = "";
|
|
206
|
+
child.stdout?.on("data", (data) => {
|
|
207
|
+
const text = data.toString();
|
|
208
|
+
stdout += text;
|
|
209
|
+
process.stdout.write(data);
|
|
210
|
+
});
|
|
211
|
+
child.stderr?.on("data", (data) => {
|
|
212
|
+
const text = data.toString();
|
|
213
|
+
stderr += text;
|
|
214
|
+
process.stderr.write(data);
|
|
215
|
+
});
|
|
216
|
+
child.on("close", () => {
|
|
217
|
+
const { passed, failed, errors } = parseBunOutput(stdout + stderr);
|
|
218
|
+
resolve({
|
|
219
|
+
platform: "bun",
|
|
220
|
+
passed,
|
|
221
|
+
failed,
|
|
222
|
+
errors
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
child.on("error", (err) => {
|
|
226
|
+
resolve({
|
|
227
|
+
platform: "bun",
|
|
228
|
+
passed: 0,
|
|
229
|
+
failed: 1,
|
|
230
|
+
errors: [{ name: "spawn error", error: err.message }]
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function runBrowserTests(bundlePath, browser, timeout, debug, cwd) {
|
|
236
|
+
let playwright;
|
|
237
|
+
try {
|
|
238
|
+
const require2 = createRequire(Path.join(cwd, "package.json"));
|
|
239
|
+
playwright = require2("playwright");
|
|
240
|
+
} catch {
|
|
241
|
+
console.error("Playwright is required for browser tests.");
|
|
242
|
+
console.error("Install it with: npm install -D playwright");
|
|
243
|
+
return {
|
|
244
|
+
platform: `browser (${browser})`,
|
|
245
|
+
passed: 0,
|
|
246
|
+
failed: 1,
|
|
247
|
+
errors: [{ name: "setup", error: "Playwright not installed" }]
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const bundleContent = await FS.readFile(bundlePath, "utf-8");
|
|
251
|
+
const html = `<!DOCTYPE html>
|
|
252
|
+
<html>
|
|
253
|
+
<head>
|
|
254
|
+
<meta charset="utf-8">
|
|
255
|
+
<title>libuild tests</title>
|
|
256
|
+
</head>
|
|
257
|
+
<body>
|
|
258
|
+
<script type="module">
|
|
259
|
+
${bundleContent}
|
|
260
|
+
</script>
|
|
261
|
+
</body>
|
|
262
|
+
</html>`;
|
|
263
|
+
let server;
|
|
264
|
+
let port;
|
|
265
|
+
await new Promise((resolve) => {
|
|
266
|
+
server = createServer((req, res) => {
|
|
267
|
+
res.setHeader("Content-Type", "text/html");
|
|
268
|
+
res.end(html);
|
|
269
|
+
});
|
|
270
|
+
server.listen(0, () => {
|
|
271
|
+
const addr = server.address();
|
|
272
|
+
port = typeof addr === "object" && addr ? addr.port : 3e3;
|
|
273
|
+
resolve();
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
try {
|
|
277
|
+
const browserInstance = await playwright[browser].launch({
|
|
278
|
+
headless: !debug
|
|
279
|
+
});
|
|
280
|
+
const context = await browserInstance.newContext();
|
|
281
|
+
const page = await context.newPage();
|
|
282
|
+
page.on("console", (msg) => {
|
|
283
|
+
const type = msg.type();
|
|
284
|
+
const text = msg.text();
|
|
285
|
+
if (type === "error") {
|
|
286
|
+
console.error(text);
|
|
287
|
+
} else {
|
|
288
|
+
console.log(text);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
page.on("pageerror", (err) => {
|
|
292
|
+
console.error("Page error:", err.message);
|
|
293
|
+
});
|
|
294
|
+
await page.goto(`http://localhost:${port}/`);
|
|
295
|
+
await page.waitForFunction(
|
|
296
|
+
() => globalThis.__LIBUILD_TEST__?.ended === true,
|
|
297
|
+
{ timeout }
|
|
298
|
+
);
|
|
299
|
+
const results = await page.evaluate(() => globalThis.__LIBUILD_TEST__);
|
|
300
|
+
if (!debug) {
|
|
301
|
+
await browserInstance.close();
|
|
302
|
+
} else {
|
|
303
|
+
console.log("\nDebug mode: browser left open. Press Ctrl+C to exit.");
|
|
304
|
+
await new Promise(() => {
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
platform: `browser (${browser})`,
|
|
309
|
+
passed: results.passed,
|
|
310
|
+
failed: results.failed,
|
|
311
|
+
errors: results.errors
|
|
312
|
+
};
|
|
313
|
+
} finally {
|
|
314
|
+
server.close();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function printResults(results) {
|
|
318
|
+
console.log("\n" + "=".repeat(60));
|
|
319
|
+
console.log("Test Results Summary");
|
|
320
|
+
console.log("=".repeat(60));
|
|
321
|
+
let allPassed = true;
|
|
322
|
+
for (const result of results) {
|
|
323
|
+
const status = result.failed === 0 ? "\u2713" : "\u2717";
|
|
324
|
+
const color = result.failed === 0 ? "\x1B[32m" : "\x1B[31m";
|
|
325
|
+
const reset = "\x1B[0m";
|
|
326
|
+
console.log(
|
|
327
|
+
`${color}${status}${reset} ${result.platform}: ${result.passed} passed, ${result.failed} failed`
|
|
328
|
+
);
|
|
329
|
+
if (result.failed > 0) {
|
|
330
|
+
allPassed = false;
|
|
331
|
+
for (const error of result.errors) {
|
|
332
|
+
console.log(` \u2717 ${error.name}`);
|
|
333
|
+
console.log(` ${error.error}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
console.log("=".repeat(60));
|
|
338
|
+
return allPassed;
|
|
339
|
+
}
|
|
340
|
+
async function runTests(options = {}) {
|
|
341
|
+
const opts = {
|
|
342
|
+
cwd: options.cwd || process.cwd(),
|
|
343
|
+
patterns: options.patterns || DEFAULT_PATTERNS,
|
|
344
|
+
platforms: options.platforms || ["bun"],
|
|
345
|
+
debug: options.debug || false,
|
|
346
|
+
timeout: options.timeout || 6e4,
|
|
347
|
+
watch: options.watch || false
|
|
348
|
+
};
|
|
349
|
+
console.log("Finding test files...");
|
|
350
|
+
const testFiles = await findTestFiles(opts.cwd, opts.patterns);
|
|
351
|
+
if (testFiles.length === 0) {
|
|
352
|
+
console.log("No test files found.");
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
console.log(`Found ${testFiles.length} test file(s)`);
|
|
356
|
+
const tempDir = Path.join(opts.cwd, ".libuild-test");
|
|
357
|
+
await FS.mkdir(tempDir, { recursive: true });
|
|
358
|
+
const results = [];
|
|
359
|
+
try {
|
|
360
|
+
for (const platform of opts.platforms) {
|
|
361
|
+
console.log(`
|
|
362
|
+
Building tests for ${platform}...`);
|
|
363
|
+
const bundlePath = await bundleTests(testFiles, platform, tempDir, opts.cwd);
|
|
364
|
+
console.log(`Running tests on ${platform}...`);
|
|
365
|
+
let result;
|
|
366
|
+
if (platform === "bun") {
|
|
367
|
+
result = await runBunTests(bundlePath, opts.timeout);
|
|
368
|
+
} else if (platform === "node") {
|
|
369
|
+
result = await runNodeTests(bundlePath, opts.timeout);
|
|
370
|
+
} else {
|
|
371
|
+
result = await runBrowserTests(bundlePath, platform, opts.timeout, opts.debug, opts.cwd);
|
|
372
|
+
}
|
|
373
|
+
results.push(result);
|
|
374
|
+
}
|
|
375
|
+
return printResults(results);
|
|
376
|
+
} finally {
|
|
377
|
+
if (!opts.debug) {
|
|
378
|
+
await FS.rm(tempDir, { recursive: true, force: true }).catch(() => {
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function detectPlatforms() {
|
|
384
|
+
const platforms = [];
|
|
385
|
+
const { spawn } = await import("child_process");
|
|
386
|
+
try {
|
|
387
|
+
await new Promise((resolve, reject) => {
|
|
388
|
+
const child = spawn("bun", ["--version"], { stdio: "ignore" });
|
|
389
|
+
child.on("close", (code) => code === 0 ? resolve() : reject());
|
|
390
|
+
child.on("error", reject);
|
|
391
|
+
});
|
|
392
|
+
platforms.push("bun");
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
platforms.push("node");
|
|
396
|
+
try {
|
|
397
|
+
await import("playwright");
|
|
398
|
+
platforms.push("browser");
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
return platforms;
|
|
402
|
+
}
|
|
403
|
+
export {
|
|
404
|
+
detectPlatforms,
|
|
405
|
+
runTests
|
|
406
|
+
};
|
package/src/test.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @b9g/libuild/test - Cross-platform test utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified testing API across Bun, Node, and browsers.
|
|
5
|
+
* Uses top-level await to detect runtime and load appropriate shim.
|
|
6
|
+
*/
|
|
7
|
+
declare const describe: typeof import("node:test").describe | import("bun:test").Describe<[]>, test: typeof import("node:test") | import("bun:test").Test<[]>, it: typeof import("node:test") | import("bun:test").Test<[]>, expect: import("expect").Expect | import("bun:test").Expect, beforeAll: typeof import("node:test").before | typeof import("bun:test").beforeAll, afterAll: typeof import("node:test").after | typeof import("bun:test").afterAll, beforeEach: typeof import("node:test").beforeEach | typeof import("bun:test").beforeEach, afterEach: typeof import("node:test").afterEach | typeof import("bun:test").afterEach;
|
|
8
|
+
export { describe, test, it, expect, beforeAll, afterAll, beforeEach, afterEach, };
|
package/src/test.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="./test.d.ts" />
|
|
2
|
+
import "./_chunks/chunk-IBOCQ33F.js";
|
|
3
|
+
|
|
4
|
+
// src/test.ts
|
|
5
|
+
var isBun = typeof Bun !== "undefined";
|
|
6
|
+
var {
|
|
7
|
+
describe,
|
|
8
|
+
test,
|
|
9
|
+
it,
|
|
10
|
+
expect,
|
|
11
|
+
beforeAll,
|
|
12
|
+
afterAll,
|
|
13
|
+
beforeEach,
|
|
14
|
+
afterEach
|
|
15
|
+
} = isBun ? await import("./test-bun.js") : await import("./test-node.js");
|
|
16
|
+
export {
|
|
17
|
+
afterAll,
|
|
18
|
+
afterEach,
|
|
19
|
+
beforeAll,
|
|
20
|
+
beforeEach,
|
|
21
|
+
describe,
|
|
22
|
+
expect,
|
|
23
|
+
it,
|
|
24
|
+
test
|
|
25
|
+
};
|
package/src/cli.cjs
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
-
mod
|
|
24
|
-
));
|
|
25
|
-
|
|
26
|
-
// src/cli.ts
|
|
27
|
-
var import_util = require("util");
|
|
28
|
-
var Path = __toESM(require("path"), 1);
|
|
29
|
-
var import_libuild = require("./libuild.cjs");
|
|
30
|
-
var { values, positionals } = (0, import_util.parseArgs)({
|
|
31
|
-
args: process.argv.slice(2),
|
|
32
|
-
options: {
|
|
33
|
-
help: { type: "boolean", short: "h" },
|
|
34
|
-
version: { type: "boolean", short: "v" },
|
|
35
|
-
save: { type: "boolean" },
|
|
36
|
-
"no-save": { type: "boolean" }
|
|
37
|
-
},
|
|
38
|
-
allowPositionals: true,
|
|
39
|
-
strict: false
|
|
40
|
-
// Allow unknown options to be passed through
|
|
41
|
-
});
|
|
42
|
-
var HELP_TEXT = `
|
|
43
|
-
libuild - Zero-config library builds
|
|
44
|
-
|
|
45
|
-
Usage:
|
|
46
|
-
libuild [command] [directory] [options]
|
|
47
|
-
|
|
48
|
-
Commands:
|
|
49
|
-
build Build the library (default command)
|
|
50
|
-
publish Build and publish the library
|
|
51
|
-
|
|
52
|
-
Arguments:
|
|
53
|
-
directory Optional directory to build (defaults to current directory)
|
|
54
|
-
|
|
55
|
-
Options:
|
|
56
|
-
--save Update root package.json to point to dist files
|
|
57
|
-
--no-save Skip package.json updates (for publish command)
|
|
58
|
-
|
|
59
|
-
IMPORTANT:
|
|
60
|
-
\u2022 libuild is zero-config - there is NO libuild.config.js file
|
|
61
|
-
\u2022 Configuration comes from your package.json (main, module, exports, etc.)
|
|
62
|
-
\u2022 Use --save to regenerate package.json fields based on built files
|
|
63
|
-
\u2022 Invalid bin/exports paths are automatically cleaned up with --save
|
|
64
|
-
|
|
65
|
-
For publish command, all additional flags are forwarded to npm publish.
|
|
66
|
-
|
|
67
|
-
Examples:
|
|
68
|
-
libuild # Build the library in current directory
|
|
69
|
-
libuild build # Same as above
|
|
70
|
-
libuild ../other-proj # Build library in ../other-proj
|
|
71
|
-
libuild build --save # Build and update package.json for npm link
|
|
72
|
-
libuild publish ../lib # Build and publish library in ../lib
|
|
73
|
-
libuild publish --dry-run --tag beta # Build and publish with npm flags
|
|
74
|
-
`;
|
|
75
|
-
async function main() {
|
|
76
|
-
if (values.help) {
|
|
77
|
-
console.log(HELP_TEXT);
|
|
78
|
-
process.exit(0);
|
|
79
|
-
}
|
|
80
|
-
if (values.version) {
|
|
81
|
-
const pkg = await import("../package.json");
|
|
82
|
-
console.log(pkg.version);
|
|
83
|
-
process.exit(0);
|
|
84
|
-
}
|
|
85
|
-
const command = positionals[0] || "build";
|
|
86
|
-
let targetDir;
|
|
87
|
-
for (let i = 1; i < positionals.length; i++) {
|
|
88
|
-
const arg = positionals[i];
|
|
89
|
-
const flagValueFlags = ["--tag", "--access", "--registry", "--otp", "--workspace"];
|
|
90
|
-
const isValueForFlag = flagValueFlags.some((flag) => {
|
|
91
|
-
const flagIndex = process.argv.indexOf(flag);
|
|
92
|
-
const argIndex = process.argv.indexOf(arg);
|
|
93
|
-
return flagIndex !== -1 && argIndex === flagIndex + 1;
|
|
94
|
-
});
|
|
95
|
-
if (isValueForFlag) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (!arg.startsWith("--")) {
|
|
99
|
-
try {
|
|
100
|
-
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/"));
|
|
101
|
-
if (isSystemPath) {
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
const resolvedPath = Path.resolve(arg);
|
|
105
|
-
if (arg.includes("/") || arg.includes("\\") || arg === "." || arg === ".." || arg.startsWith("./") || arg.startsWith("../")) {
|
|
106
|
-
targetDir = arg;
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
const fs = await import("fs/promises");
|
|
110
|
-
try {
|
|
111
|
-
const stat = await fs.stat(resolvedPath);
|
|
112
|
-
if (stat.isDirectory() && !Path.isAbsolute(arg)) {
|
|
113
|
-
targetDir = arg;
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
} catch {
|
|
117
|
-
}
|
|
118
|
-
} catch {
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const cwd = targetDir ? Path.resolve(targetDir) : process.cwd();
|
|
123
|
-
const shouldSave = values.save || command === "publish" && !values["no-save"];
|
|
124
|
-
const allowedNpmFlags = [
|
|
125
|
-
"--dry-run",
|
|
126
|
-
"--tag",
|
|
127
|
-
"--access",
|
|
128
|
-
"--registry",
|
|
129
|
-
"--otp",
|
|
130
|
-
"--provenance",
|
|
131
|
-
"--workspace",
|
|
132
|
-
"--workspaces",
|
|
133
|
-
"--include-workspace-root"
|
|
134
|
-
];
|
|
135
|
-
const rawExtraArgs = process.argv.slice(2).filter(
|
|
136
|
-
(arg) => !["build", "publish"].includes(arg) && !["--save", "--no-save", "--help", "-h", "--version", "-v"].includes(arg) && arg !== targetDir
|
|
137
|
-
// Don't filter out the target directory
|
|
138
|
-
);
|
|
139
|
-
const extraArgs = [];
|
|
140
|
-
for (let i = 0; i < rawExtraArgs.length; i++) {
|
|
141
|
-
const arg = rawExtraArgs[i];
|
|
142
|
-
if (arg.startsWith("--")) {
|
|
143
|
-
const flagName = arg.split("=")[0];
|
|
144
|
-
if (allowedNpmFlags.includes(flagName)) {
|
|
145
|
-
extraArgs.push(arg);
|
|
146
|
-
if (!arg.includes("=") && ["--tag", "--access", "--registry", "--otp", "--workspace"].includes(flagName)) {
|
|
147
|
-
if (i + 1 < rawExtraArgs.length && !rawExtraArgs[i + 1].startsWith("--")) {
|
|
148
|
-
extraArgs.push(rawExtraArgs[i + 1]);
|
|
149
|
-
i++;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
console.warn(`Warning: Ignoring unknown/unsafe npm flag: ${arg}`);
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
const prevArg = i > 0 ? rawExtraArgs[i - 1] : "";
|
|
157
|
-
const isPrevArgValueFlag = ["--tag", "--access", "--registry", "--otp", "--workspace"].includes(prevArg) && !prevArg.includes("=");
|
|
158
|
-
if (isPrevArgValueFlag && extraArgs.includes(prevArg)) {
|
|
159
|
-
extraArgs.push(arg);
|
|
160
|
-
} else {
|
|
161
|
-
console.warn(`Warning: Ignoring unexpected argument: ${arg}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
try {
|
|
166
|
-
switch (command) {
|
|
167
|
-
case "build":
|
|
168
|
-
await (0, import_libuild.build)(cwd, shouldSave);
|
|
169
|
-
break;
|
|
170
|
-
case "publish":
|
|
171
|
-
await (0, import_libuild.publish)(cwd, shouldSave, extraArgs);
|
|
172
|
-
break;
|
|
173
|
-
default:
|
|
174
|
-
console.error(`Unknown command: ${command}`);
|
|
175
|
-
console.log(HELP_TEXT);
|
|
176
|
-
process.exit(1);
|
|
177
|
-
}
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error("Error:", error.message);
|
|
180
|
-
process.exit(1);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
main();
|