aboutsummaryrefslogtreecommitdiff
path: root/docs/bundler/macros.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/bundler/macros.md')
-rw-r--r--docs/bundler/macros.md57
1 files changed, 57 insertions, 0 deletions
diff --git a/docs/bundler/macros.md b/docs/bundler/macros.md
index 3ffa49502..0d6cda6fb 100644
--- a/docs/bundler/macros.md
+++ b/docs/bundler/macros.md
@@ -44,6 +44,63 @@ Bun Macros are import statements annotated using either:
- `with { type: 'macro' }` — an [import attribute](https://github.com/tc39/proposal-import-attributes), a Stage 3 ECMA Scrd
- `assert { type: 'macro' }` — an import assertion, an earlier incarnation of import attributes that has now been abandoned (but is [already supported](https://caniuse.com/mdn-javascript_statements_import_import_assertions) by a number of browsers and runtimes)
+## Security considerations
+
+Macros must explicitly be imported with `{ type: "macro" }` in order to be executed at bundle-time. These imports have no effect if they are not called, unlike regular JavaScript imports which may have side effects.
+
+You can disable macros entirely by passing the `--no-macros` flag to Bun. It produces a build error like this:
+
+```js
+error: Macros are disabled
+
+foo();
+^
+./hello.js:3:1 53
+```
+
+To reduce the potential attack surface for malicious packages, macros cannot be _invoked_ from inside `node_modules/**/*`. If a package attempts to invoke a macro, you'll see an error like this:
+
+```js
+error: For security reasons, macros cannot be run from node_modules.
+
+beEvil();
+^
+node_modules/evil/index.js:3:1 50
+```
+
+Your application code can still import macros from `node_modules` and invoke them.
+
+```ts
+import {macro} from "some-package" with { type: "macro" };
+
+macro();
+```
+
+## Export condition `"macro"`
+
+When shipping a library containing a macro to `npm` or another package registry, use the `"macro"` [export condition](https://nodejs.org/api/packages.html#conditional-exports) to provide a special version of your package exclusively for the macro environment.
+
+```jsonc#package.json
+{
+ "name": "my-package",
+ "exports": {
+ "import": "./index.js",
+ "require": "./index.js",
+ "default": "./index.js",
+ "macro": "./index.macro.js"
+ }
+}
+```
+
+With this configuration, users can consume your package at runtime or at bundle-time using the same import specifier:
+
+```ts
+import pkg from "my-package"; // runtime import
+import {macro} from "my-package" with { type: "macro" }; // macro import
+```
+
+The first import will resolve to `./node_modules/my-package/index.js`, while the second will be resolved by Bun's bundler to `./node_modules/my-package/index.macro.js`.
+
## Execution
When Bun's transpiler sees a macro import, it calls the function inside the transpiler using Bun's JavaScript runtime and converts the return value from JavaScript into an AST node. These JavaScript functions are called at bundle-time, not runtime.