robuild 0.0.5 → 0.0.6
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/README.md +50 -1
- package/dist/_chunks/{build-eIjZ3Fk8.mjs → build-DuPhfTzX.mjs} +178 -3
- package/dist/_chunks/{config-DxLkhDt6.d.mts → config-BZW4dLYD.d.mts} +39 -0
- package/dist/cli.mjs +11 -2
- package/dist/config.d.mts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -30,9 +30,12 @@ npx robuild ./src/index.ts
|
|
|
30
30
|
|
|
31
31
|
# transform
|
|
32
32
|
npx robuild ./src/runtime/:./dist/runtime
|
|
33
|
+
|
|
34
|
+
# watch mode - rebuild on file changes
|
|
35
|
+
npx robuild ./src/index.ts --watch
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
You can use `--dir` to set the working directory.
|
|
38
|
+
You can use `--dir` to set the working directory and `--watch` to enable watch mode.
|
|
36
39
|
|
|
37
40
|
If paths end with `/`, robuild uses transpile mode using [oxc-transform](https://www.npmjs.com/package/oxc-transform) instead of bundle mode with [rolldown](https://rolldown.rs/).
|
|
38
41
|
|
|
@@ -85,6 +88,52 @@ export default defineConfig({
|
|
|
85
88
|
})
|
|
86
89
|
```
|
|
87
90
|
|
|
91
|
+
## Watch Mode
|
|
92
|
+
|
|
93
|
+
For development, robuild provides a watch mode that automatically rebuilds your project when files change.
|
|
94
|
+
|
|
95
|
+
### CLI Usage
|
|
96
|
+
|
|
97
|
+
```sh
|
|
98
|
+
# Enable watch mode for any build
|
|
99
|
+
npx robuild ./src/index.ts --watch
|
|
100
|
+
|
|
101
|
+
# Watch mode with transform
|
|
102
|
+
npx robuild ./src/runtime/:./dist/runtime --watch
|
|
103
|
+
|
|
104
|
+
# Watch mode with custom working directory
|
|
105
|
+
npx robuild ./src/index.ts --watch --dir ./my-project
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Configuration
|
|
109
|
+
|
|
110
|
+
You can configure watch behavior in your `build.config.ts`:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { defineConfig } from 'robuild'
|
|
114
|
+
|
|
115
|
+
export default defineConfig({
|
|
116
|
+
entries: ['./src/index.ts'],
|
|
117
|
+
watch: {
|
|
118
|
+
enabled: true, // Enable watch mode by default
|
|
119
|
+
include: ['src/**/*'], // Files to watch
|
|
120
|
+
exclude: ['**/*.test.ts'], // Files to ignore
|
|
121
|
+
delay: 100, // Rebuild delay in ms
|
|
122
|
+
ignoreInitial: false, // Skip initial build
|
|
123
|
+
watchNewFiles: true, // Watch for new files
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Features
|
|
129
|
+
|
|
130
|
+
- **Real-time rebuilding**: Automatically rebuilds when source files change
|
|
131
|
+
- **Smart file detection**: Automatically determines what files to watch based on your entries
|
|
132
|
+
- **Debounced rebuilds**: Configurable delay to prevent excessive rebuilds
|
|
133
|
+
- **Error recovery**: Continues watching even after build errors
|
|
134
|
+
- **Clear feedback**: Shows file changes and rebuild status
|
|
135
|
+
- **Graceful shutdown**: Clean exit with Ctrl+C
|
|
136
|
+
|
|
88
137
|
## Stub Mode
|
|
89
138
|
|
|
90
139
|
When working on a package locally, it can be tedious to rebuild or run the watch command every time.
|
|
@@ -16,6 +16,7 @@ import { minify } from "oxc-minify";
|
|
|
16
16
|
import MagicString from "magic-string";
|
|
17
17
|
import { transform } from "oxc-transform";
|
|
18
18
|
import { glob } from "tinyglobby";
|
|
19
|
+
import { watch } from "chokidar";
|
|
19
20
|
|
|
20
21
|
//#region src/utils.ts
|
|
21
22
|
function fmtPath(path) {
|
|
@@ -325,20 +326,194 @@ async function transformModule(entryPath, entry) {
|
|
|
325
326
|
return transformed;
|
|
326
327
|
}
|
|
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
|
+
|
|
328
484
|
//#endregion
|
|
329
485
|
//#region src/build.ts
|
|
330
486
|
/**
|
|
331
487
|
* Build dist/ from src/
|
|
332
488
|
*/
|
|
333
489
|
async function build(config) {
|
|
334
|
-
const start = Date.now();
|
|
335
490
|
const pkgDir = normalizePath(config.cwd);
|
|
336
491
|
const pkg = await readJSON(join(pkgDir, "package.json")).catch(() => ({}));
|
|
337
492
|
const ctx = {
|
|
338
493
|
pkg,
|
|
339
494
|
pkgDir
|
|
340
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
|
+
}
|
|
341
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();
|
|
342
517
|
const hooks = config.hooks || {};
|
|
343
518
|
await hooks.start?.(ctx);
|
|
344
519
|
const entries = (config.entries || []).map((rawEntry) => {
|
|
@@ -357,8 +532,8 @@ async function build(config) {
|
|
|
357
532
|
} else entry = rawEntry;
|
|
358
533
|
if (!entry.input) throw new Error(`Build entry missing \`input\`: ${JSON.stringify(entry, null, 2)}`);
|
|
359
534
|
entry = { ...entry };
|
|
360
|
-
entry.outDir = normalizePath(entry.outDir || "dist", pkgDir);
|
|
361
|
-
entry.input = Array.isArray(entry.input) ? entry.input.map((p) => normalizePath(p, pkgDir)) : normalizePath(entry.input, pkgDir);
|
|
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);
|
|
362
537
|
return entry;
|
|
363
538
|
});
|
|
364
539
|
await hooks.entries?.(entries, ctx);
|
|
@@ -87,10 +87,49 @@ interface BuildHooks {
|
|
|
87
87
|
rolldownConfig?: (cfg: InputOptions, ctx: BuildContext) => void | Promise<void>;
|
|
88
88
|
rolldownOutput?: (cfg: OutputOptions, res: RolldownBuild, ctx: BuildContext) => void | Promise<void>;
|
|
89
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
|
+
}
|
|
90
128
|
interface BuildConfig {
|
|
91
129
|
cwd?: string | URL;
|
|
92
130
|
entries?: (BuildEntry | string)[];
|
|
93
131
|
hooks?: BuildHooks;
|
|
132
|
+
watch?: WatchOptions;
|
|
94
133
|
}
|
|
95
134
|
//#endregion
|
|
96
135
|
//#region src/config.d.ts
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { build } from "./_chunks/build-
|
|
2
|
+
import { build } from "./_chunks/build-DuPhfTzX.mjs";
|
|
3
3
|
import { consola } from "consola";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { loadConfig } from "c12";
|
|
@@ -16,6 +16,11 @@ const args = parseArgs({
|
|
|
16
16
|
stub: {
|
|
17
17
|
type: "boolean",
|
|
18
18
|
default: false
|
|
19
|
+
},
|
|
20
|
+
watch: {
|
|
21
|
+
type: "boolean",
|
|
22
|
+
default: false,
|
|
23
|
+
short: "w"
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
});
|
|
@@ -48,7 +53,11 @@ if (rawEntries.length === 0) {
|
|
|
48
53
|
await build({
|
|
49
54
|
cwd: args.values.dir,
|
|
50
55
|
...config,
|
|
51
|
-
entries
|
|
56
|
+
entries,
|
|
57
|
+
watch: args.values.watch ? {
|
|
58
|
+
enabled: true,
|
|
59
|
+
...config.watch
|
|
60
|
+
} : config.watch
|
|
52
61
|
});
|
|
53
62
|
|
|
54
63
|
//#endregion
|
package/dist/config.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { defineConfig } from "./_chunks/config-
|
|
1
|
+
import { defineConfig } from "./_chunks/config-BZW4dLYD.mjs";
|
|
2
2
|
export { defineConfig };
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "robuild",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.6",
|
|
5
5
|
"packageManager": "pnpm@10.11.1",
|
|
6
6
|
"description": "Zero-config ESM/TS package builder. Powered by Rolldown and Oxc",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"c12": "^3.0.4",
|
|
35
|
+
"chokidar": "^4.0.3",
|
|
35
36
|
"consola": "^3.4.2",
|
|
36
37
|
"defu": "^6.1.4",
|
|
37
38
|
"exsolve": "^1.0.5",
|