weasyprint-tsx 0.0.1 → 0.1.1
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 +99 -1
- package/ReadMe.pdf +0 -0
- package/bun.lock +22 -4
- package/package.json +7 -2
- package/packages/build/package.json +4 -4
- package/packages/build/src/build.ts +10 -10
- package/packages/build/src/cli.ts +1 -0
- package/packages/build/src/config.ts +6 -5
- package/packages/build/src/index.ts +1 -0
- package/packages/build/src/orchestrator.ts +8 -6
- package/packages/build/src/server.ts +4 -5
- package/packages/create/cli.js +7 -2
- package/packages/create/package.json +5 -4
- package/packages/create/template/AGENT.md +119 -0
- package/packages/create/template/package.json +0 -1
- package/packages/create/template/weasyprint-tsx.config.ts +1 -13
- package/packages/ui/README.md +278 -0
- package/packages/ui/package.json +8 -5
- package/packages/ui/src/BlockBox.tsx +6 -3
- package/packages/ui/src/CodeBlock.module.css +4 -0
- package/packages/ui/src/CodeBlock.tsx +20 -2
- package/packages/ui/src/Stack.module.css +9 -0
- package/packages/ui/src/Stack.tsx +24 -0
- package/packages/ui/src/Title.module.css +40 -32
- package/packages/ui/src/index.ts +1 -0
- package/src/index.css +44 -0
- package/src/index.tsx +334 -3
- package/testUI/index.css +1 -0
- package/testUI/index.tsx +20 -0
- package/weasyprint-tsx.config.ts +8 -0
package/README.md
CHANGED
|
@@ -1 +1,99 @@
|
|
|
1
|
-
|
|
1
|
+
# weasyprint-tsx
|
|
2
|
+
|
|
3
|
+
Write PDFs in TSX. Uses [Bun](https://bun.sh) to bundle your Preact components into HTML, then [WeasyPrint](https://weasyprint.org) to render them as PDF.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- [Bun](https://bun.sh) ≥ 1.0
|
|
8
|
+
- [WeasyPrint](https://weasyprint.org) — `pip install weasyprint`
|
|
9
|
+
|
|
10
|
+
## Quick start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bunx create-weasyprint-tsx my-doc
|
|
14
|
+
cd my-doc
|
|
15
|
+
bun install
|
|
16
|
+
bun run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Open `http://localhost:3000` to preview the document live. The PDF is written to `output.pdf` on every save.
|
|
20
|
+
|
|
21
|
+
## Scripts
|
|
22
|
+
|
|
23
|
+
| Script | Effect |
|
|
24
|
+
|--------|--------|
|
|
25
|
+
| `bun run dev` | Watch `src/`, rebuild HTML + PDF on change, serve preview at port 3000 |
|
|
26
|
+
| `bun run build` | Build once and write `output.pdf` |
|
|
27
|
+
|
|
28
|
+
## Project structure
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
my-doc/
|
|
32
|
+
├── src/
|
|
33
|
+
│ ├── index.tsx # Document root — must export a default Preact component
|
|
34
|
+
│ └── index.css # Styles — must begin with @import "tailwindcss"
|
|
35
|
+
├── weasyprint-tsx.config.ts
|
|
36
|
+
└── package.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`src/index.tsx` must export a default component that renders a **complete HTML document** (`<html>`, `<head>`, `<body>`). WeasyPrint receives the full HTML string.
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
Create or edit `weasyprint-tsx.config.ts` at the project root:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import type { Config } from "@weasyprint-tsx/build";
|
|
47
|
+
|
|
48
|
+
const config: Config = {
|
|
49
|
+
io: { output: "report.pdf" },
|
|
50
|
+
weasyprint: { path: "weasyprint", optimize_images: true },
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default config;
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### `io`
|
|
57
|
+
|
|
58
|
+
| Field | Default | Description |
|
|
59
|
+
|-------|---------|-------------|
|
|
60
|
+
| `input` | `"src/index.tsx"` | Entry point |
|
|
61
|
+
| `output` | `"output.pdf"` | Output PDF path |
|
|
62
|
+
| `buildDir` | `".build"` | Intermediate HTML directory |
|
|
63
|
+
| `media_type` | `"print"` | CSS media type passed to WeasyPrint |
|
|
64
|
+
|
|
65
|
+
### `weasyprint`
|
|
66
|
+
|
|
67
|
+
| Field | Default | Description |
|
|
68
|
+
|-------|---------|-------------|
|
|
69
|
+
| `path` | `"weasyprint"` | Path to the WeasyPrint binary |
|
|
70
|
+
| `verbose` | `false` | Print WeasyPrint stdout |
|
|
71
|
+
| `dpi` | — | Output resolution |
|
|
72
|
+
| `optimize_images` | — | Compress images in output |
|
|
73
|
+
| `pdf_forms` | — | Enable PDF form fields |
|
|
74
|
+
| `pdf_variant` | — | `"pdf/a-1b"`, `"pdf/a-2b"`, `"pdf/a-3b"`, `"pdf/a-4b"`, `"pdf/ua-1"` |
|
|
75
|
+
| `cache` | — | WeasyPrint font/image cache folder |
|
|
76
|
+
| `full_fonts` | — | Embed full font files (not just subsets) |
|
|
77
|
+
| `srgb` | — | Force sRGB color profile |
|
|
78
|
+
| `jpeg_quality` | — | JPEG compression quality (0–95) |
|
|
79
|
+
|
|
80
|
+
### `dev`
|
|
81
|
+
|
|
82
|
+
| Field | Default | Description |
|
|
83
|
+
|-------|---------|-------------|
|
|
84
|
+
| `port` | `3000` | Dev server port |
|
|
85
|
+
| `watch` | `"src"` | Directory to watch for changes |
|
|
86
|
+
|
|
87
|
+
### `pdf`
|
|
88
|
+
|
|
89
|
+
| Field | Default | Description |
|
|
90
|
+
|-------|---------|-------------|
|
|
91
|
+
| `stylesheets` | `[]` | Additional CSS files passed to WeasyPrint via `--stylesheet` |
|
|
92
|
+
|
|
93
|
+
## UI components
|
|
94
|
+
|
|
95
|
+
`@weasyprint-tsx/ui` provides print-optimized Preact components. See [`packages/ui/README.md`](packages/ui/README.md) for the full API.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { H1, H2, Page, UL, LI, Table, Entry } from "@weasyprint-tsx/ui";
|
|
99
|
+
```
|
package/ReadMe.pdf
CHANGED
|
Binary file
|
package/bun.lock
CHANGED
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
"workspaces": {
|
|
5
5
|
"": {
|
|
6
6
|
"name": "@weasyprint-tsx/build",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@fontsource-variable/public-sans": "^5.2.7",
|
|
9
|
+
},
|
|
7
10
|
},
|
|
8
11
|
"packages/build": {
|
|
9
12
|
"name": "@weasyprint-tsx/build",
|
|
10
|
-
"version": "0.
|
|
13
|
+
"version": "0.1.1",
|
|
11
14
|
"bin": {
|
|
12
15
|
"weasyprint-tsx": "./src/cli.ts",
|
|
13
16
|
},
|
|
@@ -24,14 +27,21 @@
|
|
|
24
27
|
},
|
|
25
28
|
"packages/create": {
|
|
26
29
|
"name": "@weasyprint-tsx/create",
|
|
27
|
-
"version": "0.0
|
|
30
|
+
"version": "0.1.0",
|
|
28
31
|
"bin": {
|
|
29
|
-
"create-weasyprint-tsx": "
|
|
32
|
+
"create-weasyprint-tsx": "cli.js",
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"highlight.js": "11.11.1",
|
|
30
36
|
},
|
|
31
37
|
},
|
|
32
38
|
"packages/ui": {
|
|
33
39
|
"name": "@weasyprint-tsx/ui",
|
|
34
|
-
"version": "0.0
|
|
40
|
+
"version": "0.1.0",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"highlight.js": "^11.11.1",
|
|
43
|
+
"katex": "^0.16.47",
|
|
44
|
+
},
|
|
35
45
|
},
|
|
36
46
|
},
|
|
37
47
|
"packages": {
|
|
@@ -45,6 +55,8 @@
|
|
|
45
55
|
|
|
46
56
|
"@csstools/utilities": ["@csstools/utilities@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg=="],
|
|
47
57
|
|
|
58
|
+
"@fontsource-variable/public-sans": ["@fontsource-variable/public-sans@5.2.7", "", {}, "sha512-4mvade2J3slKkvwRkS+p8T3szet/0vhWoSnuUJTVU81Uo2pRpSZY/Y8bSLRqpSwzIPxjVmRJ53oq6JKP/l/PSg=="],
|
|
59
|
+
|
|
48
60
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
|
49
61
|
|
|
50
62
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
|
@@ -95,14 +107,20 @@
|
|
|
95
107
|
|
|
96
108
|
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
|
97
109
|
|
|
110
|
+
"commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
|
|
111
|
+
|
|
98
112
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
99
113
|
|
|
100
114
|
"enhanced-resolve": ["enhanced-resolve@5.24.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ=="],
|
|
101
115
|
|
|
102
116
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
|
103
117
|
|
|
118
|
+
"highlight.js": ["highlight.js@11.11.1", "", {}, "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w=="],
|
|
119
|
+
|
|
104
120
|
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
|
|
105
121
|
|
|
122
|
+
"katex": ["katex@0.16.47", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg=="],
|
|
123
|
+
|
|
106
124
|
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
|
107
125
|
|
|
108
126
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weasyprint-tsx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"bin": {
|
|
5
|
+
"create-weasyprint-tsx": "bun packages/create/cli.ts",
|
|
6
|
+
"build-weasyprint-tsx": "bun packages/build/src/cli.ts"
|
|
7
|
+
},
|
|
4
8
|
"scripts": {
|
|
5
9
|
"dev": "bun packages/build/src/cli.ts --watch",
|
|
6
|
-
"init"
|
|
10
|
+
"init": "bun packages/create/cli.ts",
|
|
11
|
+
"build": "bun packages/build/src/cli.ts"
|
|
7
12
|
},
|
|
8
13
|
"workspaces": [
|
|
9
14
|
"packages/*"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasyprint-tsx/build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"main": "./src/index.ts",
|
|
5
|
+
"exports": { ".": "./src/index.ts" },
|
|
6
|
+
"types": "./src/index.ts",
|
|
4
7
|
"bin": {
|
|
5
8
|
"weasyprint-tsx": "./src/cli.ts"
|
|
6
9
|
},
|
|
7
|
-
"workspaces": [
|
|
8
|
-
"packages/*"
|
|
9
|
-
],
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"bun-types": "latest"
|
|
12
12
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { DConfig } from "./config";
|
|
1
|
+
import tailwindcss from "@tailwindcss/postcss";
|
|
3
2
|
import { BunPlugin } from "bun";
|
|
3
|
+
import { join } from "path";
|
|
4
4
|
import postcss from "postcss";
|
|
5
5
|
import customProperties from "postcss-custom-properties";
|
|
6
|
-
import tailwindcss from "@tailwindcss/postcss";
|
|
7
|
-
import { injectDevScript } from "./server";
|
|
8
6
|
import { h } from "preact";
|
|
9
7
|
import { renderToStaticMarkup } from "preact-render-to-string";
|
|
8
|
+
import { DConfig } from "./config";
|
|
9
|
+
import { DEV_SCRIPT } from "./server";
|
|
10
10
|
|
|
11
11
|
const tailwindPlugin: BunPlugin = {
|
|
12
12
|
name: "tailwind-postcss",
|
|
@@ -49,17 +49,17 @@ export async function buildHTML(cfg: DConfig, dev: boolean = false) {
|
|
|
49
49
|
join(BUILD_DIR, `index.js?t=${performance.now()}`)
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
await Bun.write(BUILD_PATH, html);
|
|
52
|
+
let html = "<!DOCTYPE html>" + renderToStaticMarkup(h(Document, {}));
|
|
55
53
|
|
|
56
54
|
if (dev) {
|
|
57
|
-
|
|
55
|
+
html = html.replace("</html>", `${DEV_SCRIPT}</html>`);
|
|
58
56
|
}
|
|
57
|
+
await Bun.write(BUILD_PATH, html);
|
|
59
58
|
process.stdout.write(
|
|
60
59
|
`Bun HTML build -- ${(performance.now() - t0).toFixed(2)}ms\n`,
|
|
61
60
|
);
|
|
62
|
-
|
|
61
|
+
const css = await Bun.file(join(BUILD_DIR, "index.css")).text();
|
|
62
|
+
return Bun.hash(html + css);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function buildArgs(cfg: DConfig): string[] {
|
|
@@ -89,7 +89,7 @@ export async function buildPDF(cfg: DConfig): Promise<void> {
|
|
|
89
89
|
const t0 = performance.now();
|
|
90
90
|
const proc = Bun.spawn(args, {
|
|
91
91
|
cwd: process.cwd(),
|
|
92
|
-
stderr: "inherit",
|
|
92
|
+
stderr: cfg.weasyprint.verbose ? "inherit" : "ignore",
|
|
93
93
|
stdout: cfg.weasyprint.verbose ? "inherit" : "ignore",
|
|
94
94
|
});
|
|
95
95
|
const code = await proc.exited;
|
|
@@ -24,7 +24,7 @@ export interface Config {
|
|
|
24
24
|
};
|
|
25
25
|
dev?: {
|
|
26
26
|
port?: number;
|
|
27
|
-
watch?: string;
|
|
27
|
+
watch?: string[];
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -44,7 +44,7 @@ const defaultConfig = {
|
|
|
44
44
|
},
|
|
45
45
|
dev: {
|
|
46
46
|
port: 3000,
|
|
47
|
-
watch: "src",
|
|
47
|
+
watch: ["src"],
|
|
48
48
|
},
|
|
49
49
|
} satisfies Config;
|
|
50
50
|
|
|
@@ -80,11 +80,12 @@ export async function loadConfig(): Promise<DConfig> {
|
|
|
80
80
|
const configPath = resolve(process.cwd(), "weasyprint-tsx.config.ts");
|
|
81
81
|
let config = defaultConfig;
|
|
82
82
|
try {
|
|
83
|
-
const {default
|
|
83
|
+
const { default: userConfig } = await import(`${configPath}`);
|
|
84
84
|
config = mergeDeep(config, userConfig) as unknown as DConfig;
|
|
85
|
-
console.log(config , userConfig)
|
|
86
85
|
} catch (e) {
|
|
87
|
-
process.stdout.write(
|
|
86
|
+
process.stdout.write(
|
|
87
|
+
`No config file at ${configPath} found, using default.`,
|
|
88
|
+
);
|
|
88
89
|
}
|
|
89
90
|
return config;
|
|
90
91
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { Config, DConfig } from "./config";
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { watch } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
2
3
|
import { buildHTML, buildPDF } from "./build";
|
|
3
4
|
import { loadConfig } from "./config";
|
|
4
5
|
import { bumpPing, startServer } from "./server";
|
|
5
|
-
import { join } from "path";
|
|
6
6
|
|
|
7
7
|
export async function buildOnce(output?: string) {
|
|
8
8
|
const cfg = await loadConfig();
|
|
@@ -22,7 +22,7 @@ export async function buildOnce(output?: string) {
|
|
|
22
22
|
|
|
23
23
|
export async function devMode(output?: string) {
|
|
24
24
|
const cfg = await loadConfig();
|
|
25
|
-
const
|
|
25
|
+
const WATCH_DIRS = cfg.dev.watch.map((p) => join(process.cwd(), p));
|
|
26
26
|
|
|
27
27
|
if (output) cfg.io.output = cfg.io.output ?? output;
|
|
28
28
|
process.stdout.write(`Dev: http://localhost:${cfg.dev.port}`);
|
|
@@ -52,10 +52,12 @@ export async function devMode(output?: string) {
|
|
|
52
52
|
|
|
53
53
|
startServer(cfg.dev.port, cfg.io.buildDir);
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
WATCH_DIRS.forEach((dir) =>
|
|
56
|
+
watch(dir, { recursive: true }, async (_, f) => {
|
|
57
|
+
process.stdout.write(`file changed ${f}`);
|
|
58
|
+
await rebuild();
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
59
61
|
|
|
60
62
|
await Bun.sleep(Infinity);
|
|
61
63
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "path";
|
|
2
2
|
|
|
3
|
-
const DEV_SCRIPT = `<script>var _t=null;setInterval(function(){fetch('/__dev_ping').then(function(r){return r.text();}).then(function(s){if(_t!==null&&s!==_t)location.reload();_t=s;}).catch(function(){});},
|
|
3
|
+
export const DEV_SCRIPT = `<script>var _t=null;setInterval(function(){fetch('/__dev_ping').then(function(r){return r.text();}).then(function(s){if(_t!==null&&s!==_t)location.reload();_t=s;}).catch(function(){});},1000);</script>`;
|
|
4
4
|
|
|
5
5
|
let pingCounter = 0;
|
|
6
6
|
|
|
@@ -19,8 +19,9 @@ export function startServer(port: number, buildDir: string): void {
|
|
|
19
19
|
headers: { "Cache-Control": "no-store" },
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const pathname = url.pathname === "/" ? "/index.html" : url.pathname;
|
|
23
|
+
if (pathname == "/favicon.ico") return new Response(null);
|
|
24
|
+
const file = Bun.file(join(process.cwd(), buildDir, pathname));
|
|
24
25
|
return new Response(file);
|
|
25
26
|
},
|
|
26
27
|
});
|
|
@@ -29,8 +30,6 @@ export function startServer(port: number, buildDir: string): void {
|
|
|
29
30
|
export async function injectDevScript(htmlPath: string): Promise<void> {
|
|
30
31
|
let html = await Bun.file(htmlPath).text();
|
|
31
32
|
if (!html.includes("/__dev_ping")) {
|
|
32
|
-
html = html.replace("</html>", `${DEV_SCRIPT}</html>`);
|
|
33
33
|
await Bun.write(htmlPath, html);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
package/packages/create/cli.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { cpSync, existsSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
-
import { join } from "path";
|
|
4
|
+
import { join,dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
5
6
|
|
|
6
7
|
const name = process.argv[2] ?? "my-doc";
|
|
7
8
|
const dest = join(process.cwd(), name);
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const templateDir = join(__dirname, "template");
|
|
9
14
|
|
|
10
15
|
if (existsSync(dest)) {
|
|
11
16
|
process.stderr.write(`Error: directory '${name}' already exists\n`);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weasyprint-tsx/create",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"bin": {
|
|
5
|
-
"create-weasyprint-tsx": "
|
|
5
|
+
"create-weasyprint-tsx": "cli.js"
|
|
6
6
|
},
|
|
7
|
-
"
|
|
8
|
-
"
|
|
7
|
+
"files": [
|
|
8
|
+
"cli.js",
|
|
9
|
+
"template"
|
|
9
10
|
]
|
|
10
11
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# weasyprint-tsx Project — LLM Context
|
|
2
|
+
|
|
3
|
+
This is a **weasyprint-tsx** document project. The output is a PDF file generated from Preact/TSX source.
|
|
4
|
+
|
|
5
|
+
## Pipeline
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/index.tsx → Bun.build() → .build/index.html → WeasyPrint → output.pdf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- **Bun** bundles the TSX entry point (and its imports) into a static HTML file
|
|
12
|
+
- **WeasyPrint** converts that HTML into a PDF using CSS print rules
|
|
13
|
+
- The dev server (`port 3000`) serves the HTML with live reload injected
|
|
14
|
+
|
|
15
|
+
## Commands
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bun run dev # watch src/, rebuild on change, serve preview at localhost:3000
|
|
19
|
+
bun run build # build once, write output.pdf
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Entry point (`src/index.tsx`)
|
|
23
|
+
|
|
24
|
+
Must export a **default Preact component** that returns a **complete HTML document**:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { H1, Page } from "@weasyprint-tsx/ui";
|
|
28
|
+
import "./index.css";
|
|
29
|
+
|
|
30
|
+
export default function Document() {
|
|
31
|
+
return (
|
|
32
|
+
<html>
|
|
33
|
+
<head>
|
|
34
|
+
<meta charset="UTF-8" />
|
|
35
|
+
<title>My Document</title>
|
|
36
|
+
<link rel="stylesheet" href="index.css" />
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<Page>
|
|
40
|
+
<H1>My Document</H1>
|
|
41
|
+
</Page>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The component must render `<html>`, `<head>`, and `<body>` — WeasyPrint receives the full HTML string.
|
|
49
|
+
|
|
50
|
+
## Stylesheet (`src/index.css`)
|
|
51
|
+
|
|
52
|
+
Must start with `@import "tailwindcss"`. Page size and margins are set via `@page`:
|
|
53
|
+
|
|
54
|
+
```css
|
|
55
|
+
@import "tailwindcss";
|
|
56
|
+
|
|
57
|
+
@page {
|
|
58
|
+
size: A4;
|
|
59
|
+
margin: 2cm 1.5cm;
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
CSS `@page` rules control paper size, margins, and page headers/footers. Do **not** use `html { width: 21cm }` — let WeasyPrint handle page geometry via `@page`.
|
|
64
|
+
|
|
65
|
+
## Configuration (`weasyprint-tsx.config.ts`)
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import type { Config } from "@weasyprint-tsx/build";
|
|
69
|
+
|
|
70
|
+
const config: Config = {
|
|
71
|
+
io: {
|
|
72
|
+
output: "output.pdf", // output path
|
|
73
|
+
input: "src/index.tsx", // entry point
|
|
74
|
+
},
|
|
75
|
+
weasyprint: {
|
|
76
|
+
path: "weasyprint", // path to weasyprint binary
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export default config;
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## UI Components (`@weasyprint-tsx/ui`)
|
|
84
|
+
|
|
85
|
+
All components are Preact components (not React). Import from `@weasyprint-tsx/ui`:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import {
|
|
89
|
+
// Layout
|
|
90
|
+
Page, PageBreak,
|
|
91
|
+
BlockBox, Block,
|
|
92
|
+
// Headings
|
|
93
|
+
H1, H2, H3, H4, H5, H6, Title, ResetCounter,
|
|
94
|
+
// Lists
|
|
95
|
+
UL, OL, LI,
|
|
96
|
+
// Table
|
|
97
|
+
Table, Entry,
|
|
98
|
+
// Inline
|
|
99
|
+
DotLine, CodeBlock, Equation,
|
|
100
|
+
} from "@weasyprint-tsx/ui";
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
See the full API: https://github.com/weasyprint-tsx/weasyprint-tsx/blob/main/packages/ui/README.md
|
|
104
|
+
|
|
105
|
+
## CSS References
|
|
106
|
+
|
|
107
|
+
- **WeasyPrint CSS support**: https://doc.courtbouillon.org/weasyprint/stable/first_steps.html
|
|
108
|
+
- **Tailwind CSS v4 docs**: https://tailwindcss.com/docs
|
|
109
|
+
|
|
110
|
+
## Constraints for LLMs
|
|
111
|
+
|
|
112
|
+
- **No `position: fixed` or `position: sticky`** — these do not work in print media
|
|
113
|
+
- **No `vh` / `vw` units** — viewport units are meaningless in print context
|
|
114
|
+
- **No `@media screen`** — the document is rendered in print media; use `@media print` or no media query
|
|
115
|
+
- **Page geometry** belongs in `@page` rules, not on `html` or `body`
|
|
116
|
+
- **All layout** should use CSS that WeasyPrint supports. Flexbox and CSS Grid have partial support — prefer block layout or the `BlockBox`/`Block` components for multi-column content
|
|
117
|
+
- **Components are Preact**, not React — use `class` not `className` for raw HTML elements (Preact accepts both, but prefer `class`)
|
|
118
|
+
- **Images** must be embedded as base64 data URIs or served by the dev server; external URLs may not resolve during build
|
|
119
|
+
- **Fonts** must be declared in CSS with `@font-face` or imported via Tailwind; Google Fonts CDN links work at build time
|
|
@@ -1,17 +1,5 @@
|
|
|
1
1
|
import type { Config } from "@weasyprint-tsx/build";
|
|
2
2
|
|
|
3
|
-
const config: Config = {
|
|
4
|
-
io: {
|
|
5
|
-
engine: "weasyprint",
|
|
6
|
-
output: "output.pdf",
|
|
7
|
-
},
|
|
8
|
-
weasyprint: {
|
|
9
|
-
pdf_forms: true,
|
|
10
|
-
},
|
|
11
|
-
watchdog: {
|
|
12
|
-
path: "./src",
|
|
13
|
-
debounce: 500,
|
|
14
|
-
},
|
|
15
|
-
};
|
|
3
|
+
const config: Config = {};
|
|
16
4
|
|
|
17
5
|
export default config;
|