bun-types 1.1.44-canary.20250112T140558 → 1.1.44-canary.20250113T140648
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ambient.d.ts +10 -0
- package/bun.d.ts +292 -5
- package/docs/api/binary-data.md +1 -1
- package/docs/api/fetch.md +1 -1
- package/docs/api/html-rewriter.md +323 -20
- package/docs/api/spawn.md +1 -1
- package/docs/cli/publish.md +1 -1
- package/docs/guides/ecosystem/nuxt.md +1 -1
- package/docs/guides/install/add-peer.md +2 -2
- package/docs/guides/install/from-npm-install-to-bun-install.md +1 -1
- package/docs/guides/test/run-tests.md +3 -3
- package/docs/guides/test/snapshot.md +3 -3
- package/docs/guides/test/update-snapshots.md +1 -1
- package/docs/guides/util/version.md +1 -1
- package/docs/installation.md +4 -4
- package/docs/project/contributing.md +304 -0
- package/docs/project/licensing.md +73 -0
- package/docs/quickstart.md +1 -1
- package/docs/runtime/debugger.md +3 -3
- package/docs/runtime/hot.md +7 -0
- package/docs/runtime/modules.md +0 -1
- package/docs/test/dom.md +1 -1
- package/html-rewriter.d.ts +57 -1
- package/package.json +2 -2
package/ambient.d.ts
CHANGED
|
@@ -7,3 +7,13 @@ declare module "*.toml" {
|
|
|
7
7
|
var contents: any;
|
|
8
8
|
export = contents;
|
|
9
9
|
}
|
|
10
|
+
|
|
11
|
+
declare module "*.jsonc" {
|
|
12
|
+
var contents: any;
|
|
13
|
+
export = contents;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module "*/bun.lock" {
|
|
17
|
+
var contents: import("bun").BunLockFile;
|
|
18
|
+
export = contents;
|
|
19
|
+
}
|
package/bun.d.ts
CHANGED
|
@@ -183,6 +183,8 @@ declare module "bun" {
|
|
|
183
183
|
* ```
|
|
184
184
|
*/
|
|
185
185
|
blob(): Blob;
|
|
186
|
+
|
|
187
|
+
bytes(): Uint8Array;
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
class ShellPromise extends Promise<ShellOutput> {
|
|
@@ -2752,8 +2754,230 @@ declare module "bun" {
|
|
|
2752
2754
|
logs: Array<BuildMessage | ResolveMessage>;
|
|
2753
2755
|
}
|
|
2754
2756
|
|
|
2757
|
+
/**
|
|
2758
|
+
* Bundles JavaScript, TypeScript, CSS, HTML and other supported files into optimized outputs.
|
|
2759
|
+
*
|
|
2760
|
+
* @param {Object} config - Build configuration options
|
|
2761
|
+
* @returns {Promise<BuildOutput>} Promise that resolves to build output containing generated artifacts and build status
|
|
2762
|
+
* @throws {AggregateError} When build fails and config.throw is true (default in Bun 1.2+)
|
|
2763
|
+
*
|
|
2764
|
+
* @example Basic usage - Bundle a single entrypoint and check results
|
|
2765
|
+
```ts
|
|
2766
|
+
const result = await Bun.build({
|
|
2767
|
+
entrypoints: ['./src/index.tsx'],
|
|
2768
|
+
outdir: './dist'
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2771
|
+
if (!result.success) {
|
|
2772
|
+
console.error('Build failed:', result.logs);
|
|
2773
|
+
process.exit(1);
|
|
2774
|
+
}
|
|
2775
|
+
```
|
|
2776
|
+
*
|
|
2777
|
+
* @example Set up multiple entrypoints with code splitting enabled
|
|
2778
|
+
```ts
|
|
2779
|
+
await Bun.build({
|
|
2780
|
+
entrypoints: ['./src/app.tsx', './src/admin.tsx'],
|
|
2781
|
+
outdir: './dist',
|
|
2782
|
+
splitting: true,
|
|
2783
|
+
sourcemap: "external"
|
|
2784
|
+
});
|
|
2785
|
+
```
|
|
2786
|
+
*
|
|
2787
|
+
* @example Configure minification and optimization settings
|
|
2788
|
+
```ts
|
|
2789
|
+
await Bun.build({
|
|
2790
|
+
entrypoints: ['./src/index.tsx'],
|
|
2791
|
+
outdir: './dist',
|
|
2792
|
+
minify: {
|
|
2793
|
+
whitespace: true,
|
|
2794
|
+
identifiers: true,
|
|
2795
|
+
syntax: true
|
|
2796
|
+
},
|
|
2797
|
+
drop: ['console', 'debugger']
|
|
2798
|
+
});
|
|
2799
|
+
```
|
|
2800
|
+
*
|
|
2801
|
+
* @example Set up custom loaders and mark packages as external
|
|
2802
|
+
```ts
|
|
2803
|
+
await Bun.build({
|
|
2804
|
+
entrypoints: ['./src/index.tsx'],
|
|
2805
|
+
outdir: './dist',
|
|
2806
|
+
loader: {
|
|
2807
|
+
'.png': 'dataurl',
|
|
2808
|
+
'.svg': 'file',
|
|
2809
|
+
'.txt': 'text',
|
|
2810
|
+
'.json': 'json'
|
|
2811
|
+
},
|
|
2812
|
+
external: ['react', 'react-dom']
|
|
2813
|
+
});
|
|
2814
|
+
```
|
|
2815
|
+
*
|
|
2816
|
+
* @example Configure environment variable handling with different modes
|
|
2817
|
+
```ts
|
|
2818
|
+
// Inline all environment variables
|
|
2819
|
+
await Bun.build({
|
|
2820
|
+
entrypoints: ['./src/index.tsx'],
|
|
2821
|
+
outdir: './dist',
|
|
2822
|
+
env: 'inline'
|
|
2823
|
+
});
|
|
2824
|
+
|
|
2825
|
+
// Only include specific env vars
|
|
2826
|
+
await Bun.build({
|
|
2827
|
+
entrypoints: ['./src/index.tsx'],
|
|
2828
|
+
outdir: './dist',
|
|
2829
|
+
env: 'PUBLIC_*'
|
|
2830
|
+
});
|
|
2831
|
+
```
|
|
2832
|
+
*
|
|
2833
|
+
* @example Set up custom naming patterns for all output types
|
|
2834
|
+
```ts
|
|
2835
|
+
await Bun.build({
|
|
2836
|
+
entrypoints: ['./src/index.tsx'],
|
|
2837
|
+
outdir: './dist',
|
|
2838
|
+
naming: {
|
|
2839
|
+
entry: '[dir]/[name]-[hash].[ext]',
|
|
2840
|
+
chunk: 'chunks/[name]-[hash].[ext]',
|
|
2841
|
+
asset: 'assets/[name]-[hash].[ext]'
|
|
2842
|
+
}
|
|
2843
|
+
});
|
|
2844
|
+
```
|
|
2845
|
+
@example Work with build artifacts in different formats
|
|
2846
|
+
```ts
|
|
2847
|
+
const result = await Bun.build({
|
|
2848
|
+
entrypoints: ['./src/index.tsx']
|
|
2849
|
+
});
|
|
2850
|
+
|
|
2851
|
+
for (const artifact of result.outputs) {
|
|
2852
|
+
const text = await artifact.text();
|
|
2853
|
+
const buffer = await artifact.arrayBuffer();
|
|
2854
|
+
const bytes = await artifact.bytes();
|
|
2855
|
+
|
|
2856
|
+
new Response(artifact);
|
|
2857
|
+
await Bun.write(artifact.path, artifact);
|
|
2858
|
+
}
|
|
2859
|
+
```
|
|
2860
|
+
@example Implement comprehensive error handling with position info
|
|
2861
|
+
```ts
|
|
2862
|
+
try {
|
|
2863
|
+
const result = await Bun.build({
|
|
2864
|
+
entrypoints: ['./src/index.tsx'],
|
|
2865
|
+
throw: true
|
|
2866
|
+
});
|
|
2867
|
+
} catch (e) {
|
|
2868
|
+
const error = e as AggregateError;
|
|
2869
|
+
console.error('Build failed:');
|
|
2870
|
+
for (const msg of error.errors) {
|
|
2871
|
+
if ('position' in msg) {
|
|
2872
|
+
console.error(
|
|
2873
|
+
`${msg.message} at ${msg.position?.file}:${msg.position?.line}:${msg.position?.column}`
|
|
2874
|
+
);
|
|
2875
|
+
} else {
|
|
2876
|
+
console.error(msg.message);
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
```
|
|
2881
|
+
@example Set up Node.js target with specific configurations
|
|
2882
|
+
```ts
|
|
2883
|
+
await Bun.build({
|
|
2884
|
+
entrypoints: ['./src/server.ts'],
|
|
2885
|
+
outdir: './dist',
|
|
2886
|
+
target: 'node',
|
|
2887
|
+
format: 'cjs',
|
|
2888
|
+
sourcemap: 'external',
|
|
2889
|
+
minify: false,
|
|
2890
|
+
packages: 'external'
|
|
2891
|
+
});
|
|
2892
|
+
```
|
|
2893
|
+
*
|
|
2894
|
+
* @example Configure experimental CSS bundling with multiple themes
|
|
2895
|
+
```ts
|
|
2896
|
+
await Bun.build({
|
|
2897
|
+
entrypoints: [
|
|
2898
|
+
'./src/styles.css',
|
|
2899
|
+
'./src/themes/dark.css',
|
|
2900
|
+
'./src/themes/light.css'
|
|
2901
|
+
],
|
|
2902
|
+
outdir: './dist/css',
|
|
2903
|
+
experimentalCss: true
|
|
2904
|
+
});
|
|
2905
|
+
```
|
|
2906
|
+
@example Define compile-time constants and version information
|
|
2907
|
+
```ts
|
|
2908
|
+
await Bun.build({
|
|
2909
|
+
entrypoints: ['./src/index.tsx'],
|
|
2910
|
+
outdir: './dist',
|
|
2911
|
+
define: {
|
|
2912
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
2913
|
+
'CONSTANTS.VERSION': JSON.stringify('1.0.0'),
|
|
2914
|
+
'CONSTANTS.BUILD_TIME': JSON.stringify(new Date().toISOString())
|
|
2915
|
+
}
|
|
2916
|
+
});
|
|
2917
|
+
```
|
|
2918
|
+
@example Create a custom plugin for handling special file types
|
|
2919
|
+
```ts
|
|
2920
|
+
await Bun.build({
|
|
2921
|
+
entrypoints: ['./src/index.tsx'],
|
|
2922
|
+
outdir: './dist',
|
|
2923
|
+
plugins: [
|
|
2924
|
+
{
|
|
2925
|
+
name: 'my-plugin',
|
|
2926
|
+
setup(build) {
|
|
2927
|
+
build.onLoad({ filter: /\.custom$/ }, async (args) => {
|
|
2928
|
+
const content = await Bun.file(args.path).text();
|
|
2929
|
+
return {
|
|
2930
|
+
contents: `export default ${JSON.stringify(content)}`,
|
|
2931
|
+
loader: 'js'
|
|
2932
|
+
};
|
|
2933
|
+
});
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
]
|
|
2937
|
+
});
|
|
2938
|
+
```
|
|
2939
|
+
@example Enable bytecode generation for faster startup
|
|
2940
|
+
```ts
|
|
2941
|
+
await Bun.build({
|
|
2942
|
+
entrypoints: ['./src/server.ts'],
|
|
2943
|
+
outdir: './dist',
|
|
2944
|
+
target: 'bun',
|
|
2945
|
+
format: 'cjs',
|
|
2946
|
+
bytecode: true
|
|
2947
|
+
});
|
|
2948
|
+
```
|
|
2949
|
+
@example Add custom banner and footer to output files
|
|
2950
|
+
```ts
|
|
2951
|
+
await Bun.build({
|
|
2952
|
+
entrypoints: ['./src/index.tsx'],
|
|
2953
|
+
outdir: './dist',
|
|
2954
|
+
banner: '"use client";\n// Built with Bun',
|
|
2955
|
+
footer: '// Generated on ' + new Date().toISOString()
|
|
2956
|
+
});
|
|
2957
|
+
```
|
|
2958
|
+
@example Configure CDN public path for asset loading
|
|
2959
|
+
```ts
|
|
2960
|
+
await Bun.build({
|
|
2961
|
+
entrypoints: ['./src/index.tsx'],
|
|
2962
|
+
outdir: './dist',
|
|
2963
|
+
publicPath: 'https://cdn.example.com/assets/',
|
|
2964
|
+
loader: {
|
|
2965
|
+
'.png': 'file',
|
|
2966
|
+
'.svg': 'file'
|
|
2967
|
+
}
|
|
2968
|
+
});
|
|
2969
|
+
```
|
|
2970
|
+
@example Set up package export conditions for different environments
|
|
2971
|
+
```ts
|
|
2972
|
+
await Bun.build({
|
|
2973
|
+
entrypoints: ['./src/index.tsx'],
|
|
2974
|
+
outdir: './dist',
|
|
2975
|
+
conditions: ['production', 'browser', 'module'],
|
|
2976
|
+
packages: 'external'
|
|
2977
|
+
});
|
|
2978
|
+
```
|
|
2979
|
+
*/
|
|
2755
2980
|
function build(config: BuildConfig): Promise<BuildOutput>;
|
|
2756
|
-
|
|
2757
2981
|
/**
|
|
2758
2982
|
* A status that represents the outcome of a sent message.
|
|
2759
2983
|
*
|
|
@@ -6682,9 +6906,72 @@ declare module "bun" {
|
|
|
6682
6906
|
*/
|
|
6683
6907
|
timestamp?: number | Date,
|
|
6684
6908
|
): Buffer;
|
|
6685
|
-
}
|
|
6686
6909
|
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6910
|
+
/**
|
|
6911
|
+
* Types for `bun.lock`
|
|
6912
|
+
*/
|
|
6913
|
+
type BunLockFile = {
|
|
6914
|
+
lockfileVersion: 0;
|
|
6915
|
+
workspaces: {
|
|
6916
|
+
[workspace: string]: BunLockFileWorkspacePackage;
|
|
6917
|
+
};
|
|
6918
|
+
overrides?: Record<string, string>;
|
|
6919
|
+
patchedDependencies?: Record<string, string>;
|
|
6920
|
+
trustedDependencies?: string[];
|
|
6921
|
+
|
|
6922
|
+
/**
|
|
6923
|
+
* ```
|
|
6924
|
+
* INFO = { prod/dev/optional/peer dependencies, os, cpu, libc (TODO), bin, binDir }
|
|
6925
|
+
*
|
|
6926
|
+
* npm -> [ "name@version", registry (TODO: remove if default), INFO, integrity]
|
|
6927
|
+
* symlink -> [ "name@link:path", INFO ]
|
|
6928
|
+
* folder -> [ "name@file:path", INFO ]
|
|
6929
|
+
* workspace -> [ "name@workspace:path", INFO ]
|
|
6930
|
+
* tarball -> [ "name@tarball", INFO ]
|
|
6931
|
+
* root -> [ "name@root:", { bin, binDir } ]
|
|
6932
|
+
* git -> [ "name@git+repo", INFO, .bun-tag string (TODO: remove this) ]
|
|
6933
|
+
* github -> [ "name@github:user/repo", INFO, .bun-tag string (TODO: remove this) ]
|
|
6934
|
+
* ```
|
|
6935
|
+
* */
|
|
6936
|
+
packages: {
|
|
6937
|
+
[pkg: string]: BunLockFilePackageArray;
|
|
6938
|
+
};
|
|
6939
|
+
};
|
|
6940
|
+
|
|
6941
|
+
type BunLockFileBasePackageInfo = {
|
|
6942
|
+
dependencies?: Record<string, string>;
|
|
6943
|
+
devDependencies?: Record<string, string>;
|
|
6944
|
+
optionalDependencies?: Record<string, string>;
|
|
6945
|
+
peerDependencies?: Record<string, string>;
|
|
6946
|
+
optionalPeers?: string[];
|
|
6947
|
+
};
|
|
6948
|
+
|
|
6949
|
+
type BunLockFileWorkspacePackage = BunLockFileBasePackageInfo & {
|
|
6950
|
+
name?: string;
|
|
6951
|
+
version?: string;
|
|
6952
|
+
};
|
|
6953
|
+
|
|
6954
|
+
type BunLockFilePackageInfo = BunLockFileBasePackageInfo & {
|
|
6955
|
+
os?: string | string[];
|
|
6956
|
+
cpu?: string | string[];
|
|
6957
|
+
bin?: Record<string, string>;
|
|
6958
|
+
binDir?: string;
|
|
6959
|
+
bundled?: true;
|
|
6960
|
+
};
|
|
6961
|
+
|
|
6962
|
+
/** @see {@link BunLockFile.packages} for more info */
|
|
6963
|
+
type BunLockFilePackageArray =
|
|
6964
|
+
/** npm */
|
|
6965
|
+
| [
|
|
6966
|
+
pkg: string,
|
|
6967
|
+
registry: string,
|
|
6968
|
+
info: BunLockFilePackageInfo,
|
|
6969
|
+
integrity: string,
|
|
6970
|
+
]
|
|
6971
|
+
/** symlink, folder, tarball, workspace */
|
|
6972
|
+
| [pkg: string, info: BunLockFilePackageInfo]
|
|
6973
|
+
/** git, github */
|
|
6974
|
+
| [pkg: string, info: BunLockFilePackageInfo, bunTag: string]
|
|
6975
|
+
/** root */
|
|
6976
|
+
| [pkg: string, info: Pick<BunLockFilePackageInfo, "bin" | "binDir">];
|
|
6690
6977
|
}
|
package/docs/api/binary-data.md
CHANGED
|
@@ -235,7 +235,7 @@ The following classes are typed arrays, along with a description of how they int
|
|
|
235
235
|
---
|
|
236
236
|
|
|
237
237
|
- [`BigInt64Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt64Array)
|
|
238
|
-
- Every eight (8) bytes are interpreted as
|
|
238
|
+
- Every eight (8) bytes are interpreted as a signed `BigInt`. Range -9223372036854775808 to 9223372036854775807 (though `BigInt` is capable of representing larger numbers).
|
|
239
239
|
|
|
240
240
|
---
|
|
241
241
|
|
package/docs/api/fetch.md
CHANGED
|
@@ -195,7 +195,7 @@ This will print the request and response headers to your terminal:
|
|
|
195
195
|
```sh
|
|
196
196
|
[fetch] > HTTP/1.1 GET http://example.com/
|
|
197
197
|
[fetch] > Connection: keep-alive
|
|
198
|
-
[fetch] > User-Agent: Bun/1.1.
|
|
198
|
+
[fetch] > User-Agent: Bun/1.1.44-canary.20250113T140648
|
|
199
199
|
[fetch] > Accept: */*
|
|
200
200
|
[fetch] > Host: example.com
|
|
201
201
|
[fetch] > Accept-Encoding: gzip, deflate, br
|
|
@@ -1,31 +1,334 @@
|
|
|
1
|
-
|
|
1
|
+
HTMLRewriter lets you use CSS selectors to transform HTML documents. It works with `Request`, `Response`, as well as `string`. Bun's implementation is based on Cloudflare's [lol-html](https://github.com/cloudflare/lol-html).
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
A common usecase is rewriting URLs in HTML content. Here's an example that rewrites image sources and link URLs to use a CDN domain:
|
|
2
6
|
|
|
3
7
|
```ts
|
|
4
|
-
|
|
8
|
+
// Replace all images with a rickroll
|
|
9
|
+
const rewriter = new HTMLRewriter().on("img", {
|
|
10
|
+
element(img) {
|
|
11
|
+
// Famous rickroll video thumbnail
|
|
12
|
+
img.setAttribute(
|
|
13
|
+
"src",
|
|
14
|
+
"https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
|
|
15
|
+
);
|
|
5
16
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
17
|
+
// Wrap the image in a link to the video
|
|
18
|
+
img.before(
|
|
19
|
+
'<a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">',
|
|
20
|
+
{ html: true },
|
|
21
|
+
);
|
|
22
|
+
img.after("</a>", { html: true });
|
|
23
|
+
|
|
24
|
+
// Add some fun alt text
|
|
25
|
+
img.setAttribute("alt", "Definitely not a rickroll");
|
|
9
26
|
},
|
|
10
27
|
});
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
To parse and/or transform the HTML:
|
|
14
28
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
new Response(`
|
|
18
|
-
<!DOCTYPE html>
|
|
29
|
+
// An example HTML document
|
|
30
|
+
const html = `
|
|
19
31
|
<html>
|
|
20
|
-
<!-- comment -->
|
|
21
|
-
<head>
|
|
22
|
-
<title>My First HTML Page</title>
|
|
23
|
-
</head>
|
|
24
32
|
<body>
|
|
25
|
-
<
|
|
26
|
-
<
|
|
33
|
+
<img src="/cat.jpg">
|
|
34
|
+
<img src="dog.png">
|
|
35
|
+
<img src="https://example.com/bird.webp">
|
|
27
36
|
</body>
|
|
28
|
-
|
|
37
|
+
</html>
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const result = rewriter.transform(html);
|
|
41
|
+
console.log(result);
|
|
29
42
|
```
|
|
30
43
|
|
|
31
|
-
|
|
44
|
+
This replaces all images with a thumbnail of Rick Astley and wraps each `<img>` in a link, producing a diff like this:
|
|
45
|
+
|
|
46
|
+
```html-diff
|
|
47
|
+
<html>
|
|
48
|
+
<body>
|
|
49
|
+
- <img src="/cat.jpg">
|
|
50
|
+
- <img src="dog.png">
|
|
51
|
+
- <img src="https://example.com/bird.webp">
|
|
52
|
+
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
|
53
|
+
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
|
54
|
+
+ </a>
|
|
55
|
+
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
|
56
|
+
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
|
57
|
+
+ </a>
|
|
58
|
+
+ <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" target="_blank">
|
|
59
|
+
+ <img src="https://img.youtube.com/vi/dQw4w9WgXcQ/maxresdefault.jpg" alt="Definitely not a rickroll">
|
|
60
|
+
+ </a>
|
|
61
|
+
</body>
|
|
62
|
+
</html>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Now every image on the page will be replaced with a thumbnail of Rick Astley, and clicking any image will lead to [a very famous video](https://www.youtube.com/watch?v=dQw4w9WgXcQ).
|
|
66
|
+
|
|
67
|
+
### Input types
|
|
68
|
+
|
|
69
|
+
HTMLRewriter can transform HTML from various sources. The input is automatically handled based on its type:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// From Response
|
|
73
|
+
rewriter.transform(new Response("<div>content</div>"));
|
|
74
|
+
|
|
75
|
+
// From string
|
|
76
|
+
rewriter.transform("<div>content</div>");
|
|
77
|
+
|
|
78
|
+
// From ArrayBuffer
|
|
79
|
+
rewriter.transform(new TextEncoder().encode("<div>content</div>").buffer);
|
|
80
|
+
|
|
81
|
+
// From Blob
|
|
82
|
+
rewriter.transform(new Blob(["<div>content</div>"]));
|
|
83
|
+
|
|
84
|
+
// From File
|
|
85
|
+
rewriter.transform(Bun.file("index.html"));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Note that Cloudflare Workers implementation of HTMLRewriter only supports `Response` objects.
|
|
89
|
+
|
|
90
|
+
### Element Handlers
|
|
91
|
+
|
|
92
|
+
The `on(selector, handlers)` method allows you to register handlers for HTML elements that match a CSS selector. The handlers are called for each matching element during parsing:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
rewriter.on("div.content", {
|
|
96
|
+
// Handle elements
|
|
97
|
+
element(element) {
|
|
98
|
+
element.setAttribute("class", "new-content");
|
|
99
|
+
element.append("<p>New content</p>", { html: true });
|
|
100
|
+
},
|
|
101
|
+
// Handle text nodes
|
|
102
|
+
text(text) {
|
|
103
|
+
text.replace("new text");
|
|
104
|
+
},
|
|
105
|
+
// Handle comments
|
|
106
|
+
comments(comment) {
|
|
107
|
+
comment.remove();
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The handlers can be asynchronous and return a Promise. Note that async operations will block the transformation until they complete:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
rewriter.on("div", {
|
|
116
|
+
async element(element) {
|
|
117
|
+
await Bun.sleep(1000);
|
|
118
|
+
element.setInnerContent("<span>replace</span>", { html: true });
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### CSS Selector Support
|
|
124
|
+
|
|
125
|
+
The `on()` method supports a wide range of CSS selectors:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// Tag selectors
|
|
129
|
+
rewriter.on("p", handler);
|
|
130
|
+
|
|
131
|
+
// Class selectors
|
|
132
|
+
rewriter.on("p.red", handler);
|
|
133
|
+
|
|
134
|
+
// ID selectors
|
|
135
|
+
rewriter.on("h1#header", handler);
|
|
136
|
+
|
|
137
|
+
// Attribute selectors
|
|
138
|
+
rewriter.on("p[data-test]", handler); // Has attribute
|
|
139
|
+
rewriter.on('p[data-test="one"]', handler); // Exact match
|
|
140
|
+
rewriter.on('p[data-test="one" i]', handler); // Case-insensitive
|
|
141
|
+
rewriter.on('p[data-test="one" s]', handler); // Case-sensitive
|
|
142
|
+
rewriter.on('p[data-test~="two"]', handler); // Word match
|
|
143
|
+
rewriter.on('p[data-test^="a"]', handler); // Starts with
|
|
144
|
+
rewriter.on('p[data-test$="1"]', handler); // Ends with
|
|
145
|
+
rewriter.on('p[data-test*="b"]', handler); // Contains
|
|
146
|
+
rewriter.on('p[data-test|="a"]', handler); // Dash-separated
|
|
147
|
+
|
|
148
|
+
// Combinators
|
|
149
|
+
rewriter.on("div span", handler); // Descendant
|
|
150
|
+
rewriter.on("div > span", handler); // Direct child
|
|
151
|
+
|
|
152
|
+
// Pseudo-classes
|
|
153
|
+
rewriter.on("p:nth-child(2)", handler);
|
|
154
|
+
rewriter.on("p:first-child", handler);
|
|
155
|
+
rewriter.on("p:nth-of-type(2)", handler);
|
|
156
|
+
rewriter.on("p:first-of-type", handler);
|
|
157
|
+
rewriter.on("p:not(:first-child)", handler);
|
|
158
|
+
|
|
159
|
+
// Universal selector
|
|
160
|
+
rewriter.on("*", handler);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Element Operations
|
|
164
|
+
|
|
165
|
+
Elements provide various methods for manipulation. All modification methods return the element instance for chaining:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
rewriter.on("div", {
|
|
169
|
+
element(el) {
|
|
170
|
+
// Attributes
|
|
171
|
+
el.setAttribute("class", "new-class").setAttribute("data-id", "123");
|
|
172
|
+
|
|
173
|
+
const classAttr = el.getAttribute("class"); // "new-class"
|
|
174
|
+
const hasId = el.hasAttribute("id"); // boolean
|
|
175
|
+
el.removeAttribute("class");
|
|
176
|
+
|
|
177
|
+
// Content manipulation
|
|
178
|
+
el.setInnerContent("New content"); // Escapes HTML by default
|
|
179
|
+
el.setInnerContent("<p>HTML content</p>", { html: true }); // Parses HTML
|
|
180
|
+
el.setInnerContent(""); // Clear content
|
|
181
|
+
|
|
182
|
+
// Position manipulation
|
|
183
|
+
el.before("Content before")
|
|
184
|
+
.after("Content after")
|
|
185
|
+
.prepend("First child")
|
|
186
|
+
.append("Last child");
|
|
187
|
+
|
|
188
|
+
// HTML content insertion
|
|
189
|
+
el.before("<span>before</span>", { html: true })
|
|
190
|
+
.after("<span>after</span>", { html: true })
|
|
191
|
+
.prepend("<span>first</span>", { html: true })
|
|
192
|
+
.append("<span>last</span>", { html: true });
|
|
193
|
+
|
|
194
|
+
// Removal
|
|
195
|
+
el.remove(); // Remove element and contents
|
|
196
|
+
el.removeAndKeepContent(); // Remove only the element tags
|
|
197
|
+
|
|
198
|
+
// Properties
|
|
199
|
+
console.log(el.tagName); // Lowercase tag name
|
|
200
|
+
console.log(el.namespaceURI); // Element's namespace URI
|
|
201
|
+
console.log(el.selfClosing); // Whether element is self-closing (e.g. <div />)
|
|
202
|
+
console.log(el.canHaveContent); // Whether element can contain content (false for void elements like <br>)
|
|
203
|
+
console.log(el.removed); // Whether element was removed
|
|
204
|
+
|
|
205
|
+
// Attributes iteration
|
|
206
|
+
for (const [name, value] of el.attributes) {
|
|
207
|
+
console.log(name, value);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// End tag handling
|
|
211
|
+
el.onEndTag(endTag => {
|
|
212
|
+
endTag.before("Before end tag");
|
|
213
|
+
endTag.after("After end tag");
|
|
214
|
+
endTag.remove(); // Remove the end tag
|
|
215
|
+
console.log(endTag.name); // Tag name in lowercase
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Text Operations
|
|
222
|
+
|
|
223
|
+
Text handlers provide methods for text manipulation. Text chunks represent portions of text content and provide information about their position in the text node:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
rewriter.on("p", {
|
|
227
|
+
text(text) {
|
|
228
|
+
// Content
|
|
229
|
+
console.log(text.text); // Text content
|
|
230
|
+
console.log(text.lastInTextNode); // Whether this is the last chunk
|
|
231
|
+
console.log(text.removed); // Whether text was removed
|
|
232
|
+
|
|
233
|
+
// Manipulation
|
|
234
|
+
text.before("Before text").after("After text").replace("New text").remove();
|
|
235
|
+
|
|
236
|
+
// HTML content insertion
|
|
237
|
+
text
|
|
238
|
+
.before("<span>before</span>", { html: true })
|
|
239
|
+
.after("<span>after</span>", { html: true })
|
|
240
|
+
.replace("<span>replace</span>", { html: true });
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Comment Operations
|
|
246
|
+
|
|
247
|
+
Comment handlers allow comment manipulation with similar methods to text nodes:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
rewriter.on("*", {
|
|
251
|
+
comments(comment) {
|
|
252
|
+
// Content
|
|
253
|
+
console.log(comment.text); // Comment text
|
|
254
|
+
comment.text = "New comment text"; // Set comment text
|
|
255
|
+
console.log(comment.removed); // Whether comment was removed
|
|
256
|
+
|
|
257
|
+
// Manipulation
|
|
258
|
+
comment
|
|
259
|
+
.before("Before comment")
|
|
260
|
+
.after("After comment")
|
|
261
|
+
.replace("New comment")
|
|
262
|
+
.remove();
|
|
263
|
+
|
|
264
|
+
// HTML content insertion
|
|
265
|
+
comment
|
|
266
|
+
.before("<span>before</span>", { html: true })
|
|
267
|
+
.after("<span>after</span>", { html: true })
|
|
268
|
+
.replace("<span>replace</span>", { html: true });
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Document Handlers
|
|
274
|
+
|
|
275
|
+
The `onDocument(handlers)` method allows you to handle document-level events. These handlers are called for events that occur at the document level rather than within specific elements:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
rewriter.onDocument({
|
|
279
|
+
// Handle doctype
|
|
280
|
+
doctype(doctype) {
|
|
281
|
+
console.log(doctype.name); // "html"
|
|
282
|
+
console.log(doctype.publicId); // public identifier if present
|
|
283
|
+
console.log(doctype.systemId); // system identifier if present
|
|
284
|
+
},
|
|
285
|
+
// Handle text nodes
|
|
286
|
+
text(text) {
|
|
287
|
+
console.log(text.text);
|
|
288
|
+
},
|
|
289
|
+
// Handle comments
|
|
290
|
+
comments(comment) {
|
|
291
|
+
console.log(comment.text);
|
|
292
|
+
},
|
|
293
|
+
// Handle document end
|
|
294
|
+
end(end) {
|
|
295
|
+
end.append("<!-- Footer -->", { html: true });
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Response Handling
|
|
301
|
+
|
|
302
|
+
When transforming a Response:
|
|
303
|
+
|
|
304
|
+
- The status code, headers, and other response properties are preserved
|
|
305
|
+
- The body is transformed while maintaining streaming capabilities
|
|
306
|
+
- Content-encoding (like gzip) is handled automatically
|
|
307
|
+
- The original response body is marked as used after transformation
|
|
308
|
+
- Headers are cloned to the new response
|
|
309
|
+
|
|
310
|
+
## Error Handling
|
|
311
|
+
|
|
312
|
+
HTMLRewriter operations can throw errors in several cases:
|
|
313
|
+
|
|
314
|
+
- Invalid selector syntax in `on()` method
|
|
315
|
+
- Invalid HTML content in transformation methods
|
|
316
|
+
- Stream errors when processing Response bodies
|
|
317
|
+
- Memory allocation failures
|
|
318
|
+
- Invalid input types (e.g., passing Symbol)
|
|
319
|
+
- Body already used errors
|
|
320
|
+
|
|
321
|
+
Errors should be caught and handled appropriately:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
try {
|
|
325
|
+
const result = rewriter.transform(input);
|
|
326
|
+
// Process result
|
|
327
|
+
} catch (error) {
|
|
328
|
+
console.error("HTMLRewriter error:", error);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## See also
|
|
333
|
+
|
|
334
|
+
You can also read the [Cloudflare documentation](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/), which this API is intended to be compatible with.
|