Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
201fd42
feat!: build with tsdown instead of unbuild
danielroe Nov 10, 2026
c239c2f
feat!: drop jiti in favour of native node ts support
danielroe Nov 10, 2026
efb1cd2
feat!: translate `build.config.ts` and reject options without an equi…
danielroe Nov 10, 2026
588bb27
feat!: make `tsdown` a peer dependency
danielroe Nov 10, 2026
e78cc1b
test: build and stub a module under a path with spaces and non-ASCII …
danielroe Nov 10, 2026
81e9fc6
feat: warn about `package.json` paths the build did not produce
danielroe Nov 10, 2026
262b64a
feat: prepare a module without `.nuxt` before building it
danielroe Nov 10, 2026
4851edb
ci: run tests on the node engines floor
danielroe Nov 10, 2026
23cbf07
docs: describe the tsdown-based build
danielroe Nov 10, 2026
9f0d303
test: run test files sequentially
danielroe Nov 11, 2026
94025db
test: normalise paths for Windows
danielroe Nov 11, 2026
89c087a
fix: normalise `cwd` and `outDir` before comparing build config paths
danielroe Nov 11, 2026
d2e154d
fix: resolve a relative `tsconfig` option against the project root
danielroe Nov 11, 2026
5b1d616
fix: normalise the path the stub loader records a warned file under
danielroe Nov 11, 2026
83b9f57
fix: substitute every wildcard when locating the server routes table
danielroe Nov 11, 2026
cef726a
fix: alias stubbed `#build` exports instead of declaring them
danielroe Nov 11, 2026
c9513ab
fix: write cached declaration files atomically and only once
danielroe Nov 11, 2026
45c56c0
fix: report non-erasable syntax relative to the module root when stub…
danielroe Nov 11, 2026
f6af57f
test: fail fixture setup when a fixture build fails
danielroe Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,23 @@ jobs:

- name: 💪 Type check
run: pnpm test:types

engines-floor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: pnpm/setup@fa36a5ef7dcdefc3a7cb25655c122e00cf6e636e # v3.1.2
with:
runtime: node@22.18.0
cache: true

- name: 🔍 Check Node.js version
run: pnpm exec node -e "if (process.version !== 'v22.18.0') { console.error('Expected v22.18.0, got ' + process.version); process.exit(1) }"

- run: pnpm dev:prepare

- name: 🧪 Test project
run: pnpm test

- name: 🛠 Build project (example)
run: pnpm example:build
67 changes: 53 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
## Features

- Compatible with Nuxt 3 and Nuxt Kit
- Unified build with [unjs/unbuild](https://github.com/unjs/unbuild)
- Unified build with [tsdown](https://tsdown.dev)
- Automated build config using last module spec
- Typescript and ESM support
- Auto generated types and shims for `@nuxt/schema`

We recommend to checkout the [Nuxt modules author guide](https://nuxt.com/docs/guide/going-further/modules) before starting with the module-builder.

Upgrading a module from v1? Follow the [upgrade guide](./docs/upgrade-v2.md).

## Requirements

For a user to use a module generated from module-builder, it's recommended they have:
Expand Down Expand Up @@ -84,7 +86,7 @@ export default defineNuxtModule<ModuleOptions>({

Any runtime file and code that we need to provide by module including plugins, composables and server api, should be in this directory.

Each file will be transformed individually using [unjs/mkdist](https://github.com/unjs/mkdist) to `dist/runtime/`.
Each file will be transformed individually to `dist/runtime/`.

<!-- TODO: Docs about how to address runtime from within setup -->

Expand Down Expand Up @@ -121,37 +123,74 @@ A minimum `package.json` should look like this:
"@nuxt/kit": "latest"
},
"devDependencies": {
"@nuxt/module-builder": "latest"
"@nuxt/module-builder": "latest",
"tsdown": "^0.23.7"
}
}
```

### `build.config.ts` (optional)
`tsdown` is a peer dependency, so it has to be installed alongside `@nuxt/module-builder`.

Module builder is essentially a preset for [unjs/unbuild](https://github.com/unjs/unbuild), check out the [build command](./src/commands/build.ts#L51) for reference.
### Additional entrypoints (optional)

To customize/extend the unbuild configuration you can add a `build.config.ts` in the root of your project:
Module builder is essentially a preset for [tsdown](https://tsdown.dev); check out the [build command](./src/build/build.ts) for reference.

```ts
import { defineBuildConfig } from 'unbuild'
Entrypoints other than `src/module` can be listed under the `build.entries` field of your `package.json`:

export default defineBuildConfig({
// set additional configuration or customize using hooks
})
```json [package.json]
{
"build": {
"entries": [
"./src/utils"
]
}
}
```

> [!WARNING]
> `build.config.ts` and the `unbuild` field of `package.json` are deprecated.
> Their `entries`, `externals`, `replace` and `rollup.replace` options are still applied,
> and so are the `build:before`, `rollup:done` and `build:done` hooks. `mkdist:*` hooks are
> ignored. Any other option, including other hooks and the rest of `rollup`, makes the
> build fail. See the [upgrade guide](./docs/upgrade-v2.md#build-configuration).
>
> Import `defineBuildConfig` from `@nuxt/module-builder` rather than `unbuild`. Hooks receive
> a context with `options.name`, `options.rootDir`, `options.outDir`, `options.entries`,
> `options.declaration`, `options.stub`, `options.sourcemap` and `pkg`.

## Dist files

Module builder generates dist files in `dist/` directory:

- `module.mjs`: Module entrypoint build from `src/module`
- `module.json`: Module meta extracted from `module.mjs` + `package.json`
- `types.d.mts`: Exported types in addition to shims for `nuxt.config` auto completion.
- `runtime/*`: Individually transformed files using [unjs/mkdist](https://github.com/unjs/mkdist)
- Javascript and `.ts` files will be transformed to `.js` with extracted types on `.d.ts` file with same name
- `.vue` files will be transformed with extracted `.d.ts` file
- `runtime/*`: Individually transformed files
- JavaScript and `.ts` files will be transformed to `.js` with extracted types in a `.d.ts` file with the same name
- `.vue` files will be transformed with extracted `.d.vue.ts` and `.vue.d.ts` files
- `.css` files will be nested-flattened, prefixed and minified
- `.scss` and `.sass` files will be compiled to `.css` with the module's `sass` or `sass-embedded`; `_` partials are not emitted
- Other files will be copied as is

## Transpiling a directory with tsdown

The runtime build is a rolldown plugin, `transpileDir`, exported from `@nuxt/module-builder/rolldown` for any tsdown or rolldown build that needs a directory transpiled file by file rather than bundled:

```ts
// tsdown.config.ts
import { defineConfig } from 'tsdown'
import { transpileDir } from '@nuxt/module-builder/rolldown'

export default defineConfig({
entry: ['src/index.ts'],
plugins: [
transpileDir({ srcDir: 'src/runtime', outBase: 'runtime' }),
],
})
```

Every file under `srcDir` is emitted under `outBase` in the build's output directory with its name intact. Scripts are transpiled with oxc and keep their imports as written, apart from relative imports, which get the emitted extension. `.vue` files go through `vue-sfc-transformer`, stylesheets are processed as above, and other files are copied. Declarations come from one TypeScript program using the nearest `tsconfig.json`. The options are documented on `TranspileDirOptions`; `resolveExternal` decides what happens to relative imports that resolve outside `srcDir`. A build with nothing to bundle can pass `emptyEntry` from the same export as its `entry`.

## 💻 Development

- Clone repository
Expand Down
Loading
Loading