> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Upgrading from 0.x to v1

This document lists all breaking changes from Rslib 0.23 to 1.0. You can use it as a migration reference.

## Agent prompt

If you are using a Coding Agent, copy the prompt below and send it to the Agent:


For your Agent

Upgrade from 0.x to v1

Copy this prompt and send it to your Coding Agent.

Copy Prompt

Upgrade this project from Rslib 0.x to 1.0. Read and follow the migration guide:
https://rslib.rs/guide/upgrade/v0-to-v1.md

## Upgrade Rslib to v1

Upgrade `@rslib/core` to version 1.0:

```json title="package.json"
{
  "devDependencies": {
    "@rslib/core": "^1.0.0"
  }
}
```

## Rsbuild v2

Because Rslib v1 is based on Rsbuild v2, you can check the `peerDependencies` of the Rsbuild plugins in your project for `@rsbuild/core` v2 support. We recommend using [Taze](https://github.com/antfu-collective/taze) to upgrade the Rsbuild plugins in your project to their latest versions:

```bash
# Upgrade Rsbuild plugins in the current directory
npx taze major --include "/rsbuild/" -w

# Or recursively upgrade Rsbuild plugins across the monorepo
npx taze major --include "/rsbuild/" -w -r
```

If your project directly uses Rsbuild configuration or JavaScript APIs, you can refer to the [Rsbuild v2 upgrade guide](https://rsbuild.rs/guide/upgrade/v1-to-v2) for the related changes.

## Default syntax target update

When [output.target](/config/rsbuild/output.md#outputtarget) is `'node'` and [lib.syntax](/config/lib/syntax.md) is not configured, Rslib v1 attempts to infer the syntax target from `package.json#engines.node`.

For example, the following `engines.node`:

```json title="package.json"
{
  "engines": {
    "node": "^20.19.0 || >=22.12.0"
  }
}
```

Rslib resolves it to the following syntax target:

```ts title="rslib.config.ts"
export default {
  lib: [
    {
      syntax: ['node >= 20.19.0'],
    },
  ],
};
```

If `engines.node` is missing or no minimum version can be inferred, Rslib continues to use `'esnext'`.

An explicit [lib.syntax](/config/lib/syntax.md) value takes precedence over automatic inference, so existing configurations are unaffected and you can set it explicitly to override the target inferred from `engines.node`.

In addition, Rslib v1 adjusts the Browserslist baselines for `es2023` and `es2024`, and adds the new `es2025` target:

| `lib.syntax` | Rslib v0.x                                                     | Rslib v1                                                      |
| ------------ | -------------------------------------------------------------- | ------------------------------------------------------------- |
| `es2023`     | Chrome / Edge 94, Firefox 93, Safari / iOS 16.4, Node.js 16.11 | Chrome / Edge 110, Firefox 115, Safari / iOS 17, Node.js 20   |
| `es2024`     | Same as `esnext`, using the latest browser or Node.js version  | Chrome / Edge 112, Firefox 116, Safari / iOS 17, Node.js 20   |
| `es2025`     | Not supported                                                  | Chrome / Edge 126, Firefox 132, Safari / iOS 17.4, Node.js 23 |

These options only control JavaScript and CSS syntax transformations. They do not inject polyfills for runtime APIs missing from the target environment. The practical impact of the new baselines on JavaScript transformations is limited. The main difference is that Lightning CSS may emit more modern CSS.

No changes are needed if the new baselines are suitable. To preserve the syntax target behavior from Rslib v0.x:

- If the project previously used `es2023` and needs to retain the more conservative compatibility range:

  ```diff title="rslib.config.ts"
  export default {
    lib: [
      {
  -      syntax: 'es2023',
  +      syntax: 'es2022',
      },
    ],
  };
  ```

- If the project previously used `es2024` and needs to continue using a dynamic Browserslist target:

  ```diff title="rslib.config.ts"
  export default {
    lib: [
      {
  -      syntax: 'es2024',
  +      syntax: 'esnext',
      },
    ],
  };
  ```

## Default `externalsType` update

For ESM output ([`format: 'esm'`](/config/lib/format.md)), Rslib v1 changes Rspack's default [`externalsType: 'module-import'`](https://rspack.rs/config/externals#externalstypemodule-import) to [`externalsType: 'modern-module'`](https://rspack.rs/config/externals#externalstypemodern-module):

| Source syntax                           | Rslib v0.x               | Rslib v1                 |
| --------------------------------------- | ------------------------ | ------------------------ |
| Static `import`                         | Emitted as an ESM import | Emitted as an ESM import |
| Dynamic `import()`                      | Remains dynamic          | Remains dynamic          |
| CommonJS `require()` (`target: 'node'`) | Emitted as an ESM import | Uses `createRequire()`   |
| CommonJS `require()` (`target: 'web'`)  | Emitted as an ESM import | Preserves `require()`    |

This change only affects external CommonJS modules loaded with `require()` when `externalsType` is not explicitly configured. This includes dependencies externalized through [lib.autoExternal](/config/lib/auto-external.md), [output.autoExternal](/config/rsbuild/output.md#outputautoexternal), or [output.externals](/config/rsbuild/output.md#outputexternals), as well as Node.js built-ins externalized automatically for `target: 'node'`. Externals loaded with ESM imports retain their previous behavior and generally require no changes.

Note that if the output contains an external loaded through `createRequire()` and is bundled again, the consuming bundler must be able to statically analyze this call. Rsbuild / Rspack projects can enable [`module.parser.javascript.createRequire`](https://rspack.rs/config/module-parser#javascriptcreaterequire). If the change in module-loading semantics is acceptable, you can also consider migrating CommonJS `require()` calls in the source to ESM imports.

If only one dependency needs to retain the Rslib v0.x behavior, and its loading semantics are compatible with ESM imports, use the `${externalsType} ${libraryName}` syntax in [output.externals](/config/rsbuild/output.md#outputexternals) to use `module-import` for that dependency:

```ts title="rslib.config.ts"
export default {
  lib: [
    {
      output: {
        externals: {
          'some-package': 'module-import some-package',
        },
      },
    },
  ],
};
```

To preserve the Rslib v0.x behavior for all dependencies, set `externalsType` to `module-import` through [tools.rspack](/config/rsbuild/tools.md#toolsrspack):

```ts title="rslib.config.ts"
export default {
  lib: [
    {
      tools: {
        rspack(config) {
          config.externalsType = 'module-import';
        },
      },
    },
  ],
};
```

## Default environment variable handling update

In Rslib v0.x, the following [Rsbuild default environment variables](https://rsbuild.rs/guide/advanced/env-vars#default-variables) are replaced with specified values at build time:

- `import.meta.env.MODE`
- `import.meta.env.DEV`
- `import.meta.env.PROD`
- `import.meta.env.SSR`
- `import.meta.env.BASE_URL`
- `import.meta.env.ASSET_PREFIX`
- `process.env.BASE_URL`
- `process.env.ASSET_PREFIX`

Rslib v1 changes how these variables are handled when [format](/config/lib/format.md) is `'esm'` or `'cjs'`:

| format | Rslib v0.x             | Rslib v1                                                                                                                                |
| ------ | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| esm    | Replaced at build time | `import.meta.env.*`, `process.env.BASE_URL`, and `process.env.ASSET_PREFIX` are preserved in the build output                           |
| cjs    | Replaced at build time | `import.meta.env` is replaced with `undefined`; `process.env.BASE_URL` and `process.env.ASSET_PREFIX` are preserved in the build output |

For complete details on how Rslib v1 handles environment variables, see [Environment variables](/guide/advanced/env-vars.md).

If your project relies on Rslib v0.x replacing these variables at build time, explicitly define the variables it uses with [source.define](/config/rsbuild/source.md#sourcedefine) to restore the previous behavior. To use `import.meta.env.*` in CJS output, you must also explicitly define the corresponding variables:

```ts title="rslib.config.ts"
export default {
  source: {
    define: {
      'import.meta.env.MODE': JSON.stringify('production'),
      'process.env.BASE_URL': JSON.stringify('/'),
      'process.env.ASSET_PREFIX': JSON.stringify(''),
    },
  },
};
```

## Resource module handling updates

Rslib v1 changes how ESM output ([`format: 'esm'`](/config/lib/format.md)) handles static assets referenced by `new URL()`, Web Workers, and Wasm modules.

### Static assets with `new URL()`

Rslib v1 treats statically analyzable `new URL()` references as static assets when building ESM output. Consider a source file that references `logo.svg`:

```ts title="src/index.ts"
const logo = new URL('./assets/logo.svg', import.meta.url);
```

For project source, Rslib v0.x preserved the expression and did not emit `logo.svg`. Rslib v1 emits the file and rewrites the path in `new URL()` to a relative path that points to the emitted asset.

```js title="dist/index.js"
const logo = new URL('./static/svg/logo.svg', import.meta.url);
```

For third-party dependencies bundled into the output, Rslib v0.x rewrote the asset path in `new URL()` as a module reference and injected runtime code to load that module and compute the base URL. Rslib v1 handles these references in the same way as project source, emitting the referenced assets and rewriting the paths in `new URL()` to relative paths that point to the emitted files.

If a project previously copied these assets through [output.copy](/config/rsbuild/output.md#outputcopy) or a script, and Rslib emits them from `new URL()` references after the upgrade, remove the corresponding configuration or script to avoid duplicate outputs. In bundleless mode ([`bundle: false`](/config/lib/bundle.md)), if [source.entry](/config/rsbuild/source.md#sourceentry) also matches these assets, exclude them to avoid generating an additional JavaScript entry for the same file.

To skip Rslib's static asset processing for `new URL()` references, choose an approach based on the required scope. For details, see [Skip `new URL()` processing](/guide/advanced/static-assets.md#skip-new-url-processing).

- **Skip one reference:** Add the [rspackIgnore](https://rspack.rs/api/runtime-api/module-methods#rspackignore) magic comment before the first argument of `new URL()`.

  ```ts title="src/index.ts"
  const logo = new URL(
    /* rspackIgnore: true */ './assets/logo.svg',
    import.meta.url,
  );
  ```

- **Skip all references:** Use [tools.bundlerChain](/config/rsbuild/tools.md#toolsbundlerchain) to set the `url` parser option of the `rslib:new-url` rule to `false`.

  ```ts title="rslib.config.ts"
  import { defineConfig } from '@rslib/core';

  export default defineConfig({
    tools: {
      bundlerChain(chain) {
        chain.module.rule('rslib:new-url').parser({
          url: false,
        });
      },
    },
  });
  ```

Additionally, Rslib's default handling requires the target of a `new URL()` reference to resolve to an existing source file at build time, while directories and files that exist only in the build output cannot be processed as static assets. For example:

```ts title="src/index.ts"
const currentDirectory = new URL('.', import.meta.url);
const generatedFile = new URL('./generated.js', import.meta.url);
```

For these references, use one of the approaches above to skip `new URL()` processing. If these references are only used to obtain file system paths in Node.js, you can also modify the source to use Node.js `path` and `url` APIs:

```ts title="src/index.ts"
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const currentDirectory = path.dirname(fileURLToPath(import.meta.url));
const generatedFile = path.join(currentDirectory, 'generated.js');
```

For more details, see [Static assets - `new URL` imports](/guide/advanced/static-assets.md#new-url-imports).

### Web Workers

When building ESM output, Rslib v1 parses `new Worker(new URL(...))` and treats the referenced local script as a Worker entry. Consider a Worker defined in `worker.ts`:

```ts title="src/index.ts"
new Worker(new URL('./worker.ts', import.meta.url));
```

Rslib v0.x preserved this expression and did not build `worker.ts` from the reference. Rslib v1 builds the Worker and its dependencies, rewrites the URL to the corresponding output path, and adds `type: 'module'`:

```js title="dist/index.js"
new Worker(new URL('./worker.js', import.meta.url), {
  type: 'module',
});
```

If the project previously configured the Worker source as a separate entry and referenced the expected `.js` output in the source code, remove the entry after upgrading and reference the Worker source file directly:

```diff title="rslib.config.ts"
 export default {
   source: {
     entry: {
       index: './src/index.ts',
-      worker: './src/worker.ts',
     },
   },
 };
```

```diff title="src/index.ts"
-new Worker(new URL('./worker.js', import.meta.url));
+new Worker(new URL('./worker.ts', import.meta.url));
```

For more details, see [Web Workers](/guide/advanced/web-workers.md).

### Wasm

Rslib v1 provides two output modes for Wasm modules in ESM output:

- [`compile` mode](/guide/advanced/wasm.md#compile-mode): Rslib generates the JavaScript code required to load and instantiate Wasm modules and emits hashed `.wasm` files.
- [`preserve` mode](/guide/advanced/wasm.md#preserve-mode): JavaScript retains its `.wasm` imports, while `.wasm` files keep their original filenames and source-relative directory structure. These imports must be processed by a downstream bundler or target runtime that supports [WebAssembly ESM Integration](https://github.com/WebAssembly/esm-integration).

In [bundleless mode](/config/lib/bundle.md), Rslib v0.x generated the JavaScript code required to load and instantiate Wasm modules. Rslib v1 uses `preserve` mode by default and keeps `.wasm` imports in JavaScript. To use `compile` mode instead, configure [`wasm.mode`](/config/lib/wasm.md#wasmmode):

```ts title="rslib.config.ts"
export default {
  lib: [
    {
      format: 'esm',
      bundle: false,
      wasm: {
        mode: 'compile',
      },
    },
  ],
};
```

Wasm handling in [bundle mode](/config/lib/bundle.md) remains unchanged.

For more details, see [Wasm - Output modes](/guide/advanced/wasm.md#output-modes).

## `@typescript/native-preview` support update

In Rslib v0.x, enabling [dts.tsgo](/config/lib/dts.md#dtstsgo) caused Rslib to load `@typescript/native-preview` automatically to generate declaration files.

Rslib v1 does not load `@typescript/native-preview` by default. Instead, it resolves `typescript` from the project root and selects the declaration generation method based on the resolved version. When TypeScript 7+ is detected, Rslib automatically enables `dts.tsgo`.

To continue using `@typescript/native-preview`, explicitly specify its module entry through [dts.typescriptPath](/config/lib/dts.md#dtstypescriptpath):

```ts title="rslib.config.ts"
import { fileURLToPath } from 'node:url';

export default {
  lib: [
    {
      dts: {
        typescriptPath: fileURLToPath(
          import.meta.resolve('@typescript/native-preview'),
        ),
      },
    },
  ],
};
```

## Temporary declaration directory update

Rslib generates temporary declaration files during declaration bundling. Rslib v1 changes the directory where these files are stored from `.rslib/declarations` to `.rstack/declarations`. After upgrading, you can safely remove the old `.rslib` directory.

## Node.js template update

Rslib v1 no longer provides a dual ESM/CJS Node.js template and only provides a pure ESM template. When creating a project, update the `--template` argument as follows:

| Rslib v0.x `--template` argument | Rslib v1 `--template` argument |
| -------------------------------- | ------------------------------ |
| `node-esm`                       | `node`                         |
| `node-esm-js`                    | `node-js`                      |
| `node-esm-ts`                    | `node-ts`                      |
| `node-dual`                      | Not supported                  |
| `node-dual-js`                   | Not supported                  |
| `node-dual-ts`                   | Not supported                  |

For example, update a command that uses the previous pure ESM template as follows:

```diff
-npx create-rslib my-project --template node-esm
+npx create-rslib my-project --template node
```

Additionally, the new template sets `engines.node` to `^20.19.0 || >=22.12.0` by default and no longer configures `lib.syntax` explicitly. Rslib automatically infers `lib.syntax` from `engines.node`; see [Default syntax target update](#default-syntax-target-update) for details.

Every Node.js version covered by `engines.node` supports `require(ESM)`, so existing CommonJS consumers can now load the pure ESM package directly with `require()`, provided that neither the entry nor its dependencies use top-level `await`:

```js
const packageExports = require('pure-esm-package');
```

If you still need a dual ESM/CJS template, use the following command to create one with the previous generator:

```bash
npx -y create-rslib@0.23.2 my-project --template node-dual
```

## Configuration

### Enable `redirect.dts.extension` by default

Rslib v1 enables [redirect.dts.extension](/config/lib/redirect.md#redirectdtsextension) by default. When bundleless declaration files are generated, import paths automatically gain or replace their extensions with JavaScript file extensions that resolve to the corresponding declaration files.

For example, when an import path corresponds to `foo.d.ts`, the generated output changes as follows:

```diff title="dist/index.d.ts"
-export type { Foo } from './foo';
+export type { Foo } from './foo.js';
```

If your consuming tools depend on type imports without extensions, or another tool handles extension rewriting, restore the Rslib 0.x behavior:

```ts title="rslib.config.ts"
export default {
  lib: [
    {
      redirect: {
        dts: {
          extension: false,
        },
      },
    },
  ],
};
```

If you also configure `compilerOptions.paths` or [dts.alias](/config/lib/dts.md#dtsalias), check whether the mapped type import paths need to point directly to a concrete declaration entry. See [redirect.dts.extension](/config/lib/redirect.md#notes) for details.

### Migrate `lib.autoExternal`

[lib.autoExternal](/config/lib/auto-external.md) is deprecated in Rslib v1, but has not yet been removed and can still be used.

We recommend replacing it with Rsbuild's [output.autoExternal](/config/rsbuild/output.md#outputautoexternal) option:

```diff title="rslib.config.ts"
 export default {
   lib: [
     {
-      autoExternal: false,
+      output: {
+        autoExternal: false,
+      },
     },
   ],
 };
```

### Remove `experiments.advancedEsm`

The `experiments.advancedEsm` option has been removed.

This option was originally used to generate ESM output that was more suitable for static analysis and supported code splitting. In Rslib v1, this behavior is enabled by default for ESM output, so the option is no longer needed.

```diff title="rslib.config.ts"
 export default {
   lib: [
     {
-      experiments: {
-        advancedEsm: true,
-      },
     },
   ],
 };
```

## JavaScript API

- The type of `lib` in [RslibConfig](/api/javascript-api/types.md#rslibconfig) has changed from `LibConfig[]` to `LibConfig[] | undefined`. Omitting `lib` is equivalent to configuring `lib: [{}]`.
- The invalid `'none'` value has been removed from the `mode` option of [`rslib.inspectConfig()`](/api/javascript-api/instance.md#rslibinspectconfig). When `mode` is omitted, it is now inferred from `process.env.NODE_ENV`: it is `'development'` when `NODE_ENV` is `'development'`, and `'production'` otherwise. When `mode` is `'development'`, `rslib.inspectConfig()` now only outputs configurations for libraries with `format: 'mf'`.
