unbuild

repository·main·Indexed 11 days ago

https://github.com/unjs/unbuild

A unified JavaScript build system for bundling TypeScript projects into CommonJS and ESM formats. It supports zero-configuration by inferring settings from package.json exports, bundleless builds via mkdist, and automated type declaration generation. Version 3.6.1 provides features like stub mode for development, a flexible build.config.ts for advanced control, and lifecycle hooks to extend the build process.

Tokens
10K
Snippets
35
Records
45
Agent score
82%

What's inside unbuild

  1. Use unbuild --stub for passive development

    main

    You can use the unbuild --stub command (powered by jiti) to create a stub of your dist directory. This allows you to link your project and work on it without needing to constantly watch and rebuild the entire package during development.

    npx unbuild --stub
  2. Quickstart: Build a project with unbuild

    main

    To use unbuild, create your source code in src/index.ts and configure your package.json to define the entry points and output files. unbuild will automatically infer the build configuration from your package.json fields.

    // 1. Create src/index.ts
    export const log = (...args) => {
      console.log(...args);
    };
    // 2. Update package.json
    {
      "type": "module",
      "scripts": {
        "build": "unbuild",
        "prepack": "unbuild"
      },
      "exports": {
        ".": {
          "import": "./dist/index.mjs",
          "require": "./dist/index.cjs"
        }
      },
      "main": "./dist/index.cjs",
      "types": "./dist/index.d.ts",
      "files": ["dist"]
    }
    # 3. Run the build
    npx unbuild
  3. Configure unbuild using build.config.ts

    main

    While unbuild works with zero config by reading package.json, you can create a build.config.ts (or .js, .mjs, .cjs, .json) for advanced control. Use defineBuildConfig to provide a configuration object or an array of configuration objects for multiple builds.

    import { defineBuildConfig } from "unbuild";
    
    export default defineBuildConfig({
      entries: [
        // default bundler
        "./src/index",
        // mkdist builder for bundleless file-to-file transpilation
        {
          builder: "mkdist",
          input: "./src/package/components/",
          outDir: "./build/components",
        },
      ],
    
      // Change outDir, default is 'dist'
      outDir: "build",
    
      // Generates .d.ts declaration file
      declaration: true,
    });
  4. Explore unbuild usage examples

    main

    The examples/ directory contains several reference implementations demonstrating different ways to use unbuild depending on your project needs:

    • Zero Config: Demonstrates the simplest usage where unbuild works out-of-the-box without a configuration file.
    • mkdist: Shows how to use unbuild in conjunction with mkdist for bundleless builds.
    • untyped: Demonstrates how to integrate unbuild with untyped for generating type definitions.
  5. Use unbuild with zero configuration

    main

    unbuild can operate without a dedicated configuration file by automatically inferring build settings from the exports field in your package.json.

    If your package.json includes types, import, and require fields within the exports object, unbuild will automatically include these formats in the build output. This allows for a rapid setup where the build process is driven entirely by your package's entry point definitions.

    {
      "exports": {
        ".": {
          "types": "./dist/index.d.ts",
          "import": "./dist/index.mjs",
          "require": "./dist/index.cjs"
        }
      }
    }
  6. Extend the build process using hooks

    main

    The unbuild build process exposes a lifecycle via hookable. You can register hooks in your build.config.ts to execute custom logic at specific stages of the build.

    Available Lifecycle Hooks:

    • build:prepare: Called after configuration is merged and before any build tasks start. Use this to prepare the environment or modify the BuildContext.
    • build:before: Called after the context is created and entries are normalized, but before the actual build tasks (Rollup, mkdist, etc.) are executed.
    • build:done: Called after the build tasks have completed. In stub or watch mode, this is called immediately after tasks start.

    Hook Context: Hooks receive a BuildContext object which includes:

    • options: The final resolved BuildOptions.
    • jiti: A jiti instance for loading files.
    • warnings: A Set of warnings collected during the build.
    • pkg: The project's package.json content.
    • hooks: The hookable instance itself.
  7. How unbuild automatically detects build entries

    main

    When no entries are explicitly provided in your configuration, unbuild uses the autoPreset to automatically infer build entry points by analyzing your package.json and your src/ directory.

    It looks for the following fields in package.json to determine what needs to be built:

    • exports: Used to map export patterns to source files.
    • bin: For binary entry points.
    • main: For CommonJS entry points.
    • module: For ESM entry points.
    • types or typings: For declaration files.

    Based on these findings, unbuild will:

    1. Automatically populate the entries array.
    2. Enable CJS output (rollup.emitCJS = true) if CJS files are detected.
    3. Set declaration to "compatible" if type definitions are detected, or false otherwise.

    If you provide your own entries in the configuration, this automatic detection is disabled.

  8. Untyped builder outputs

    main

    The untyped builder generates several types of files for each entry. These are written to the specified outDir:

    • Markdown Documentation: A .md file containing documentation generated from the schema (e.g., name.md).
    • JSON Schema: A .schema.json file representing the resolved schema (e.g., name.schema.json).
    • Defaults JSON: A .defaults.json file containing the default values provided in the entry configuration (e.g., name.defaults.json).
    • TypeScript Declarations: If declaration: true is set for the entry, a .d.ts file is generated (e.g., name.d.ts). The interface name is automatically generated using pascalCase based on the entry name (e.g., my-module-schema).
  9. Define Build Entries

    main

    The entries option accepts an array of BuildEntry objects. Each entry can use different builders to achieve specific output formats:

    • rollup: Standard bundling.
    • mkdist: For bundleless builds.
    • untyped: For generating types via untyped.
    • copy: For simply copying files.

    A BaseBuildEntry includes:

    • builder: The type of builder to use ("untyped" | "rollup" | "mkdist" | "copy").
    • input: The entry file path.
    • name: Optional name for the entry.
    • outDir: Optional override for the output directory.
    • declaration: Optional declaration strategy.
  10. Understand the BuildContext object

    main

    When running hooks, you receive a BuildContext object which contains the state and tools available during the build:

    • options: The current BuildOptions.
    • pkg: The project's package.json content.
    • jiti: A jiti instance for loading TypeScript/ESM files.
    • buildEntries: Metadata about the entries being built (paths, bytes, exports, etc.).
    • usedImports: A Set of imports used in the project.
    • warnings: A Set of warnings encountered.
    • hooks: The Hookable instance to trigger or listen to hooks.
  11. Configure ESLint with eslint-config-unjs

    main

    To use the unjs ESLint configuration, import unjs from eslint-config-unjs and export it as the default configuration. The unjs() function accepts multiple configuration objects, allowing you to define global settings and then apply specific overrides for certain file patterns (e.g., TypeScript files).

    Each configuration object can include:

    • ignores: An array of patterns to ignore.
    • rules: An object defining ESLint rule overrides.
    • files: An array of glob patterns to which the configuration applies.
    import unjs from "eslint-config-unjs";
    
    export default unjs(
      {
        ignores: [".git", "test/fixture/dist"],
        rules: {
          "unicorn/no-null": 0,
        }
      },
      {
        files: ["**/*.ts"],
        rules: {
          "@typescript-eslint/explicit-function-return-type": "error",
        }
      }
    );
  12. Enable Decorators support in unbuild

    main

    To support TypeScript decorators, pass the experimentalDecorators flag through the rollup.esbuild.tsconfigRaw option in your build.config.ts.

    import { defineBuildConfig } from "unbuild";
    
    export default defineBuildConfig({
      rollup: {
        esbuild: {
          tsconfigRaw: {
            compilerOptions: {
              experimentalDecorators: true,
            },
          },
        },
      },
    });