satoru-render 0.0.1 → 0.0.3
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/README.md +113 -134
- package/dist/core.d.ts +3 -2
- package/dist/core.js +25 -6
- package/dist/satoru-single.js +0 -0
- package/dist/satoru.js +1 -1
- package/dist/satoru.wasm +0 -0
- package/dist/web-workers.js +378 -447
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,201 +1,180 @@
|
|
|
1
|
-
# Satoru
|
|
1
|
+
# Satoru Render: High-Performance HTML to Image/PDF Engine
|
|
2
2
|
|
|
3
3
|
[](https://sorakumo001.github.io/satoru/)
|
|
4
|
+
[](https://www.npmjs.com/package/satoru-render)
|
|
5
|
+
[](https://www.npmjs.com/package/satoru-render)
|
|
6
|
+
[](https://www.npmjs.com/package/satoru-render)
|
|
7
|
+
[](https://deepwiki.com/SoraKumo001/satoru)
|
|
4
8
|
|
|
5
|
-
**Satoru** is a
|
|
9
|
+
**Satoru Render** is a high-fidelity HTML-to-Image/PDF conversion engine built with WebAssembly. It provides a lightweight, dependency-free solution for generating high-quality visuals and documents across **Node.js**, **Cloudflare Workers**, **Deno**, and **Web Browsers**.
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
By combining the **Skia** graphics engine with a custom **litehtml** layout core, Satoru performs all layout and drawing operations entirely within WASM, eliminating the need for headless browsers or system-level dependencies.
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+

|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
---
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
- **Edge Native**: Specialized wrapper for Cloudflare Workers ensures smooth execution in restricted environments.
|
|
15
|
-
- **Triple Output Modes**:
|
|
16
|
-
- **SVG**: Generates lean, vector-based Pure SVG strings with post-processed effects (Filters, Gradients).
|
|
17
|
-
- **PNG**: Generates high-quality raster images via Skia, transferred as binary data for maximum performance.
|
|
18
|
-
- **PDF**: Generates high-fidelity vector documents via Skia's PDF backend, including native support for text, gradients, images, and **multi-page output**.
|
|
19
|
-
- **High-Level TS Wrapper**: Includes a `Satoru` class that abstracts Wasm memory management and provides a clean async API.
|
|
20
|
-
- **Dynamic Font Loading**: Supports loading `.ttf` / `.woff2` / `.ttc` files at runtime with automatic weight/style inference.
|
|
21
|
-
- **Japanese Support**: Full support for Japanese rendering with multi-font fallback logic.
|
|
22
|
-
- **Image Format Support**: Native support for **PNG**, **JPEG**, **WebP**, **AVIF**, **GIF**, **BMP**, and **ICO** image formats.
|
|
23
|
-
- **Advanced CSS Support**:
|
|
24
|
-
- **Box Model**: Margin, padding, border, and accurate **Border Radius**.
|
|
25
|
-
- **Box Shadow**: High-quality **Outer** and **Inset** shadows using advanced SVG filters (SVG) or Skia blurs (PNG/PDF).
|
|
26
|
-
- **Gradients**: Linear, **Elliptical Radial**, and **Conic** (Sweep) gradient support.
|
|
27
|
-
- **Standard Tags**: Full support for `<b>`, `<strong>`, `<i>`, `<u>`, and `<h1>`-`<h6>` via integrated master CSS.
|
|
28
|
-
- **Text Decoration**: Supports `underline`, `line-through`, `overline` with `solid`, `dotted`, and `dashed` styles.
|
|
29
|
-
- **Text Shadow**: Multiple shadows with blur, offset, and color support (PNG/SVG/PDF).
|
|
17
|
+
## ✨ Key Features
|
|
30
18
|
|
|
31
|
-
|
|
19
|
+
- **Pure WebAssembly Engine**: 100% independent of browser DOM or `<canvas>`. Runs anywhere WASM is supported.
|
|
20
|
+
- **Edge Native**: Specifically optimized for **Cloudflare Workers (workerd)** and other serverless environments.
|
|
21
|
+
- **Professional Graphics**:
|
|
22
|
+
- **SVG**: Lean, vector-based strings with post-processed filters and gradients.
|
|
23
|
+
- **PNG / WebP**: High-performance raster images with advanced Skia rendering.
|
|
24
|
+
- **PDF**: Multi-page vector documents with native text and gradient support.
|
|
25
|
+
- **Advanced CSS Capabilities**:
|
|
26
|
+
- **Box Model**: Precise margin, padding, borders, and **Border Radius**.
|
|
27
|
+
- **Shadows**: High-quality **Outer** and **Inset** shadows (using SVG filters or Skia blurs).
|
|
28
|
+
- **Gradients**: Linear, **Elliptical Radial**, and **Conic** (Sweep) support.
|
|
29
|
+
- **Text Styling**: Multi-shadows, decorations (solid/dotted/dashed), and automatic weight/style inference.
|
|
30
|
+
- **Internationalization**: Robust support for complex text layouts, including **Japanese** and multi-font fallback logic.
|
|
31
|
+
- **Smart Resource Management**: Dynamic loading of `.ttf`, `.woff2`, and `.ttc` fonts, plus native support for all major image formats (AVIF, WebP, PNG, JPEG, etc.).
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
---
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
## 📦 Installation
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
```bash
|
|
38
|
+
npm install satoru-render
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🚀 Quick Start
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
### Basic Usage (TypeScript)
|
|
46
|
+
|
|
47
|
+
The `render` function is the primary entry point. It handles WASM instantiation, resource resolution, and conversion in a single call.
|
|
40
48
|
|
|
41
49
|
```typescript
|
|
42
|
-
import { render
|
|
50
|
+
import { render } from "satoru-render";
|
|
43
51
|
|
|
44
52
|
const html = `
|
|
45
|
-
<style>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
src: url('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu4mxK.woff2');
|
|
49
|
-
}
|
|
50
|
-
</style>
|
|
51
|
-
<div style="font-family: 'Roboto'; color: #2196F3; font-size: 40px;">
|
|
52
|
-
Hello Satoru!
|
|
53
|
-
<img src="logo.png" style="width: 50px;">
|
|
53
|
+
<div style="padding: 40px; background: #f8f9fa; border-radius: 12px; border: 2px solid #dee2e6;">
|
|
54
|
+
<h1 style="color: #007bff; font-family: sans-serif;">Hello Satoru!</h1>
|
|
55
|
+
<p style="color: #495057;">This document was rendered entirely in WebAssembly.</p>
|
|
54
56
|
</div>
|
|
55
57
|
`;
|
|
56
58
|
|
|
57
|
-
// Render to
|
|
58
|
-
const
|
|
59
|
+
// Render to PNG
|
|
60
|
+
const png = await render({
|
|
59
61
|
value: html,
|
|
60
62
|
width: 600,
|
|
61
|
-
format: "pdf",
|
|
62
|
-
baseUrl: "https://example.com/assets/", // Optional: resolve relative URLs
|
|
63
|
-
logLevel: LogLevel.Info,
|
|
64
|
-
resolveResource: async (resource, defaultResolver) => {
|
|
65
|
-
if (resource.url.startsWith("custom://")) {
|
|
66
|
-
return myCustomData;
|
|
67
|
-
}
|
|
68
|
-
return defaultResolver(resource);
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Render from a URL directly
|
|
73
|
-
const png = await render({
|
|
74
|
-
url: "https://example.com/page.html",
|
|
75
|
-
width: 1024,
|
|
76
63
|
format: "png",
|
|
77
64
|
});
|
|
78
65
|
```
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
| Option | Type | Description |
|
|
83
|
-
| ----------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
84
|
-
| `value` | `string \| string[]` | HTML string or array of HTML strings. (One of `value` or `url` is required) |
|
|
85
|
-
| `url` | `string` | URL to fetch HTML from. (One of `value` or `url` is required) |
|
|
86
|
-
| `width` | `number` | **Required.** Width of the output in pixels. |
|
|
87
|
-
| `height` | `number` | Height of the output in pixels. Default is `0` (automatic height). |
|
|
88
|
-
| `format` | `"svg" \| "png" \| "webp" \| "pdf"` | Output format. Default is `"svg"`. |
|
|
89
|
-
| `textToPaths` | `boolean` | Whether to convert SVG text to paths. Default is `true`. |
|
|
90
|
-
| `resolveResource` | `ResourceResolver` | Async callback to fetch missing fonts, images, or CSS. Returns `Uint8Array`, `ArrayBufferView` or `null`. It receives a second argument `defaultResolver` to fallback to the standard resolution logic. |
|
|
91
|
-
| `fonts` | `Object[]` | Array of `{ name, data: Uint8Array }` to pre-load fonts. |
|
|
92
|
-
| `images` | `Object[]` | Array of `{ name, url, width?, height? }` to pre-load images. |
|
|
93
|
-
| `css` | `string` | Extra CSS to inject into the rendering process. |
|
|
94
|
-
| `baseUrl` | `string` | Base URL used to resolve relative URLs in fonts, images, and links. |
|
|
95
|
-
| `userAgent` | `string` | User-Agent header for fetching resources (Node.js environment). |
|
|
96
|
-
| `logLevel` | `LogLevel` | Logging verbosity (`None`, `Error`, `Warning`, `Info`, `Debug`). |
|
|
97
|
-
| `onLog` | `(level, msg) => void` | Custom callback for receiving log messages. |
|
|
98
|
-
|
|
99
|
-
### 💻 CLI Usage
|
|
100
|
-
|
|
101
|
-
Satoru includes a command-line interface for easy conversion.
|
|
67
|
+
---
|
|
102
68
|
|
|
103
|
-
|
|
104
|
-
# Convert a local HTML file to PNG
|
|
105
|
-
npx satoru-render input.html -o output.png
|
|
69
|
+
## 🛠️ Advanced Usage
|
|
106
70
|
|
|
107
|
-
|
|
108
|
-
npx satoru-render https://example.com -o example.pdf -w 1280
|
|
71
|
+
### 1. Dynamic Resource Resolution
|
|
109
72
|
|
|
110
|
-
|
|
111
|
-
npx satoru-render input.html -w 1024 -f webp --verbose
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
#### CLI Options
|
|
73
|
+
Satoru can automatically fetch missing fonts, images, or external CSS via a `resolveResource` callback.
|
|
115
74
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
75
|
+
```typescript
|
|
76
|
+
const pdf = await render({
|
|
77
|
+
value: html,
|
|
78
|
+
width: 800,
|
|
79
|
+
format: "pdf",
|
|
80
|
+
baseUrl: "https://example.com/assets/",
|
|
81
|
+
resolveResource: async (resource, defaultResolver) => {
|
|
82
|
+
// Custom intercept logic
|
|
83
|
+
if (resource.url.startsWith("my-app://")) {
|
|
84
|
+
return myAssetBuffer;
|
|
85
|
+
}
|
|
86
|
+
// Fallback to default fetch/filesystem resolver
|
|
87
|
+
return defaultResolver(resource);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
123
91
|
|
|
124
|
-
###
|
|
92
|
+
### 2. Multi-page PDF Generation
|
|
125
93
|
|
|
126
|
-
|
|
94
|
+
Generate complex documents by passing an array of HTML strings. Each element in the array becomes a new page.
|
|
127
95
|
|
|
128
96
|
```typescript
|
|
129
|
-
const pdf = await
|
|
130
|
-
value: [
|
|
131
|
-
|
|
132
|
-
"<h1>Page 2</h1><p>Second page content.</p>",
|
|
133
|
-
"<h1>Page 3</h1><p>Third page content.</p>",
|
|
134
|
-
],
|
|
135
|
-
width: 600,
|
|
97
|
+
const pdf = await render({
|
|
98
|
+
value: ["<h1>Page One</h1>", "<h1>Page Two</h1>", "<h1>Page Three</h1>"],
|
|
99
|
+
width: 595, // A4 width in points
|
|
136
100
|
format: "pdf",
|
|
137
101
|
});
|
|
138
102
|
```
|
|
139
103
|
|
|
140
|
-
###
|
|
104
|
+
### 3. Edge/Cloudflare Workers
|
|
141
105
|
|
|
142
|
-
|
|
106
|
+
Use the specialized `workerd` export for serverless environments.
|
|
143
107
|
|
|
144
108
|
```typescript
|
|
145
109
|
import { render } from "satoru-render";
|
|
146
110
|
|
|
147
111
|
export default {
|
|
148
112
|
async fetch(request) {
|
|
149
|
-
const
|
|
150
|
-
value: "<h1>Edge
|
|
113
|
+
const png = await render({
|
|
114
|
+
value: "<h1>Edge Generated Image</h1>",
|
|
151
115
|
width: 800,
|
|
152
|
-
format: "
|
|
153
|
-
baseUrl: "https://example.com/",
|
|
116
|
+
format: "png",
|
|
154
117
|
});
|
|
155
118
|
|
|
156
|
-
return new Response(
|
|
157
|
-
headers: { "Content-Type": "application/pdf" },
|
|
158
|
-
});
|
|
119
|
+
return new Response(png, { headers: { "Content-Type": "image/png" } });
|
|
159
120
|
},
|
|
160
121
|
};
|
|
161
122
|
```
|
|
162
123
|
|
|
163
|
-
###
|
|
124
|
+
### 6. Multi-threaded Rendering (Worker Proxy)
|
|
164
125
|
|
|
165
|
-
|
|
126
|
+
Distribute rendering tasks across multiple background workers for high-throughput applications.
|
|
166
127
|
|
|
167
128
|
```typescript
|
|
168
|
-
import {
|
|
129
|
+
import { createSatoruWorker } from "satoru-render/workers";
|
|
169
130
|
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
131
|
+
const satoru = createSatoruWorker({ maxParallel: 4 });
|
|
132
|
+
|
|
133
|
+
const png = await satoru.render({
|
|
134
|
+
value: "<h1>Parallel Task</h1>",
|
|
135
|
+
width: 800,
|
|
173
136
|
format: "png",
|
|
174
137
|
});
|
|
175
138
|
```
|
|
176
139
|
|
|
177
|
-
|
|
140
|
+
---
|
|
178
141
|
|
|
179
|
-
|
|
142
|
+
## 💻 CLI Tool
|
|
180
143
|
|
|
181
|
-
|
|
182
|
-
import { createSatoruWorker, LogLevel } from "satoru-render/workers";
|
|
144
|
+
Convert files or URLs directly from your terminal.
|
|
183
145
|
|
|
184
|
-
|
|
185
|
-
|
|
146
|
+
```bash
|
|
147
|
+
# Local HTML to PNG
|
|
148
|
+
npx satoru-render input.html -o output.png
|
|
186
149
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
baseUrl: "https://example.com/assets/",
|
|
193
|
-
logLevel: LogLevel.Debug, // Enable debug logs for this task
|
|
194
|
-
fonts: [{ name: "CustomFont", data: fontData }], // Pre-load fonts
|
|
195
|
-
css: "h1 { color: red; }", // Inject extra CSS
|
|
196
|
-
});
|
|
150
|
+
# URL to PDF with specific width
|
|
151
|
+
npx satoru-render https://example.com -o site.pdf -w 1280
|
|
152
|
+
|
|
153
|
+
# WebP conversion with verbose logs
|
|
154
|
+
npx satoru-render input.html -f webp --verbose
|
|
197
155
|
```
|
|
198
156
|
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 📖 API Reference
|
|
160
|
+
|
|
161
|
+
### Render Options
|
|
162
|
+
|
|
163
|
+
| Option | Type | Description |
|
|
164
|
+
| :---------------- | :---------------------------------- | :------------------------------------------------------ |
|
|
165
|
+
| `value` | `string \| string[]` | HTML string or array of strings (for multi-page PDF). |
|
|
166
|
+
| `url` | `string` | URL to fetch HTML from. |
|
|
167
|
+
| `width` | `number` | **Required.** Output width in pixels. |
|
|
168
|
+
| `height` | `number` | Output height. Default: `0` (auto-calculate). |
|
|
169
|
+
| `format` | `"svg" \| "png" \| "webp" \| "pdf"` | Output format. Default: `"svg"`. |
|
|
170
|
+
| `resolveResource` | `ResourceResolver` | Async callback to fetch assets (fonts, images, CSS). |
|
|
171
|
+
| `fonts` | `Object[]` | Pre-load fonts: `[{ name, data: Uint8Array }]`. |
|
|
172
|
+
| `css` | `string` | Extra CSS to inject into the document. |
|
|
173
|
+
| `baseUrl` | `string` | Base URL for relative path resolution. |
|
|
174
|
+
| `logLevel` | `LogLevel` | Verbosity: `None`, `Error`, `Warning`, `Info`, `Debug`. |
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
199
178
|
## 📜 License
|
|
200
179
|
|
|
201
|
-
MIT License
|
|
180
|
+
This project is licensed under the **MIT License**.
|
package/dist/core.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { LogLevel } from "./log-level.js";
|
|
|
2
2
|
export interface SatoruModule {
|
|
3
3
|
create_instance: () => any;
|
|
4
4
|
destroy_instance: (inst: any) => void;
|
|
5
|
-
collect_resources: (inst: any, html: string, width: number) => void;
|
|
5
|
+
collect_resources: (inst: any, html: string, width: number, height: number) => void;
|
|
6
6
|
get_pending_resources: (inst: any) => string;
|
|
7
7
|
add_resource: (inst: any, url: string, type: number, data: Uint8Array) => void;
|
|
8
8
|
scan_css: (inst: any, css: string) => void;
|
|
@@ -10,7 +10,7 @@ export interface SatoruModule {
|
|
|
10
10
|
load_image: (inst: any, name: string, url: string, width: number, height: number) => void;
|
|
11
11
|
set_font_map: (inst: any, fontMap: Record<string, string>) => void;
|
|
12
12
|
set_log_level: (level: number) => void;
|
|
13
|
-
init_document: (inst: any, html: string, width: number) => void;
|
|
13
|
+
init_document: (inst: any, html: string, width: number, height: number) => void;
|
|
14
14
|
layout_document: (inst: any, width: number) => void;
|
|
15
15
|
render_from_state: (inst: any, width: number, height: number, format: number, svgTextToPaths: boolean) => Uint8Array | null;
|
|
16
16
|
render: (inst: any, htmls: string | string[], width: number, height: number, format: number, svgTextToPaths: boolean) => Uint8Array | null;
|
|
@@ -21,6 +21,7 @@ export interface RequiredResource {
|
|
|
21
21
|
type: "font" | "css" | "image";
|
|
22
22
|
url: string;
|
|
23
23
|
name: string;
|
|
24
|
+
characters?: string;
|
|
24
25
|
redraw_on_ready?: boolean;
|
|
25
26
|
}
|
|
26
27
|
export type ResourceResolver = (resource: RequiredResource, defaultResolver: (resource: RequiredResource) => Promise<Uint8Array | null>) => Promise<Uint8Array | ArrayBufferView | null>;
|
package/dist/core.js
CHANGED
|
@@ -15,6 +15,7 @@ export async function resolveGoogleFonts(resource, userAgent) {
|
|
|
15
15
|
return null;
|
|
16
16
|
const weight = urlObj.searchParams.get("weight") || "400";
|
|
17
17
|
const italic = urlObj.searchParams.get("italic") === "1";
|
|
18
|
+
const text = urlObj.searchParams.get("text") || resource.characters;
|
|
18
19
|
let targetFamily = family;
|
|
19
20
|
let forceNormalStyle = false;
|
|
20
21
|
if (targetFamily.includes("Noto Sans JP") ||
|
|
@@ -23,7 +24,14 @@ export async function resolveGoogleFonts(resource, userAgent) {
|
|
|
23
24
|
forceNormalStyle = true;
|
|
24
25
|
}
|
|
25
26
|
const useItalic = italic && !forceNormalStyle;
|
|
26
|
-
|
|
27
|
+
let googleFontUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(targetFamily)}:ital,wght@${useItalic ? "1" : "0"},${weight}&display=swap`;
|
|
28
|
+
if (text) {
|
|
29
|
+
// Google Fonts text parameter has a limit (around 1000 chars is usually safe for URL length)
|
|
30
|
+
// If it's too long, we fall back to full character set to avoid URL too long errors
|
|
31
|
+
if (text.length < 800) {
|
|
32
|
+
googleFontUrl += `&text=${encodeURIComponent(text)}`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
27
35
|
const headers = {};
|
|
28
36
|
if (userAgent) {
|
|
29
37
|
headers["User-Agent"] = userAgent;
|
|
@@ -109,7 +117,7 @@ export class SatoruBase {
|
|
|
109
117
|
async initDocument(options) {
|
|
110
118
|
const mod = await this.getModule();
|
|
111
119
|
const inst = mod.create_instance();
|
|
112
|
-
mod.init_document(inst, options.html, options.width);
|
|
120
|
+
mod.init_document(inst, options.html, options.width, options.height ?? 0);
|
|
113
121
|
return inst;
|
|
114
122
|
}
|
|
115
123
|
async layoutDocument(inst, width) {
|
|
@@ -187,16 +195,19 @@ export class SatoruBase {
|
|
|
187
195
|
: defaultResolver;
|
|
188
196
|
const inputHtmls = Array.isArray(value) ? value : [value];
|
|
189
197
|
const processedHtmls = [];
|
|
190
|
-
const
|
|
198
|
+
const resolvedResources = new Set();
|
|
191
199
|
for (const rawHtml of inputHtmls) {
|
|
192
200
|
let processedHtml = rawHtml;
|
|
193
201
|
for (let i = 0; i < 10; i++) {
|
|
194
|
-
mod.collect_resources(instancePtr, processedHtml, width);
|
|
202
|
+
mod.collect_resources(instancePtr, processedHtml, width, height);
|
|
195
203
|
const json = mod.get_pending_resources(instancePtr);
|
|
196
204
|
if (!json)
|
|
197
205
|
break;
|
|
198
206
|
const resources = JSON.parse(json);
|
|
199
|
-
const pending = resources.filter((r) =>
|
|
207
|
+
const pending = resources.filter((r) => {
|
|
208
|
+
const key = `${r.type}:${r.url}:${r.characters ?? ""}`;
|
|
209
|
+
return !resolvedResources.has(key);
|
|
210
|
+
});
|
|
200
211
|
if (pending.length === 0)
|
|
201
212
|
break;
|
|
202
213
|
await Promise.all(pending.map(async (r) => {
|
|
@@ -204,7 +215,8 @@ export class SatoruBase {
|
|
|
204
215
|
if (r.url.startsWith("data:")) {
|
|
205
216
|
return;
|
|
206
217
|
}
|
|
207
|
-
|
|
218
|
+
const key = `${r.type}:${r.url}:${r.characters ?? ""}`;
|
|
219
|
+
resolvedResources.add(key);
|
|
208
220
|
const data = await resolver({ ...r });
|
|
209
221
|
if (data &&
|
|
210
222
|
(data instanceof Uint8Array || ArrayBuffer.isView(data))) {
|
|
@@ -224,6 +236,13 @@ export class SatoruBase {
|
|
|
224
236
|
}
|
|
225
237
|
}));
|
|
226
238
|
}
|
|
239
|
+
const resolvedUrls = new Set();
|
|
240
|
+
resolvedResources.forEach((key) => {
|
|
241
|
+
const parts = key.split(":");
|
|
242
|
+
if (parts.length >= 2) {
|
|
243
|
+
resolvedUrls.add(parts.slice(1, -1).join(":"));
|
|
244
|
+
}
|
|
245
|
+
});
|
|
227
246
|
resolvedUrls.forEach((url) => {
|
|
228
247
|
const escapedUrl = url.replace(/[.*+?^${}()|[\]]/g, "\\$&");
|
|
229
248
|
const linkRegex = new RegExp(`<link[^>]*href\\s*=\\s*(["'])${escapedUrl}\\1[^>]*>`, "gi");
|
package/dist/satoru-single.js
CHANGED
|
Binary file
|