robuild 0.0.6 → 0.0.8

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.
@@ -1,563 +0,0 @@
1
- import { mkdir, readFile, rm, symlink, writeFile } from "node:fs/promises";
2
- import { basename, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { consola } from "consola";
5
- import { colors } from "consola/utils";
6
- import prettyBytes from "pretty-bytes";
7
- import { builtinModules } from "node:module";
8
- import { defu } from "defu";
9
- import { resolveModulePath } from "exsolve";
10
- import { parseSync } from "oxc-parser";
11
- import { rolldown } from "rolldown";
12
- import { dts } from "rolldown-plugin-dts";
13
- import { promises, readdirSync, statSync } from "node:fs";
14
- import { gzipSync } from "node:zlib";
15
- import { minify } from "oxc-minify";
16
- import MagicString from "magic-string";
17
- import { transform } from "oxc-transform";
18
- import { glob } from "tinyglobby";
19
- import { watch } from "chokidar";
20
-
21
- //#region src/utils.ts
22
- function fmtPath(path) {
23
- return resolve(path).replace(process.cwd(), ".");
24
- }
25
- function analyzeDir(dir) {
26
- if (Array.isArray(dir)) {
27
- let totalSize$1 = 0;
28
- let totalFiles = 0;
29
- for (const d of dir) {
30
- const { size, files: files$1 } = analyzeDir(d);
31
- totalSize$1 += size;
32
- totalFiles += files$1;
33
- }
34
- return {
35
- size: totalSize$1,
36
- files: totalFiles
37
- };
38
- }
39
- let totalSize = 0;
40
- const files = readdirSync(dir, {
41
- withFileTypes: true,
42
- recursive: true
43
- });
44
- for (const file of files) {
45
- const fullPath = join(file.parentPath, file.name);
46
- if (file.isFile()) {
47
- const { size } = statSync(fullPath);
48
- totalSize += size;
49
- }
50
- }
51
- return {
52
- size: totalSize,
53
- files: files.length
54
- };
55
- }
56
- async function distSize(dir, entry) {
57
- const build$1 = await rolldown({
58
- input: join(dir, entry),
59
- plugins: [],
60
- platform: "neutral",
61
- external: (id) => id[0] !== "." && !id.startsWith(dir)
62
- });
63
- const { output } = await build$1.generate({ inlineDynamicImports: true });
64
- const code = output[0].code;
65
- const { code: minified } = await minify(entry, code);
66
- return {
67
- size: Buffer.byteLength(code),
68
- minSize: Buffer.byteLength(minified),
69
- minGzipSize: gzipSync(minified).length
70
- };
71
- }
72
- async function sideEffectSize(dir, entry) {
73
- const virtualEntry = {
74
- name: "virtual-entry",
75
- async resolveId(id, importer, opts) {
76
- if (id === "#entry") return { id };
77
- const resolved = await this.resolve(id, importer, opts);
78
- if (!resolved) return null;
79
- resolved.moduleSideEffects = null;
80
- return resolved;
81
- },
82
- load(id) {
83
- if (id === "#entry") return `import * as _lib from "${join(dir, entry)}";`;
84
- }
85
- };
86
- const build$1 = await rolldown({
87
- input: "#entry",
88
- platform: "neutral",
89
- external: (id) => id[0] !== "." && !id.startsWith(dir),
90
- plugins: [virtualEntry]
91
- });
92
- const { output } = await build$1.generate({ inlineDynamicImports: true });
93
- if (process.env.INSPECT_BUILD) {
94
- console.log("---------[side effects]---------");
95
- console.log(entry);
96
- console.log(output[0].code);
97
- console.log("-------------------------------");
98
- }
99
- return Buffer.byteLength(output[0].code.trim());
100
- }
101
-
102
- //#endregion
103
- //#region src/builders/plugins/shebang.ts
104
- const SHEBANG_RE = /^#![^\n]*/;
105
- function shebangPlugin() {
106
- return {
107
- name: "robuild-shebang",
108
- async writeBundle(options, bundle) {
109
- for (const [fileName, output] of Object.entries(bundle)) {
110
- if (output.type !== "chunk") continue;
111
- if (hasShebang(output.code)) {
112
- const outFile = resolve(options.dir, fileName);
113
- await makeExecutable(outFile);
114
- }
115
- }
116
- }
117
- };
118
- }
119
- function hasShebang(code) {
120
- return SHEBANG_RE.test(code);
121
- }
122
- async function makeExecutable(filePath) {
123
- await promises.chmod(filePath, 493).catch(() => {});
124
- }
125
-
126
- //#endregion
127
- //#region src/builders/bundle.ts
128
- async function rolldownBuild(ctx, entry, hooks) {
129
- const inputs = normalizeBundleInputs(entry.input, ctx);
130
- if (entry.stub) {
131
- for (const [distName, srcPath] of Object.entries(inputs)) {
132
- const distPath = join(ctx.pkgDir, "dist", `${distName}.mjs`);
133
- await mkdir(dirname(distPath), { recursive: true });
134
- consola.log(`${colors.magenta("[stub bundle] ")} ${colors.underline(fmtPath(distPath))}`);
135
- const srcContents = await readFile(srcPath, "utf8");
136
- const parsed = parseSync(srcPath, srcContents);
137
- const exportNames = parsed.module.staticExports.flatMap((e) => e.entries.map((e$1) => e$1.exportName.kind === "Default" ? "default" : e$1.exportName.name));
138
- const hasDefaultExport = exportNames.includes("default");
139
- const firstLine = srcContents.split("\n")[0];
140
- const hasShebangLine = firstLine.startsWith("#!");
141
- await writeFile(distPath, `${hasShebangLine ? `${firstLine}\n` : ""}export * from "${srcPath}";\n${hasDefaultExport ? `export { default } from "${srcPath}";\n` : ""}`, "utf8");
142
- if (hasShebangLine) await makeExecutable(distPath);
143
- await writeFile(distPath.replace(/\.mjs$/, ".d.mts"), `export * from "${srcPath}";\n${hasDefaultExport ? `export { default } from "${srcPath}";\n` : ""}`, "utf8");
144
- }
145
- return;
146
- }
147
- const rolldownConfig = defu(entry.rolldown, {
148
- cwd: ctx.pkgDir,
149
- input: inputs,
150
- plugins: [shebangPlugin()],
151
- platform: "neutral",
152
- external: [
153
- ...builtinModules,
154
- ...builtinModules.map((m) => `node:${m}`),
155
- ...[...Object.keys(ctx.pkg.dependencies || {}), ...Object.keys(ctx.pkg.peerDependencies || {})].flatMap((p) => [p, /* @__PURE__ */ new RegExp(`^${p}/`)])
156
- ]
157
- });
158
- if (entry.dts !== false) rolldownConfig.plugins.push(...dts({ ...entry.dts }));
159
- await hooks.rolldownConfig?.(rolldownConfig, ctx);
160
- const res = await rolldown(rolldownConfig);
161
- const outDir = resolve(ctx.pkgDir, entry.outDir || "dist");
162
- const outConfig = {
163
- dir: outDir,
164
- entryFileNames: "[name].mjs",
165
- chunkFileNames: "_chunks/[name]-[hash].mjs",
166
- minify: entry.minify
167
- };
168
- await hooks.rolldownOutput?.(outConfig, res, ctx);
169
- const { output } = await res.write(outConfig);
170
- await res.close();
171
- const outputEntries = [];
172
- const depsCache = /* @__PURE__ */ new Map();
173
- const resolveDeps = (chunk) => {
174
- if (!depsCache.has(chunk)) depsCache.set(chunk, /* @__PURE__ */ new Set());
175
- const deps = depsCache.get(chunk);
176
- for (const id of chunk.imports) {
177
- if (builtinModules.includes(id) || id.startsWith("node:")) {
178
- deps.add(`[Node.js]`);
179
- continue;
180
- }
181
- const depChunk = output.find((o) => o.type === "chunk" && o.fileName === id);
182
- if (depChunk) {
183
- for (const dep of resolveDeps(depChunk)) deps.add(dep);
184
- continue;
185
- }
186
- deps.add(id);
187
- }
188
- return [...deps].sort();
189
- };
190
- for (const chunk of output) {
191
- if (chunk.type !== "chunk" || !chunk.isEntry) continue;
192
- if (chunk.fileName.endsWith("ts")) continue;
193
- outputEntries.push({
194
- name: chunk.fileName,
195
- exports: chunk.exports,
196
- deps: resolveDeps(chunk),
197
- ...await distSize(outDir, chunk.fileName),
198
- sideEffectSize: await sideEffectSize(outDir, chunk.fileName)
199
- });
200
- }
201
- consola.log(`\n${outputEntries.map((o) => [
202
- `${colors.magenta(`[bundle] `)}${colors.underline(fmtPath(join(outDir, o.name)))}`,
203
- colors.dim(`${colors.bold("Size:")} ${prettyBytes(o.size)}, ${colors.bold(prettyBytes(o.minSize))} minified, ${prettyBytes(o.minGzipSize)} min+gzipped (Side effects: ${prettyBytes(o.sideEffectSize)})`),
204
- o.exports.some((e) => e !== "default") ? colors.dim(`${colors.bold("Exports:")} ${o.exports.map((e) => e).join(", ")}`) : "",
205
- o.deps.length > 0 ? colors.dim(`${colors.bold("Dependencies:")} ${o.deps.join(", ")}`) : ""
206
- ].filter(Boolean).join("\n")).join("\n\n")}`);
207
- }
208
- function normalizeBundleInputs(input, ctx) {
209
- const inputs = {};
210
- for (let src of Array.isArray(input) ? input : [input]) {
211
- src = resolveModulePath(src, {
212
- from: ctx.pkgDir,
213
- extensions: [
214
- ".ts",
215
- ".js",
216
- ".mjs",
217
- ".cjs",
218
- ".json"
219
- ]
220
- });
221
- let relativeSrc = relative(join(ctx.pkgDir, "src"), src);
222
- if (relativeSrc.startsWith("..")) relativeSrc = relative(join(ctx.pkgDir), src);
223
- if (relativeSrc.startsWith("..")) throw new Error(`Source should be within the package directory (${ctx.pkgDir}): ${src}`);
224
- const distName = join(dirname(relativeSrc), basename(relativeSrc, extname(relativeSrc)));
225
- if (inputs[distName]) throw new Error(`Rename one of the entries to avoid a conflict in the dist name "${distName}":\n - ${src}\n - ${inputs[distName]}`);
226
- inputs[distName] = src;
227
- }
228
- return inputs;
229
- }
230
-
231
- //#endregion
232
- //#region src/builders/transform.ts
233
- /**
234
- * Transform all .ts modules in a directory using oxc-transform.
235
- */
236
- async function transformDir(ctx, entry) {
237
- if (entry.stub) {
238
- consola.log(`${colors.magenta("[stub transform] ")} ${colors.underline(`${fmtPath(entry.outDir)}/`)}`);
239
- await symlink(entry.input, entry.outDir, "junction");
240
- return;
241
- }
242
- const promises$1 = [];
243
- for await (const entryName of await glob("**/*.*", { cwd: entry.input })) promises$1.push((async () => {
244
- const entryPath = join(entry.input, entryName);
245
- const ext = extname(entryPath);
246
- switch (ext) {
247
- case ".ts": {
248
- const transformed = await transformModule(entryPath, entry);
249
- const entryDistPath = join(entry.outDir, entryName.replace(/\.ts$/, ".mjs"));
250
- await mkdir(dirname(entryDistPath), { recursive: true });
251
- await writeFile(entryDistPath, transformed.code, "utf8");
252
- if (SHEBANG_RE.test(transformed.code)) await makeExecutable(entryDistPath);
253
- if (transformed.declaration) await writeFile(entryDistPath.replace(/\.mjs$/, ".d.mts"), transformed.declaration, "utf8");
254
- return entryDistPath;
255
- }
256
- default: {
257
- const entryDistPath = join(entry.outDir, entryName);
258
- await mkdir(dirname(entryDistPath), { recursive: true });
259
- const code = await readFile(entryPath, "utf8");
260
- await writeFile(entryDistPath, code, "utf8");
261
- if (SHEBANG_RE.test(code)) await makeExecutable(entryDistPath);
262
- return entryDistPath;
263
- }
264
- }
265
- })());
266
- const writtenFiles = await Promise.all(promises$1);
267
- consola.log(`\n${colors.magenta("[transform] ")}${colors.underline(`${fmtPath(entry.outDir)}/`)}\n${writtenFiles.map((f) => colors.dim(fmtPath(f))).join("\n\n")}`);
268
- }
269
- /**
270
- * Transform a .ts module using oxc-transform.
271
- */
272
- async function transformModule(entryPath, entry) {
273
- let sourceText = await readFile(entryPath, "utf8");
274
- const sourceOptions = {
275
- lang: "ts",
276
- sourceType: "module"
277
- };
278
- const parsed = parseSync(entryPath, sourceText, { ...sourceOptions });
279
- if (parsed.errors.length > 0) throw new Error(`Errors while parsing ${entryPath}:`, { cause: parsed.errors });
280
- const resolveOptions = {
281
- ...entry.resolve,
282
- from: pathToFileURL(entryPath),
283
- extensions: entry.resolve?.extensions ?? [
284
- ".ts",
285
- ".js",
286
- ".mjs",
287
- ".cjs",
288
- ".json"
289
- ],
290
- suffixes: entry.resolve?.suffixes ?? ["", "/index"]
291
- };
292
- const magicString = new MagicString(sourceText);
293
- const updatedStarts = /* @__PURE__ */ new Set();
294
- const rewriteSpecifier = (req) => {
295
- const moduleId = req.value;
296
- if (!moduleId.startsWith(".")) return;
297
- if (updatedStarts.has(req.start)) return;
298
- updatedStarts.add(req.start);
299
- const resolvedAbsolute = resolveModulePath(moduleId, resolveOptions);
300
- const newId = relative(dirname(entryPath), resolvedAbsolute.replace(/\.ts$/, ".mjs"));
301
- magicString.remove(req.start, req.end);
302
- magicString.prependLeft(req.start, JSON.stringify(newId.startsWith(".") ? newId : `./${newId}`));
303
- };
304
- for (const staticImport of parsed.module.staticImports) rewriteSpecifier(staticImport.moduleRequest);
305
- for (const staticExport of parsed.module.staticExports) for (const staticExportEntry of staticExport.entries) if (staticExportEntry.moduleRequest) rewriteSpecifier(staticExportEntry.moduleRequest);
306
- sourceText = magicString.toString();
307
- const transformed = transform(entryPath, sourceText, {
308
- ...entry.oxc,
309
- ...sourceOptions,
310
- cwd: dirname(entryPath),
311
- typescript: {
312
- declaration: { stripInternal: true },
313
- ...entry.oxc?.typescript
314
- }
315
- });
316
- const transformErrors = transformed.errors.filter((err) => !err.message.includes("--isolatedDeclarations"));
317
- if (transformErrors.length > 0) {
318
- await writeFile("build-dump.ts", `/** Error dump for ${entryPath} */\n\n${sourceText}`, "utf8");
319
- throw new Error(`Errors while transforming ${entryPath}: (hint: check build-dump.ts)`, { cause: transformErrors });
320
- }
321
- if (entry.minify) {
322
- const res = minify(entryPath, transformed.code, entry.minify === true ? {} : entry.minify);
323
- transformed.code = res.code;
324
- transformed.map = res.map;
325
- }
326
- return transformed;
327
- }
328
-
329
- //#endregion
330
- //#region src/watch.ts
331
- /**
332
- * Start watching files and rebuild on changes
333
- */
334
- async function startWatch(config, ctx, buildFn) {
335
- const watchOptions = config.watch || {};
336
- if (!watchOptions.enabled) throw new Error("Watch mode is not enabled");
337
- const watchCtx = {
338
- config,
339
- ctx,
340
- buildFn,
341
- isBuilding: false,
342
- pendingRebuild: false
343
- };
344
- const watchPatterns = await getWatchPatterns(config, ctx, watchOptions);
345
- const ignorePatterns = getIgnorePatterns(watchOptions);
346
- consola.info(`👀 Starting watch mode...`);
347
- consola.info(`📁 Watching: ${colors.dim(watchPatterns.join(", "))}`);
348
- if (ignorePatterns.length > 0) consola.info(`🚫 Ignoring: ${colors.dim(ignorePatterns.join(", "))}`);
349
- const delay = watchOptions.delay ?? 100;
350
- if (delay > 0) consola.info(`⏱️ Rebuild delay: ${colors.dim(`${delay}ms`)}`);
351
- const watcher = watch(watchPatterns, {
352
- ignored: ignorePatterns,
353
- ignoreInitial: watchOptions.ignoreInitial ?? false,
354
- persistent: true,
355
- followSymlinks: false,
356
- cwd: ctx.pkgDir
357
- });
358
- watcher.on("change", (path) => handleFileChange(watchCtx, path, "changed"));
359
- watcher.on("add", (path) => {
360
- if (watchOptions.watchNewFiles !== false) handleFileChange(watchCtx, path, "added");
361
- });
362
- watcher.on("unlink", (path) => handleFileChange(watchCtx, path, "removed"));
363
- watcher.on("error", (error) => {
364
- consola.error("❌ Watch error:", error);
365
- });
366
- if (process.env.DEBUG) {
367
- watcher.on("addDir", (path) => consola.debug(`📁 Directory added: ${path}`));
368
- watcher.on("unlinkDir", (path) => consola.debug(`📁 Directory removed: ${path}`));
369
- }
370
- await new Promise((resolve$1) => {
371
- watcher.on("ready", () => {
372
- const watchedPaths = watcher.getWatched();
373
- const totalFiles = Object.values(watchedPaths).reduce((sum, files) => sum + files.length, 0);
374
- consola.success(`🚀 Watch mode ready - watching ${totalFiles} files`);
375
- consola.info(`💡 Press ${colors.cyan("Ctrl+C")} to stop watching`);
376
- resolve$1();
377
- });
378
- });
379
- return () => {
380
- if (watchCtx.rebuildTimer) clearTimeout(watchCtx.rebuildTimer);
381
- return watcher.close();
382
- };
383
- }
384
- /**
385
- * Handle file change events
386
- */
387
- function handleFileChange(watchCtx, filePath, changeType) {
388
- const { config, ctx } = watchCtx;
389
- const watchOptions = config.watch || {};
390
- const delay = watchOptions.delay ?? 100;
391
- const relativePath = relative(ctx.pkgDir, join(ctx.pkgDir, filePath));
392
- const formattedPath = fmtPath(relativePath);
393
- const changeIcon = changeType === "changed" ? "📝" : changeType === "added" ? "➕" : "➖";
394
- consola.info(`${changeIcon} ${colors.cyan(formattedPath)} ${changeType}`);
395
- if (watchCtx.rebuildTimer) clearTimeout(watchCtx.rebuildTimer);
396
- watchCtx.pendingRebuild = true;
397
- watchCtx.rebuildTimer = setTimeout(() => {
398
- triggerRebuild(watchCtx);
399
- }, delay);
400
- }
401
- /**
402
- * Trigger a rebuild
403
- */
404
- async function triggerRebuild(watchCtx) {
405
- const { config, buildFn } = watchCtx;
406
- if (watchCtx.isBuilding) return;
407
- if (!watchCtx.pendingRebuild) return;
408
- watchCtx.isBuilding = true;
409
- watchCtx.pendingRebuild = false;
410
- try {
411
- consola.info(`🔄 Rebuilding...`);
412
- const start = Date.now();
413
- const buildConfig = {
414
- ...config,
415
- watch: {
416
- ...config.watch,
417
- enabled: false
418
- }
419
- };
420
- await buildFn(buildConfig);
421
- const duration = Date.now() - start;
422
- consola.success(`✅ Rebuild completed in ${duration}ms`);
423
- } catch (error) {
424
- consola.error("❌ Rebuild failed:");
425
- if (error instanceof Error) {
426
- consola.error(` ${error.message}`);
427
- if (process.env.DEBUG && error.stack) consola.debug(error.stack);
428
- } else consola.error(` ${String(error)}`);
429
- consola.info("👀 Still watching for changes...");
430
- } finally {
431
- watchCtx.isBuilding = false;
432
- if (watchCtx.pendingRebuild) setTimeout(() => triggerRebuild(watchCtx), watchCtx.config.watch?.delay ?? 100);
433
- }
434
- }
435
- /**
436
- * Get patterns for files to watch
437
- */
438
- async function getWatchPatterns(config, ctx, watchOptions) {
439
- if (watchOptions.include && watchOptions.include.length > 0) return watchOptions.include;
440
- const patterns = [];
441
- for (const entry of config.entries || []) if (typeof entry === "string") {
442
- const [input] = entry.split(":");
443
- if (input.endsWith("/")) patterns.push(`${input}**/*`);
444
- else {
445
- const inputs = input.split(",");
446
- for (const inputFile of inputs) {
447
- patterns.push(inputFile);
448
- const dir = inputFile.substring(0, inputFile.lastIndexOf("/"));
449
- if (dir) patterns.push(`${dir}/**/*`);
450
- }
451
- }
452
- } else if (entry.type === "transform") patterns.push(`${entry.input}/**/*`);
453
- else {
454
- const inputs = Array.isArray(entry.input) ? entry.input : [entry.input];
455
- for (const inputFile of inputs) {
456
- patterns.push(inputFile);
457
- const dir = inputFile.substring(0, inputFile.lastIndexOf("/"));
458
- if (dir) patterns.push(`${dir}/**/*`);
459
- }
460
- }
461
- if (patterns.length === 0) patterns.push("src/**/*", "*.ts", "*.js", "*.mjs", "*.json");
462
- return [...new Set(patterns)];
463
- }
464
- /**
465
- * Get patterns for files to ignore
466
- */
467
- function getIgnorePatterns(watchOptions) {
468
- const defaultIgnores = [
469
- "**/node_modules/**",
470
- "**/dist/**",
471
- "**/build/**",
472
- "**/coverage/**",
473
- "**/.git/**",
474
- "**/.DS_Store",
475
- "**/Thumbs.db",
476
- "**/*.log",
477
- "**/tmp/**",
478
- "**/temp/**"
479
- ];
480
- if (watchOptions.exclude && watchOptions.exclude.length > 0) return [...defaultIgnores, ...watchOptions.exclude];
481
- return defaultIgnores;
482
- }
483
-
484
- //#endregion
485
- //#region src/build.ts
486
- /**
487
- * Build dist/ from src/
488
- */
489
- async function build(config) {
490
- const pkgDir = normalizePath(config.cwd);
491
- const pkg = await readJSON(join(pkgDir, "package.json")).catch(() => ({}));
492
- const ctx = {
493
- pkg,
494
- pkgDir
495
- };
496
- if (config.watch?.enabled) {
497
- consola.log(`👀 Starting watch mode for \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
498
- await performBuild(config, ctx);
499
- const stopWatch = await startWatch(config, ctx, build);
500
- const cleanup = () => {
501
- consola.info("🛑 Stopping watch mode...");
502
- stopWatch();
503
- process.exit(0);
504
- };
505
- process.on("SIGINT", cleanup);
506
- process.on("SIGTERM", cleanup);
507
- return new Promise(() => {});
508
- }
509
- consola.log(`📦 Building \`${ctx.pkg.name || "<no name>"}\` (\`${ctx.pkgDir}\`)`);
510
- await performBuild(config, ctx);
511
- }
512
- /**
513
- * Perform the actual build process
514
- */
515
- async function performBuild(config, ctx) {
516
- const start = Date.now();
517
- const hooks = config.hooks || {};
518
- await hooks.start?.(ctx);
519
- const entries = (config.entries || []).map((rawEntry) => {
520
- let entry;
521
- if (typeof rawEntry === "string") {
522
- const [input, outDir] = rawEntry.split(":");
523
- entry = input.endsWith("/") ? {
524
- type: "transform",
525
- input,
526
- outDir
527
- } : {
528
- type: "bundle",
529
- input: input.split(","),
530
- outDir
531
- };
532
- } else entry = rawEntry;
533
- if (!entry.input) throw new Error(`Build entry missing \`input\`: ${JSON.stringify(entry, null, 2)}`);
534
- entry = { ...entry };
535
- entry.outDir = normalizePath(entry.outDir || "dist", ctx.pkgDir);
536
- entry.input = Array.isArray(entry.input) ? entry.input.map((p) => normalizePath(p, ctx.pkgDir)) : normalizePath(entry.input, ctx.pkgDir);
537
- return entry;
538
- });
539
- await hooks.entries?.(entries, ctx);
540
- const outDirs = [];
541
- for (const outDir of entries.map((e) => e.outDir).sort()) if (!outDirs.some((dir) => outDir.startsWith(dir))) outDirs.push(outDir);
542
- for (const outDir of outDirs) {
543
- consola.log(`🧻 Cleaning up \`${fmtPath(outDir)}\``);
544
- await rm(outDir, {
545
- recursive: true,
546
- force: true
547
- });
548
- }
549
- for (const entry of entries) await (entry.type === "bundle" ? rolldownBuild(ctx, entry, hooks) : transformDir(ctx, entry));
550
- await hooks.end?.(ctx);
551
- const dirSize = analyzeDir(outDirs);
552
- consola.log(colors.dim(`\nΣ Total dist byte size: ${colors.underline(prettyBytes(dirSize.size))} (${colors.underline(dirSize.files)} files)`));
553
- consola.log(`\n✅ robuild finished in ${Date.now() - start}ms`);
554
- }
555
- function normalizePath(path, resolveFrom) {
556
- return typeof path === "string" && isAbsolute(path) ? path : path instanceof URL ? fileURLToPath(path) : resolve(resolveFrom || ".", path || ".");
557
- }
558
- function readJSON(specifier) {
559
- return import(specifier, { with: { type: "json" } }).then((r) => r.default);
560
- }
561
-
562
- //#endregion
563
- export { build };
@@ -1,138 +0,0 @@
1
- import { ResolveOptions } from "exsolve";
2
- import { InputOptions, MinifyOptions, OutputOptions, RolldownBuild, RolldownPluginOption } from "rolldown";
3
- import { Options } from "rolldown-plugin-dts";
4
- import { MinifyOptions as MinifyOptions$1 } from "oxc-minify";
5
- import { TransformOptions } from "oxc-transform";
6
-
7
- //#region src/types.d.ts
8
- interface BuildContext {
9
- pkgDir: string;
10
- pkg: {
11
- name: string;
12
- } & Record<string, unknown>;
13
- }
14
- interface _BuildEntry {
15
- /**
16
- * Output directory relative to project root.
17
- *
18
- * Defaults to `dist/` if not provided.
19
- */
20
- outDir?: string;
21
- /**
22
- * Avoid actual build but instead link to the source files.
23
- */
24
- stub?: boolean;
25
- }
26
- type BundleEntry = _BuildEntry & {
27
- type: "bundle";
28
- /**
29
- * Entry point(s) to bundle relative to the project root.
30
- */
31
- input: string | string[];
32
- /**
33
- * Minify the output using rolldown.
34
- *
35
- * Defaults to `false` if not provided.
36
- */
37
- minify?: boolean | "dce-only" | MinifyOptions;
38
- /**
39
- * Options passed to rolldown.
40
- *
41
- * See [rolldown config options](https://rolldown.rs/reference/config-options) for more details.
42
- */
43
- rolldown?: InputOptions & {
44
- plugins?: RolldownPluginOption[];
45
- };
46
- /**
47
- * Declaration generation options.
48
- *
49
- * See [rolldown-plugin-dts](https://github.com/sxzz/rolldown-plugin-dts) for more details.
50
- *
51
- * Options are inferred from the `tsconfig.json` file if available.
52
- *
53
- * Set to `false` to disable.
54
- */
55
- dts?: boolean | Options;
56
- };
57
- type TransformEntry = _BuildEntry & {
58
- type: "transform";
59
- /**
60
- * Directory to transform relative to the project root.
61
- */
62
- input: string;
63
- /**
64
- * Minify the output using oxc-minify.
65
- *
66
- * Defaults to `false` if not provided.
67
- */
68
- minify?: boolean | MinifyOptions$1;
69
- /**
70
- * Options passed to oxc-transform.
71
- *
72
- * See [oxc-transform](https://www.npmjs.com/package/oxc-transform) for more details.
73
- */
74
- oxc?: TransformOptions;
75
- /**
76
- * Options passed to exsolve for module resolution.
77
- *
78
- * See [exsolve](https://github.com/unjs/exsolve) for more details.
79
- */
80
- resolve?: Omit<ResolveOptions, "from">;
81
- };
82
- type BuildEntry = BundleEntry | TransformEntry;
83
- interface BuildHooks {
84
- start?: (ctx: BuildContext) => void | Promise<void>;
85
- end?: (ctx: BuildContext) => void | Promise<void>;
86
- entries?: (entries: BuildEntry[], ctx: BuildContext) => void | Promise<void>;
87
- rolldownConfig?: (cfg: InputOptions, ctx: BuildContext) => void | Promise<void>;
88
- rolldownOutput?: (cfg: OutputOptions, res: RolldownBuild, ctx: BuildContext) => void | Promise<void>;
89
- }
90
- interface WatchOptions {
91
- /**
92
- * Enable watch mode.
93
- *
94
- * Defaults to `false` if not provided.
95
- */
96
- enabled?: boolean;
97
- /**
98
- * Glob patterns for files to watch.
99
- *
100
- * Defaults to watching all source files if not provided.
101
- */
102
- include?: string[];
103
- /**
104
- * Glob patterns for files to ignore.
105
- *
106
- * Defaults to common ignore patterns if not provided.
107
- */
108
- exclude?: string[];
109
- /**
110
- * Delay in milliseconds before rebuilding after a file change.
111
- *
112
- * Defaults to `100` if not provided.
113
- */
114
- delay?: number;
115
- /**
116
- * Whether to ignore the initial build when starting watch mode.
117
- *
118
- * Defaults to `false` if not provided.
119
- */
120
- ignoreInitial?: boolean;
121
- /**
122
- * Whether to watch for new files being added.
123
- *
124
- * Defaults to `true` if not provided.
125
- */
126
- watchNewFiles?: boolean;
127
- }
128
- interface BuildConfig {
129
- cwd?: string | URL;
130
- entries?: (BuildEntry | string)[];
131
- hooks?: BuildHooks;
132
- watch?: WatchOptions;
133
- }
134
- //#endregion
135
- //#region src/config.d.ts
136
- declare function defineConfig(config: BuildConfig): BuildConfig;
137
- //#endregion
138
- export { BuildConfig, BuildEntry, BundleEntry, TransformEntry, defineConfig };