Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 0 additions & 23 deletions .eslintrc.js

This file was deleted.

4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ jobs:
node-version-file: .nvmrc
cache: pnpm
- run: pnpm install
- run: pnpm run lint
- run: pnpm run typecheck # TypeScript integrity
- run: pnpm run lint # code style linting
- run: pnpm run build # test if building works fine

test:
runs-on: ubuntu-24.04
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/node_modules/
/dist/
Comment thread
makmic marked this conversation as resolved.
Resolved

/package-lock.json
/yarn.lock
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

## Unreleased changes

*
* Rewrite the plugin in TypeScript. The package now ships bundled ESM and CommonJS builds with type definitions; `ManifestPluginOptions` is exported for TypeScript consumers. No API changes — both `require('esbuild-manifest-plugin')` and `import manifestPlugin from 'esbuild-manifest-plugin'` return the plugin directly.
* Replace Jest with Vitest and ESLint with Biome.
* Support all three esbuild `entryPoints` option shapes: string array, `{ in, out }` object array, and plain object (`{ outputName: 'input.js' }`).
* Throw an error when required esbuild options `outdir`, `absWorkingDir`, or `entryPoints` are not set, instead of failing with a cryptic runtime error.

## 1.0.0 (2026-04-21)

Expand Down
50 changes: 34 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ That manifest can be used by applications to resolve output filenames (which may

If you are using the `esbuild-sass-plugin` to have esbuild also compile Sass files, they will be included in the manifest.

This library is written in TypeScript and shipped as both ESM and CommonJS with bundled type definitions.

## Installation

Expand All @@ -15,14 +16,32 @@ npm install -D esbuild-manifest-plugin

Or add to your project's `package.json`.


## Usage

Simply `require` and add the plugin to the `plugins` section of the esbuild config.
Add the plugin to the `plugins` section of the esbuild config.

Example:
ESM:

```js
import path from 'path'
import esbuild from 'esbuild'
import manifestPlugin from 'esbuild-manifest-plugin'
Comment thread
makmic marked this conversation as resolved.
Resolved

await esbuild.build({
entryPoints: ['application.js'],
entryNames: '[dir]/[name]-[hash]',
assetNames: '[dir]/[name]-[hash]',
bundle: true,
outdir: path.join(__dirname, 'public/assets'),
absWorkingDir: path.join(__dirname, 'app/assets'),
plugins: [manifestPlugin()],
})
Comment thread
makmic marked this conversation as resolved.
Resolved
```

CommonJS:

```js
const path = require('path')
const esbuild = require('esbuild')
const manifestPlugin = require('esbuild-manifest-plugin')

Expand All @@ -32,7 +51,8 @@ esbuild.build({
assetNames: '[dir]/[name]-[hash]',
bundle: true,
outdir: path.join(__dirname, 'public/assets'),
plugins: [manifestPlugin()]
absWorkingDir: path.join(__dirname, 'app/assets'),
plugins: [manifestPlugin()],
})
```

Expand All @@ -47,12 +67,11 @@ That generates a `manifest.json` in the configured `outdir` which looks like thi
}
```

Note that the plugin requires the `metafile` option to be enabled in esbuild for the plugin to work; it will automaticall do that for you.

Note that the plugin requires the `metafile` option to be enabled in esbuild for the plugin to work; it will automatically do that for you.

## Options

The plugin may be called with the following options.
The plugin accepts the following `ManifestPluginOptions`:

### `filename`

Expand Down Expand Up @@ -83,10 +102,14 @@ After checking out the repo, install dependencies with [pnpm](https://pnpm.io/):
pnpm install
```

Lint the codebase with:
Common scripts:

```sh
pnpm run lint
pnpm run lint # Biome lint + format check
pnpm run format # Biome auto-format
pnpm run typecheck # tsc --noEmit
pnpm run build # build.mjs — emits dist/index.{js,cjs,d.ts,d.cts}
pnpm test # Vitest
```

