Bundlemonkey is a userscript bundler designed to make developing browser userscripts blazing fast and efficient 🔥.
With TypeScript support and type-safe header comments, managing your code becomes a breeze ✨.
Works well with Tampermonkey, Violentmonkey or Greasemonkey ✅.
-
Lightning-Fast Builds
Incredibly fast bundling speeds, thanks to esbuild. -
TypeScript Support
-
Module Bundling
-
Type-Safe Header Comments
Write userscript header metadata in a type-safe and straightforward way. This not only minimizes errors but also makes your code much more maintainable 🛡️. -
Watch mode
// src/sample/message.ts
export const message = "Hello from sample script and bundlemonkey!";
// src/sample/index.user.ts
import { defineUserScript } from "bundlemonkey";
import { message } from "./message";
export default defineUserScript({
name: "Sample userscript",
version: "1.0.0",
description: "Write userscripts with ease using bundlemonkey!",
match: ["https://example.com/*"],
main: () => {
console.log(message);
},
});
// ==UserScript==
// @name Sample userscript
// @version 1.0.0
// @description Write userscripts with ease using bundlemonkey!
// @match https://example.com/*
// ==/UserScript==
// src/sample/message.ts
var message = "Hello from sample script and bundlemonkey!";
// src/sample/index.user.ts
void (() => {
console.log(message);
})();
You can set up new project quickly using the template (1a), or do it manually (1b).
npx bundlemonkey --create
# or like
pnpx bundlemonkey --create
bunx bundlemonkey --create
Install Bundlemonkey using npm, pnpm, or bun:
npm install --save bundlemonkey
# or like
pnpm add bundlemonkey
bun add bundlemonkey
Create a directory for your userscripts named with the slug under src
directory. Your project structure might look like this:
├── src/ # configurable
│ ├── script-a/
│ │ ├── index.user.ts
│ │ └── some-module.ts
│ └── script-b/
│ └── index.user.ts
├── dist/ # configurable
│ └── # bundled code goes here
├── bundlemonkey.config.ts # optional
└── package.json
Tip
Source scripts are collected by glob src/*/index.user.{ts,js}
by default, and srcDir
is configurable.
Head into any of the source scripts (index.user.ts
or index.user.js
) and start writing your code! See Define UserScript section for more details.
Run the following command to compile your code:
npx bundlemonkey
# or like
pnpx bundlemonkey
bunx bundlemonkey
Bundlemonkey compiles your code into dist
directory 🎉.
Watch mode and Remote watch mode are supported as well, so you can have it rebuild your scripts automatically. See CLI docs below for more details.
Each source script must define its main code, name, version, etc. using defineUserScript
and export it as the default export. It might look like:
import { defineUserScript } from "bundlemonkey";
export default defineUserScript({
name: "Sample userscript",
version: "1.0.0",
description: "Write userscripts with ease using bundlemonkey!",
match: ["https://example.com/*"],
config: {
message: "hello!",
},
main: (config) => {
// your main code here!
console.log(config.message)
},
});
Please see docs of Tampermonkey or Violentmonkey for more details about props other than config
and main
.
✅ - required
name | type | default |
---|---|---|
config | T extends any |
|
main ✅ | (config: T) => unknown |
--- |
name ✅ | string |
--- |
namespace | string |
|
copyright | string |
|
version ✅ | string |
--- |
description | string |
|
icon | string |
|
grant | Grant[] | "none" |
[] |
author | string |
|
homepage | string |
|
antiFeature | AntiFeature[] |
[] |
require | string[] |
[] |
resource | { name: string; url: string; }[] |
[] |
match | string[] |
[] |
excludeMatch | string[] |
[] |
include | string[] |
[] |
exclude | string[] |
[] |
runAt | RunAt |
|
runIn | string[] |
[] |
sandbox | "raw" | "JavaScript" | "DOM" |
|
injectInto | "page" | "content" | "auto" |
|
tag | string[] |
[] |
connect | string[] |
[] |
noframes | boolean |
false |
updateURL | string |
|
downloadURL | string |
|
supportURL | string |
|
unwrap | boolean |
false |
topLevelAwait | boolean |
false |
Config for the script which is intended to be modifiable by the users of your script.
If present, the value will be defined at the beginning of the compiled userscript as a variable named userscriptConfig
to make it easy for users to edit.
For example:
export default defineUserScript({
// ...
config: {
/**
* Edit this to change the message
* @type string
*/
message: "hello!"
},
main: (config) => {
window.alert(config.message);
},
});
will be compiled into:
// ==UserScript==
// ...
// ==/UserScript==
var userscriptConfig = {
/**
* Edit this to change the message
* @type string
*/
message: "hello!"
};
void ((config) => {
window.alert(config.message);
})(userscriptConfig);
Your main userscript code.
It can be either a synchronous or an asynchronous function, and can receive config as a prop.
All APIs supported by Tampermonkey, Violentmonkey and Greasemonkey (GM_*
, GM.*
, unsafeWindow
, window.onurlchange
, window.close
and window.focus
) can be specified.
type Grant =
| "unsafeWindow"
| "GM_addElement"
| "GM_addStyle"
// ...
| "GM.addStyle"
| "GM.setValue"
// ...
| "window.onurlchange"
| "window.close"
| "window.focus";
type AntiFeature = {
type: "ads" | "tracking" | "miner";
description: string;
};
type RunAt =
| "document-end"
| "document-start"
| "document-body"
| "document-idle"
| "context-menu";
All source scripts will be compiled at once.
Compiled scripts will be located in dist.production
directory.
Bundlemonkey monitors edits to the source scripts. When an edit is detected, it compiles the source and copies the output to the clipboard. Please paste and save it in your userscripts manager's editor for use.
Compiled scripts will be located in dist.dev
directory.
Similar to Watch mode, it monitors edits to the source scripts; however, in this mode, you do not need to paste into the editor every time you make changes.
When a source script is edited, a remote script will be copied to the clipboard only the first time. Once you paste and save this remote script in your userscripts manager's editor, subsequent edits to the source script will be automatically reflected.
You need to allow your userscript manager access to local files to use this mode. Please refer to Tampermonkey's FAQ for more details.
Tip
A remote script is a plain userscript that simply @require
s the actual userscript code.
bundlemonkey [--watch [--remote]] [--create]
Enable Watch mode.
Use with --watch
to enable Remote watch mode.
If you need to customize Bundlemonkey’s behavior, you can create a configuration file named bundlemonkey.config.ts
or bundlemonkey.config.js
in your project’s root directory.
bundlemonkey.config.ts
import type { Config } from "bundlemonkey";
const config: Config = {
srcDir: "src", // default value
dist: {
dev: ".dev", // default value
production: "dist", // default value
},
defaultMeta: {
author: "John Doe",
namespace: "johndoe",
homepage: "https://github.com/johndoe/userscripts",
updateURL: ({ scriptName }) =>
`https://github.com/johndoe/userscripts/raw/main/dist/${scriptName}.user.js`,
downloadURL: ({ scriptName }) =>
`https://github.com/johndoe/userscripts/raw/main/dist/${scriptName}.user.js`,
},
};
export default config;
- type:
string
- Default:
"src"
Directory where your source scripts are located.
Note
The glob for collecting the source files is like: <cwd>/<srcDir>/*/index.user.{ts,js}
- type:
object
- type:
string
- Default:
".dev"
Dist directory in watch mode.
- type:
string
- Default:
"dist"
- type:
object
- Default:
undefined
Default meta used for all userscripts. Metadata defined in defineUserScript
overrides this.
All meta properties in defineUserScript
can be used here as well, while updateURL
/downloadURL
have different signatures like below.
- type:
(args: { scriptName: string; version: string }) => string
- Default:
undefined
- type:
(args: { scriptName: string; version: string }) => string
- Default:
undefined