Skip to content

Commit

Permalink
feat: experimental active watcher for rollup (#364)
Browse files Browse the repository at this point in the history
Co-authored-by: Pooya Parsa <pooya@pi0.io>
  • Loading branch information
joemckenney and pi0 authored Jun 4, 2024
1 parent 648b9b4 commit 2a3f908
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 11 deletions.
37 changes: 31 additions & 6 deletions src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { defu } from "defu";
import { createHooks } from "hookable";
import prettyBytes from "pretty-bytes";
import { globby } from "globby";
import type { RollupOptions } from "rollup";
import {
dumpObject,
rmdir,
Expand All @@ -18,7 +19,7 @@ import {
} from "./utils";
import type { BuildContext, BuildConfig, BuildOptions } from "./types";
import { validatePackage, validateDependencies } from "./validate";
import { rollupBuild } from "./builder/rollup";
import { getRollupOptions, rollupBuild } from "./builder/rollup";
import { typesBuild } from "./builder/untyped";
import { mkdistBuild } from "./builder/mkdist";
import { copyBuild } from "./builder/copy";
Expand All @@ -42,18 +43,34 @@ export async function build(

// Invoke build for every build config defined in build.config.ts
const cleanedDirs: string[] = [];
const rollupOptions: RollupOptions[] = [];

const _watchMode = inputConfig.watch === true;
const _stubMode = !_watchMode && (stub || inputConfig.stub === true);

for (const buildConfig of buildConfigs) {
await _build(rootDir, stub, inputConfig, buildConfig, pkg, cleanedDirs);
await _build(
rootDir,
inputConfig,
buildConfig,
pkg,
cleanedDirs,
rollupOptions,
_stubMode,
_watchMode,
);
}
}

async function _build(
rootDir: string,
stub: boolean,
inputConfig: BuildConfig = {},
buildConfig: BuildConfig,
pkg: PackageJson & Record<"unbuild" | "build", BuildConfig>,
cleanedDirs: string[],
rollupOptions: RollupOptions[],
_stubMode: boolean,
_watchMode: boolean,
) {
// Resolve preset
const preset = resolvePreset(
Expand All @@ -78,7 +95,7 @@ async function _build(
clean: true,
declaration: false,
outDir: "dist",
stub,
stub: _stubMode,
stubOptions: {
/**
* See https://github.com/unjs/jiti#options
Expand All @@ -89,6 +106,13 @@ async function _build(
alias: {},
},
},
watch: _watchMode,
watchOptions: _watchMode
? {
exclude: "node_modules/**",
include: "src/**",
}
: undefined,
externals: [
...Module.builtinModules,
...Module.builtinModules.map((m) => "node:" + m),
Expand All @@ -102,6 +126,7 @@ async function _build(
sourcemap: false,
rollup: {
emitCJS: false,
watch: false,
cjsBridge: false,
inlineDependencies: false,
preserveDynamicImports: true,
Expand Down Expand Up @@ -252,8 +277,8 @@ async function _build(
// copy
await copyBuild(ctx);

// Skip rest for stub
if (options.stub) {
// Skip rest for stub and watch mode
if (options.stub || options.watch) {
await ctx.hooks.callHook("build:done", ctx);
return;
}
Expand Down
5 changes: 5 additions & 0 deletions src/builder/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { relative, resolve } from "pathe";
import { globby } from "globby";
import { symlink, rmdir, warn } from "../utils";
import type { CopyBuildEntry, BuildContext } from "../types";
import consola from "consola";

const copy = fsp.cp || fsp.copyFile;

Expand Down Expand Up @@ -51,4 +52,8 @@ export async function copyBuild(ctx: BuildContext) {
}
}
await ctx.hooks.callHook("copy:done", ctx);

if (entries.length > 0 && ctx.options.watch) {
consola.warn("`untyped` builder does not support watch mode yet.");
}
}
5 changes: 5 additions & 0 deletions src/builder/mkdist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { relative } from "pathe";
import { mkdist, MkdistOptions } from "mkdist";
import { symlink, rmdir } from "../utils";
import type { MkdistBuildEntry, BuildContext } from "../types";
import consola from "consola";

export async function mkdistBuild(ctx: BuildContext) {
const entries = ctx.options.entries.filter(
Expand Down Expand Up @@ -36,4 +37,8 @@ export async function mkdistBuild(ctx: BuildContext) {
}
}
await ctx.hooks.callHook("mkdist:done", ctx);

if (entries.length > 0 && ctx.options.watch) {
consola.warn("`mkdist` builder does not support watch mode yet.");
}
}
57 changes: 56 additions & 1 deletion src/builder/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,31 @@ import { nodeResolve } from "@rollup/plugin-node-resolve";
import alias from "@rollup/plugin-alias";
import dts from "rollup-plugin-dts";
import replace from "@rollup/plugin-replace";
import { resolve, dirname, normalize, extname, isAbsolute } from "pathe";
import {
resolve,
dirname,
normalize,
extname,
isAbsolute,
relative,
} from "pathe";
import { resolvePath, resolveModuleExportNames } from "mlly";
import { watch as rollupWatch } from "rollup";
import { arrayIncludes, getpkg, tryResolve, warn } from "../utils";
import type { BuildContext } from "../types";
import { esbuild } from "./plugins/esbuild";
import { JSONPlugin } from "./plugins/json";
import { rawPlugin } from "./plugins/raw";
import { cjsPlugin } from "./plugins/cjs";
import { klona } from "klona/full";
import {
shebangPlugin,
makeExecutable,
getShebang,
removeShebangPlugin,
} from "./plugins/shebang";
import consola from "consola";
import chalk from "chalk";

const DEFAULT_EXTENSIONS = [
".ts",
Expand Down Expand Up @@ -193,6 +204,16 @@ export async function rollupBuild(ctx: BuildContext) {
}
}

// Watch
if (ctx.options.watch) {
_watch(rollupOptions);
// TODO: Clone rollup options to continue types watching
if (ctx.options.declaration && ctx.options.watch) {
consola.warn("`rollup` DTS builder does not support watch mode yet.");
}
return;
}

// Types
if (ctx.options.declaration) {
rollupOptions.plugins = [
Expand Down Expand Up @@ -400,3 +421,37 @@ function resolveAliases(ctx: BuildContext) {

return aliases;
}

export function _watch(rollupOptions: RollupOptions) {
const watcher = rollupWatch(rollupOptions);

let inputs: string[];
if (Array.isArray(rollupOptions.input)) {
inputs = rollupOptions.input;
} else if (typeof rollupOptions.input === "string") {
inputs = [rollupOptions.input];
} else {
inputs = Object.keys(rollupOptions.input || {});
}
consola.info(
`[unbuild] [rollup] Starting watchers for entries: ${inputs.map((input) => "./" + relative(process.cwd(), input)).join(", ")}`,
);

consola.warn(
"[unbuild] [rollup] Watch mode is experimental and may be unstable",
);

watcher.on("change", (id, { event }) => {
consola.info(`${chalk.cyan(relative(".", id))} was ${event}d`);
});

watcher.on("restart", () => {
consola.info(chalk.gray("[unbuild] [rollup] Rebuilding bundle"));
});

watcher.on("event", (event) => {
if (event.code === "END") {
consola.success(chalk.green("[unbuild] [rollup] Rebuild finished\n"));
}
});
}
5 changes: 5 additions & 0 deletions src/builder/untyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import untypedPlugin from "untyped/babel-plugin";
import jiti from "jiti";
import { pascalCase } from "scule";
import type { BuildContext, UntypedBuildEntry, UntypedOutputs } from "../types";
import consola from "consola";

export async function typesBuild(ctx: BuildContext) {
const entries = ctx.options.entries.filter(
Expand Down Expand Up @@ -69,4 +70,8 @@ export async function typesBuild(ctx: BuildContext) {
}
}
await ctx.hooks.callHook("untyped:done", ctx);

if (entries.length > 0 && ctx.options.watch) {
consola.warn("`untyped` builder does not support watch mode yet.");
}
}
8 changes: 7 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ const main = defineCommand({
description: "The directory to build",
required: false,
},
watch: {
type: "boolean",
description: "Watch the src dir and rebuild on change (experimental)",
},
stub: {
type: "boolean",
description: "Stub build",
description: "Stub the package for JIT compilation",
},
minify: {
type: "boolean",
Expand All @@ -34,6 +38,8 @@ const main = defineCommand({
const rootDir = resolve(process.cwd(), args.dir || ".");
await build(rootDir, args.stub, {
sourcemap: args.sourcemap,
stub: args.stub,
watch: args.watch,
rollup: {
esbuild: {
minify: args.minify,
Expand Down
30 changes: 27 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { PackageJson } from "pkg-types";
import type { Hookable } from "hookable";
import type { RollupOptions, RollupBuild, OutputOptions } from "rollup";
import type {
RollupOptions,
RollupBuild,
OutputOptions,
WatcherOptions,
} from "rollup";
import type { MkdistOptions } from "mkdist";
import type { Schema } from "untyped";
import type { RollupReplaceOptions } from "@rollup/plugin-replace";
Expand Down Expand Up @@ -55,6 +60,13 @@ export interface RollupBuildOptions {
*/
emitCJS?: boolean;

/**
* Enable experimental active watcher
*
* @experimental
*/
watch?: boolean;

/**
* If enabled, unbuild generates CommonJS polyfills for ESM builds.
*/
Expand Down Expand Up @@ -168,11 +180,23 @@ export interface BuildOptions {
outDir: string;

/**
* Whether to generate declaration files.
* [stubbing](https://antfu.me/posts/publish-esm-and-cjs#stubbing)
* Whether to build with JIT stubs.
* Read more: [stubbing](https://antfu.me/posts/publish-esm-and-cjs#stubbing)
*/
stub: boolean;

/**
* Whether to build and actively watch the file changes.
*
* @experimental This feature is experimental and incomplete.
*/
watch: boolean;

/**
* Watch mode options.
*/
watchOptions: WatcherOptions;

/**
* Stub options, where [jiti](https://github.com/unjs/jiti)
* is an object of type `Omit<JITIOptions, "transform" | "onError">`.
Expand Down

0 comments on commit 2a3f908

Please sign in to comment.