## Running tests
Expand All @@ -99,7 +122,7 @@ Run the full suite:
pnpm test
```

GitHub Actions runs `lint` once and `test` on a Node matrix (18, 20, 22, 24) on every push and pull request to `main`; see `.github/workflows/test.yml`.
GitHub Actions runs `lint`, `typecheck`, and `build` once, and `test` on a Node matrix (18, 20, 22, 24) on every push and pull request to `main`; see `.github/workflows/test.yml`.

## Release process

Expand All @@ -109,9 +132,4 @@ To release a new version:
2. Move the "Unreleased changes" entries in `CHANGELOG.md` under a new version heading with today's date.
3. Commit the changes and tag the commit (`git tag vX.Y.Z`).
4. Push the commit and the tag (`git push && git push --tags`).
5. Publish to npm with `npm publish` (only the `lib/` directory is shipped; see `files` in `package.json`).

## Roadmap

1. Migrate to TypeScript
2. More configuration options, if applicable (like `serializeManifest`)
5. Publish to npm with `npm publish` (only the `dist/` directory is shipped).
39 changes: 39 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"files": {
"ignore": ["dist/", "node_modules/", "test/fixtures/**", "pnpm-lock.yaml"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"bracketSpacing": true
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded",
"trailingCommas": "all",
"arrowParentheses": "asNeeded"
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"overrides": [
{
"include": ["test/**/*.ts"],
"linter": {
"rules": {
"style": {
"noNonNullAssertion": "off"
}
}
}
}
]
}
42 changes: 42 additions & 0 deletions build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
////
// Compiles our TypeScript from /src to dist/ twice for two different module formats:
// ESM: dist/index.js + dist/index.d.ts (code and types for `import` consumers)
// CommonJS: dist/index.cjs + dist/index.d.cts (code and types for `require` consumers)
//
import { execSync } from 'node:child_process'
import { copyFileSync, readdirSync, rmSync } from 'node:fs'
import * as esbuild from 'esbuild'

rmSync('dist', { recursive: true, force: true })

execSync('tsc --project tsconfig.build.json --emitDeclarationOnly --declaration', {
stdio: 'inherit',
})

// tsc emits the shared type declarations (.d.ts); we copy them to .d.cts so
// TypeScript can resolve types for CJS consumers via the package.json "exports" map.
// esbuild then compiles the actual JS for both formats in parallel.
for (const file of readdirSync('dist').filter(f => f.endsWith('.d.ts'))) {
copyFileSync(`dist/${file}`, `dist/${file.replace(/\.d\.ts$/, '.d.cts')}`)
}

await Promise.all([
esbuild.build({
entryPoints: ['src/index.ts'],
format: 'esm',
outfile: 'dist/index.js',
platform: 'node',
sourcemap: true,
}),
esbuild.build({
entryPoints: ['src/index.ts'],
format: 'cjs',
outfile: 'dist/index.cjs',
platform: 'node',
sourcemap: true,
// esbuild's CJS output puts the default export on module.exports.default.
// Object.assign merges it back so `require('pkg')` returns the function directly,
// while `require('pkg').default` still works for TypeScript's esModuleInterop.
footer: { js: 'module.exports = Object.assign(module.exports.default, module.exports);' },
}),
])
87 changes: 0 additions & 87 deletions lib/index.js

This file was deleted.

44 changes: 29 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@
"name": "esbuild-manifest-plugin",
"version": "1.0.0",
"description": "esbuild plugin to generate manifest of compiled assets",
"main": "lib/index.js",
"files": [
"lib"
],
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": ["dist"],
"repository": {
"type": "git",
"url": "https://github.com/makandra/esbuild-manifest-plugin"
Expand All @@ -14,24 +27,25 @@
"author": "Arne Hartherz <arne.hartherz@makandra.de>",
"license": "MIT",
"scripts": {
"lint": "eslint .",
"test": "jest"
"build": "node build.mjs",
"lint": "biome check",
"format": "biome format --write",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"prepublishOnly": "pnpm build"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/node": "^18.19.0",
"esbuild": "^0.25.0",
"eslint": "^8.20.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.2.4",
"eslint-plugin-promise": "^6.6.0",
"jest": "^30.0.0"
"typescript": "^5.7.0",
"vitest": "^2.1.0"
},
"peerDependencies": {
"esbuild": ">= 0"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
]
"onlyBuiltDependencies": ["esbuild"]
}
}
Loading
Loading