@floffah/astro-typst 0.0.0
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/LICENSE +21 -0
- package/README.md +38 -0
- package/dist/index.cjs +22 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +6 -0
- package/dist/loader-CxNOQzU1.js +109 -0
- package/dist/loader-CxNOQzU1.js.map +1 -0
- package/dist/loader-DetFooDG.cjs +114 -0
- package/dist/loader-DetFooDG.cjs.map +1 -0
- package/dist/loader.cjs +4 -0
- package/dist/loader.d.cts +13 -0
- package/dist/loader.d.ts +13 -0
- package/dist/loader.js +3 -0
- package/dist/render-UEphEA_d.cjs +69 -0
- package/dist/render-UEphEA_d.cjs.map +1 -0
- package/dist/render-hwAGjpa0.js +58 -0
- package/dist/render-hwAGjpa0.js.map +1 -0
- package/dist/vite.cjs +25 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +13 -0
- package/dist/vite.d.ts +13 -0
- package/dist/vite.js +24 -0
- package/dist/vite.js.map +1 -0
- package/dist/wasm.cjs +15 -0
- package/dist/wasm.d.cts +2 -0
- package/dist/wasm.d.ts +2 -0
- package/dist/wasm.js +3 -0
- package/package.json +130 -0
- package/wasm/package.json +4 -0
- package/wasm/typst_wasm.d.ts +9 -0
- package/wasm/typst_wasm.js +191 -0
- package/wasm/typst_wasm_bg.wasm +0 -0
- package/wasm/typst_wasm_bg.wasm.d.ts +9 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Floffah
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @floffah/astro-typst
|
|
2
|
+
|
|
3
|
+
Typst HTML rendering primitives, an Astro content loader, and a Vite plugin powered by WebAssembly.
|
|
4
|
+
|
|
5
|
+
This package differs from the real astro-typst as it is targeted at Astro content collections and rendering to true HTML rather than SVGs. If you want to be able to import Typst documents as Astro components or use a more configurable vite plugin, this project is not for you.
|
|
6
|
+
|
|
7
|
+
You can see this project in action at https://github.com/Floffah/luminous! I also have a full Astro theme for worldbuilding based on Typst at https://github.com/Floffah/astro-world
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// src/content.config.ts
|
|
11
|
+
import { typstLoader } from "@floffah/astro-typst/loader";
|
|
12
|
+
import { defineCollection, z } from "astro:content";
|
|
13
|
+
|
|
14
|
+
export const collections = {
|
|
15
|
+
docs: defineCollection({
|
|
16
|
+
loader: typstLoader({ base: "../docs" }),
|
|
17
|
+
schema: z.object({ title: z.string() }),
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// astro.config.ts
|
|
24
|
+
import { typst } from "@floffah/astro-typst/vite";
|
|
25
|
+
import { defineConfig } from "astro/config";
|
|
26
|
+
|
|
27
|
+
export default defineConfig({ vite: { plugins: [typst()] } });
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Typst content can define an `astro` dictionary. Its JSON-compatible value is passed to the collection schema:
|
|
31
|
+
|
|
32
|
+
```typst
|
|
33
|
+
#let astro = (title: "Hello", draft: false)
|
|
34
|
+
|
|
35
|
+
= Hello
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The root package exports `renderTypst`, `compileTypst`, `typstLoader`, and `typst`. The raw `typstToHtml` and `typstToHtmlWithMetadata` WebAssembly entrypoints are also available from `@floffah/astro-typst/wasm`.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_loader = require('./loader-DetFooDG.cjs');
|
|
3
|
+
const require_render = require('./render-UEphEA_d.cjs');
|
|
4
|
+
const require_vite = require('./vite.cjs');
|
|
5
|
+
let wasm_typst_wasm_js = require("../wasm/typst_wasm.js");
|
|
6
|
+
|
|
7
|
+
exports.compileTypst = require_render.compileTypst;
|
|
8
|
+
exports.renderTypst = require_render.renderTypst;
|
|
9
|
+
exports.typst = require_vite.typst;
|
|
10
|
+
exports.typstLoader = require_loader.typstLoader;
|
|
11
|
+
Object.defineProperty(exports, 'typstToHtml', {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () {
|
|
14
|
+
return wasm_typst_wasm_js.typstToHtml;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(exports, 'typstToHtmlWithMetadata', {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () {
|
|
20
|
+
return wasm_typst_wasm_js.typstToHtmlWithMetadata;
|
|
21
|
+
}
|
|
22
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TypstLoaderOptions, typstLoader } from "./loader.cjs";
|
|
2
|
+
import { TypstPluginOptions, typst } from "./vite.cjs";
|
|
3
|
+
import { typstToHtml, typstToHtmlWithMetadata } from "./wasm.cjs";
|
|
4
|
+
import { MarkdownHeading } from "astro";
|
|
5
|
+
|
|
6
|
+
//#region src/render.d.ts
|
|
7
|
+
interface CompiledTypst {
|
|
8
|
+
html: string;
|
|
9
|
+
headings: MarkdownHeading[];
|
|
10
|
+
metadata: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
declare function renderTypst(typst: string): string;
|
|
13
|
+
declare function compileTypst(typst: string): CompiledTypst;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { type CompiledTypst, type TypstLoaderOptions, type TypstPluginOptions, compileTypst, renderTypst, typst, typstLoader, typstToHtml, typstToHtmlWithMetadata };
|
|
16
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TypstLoaderOptions, typstLoader } from "./loader.js";
|
|
2
|
+
import { TypstPluginOptions, typst } from "./vite.js";
|
|
3
|
+
import { typstToHtml, typstToHtmlWithMetadata } from "./wasm.js";
|
|
4
|
+
import { MarkdownHeading } from "astro";
|
|
5
|
+
|
|
6
|
+
//#region src/render.d.ts
|
|
7
|
+
interface CompiledTypst {
|
|
8
|
+
html: string;
|
|
9
|
+
headings: MarkdownHeading[];
|
|
10
|
+
metadata: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
declare function renderTypst(typst: string): string;
|
|
13
|
+
declare function compileTypst(typst: string): CompiledTypst;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { type CompiledTypst, type TypstLoaderOptions, type TypstPluginOptions, compileTypst, renderTypst, typst, typstLoader, typstToHtml, typstToHtmlWithMetadata };
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { t as typstLoader } from "./loader-CxNOQzU1.js";
|
|
2
|
+
import { typstToHtml, typstToHtmlWithMetadata } from "./wasm.js";
|
|
3
|
+
import { n as renderTypst, t as compileTypst } from "./render-hwAGjpa0.js";
|
|
4
|
+
import { typst } from "./vite.js";
|
|
5
|
+
|
|
6
|
+
export { compileTypst, renderTypst, typst, typstLoader, typstToHtml, typstToHtmlWithMetadata };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { t as compileTypst } from "./render-hwAGjpa0.js";
|
|
2
|
+
import { relative, resolve, sep } from "node:path";
|
|
3
|
+
import { glob, readFile } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
//#region src/paths.ts
|
|
7
|
+
function toPosixPath(path) {
|
|
8
|
+
return path.split(sep).join("/");
|
|
9
|
+
}
|
|
10
|
+
function isTypstEntryPath(path) {
|
|
11
|
+
const fileName = path.split("/").at(-1);
|
|
12
|
+
return path.endsWith(".typ") && path !== ".." && !path.startsWith("../") && !fileName?.startsWith("_");
|
|
13
|
+
}
|
|
14
|
+
function typstEntryId(path) {
|
|
15
|
+
const id = path.slice(0, -4);
|
|
16
|
+
return id.endsWith("/index") ? id.slice(0, -6) : id;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/loader.ts
|
|
21
|
+
function typstLoader({ base = "src/content" } = {}) {
|
|
22
|
+
return {
|
|
23
|
+
name: "astro-typst-loader",
|
|
24
|
+
async load({ config, generateDigest, logger, parseData, store, watcher }) {
|
|
25
|
+
const rootPath = fileURLToPath(config.root);
|
|
26
|
+
const basePath = fileURLToPath(new URL(base.endsWith("/") ? base : `${base}/`, config.root));
|
|
27
|
+
async function rebuildAll() {
|
|
28
|
+
const filePaths = [];
|
|
29
|
+
for await (const entry of glob("**/*.typ", { cwd: basePath })) if (isTypstEntryPath(toPosixPath(entry))) filePaths.push(resolve(basePath, entry));
|
|
30
|
+
if (filePaths.length === 0) logger.warn(`No Typst files found in ${basePath}`);
|
|
31
|
+
const entries = await Promise.all(filePaths.map(async (filePath) => {
|
|
32
|
+
const id = typstEntryId(toPosixPath(relative(basePath, filePath)));
|
|
33
|
+
const source = await readFile(filePath, "utf8");
|
|
34
|
+
const compiled = compileTypst(source);
|
|
35
|
+
return {
|
|
36
|
+
id,
|
|
37
|
+
source,
|
|
38
|
+
data: await parseData({
|
|
39
|
+
id,
|
|
40
|
+
data: compiled.metadata,
|
|
41
|
+
filePath
|
|
42
|
+
}),
|
|
43
|
+
html: compiled.html,
|
|
44
|
+
headings: compiled.headings,
|
|
45
|
+
filePath: toPosixPath(relative(rootPath, filePath)),
|
|
46
|
+
digest: generateDigest(JSON.stringify({
|
|
47
|
+
source,
|
|
48
|
+
html: compiled.html
|
|
49
|
+
}))
|
|
50
|
+
};
|
|
51
|
+
}));
|
|
52
|
+
entries.sort((left, right) => left.id.localeCompare(right.id));
|
|
53
|
+
const entryIds = /* @__PURE__ */ new Set();
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entryIds.has(entry.id)) throw new Error(`More than one Typst content file resolves to id "${entry.id}"`);
|
|
56
|
+
entryIds.add(entry.id);
|
|
57
|
+
}
|
|
58
|
+
const untouchedEntries = new Set(store.keys());
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
untouchedEntries.delete(entry.id);
|
|
61
|
+
store.set({
|
|
62
|
+
id: entry.id,
|
|
63
|
+
data: entry.data,
|
|
64
|
+
body: entry.source,
|
|
65
|
+
digest: entry.digest,
|
|
66
|
+
filePath: entry.filePath,
|
|
67
|
+
rendered: {
|
|
68
|
+
html: entry.html,
|
|
69
|
+
metadata: { headings: entry.headings }
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
untouchedEntries.forEach((id) => store.delete(id));
|
|
74
|
+
}
|
|
75
|
+
await rebuildAll();
|
|
76
|
+
if (!watcher) return;
|
|
77
|
+
watcher.add(basePath);
|
|
78
|
+
let activeRebuild;
|
|
79
|
+
let rebuildAgain = false;
|
|
80
|
+
const reload = async (filePath) => {
|
|
81
|
+
const entryPath = toPosixPath(relative(basePath, filePath));
|
|
82
|
+
if (!isTypstEntryPath(entryPath)) return;
|
|
83
|
+
try {
|
|
84
|
+
rebuildAgain = true;
|
|
85
|
+
activeRebuild ??= (async () => {
|
|
86
|
+
while (rebuildAgain) {
|
|
87
|
+
rebuildAgain = false;
|
|
88
|
+
await rebuildAll();
|
|
89
|
+
}
|
|
90
|
+
})().finally(() => {
|
|
91
|
+
activeRebuild = void 0;
|
|
92
|
+
});
|
|
93
|
+
await activeRebuild;
|
|
94
|
+
logger.info(`Reloaded Typst content after ${entryPath}`);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
logger.error(`Failed to reload Typst content after ${entryPath}: ${message}`);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
watcher.on("add", reload);
|
|
101
|
+
watcher.on("change", reload);
|
|
102
|
+
watcher.on("unlink", reload);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { typstLoader as t };
|
|
109
|
+
//# sourceMappingURL=loader-CxNOQzU1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader-CxNOQzU1.js","names":[],"sources":["../src/paths.ts","../src/loader.ts"],"sourcesContent":["import { sep } from \"node:path\";\n\nexport function toPosixPath(path: string): string {\n return path.split(sep).join(\"/\");\n}\n\nexport function isTypstEntryPath(path: string): boolean {\n const fileName = path.split(\"/\").at(-1);\n\n return (\n path.endsWith(\".typ\") &&\n path !== \"..\" &&\n !path.startsWith(\"../\") &&\n !fileName?.startsWith(\"_\")\n );\n}\n\nexport function typstEntryId(path: string): string {\n const id = path.slice(0, -\".typ\".length);\n return id.endsWith(\"/index\") ? id.slice(0, -\"/index\".length) : id;\n}\n","import { isTypstEntryPath, toPosixPath, typstEntryId } from \"./paths.js\";\nimport { type CompiledTypst, compileTypst } from \"./render.js\";\nimport type { Loader } from \"astro/loaders\";\nimport { glob, readFile } from \"node:fs/promises\";\nimport { relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface TypstLoaderOptions {\n /** Directory containing Typst entries, resolved relative to Astro's root. */\n base?: string;\n}\n\ninterface CompiledEntry {\n id: string;\n source: string;\n data: Record<string, unknown>;\n html: string;\n headings: CompiledTypst[\"headings\"];\n filePath: string;\n digest: string;\n}\n\nexport function typstLoader({\n base = \"src/content\",\n}: TypstLoaderOptions = {}): Loader {\n return {\n name: \"astro-typst-loader\",\n async load({\n config,\n generateDigest,\n logger,\n parseData,\n store,\n watcher,\n }) {\n const rootPath = fileURLToPath(config.root);\n const basePath = fileURLToPath(\n new URL(base.endsWith(\"/\") ? base : `${base}/`, config.root),\n );\n\n async function rebuildAll(): Promise<void> {\n const filePaths: string[] = [];\n for await (const entry of glob(\"**/*.typ\", { cwd: basePath })) {\n const entryPath = toPosixPath(entry);\n if (isTypstEntryPath(entryPath))\n filePaths.push(resolve(basePath, entry));\n }\n\n if (filePaths.length === 0)\n logger.warn(`No Typst files found in ${basePath}`);\n\n const entries = await Promise.all(\n filePaths.map(async (filePath): Promise<CompiledEntry> => {\n const entryPath = toPosixPath(\n relative(basePath, filePath),\n );\n const id = typstEntryId(entryPath);\n const source = await readFile(filePath, \"utf8\");\n const compiled = compileTypst(source);\n const data = (await parseData({\n id,\n data: compiled.metadata,\n filePath,\n })) as Record<string, unknown>;\n\n return {\n id,\n source,\n data,\n html: compiled.html,\n headings: compiled.headings,\n filePath: toPosixPath(relative(rootPath, filePath)),\n digest: generateDigest(\n JSON.stringify({ source, html: compiled.html }),\n ),\n };\n }),\n );\n\n entries.sort((left, right) => left.id.localeCompare(right.id));\n const entryIds = new Set<string>();\n for (const entry of entries) {\n if (entryIds.has(entry.id)) {\n throw new Error(\n `More than one Typst content file resolves to id \"${entry.id}\"`,\n );\n }\n entryIds.add(entry.id);\n }\n\n const untouchedEntries = new Set(store.keys());\n for (const entry of entries) {\n untouchedEntries.delete(entry.id);\n store.set({\n id: entry.id,\n data: entry.data,\n body: entry.source,\n digest: entry.digest,\n filePath: entry.filePath,\n rendered: {\n html: entry.html,\n metadata: { headings: entry.headings },\n },\n });\n }\n untouchedEntries.forEach((id) => store.delete(id));\n }\n\n await rebuildAll();\n if (!watcher) return;\n\n watcher.add(basePath);\n let activeRebuild: Promise<void> | undefined;\n let rebuildAgain = false;\n\n const reload = async (filePath: string): Promise<void> => {\n const entryPath = toPosixPath(relative(basePath, filePath));\n if (!isTypstEntryPath(entryPath)) return;\n\n try {\n rebuildAgain = true;\n activeRebuild ??= (async () => {\n while (rebuildAgain) {\n rebuildAgain = false;\n await rebuildAll();\n }\n })().finally(() => {\n activeRebuild = undefined;\n });\n await activeRebuild;\n logger.info(`Reloaded Typst content after ${entryPath}`);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `Failed to reload Typst content after ${entryPath}: ${message}`,\n );\n }\n };\n\n watcher.on(\"add\", reload);\n watcher.on(\"change\", reload);\n watcher.on(\"unlink\", reload);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const require_render = require('./render-UEphEA_d.cjs');
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
let node_fs_promises = require("node:fs/promises");
|
|
4
|
+
let node_url = require("node:url");
|
|
5
|
+
|
|
6
|
+
//#region src/paths.ts
|
|
7
|
+
function toPosixPath(path) {
|
|
8
|
+
return path.split(node_path.sep).join("/");
|
|
9
|
+
}
|
|
10
|
+
function isTypstEntryPath(path) {
|
|
11
|
+
const fileName = path.split("/").at(-1);
|
|
12
|
+
return path.endsWith(".typ") && path !== ".." && !path.startsWith("../") && !fileName?.startsWith("_");
|
|
13
|
+
}
|
|
14
|
+
function typstEntryId(path) {
|
|
15
|
+
const id = path.slice(0, -4);
|
|
16
|
+
return id.endsWith("/index") ? id.slice(0, -6) : id;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/loader.ts
|
|
21
|
+
function typstLoader({ base = "src/content" } = {}) {
|
|
22
|
+
return {
|
|
23
|
+
name: "astro-typst-loader",
|
|
24
|
+
async load({ config, generateDigest, logger, parseData, store, watcher }) {
|
|
25
|
+
const rootPath = (0, node_url.fileURLToPath)(config.root);
|
|
26
|
+
const basePath = (0, node_url.fileURLToPath)(new URL(base.endsWith("/") ? base : `${base}/`, config.root));
|
|
27
|
+
async function rebuildAll() {
|
|
28
|
+
const filePaths = [];
|
|
29
|
+
for await (const entry of (0, node_fs_promises.glob)("**/*.typ", { cwd: basePath })) if (isTypstEntryPath(toPosixPath(entry))) filePaths.push((0, node_path.resolve)(basePath, entry));
|
|
30
|
+
if (filePaths.length === 0) logger.warn(`No Typst files found in ${basePath}`);
|
|
31
|
+
const entries = await Promise.all(filePaths.map(async (filePath) => {
|
|
32
|
+
const id = typstEntryId(toPosixPath((0, node_path.relative)(basePath, filePath)));
|
|
33
|
+
const source = await (0, node_fs_promises.readFile)(filePath, "utf8");
|
|
34
|
+
const compiled = require_render.compileTypst(source);
|
|
35
|
+
return {
|
|
36
|
+
id,
|
|
37
|
+
source,
|
|
38
|
+
data: await parseData({
|
|
39
|
+
id,
|
|
40
|
+
data: compiled.metadata,
|
|
41
|
+
filePath
|
|
42
|
+
}),
|
|
43
|
+
html: compiled.html,
|
|
44
|
+
headings: compiled.headings,
|
|
45
|
+
filePath: toPosixPath((0, node_path.relative)(rootPath, filePath)),
|
|
46
|
+
digest: generateDigest(JSON.stringify({
|
|
47
|
+
source,
|
|
48
|
+
html: compiled.html
|
|
49
|
+
}))
|
|
50
|
+
};
|
|
51
|
+
}));
|
|
52
|
+
entries.sort((left, right) => left.id.localeCompare(right.id));
|
|
53
|
+
const entryIds = /* @__PURE__ */ new Set();
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entryIds.has(entry.id)) throw new Error(`More than one Typst content file resolves to id "${entry.id}"`);
|
|
56
|
+
entryIds.add(entry.id);
|
|
57
|
+
}
|
|
58
|
+
const untouchedEntries = new Set(store.keys());
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
untouchedEntries.delete(entry.id);
|
|
61
|
+
store.set({
|
|
62
|
+
id: entry.id,
|
|
63
|
+
data: entry.data,
|
|
64
|
+
body: entry.source,
|
|
65
|
+
digest: entry.digest,
|
|
66
|
+
filePath: entry.filePath,
|
|
67
|
+
rendered: {
|
|
68
|
+
html: entry.html,
|
|
69
|
+
metadata: { headings: entry.headings }
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
untouchedEntries.forEach((id) => store.delete(id));
|
|
74
|
+
}
|
|
75
|
+
await rebuildAll();
|
|
76
|
+
if (!watcher) return;
|
|
77
|
+
watcher.add(basePath);
|
|
78
|
+
let activeRebuild;
|
|
79
|
+
let rebuildAgain = false;
|
|
80
|
+
const reload = async (filePath) => {
|
|
81
|
+
const entryPath = toPosixPath((0, node_path.relative)(basePath, filePath));
|
|
82
|
+
if (!isTypstEntryPath(entryPath)) return;
|
|
83
|
+
try {
|
|
84
|
+
rebuildAgain = true;
|
|
85
|
+
activeRebuild ??= (async () => {
|
|
86
|
+
while (rebuildAgain) {
|
|
87
|
+
rebuildAgain = false;
|
|
88
|
+
await rebuildAll();
|
|
89
|
+
}
|
|
90
|
+
})().finally(() => {
|
|
91
|
+
activeRebuild = void 0;
|
|
92
|
+
});
|
|
93
|
+
await activeRebuild;
|
|
94
|
+
logger.info(`Reloaded Typst content after ${entryPath}`);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
logger.error(`Failed to reload Typst content after ${entryPath}: ${message}`);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
watcher.on("add", reload);
|
|
101
|
+
watcher.on("change", reload);
|
|
102
|
+
watcher.on("unlink", reload);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
Object.defineProperty(exports, 'typstLoader', {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
get: function () {
|
|
111
|
+
return typstLoader;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=loader-DetFooDG.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader-DetFooDG.cjs","names":["sep","compileTypst"],"sources":["../src/paths.ts","../src/loader.ts"],"sourcesContent":["import { sep } from \"node:path\";\n\nexport function toPosixPath(path: string): string {\n return path.split(sep).join(\"/\");\n}\n\nexport function isTypstEntryPath(path: string): boolean {\n const fileName = path.split(\"/\").at(-1);\n\n return (\n path.endsWith(\".typ\") &&\n path !== \"..\" &&\n !path.startsWith(\"../\") &&\n !fileName?.startsWith(\"_\")\n );\n}\n\nexport function typstEntryId(path: string): string {\n const id = path.slice(0, -\".typ\".length);\n return id.endsWith(\"/index\") ? id.slice(0, -\"/index\".length) : id;\n}\n","import { isTypstEntryPath, toPosixPath, typstEntryId } from \"./paths.js\";\nimport { type CompiledTypst, compileTypst } from \"./render.js\";\nimport type { Loader } from \"astro/loaders\";\nimport { glob, readFile } from \"node:fs/promises\";\nimport { relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport interface TypstLoaderOptions {\n /** Directory containing Typst entries, resolved relative to Astro's root. */\n base?: string;\n}\n\ninterface CompiledEntry {\n id: string;\n source: string;\n data: Record<string, unknown>;\n html: string;\n headings: CompiledTypst[\"headings\"];\n filePath: string;\n digest: string;\n}\n\nexport function typstLoader({\n base = \"src/content\",\n}: TypstLoaderOptions = {}): Loader {\n return {\n name: \"astro-typst-loader\",\n async load({\n config,\n generateDigest,\n logger,\n parseData,\n store,\n watcher,\n }) {\n const rootPath = fileURLToPath(config.root);\n const basePath = fileURLToPath(\n new URL(base.endsWith(\"/\") ? base : `${base}/`, config.root),\n );\n\n async function rebuildAll(): Promise<void> {\n const filePaths: string[] = [];\n for await (const entry of glob(\"**/*.typ\", { cwd: basePath })) {\n const entryPath = toPosixPath(entry);\n if (isTypstEntryPath(entryPath))\n filePaths.push(resolve(basePath, entry));\n }\n\n if (filePaths.length === 0)\n logger.warn(`No Typst files found in ${basePath}`);\n\n const entries = await Promise.all(\n filePaths.map(async (filePath): Promise<CompiledEntry> => {\n const entryPath = toPosixPath(\n relative(basePath, filePath),\n );\n const id = typstEntryId(entryPath);\n const source = await readFile(filePath, \"utf8\");\n const compiled = compileTypst(source);\n const data = (await parseData({\n id,\n data: compiled.metadata,\n filePath,\n })) as Record<string, unknown>;\n\n return {\n id,\n source,\n data,\n html: compiled.html,\n headings: compiled.headings,\n filePath: toPosixPath(relative(rootPath, filePath)),\n digest: generateDigest(\n JSON.stringify({ source, html: compiled.html }),\n ),\n };\n }),\n );\n\n entries.sort((left, right) => left.id.localeCompare(right.id));\n const entryIds = new Set<string>();\n for (const entry of entries) {\n if (entryIds.has(entry.id)) {\n throw new Error(\n `More than one Typst content file resolves to id \"${entry.id}\"`,\n );\n }\n entryIds.add(entry.id);\n }\n\n const untouchedEntries = new Set(store.keys());\n for (const entry of entries) {\n untouchedEntries.delete(entry.id);\n store.set({\n id: entry.id,\n data: entry.data,\n body: entry.source,\n digest: entry.digest,\n filePath: entry.filePath,\n rendered: {\n html: entry.html,\n metadata: { headings: entry.headings },\n },\n });\n }\n untouchedEntries.forEach((id) => store.delete(id));\n }\n\n await rebuildAll();\n if (!watcher) return;\n\n watcher.add(basePath);\n let activeRebuild: Promise<void> | undefined;\n let rebuildAgain = false;\n\n const reload = async (filePath: string): Promise<void> => {\n const entryPath = toPosixPath(relative(basePath, filePath));\n if (!isTypstEntryPath(entryPath)) return;\n\n try {\n rebuildAgain = true;\n activeRebuild ??= (async () => {\n while (rebuildAgain) {\n rebuildAgain = false;\n await rebuildAll();\n }\n })().finally(() => {\n activeRebuild = undefined;\n });\n await activeRebuild;\n logger.info(`Reloaded Typst content after ${entryPath}`);\n } catch (error) {\n const message =\n error instanceof Error ? error.message : String(error);\n logger.error(\n `Failed to reload Typst content after ${entryPath}: ${message}`,\n );\n }\n };\n\n watcher.on(\"add\", reload);\n watcher.on(\"change\", reload);\n watcher.on(\"unlink\", reload);\n },\n };\n}\n"],"mappings":";;;;;;AAEA,SAAgB,YAAY,MAAsB;AAC9C,QAAO,KAAK,MAAMA,cAAI,CAAC,KAAK,IAAI;;AAGpC,SAAgB,iBAAiB,MAAuB;CACpD,MAAM,WAAW,KAAK,MAAM,IAAI,CAAC,GAAG,GAAG;AAEvC,QACI,KAAK,SAAS,OAAO,IACrB,SAAS,QACT,CAAC,KAAK,WAAW,MAAM,IACvB,CAAC,UAAU,WAAW,IAAI;;AAIlC,SAAgB,aAAa,MAAsB;CAC/C,MAAM,KAAK,KAAK,MAAM,GAAG,GAAe;AACxC,QAAO,GAAG,SAAS,SAAS,GAAG,GAAG,MAAM,GAAG,GAAiB,GAAG;;;;;ACGnE,SAAgB,YAAY,EACxB,OAAO,kBACa,EAAE,EAAU;AAChC,QAAO;EACH,MAAM;EACN,MAAM,KAAK,EACP,QACA,gBACA,QACA,WACA,OACA,WACD;GACC,MAAM,uCAAyB,OAAO,KAAK;GAC3C,MAAM,uCACF,IAAI,IAAI,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,KAAK,IAAI,OAAO,KAAK,CAC/D;GAED,eAAe,aAA4B;IACvC,MAAM,YAAsB,EAAE;AAC9B,eAAW,MAAM,oCAAc,YAAY,EAAE,KAAK,UAAU,CAAC,CAEzD,KAAI,iBADc,YAAY,MACA,CAAC,CAC3B,WAAU,4BAAa,UAAU,MAAM,CAAC;AAGhD,QAAI,UAAU,WAAW,EACrB,QAAO,KAAK,2BAA2B,WAAW;IAEtD,MAAM,UAAU,MAAM,QAAQ,IAC1B,UAAU,IAAI,OAAO,aAAqC;KAItD,MAAM,KAAK,aAHO,oCACL,UAAU,SAAS,CAEC,CAAC;KAClC,MAAM,SAAS,qCAAe,UAAU,OAAO;KAC/C,MAAM,WAAWC,4BAAa,OAAO;AAOrC,YAAO;MACH;MACA;MACA,YATgB,UAAU;OAC1B;OACA,MAAM,SAAS;OACf;OACH,CAAC;MAME,MAAM,SAAS;MACf,UAAU,SAAS;MACnB,UAAU,oCAAqB,UAAU,SAAS,CAAC;MACnD,QAAQ,eACJ,KAAK,UAAU;OAAE;OAAQ,MAAM,SAAS;OAAM,CAAC,CAClD;MACJ;MACH,CACL;AAED,YAAQ,MAAM,MAAM,UAAU,KAAK,GAAG,cAAc,MAAM,GAAG,CAAC;IAC9D,MAAM,2BAAW,IAAI,KAAa;AAClC,SAAK,MAAM,SAAS,SAAS;AACzB,SAAI,SAAS,IAAI,MAAM,GAAG,CACtB,OAAM,IAAI,MACN,oDAAoD,MAAM,GAAG,GAChE;AAEL,cAAS,IAAI,MAAM,GAAG;;IAG1B,MAAM,mBAAmB,IAAI,IAAI,MAAM,MAAM,CAAC;AAC9C,SAAK,MAAM,SAAS,SAAS;AACzB,sBAAiB,OAAO,MAAM,GAAG;AACjC,WAAM,IAAI;MACN,IAAI,MAAM;MACV,MAAM,MAAM;MACZ,MAAM,MAAM;MACZ,QAAQ,MAAM;MACd,UAAU,MAAM;MAChB,UAAU;OACN,MAAM,MAAM;OACZ,UAAU,EAAE,UAAU,MAAM,UAAU;OACzC;MACJ,CAAC;;AAEN,qBAAiB,SAAS,OAAO,MAAM,OAAO,GAAG,CAAC;;AAGtD,SAAM,YAAY;AAClB,OAAI,CAAC,QAAS;AAEd,WAAQ,IAAI,SAAS;GACrB,IAAI;GACJ,IAAI,eAAe;GAEnB,MAAM,SAAS,OAAO,aAAoC;IACtD,MAAM,YAAY,oCAAqB,UAAU,SAAS,CAAC;AAC3D,QAAI,CAAC,iBAAiB,UAAU,CAAE;AAElC,QAAI;AACA,oBAAe;AACf,wBAAmB,YAAY;AAC3B,aAAO,cAAc;AACjB,sBAAe;AACf,aAAM,YAAY;;SAEtB,CAAC,cAAc;AACf,sBAAgB;OAClB;AACF,WAAM;AACN,YAAO,KAAK,gCAAgC,YAAY;aACnD,OAAO;KACZ,MAAM,UACF,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC1D,YAAO,MACH,wCAAwC,UAAU,IAAI,UACzD;;;AAIT,WAAQ,GAAG,OAAO,OAAO;AACzB,WAAQ,GAAG,UAAU,OAAO;AAC5B,WAAQ,GAAG,UAAU,OAAO;;EAEnC"}
|
package/dist/loader.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Loader } from "astro/loaders";
|
|
2
|
+
|
|
3
|
+
//#region src/loader.d.ts
|
|
4
|
+
interface TypstLoaderOptions {
|
|
5
|
+
/** Directory containing Typst entries, resolved relative to Astro's root. */
|
|
6
|
+
base?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function typstLoader({
|
|
9
|
+
base
|
|
10
|
+
}?: TypstLoaderOptions): Loader;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { TypstLoaderOptions, typstLoader };
|
|
13
|
+
//# sourceMappingURL=loader.d.cts.map
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Loader } from "astro/loaders";
|
|
2
|
+
|
|
3
|
+
//#region src/loader.d.ts
|
|
4
|
+
interface TypstLoaderOptions {
|
|
5
|
+
/** Directory containing Typst entries, resolved relative to Astro's root. */
|
|
6
|
+
base?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function typstLoader({
|
|
9
|
+
base
|
|
10
|
+
}?: TypstLoaderOptions): Loader;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { TypstLoaderOptions, typstLoader };
|
|
13
|
+
//# sourceMappingURL=loader.d.ts.map
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
let cheerio = require("cheerio");
|
|
2
|
+
let wasm_typst_wasm_js = require("../wasm/typst_wasm.js");
|
|
3
|
+
|
|
4
|
+
//#region src/render.ts
|
|
5
|
+
function renderTypst(typst) {
|
|
6
|
+
return prepareHtml((0, wasm_typst_wasm_js.typstToHtml)(typst)).html;
|
|
7
|
+
}
|
|
8
|
+
function compileTypst(typst) {
|
|
9
|
+
const compiled = JSON.parse((0, wasm_typst_wasm_js.typstToHtmlWithMetadata)(typst));
|
|
10
|
+
if (!isRecord(compiled) || typeof compiled.html !== "string") throw new TypeError("Typst WASM returned an invalid compilation result");
|
|
11
|
+
if (!isRecord(compiled.metadata)) throw new TypeError("the Typst `astro` variable must be a dictionary");
|
|
12
|
+
const rendered = prepareHtml(compiled.html);
|
|
13
|
+
return {
|
|
14
|
+
html: rendered.html,
|
|
15
|
+
headings: rendered.headings,
|
|
16
|
+
metadata: compiled.metadata
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function isRecord(value) {
|
|
20
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
function prepareHtml(html) {
|
|
23
|
+
const $ = (0, cheerio.load)(html, null, false);
|
|
24
|
+
const headings = [];
|
|
25
|
+
const usedIds = new Set($("[id]").map((_, element) => $(element).attr("id")).get().filter((id) => Boolean(id)));
|
|
26
|
+
$("h1, h2, h3, h4, h5, h6").each((_, heading) => {
|
|
27
|
+
const element = $(heading);
|
|
28
|
+
const text = element.text().trim();
|
|
29
|
+
let id = element.attr("id");
|
|
30
|
+
if (!id) {
|
|
31
|
+
const base = slugify(text) || "heading";
|
|
32
|
+
id = base;
|
|
33
|
+
let suffix = 2;
|
|
34
|
+
while (usedIds.has(id)) {
|
|
35
|
+
id = `${base}-${suffix}`;
|
|
36
|
+
suffix += 1;
|
|
37
|
+
}
|
|
38
|
+
element.attr("id", id);
|
|
39
|
+
usedIds.add(id);
|
|
40
|
+
}
|
|
41
|
+
headings.push({
|
|
42
|
+
depth: Number(heading.tagName.slice(1)),
|
|
43
|
+
slug: id,
|
|
44
|
+
text
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
html: $.html(),
|
|
49
|
+
headings
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function slugify(value) {
|
|
53
|
+
return value.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en").replace(/[’']/gu, "").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
Object.defineProperty(exports, 'compileTypst', {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
get: function () {
|
|
60
|
+
return compileTypst;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
Object.defineProperty(exports, 'renderTypst', {
|
|
64
|
+
enumerable: true,
|
|
65
|
+
get: function () {
|
|
66
|
+
return renderTypst;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=render-UEphEA_d.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-UEphEA_d.cjs","names":[],"sources":["../src/render.ts"],"sourcesContent":["import { typstToHtml, typstToHtmlWithMetadata } from \"./wasm.js\";\nimport type { MarkdownHeading } from \"astro\";\nimport { load } from \"cheerio\";\n\nexport interface CompiledTypst {\n html: string;\n headings: MarkdownHeading[];\n metadata: Record<string, unknown>;\n}\n\nexport function renderTypst(typst: string): string {\n return prepareHtml(typstToHtml(typst)).html;\n}\n\nexport function compileTypst(typst: string): CompiledTypst {\n const compiled: unknown = JSON.parse(typstToHtmlWithMetadata(typst));\n\n if (!isRecord(compiled) || typeof compiled.html !== \"string\") {\n throw new TypeError(\n \"Typst WASM returned an invalid compilation result\",\n );\n }\n\n if (!isRecord(compiled.metadata)) {\n throw new TypeError(\"the Typst `astro` variable must be a dictionary\");\n }\n\n const rendered = prepareHtml(compiled.html);\n return {\n html: rendered.html,\n headings: rendered.headings,\n metadata: compiled.metadata,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction prepareHtml(html: string): {\n html: string;\n headings: MarkdownHeading[];\n} {\n const $ = load(html, null, false);\n const headings: MarkdownHeading[] = [];\n const usedIds = new Set(\n $(\"[id]\")\n .map((_, element) => $(element).attr(\"id\"))\n .get()\n .filter((id): id is string => Boolean(id)),\n );\n\n $(\"h1, h2, h3, h4, h5, h6\").each((_, heading) => {\n const element = $(heading);\n const text = element.text().trim();\n let id = element.attr(\"id\");\n\n if (!id) {\n const base = slugify(text) || \"heading\";\n id = base;\n let suffix = 2;\n while (usedIds.has(id)) {\n id = `${base}-${suffix}`;\n suffix += 1;\n }\n element.attr(\"id\", id);\n usedIds.add(id);\n }\n\n headings.push({\n depth: Number(heading.tagName.slice(1)),\n slug: id,\n text,\n });\n });\n\n return { html: $.html(), headings };\n}\n\nfunction slugify(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/\\p{Mark}+/gu, \"\")\n .toLocaleLowerCase(\"en\")\n .replace(/[’']/gu, \"\")\n .replace(/[^\\p{Letter}\\p{Number}]+/gu, \"-\")\n .replace(/^-+|-+$/gu, \"\");\n}\n"],"mappings":";;;;AAUA,SAAgB,YAAY,OAAuB;AAC/C,QAAO,gDAAwB,MAAM,CAAC,CAAC;;AAG3C,SAAgB,aAAa,OAA8B;CACvD,MAAM,WAAoB,KAAK,sDAA8B,MAAM,CAAC;AAEpE,KAAI,CAAC,SAAS,SAAS,IAAI,OAAO,SAAS,SAAS,SAChD,OAAM,IAAI,UACN,oDACH;AAGL,KAAI,CAAC,SAAS,SAAS,SAAS,CAC5B,OAAM,IAAI,UAAU,kDAAkD;CAG1E,MAAM,WAAW,YAAY,SAAS,KAAK;AAC3C,QAAO;EACH,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,UAAU,SAAS;EACtB;;AAGL,SAAS,SAAS,OAAkD;AAChE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG/E,SAAS,YAAY,MAGnB;CACE,MAAM,sBAAS,MAAM,MAAM,MAAM;CACjC,MAAM,WAA8B,EAAE;CACtC,MAAM,UAAU,IAAI,IAChB,EAAE,OAAO,CACJ,KAAK,GAAG,YAAY,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC,CAC1C,KAAK,CACL,QAAQ,OAAqB,QAAQ,GAAG,CAAC,CACjD;AAED,GAAE,yBAAyB,CAAC,MAAM,GAAG,YAAY;EAC7C,MAAM,UAAU,EAAE,QAAQ;EAC1B,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM;EAClC,IAAI,KAAK,QAAQ,KAAK,KAAK;AAE3B,MAAI,CAAC,IAAI;GACL,MAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,QAAK;GACL,IAAI,SAAS;AACb,UAAO,QAAQ,IAAI,GAAG,EAAE;AACpB,SAAK,GAAG,KAAK,GAAG;AAChB,cAAU;;AAEd,WAAQ,KAAK,MAAM,GAAG;AACtB,WAAQ,IAAI,GAAG;;AAGnB,WAAS,KAAK;GACV,OAAO,OAAO,QAAQ,QAAQ,MAAM,EAAE,CAAC;GACvC,MAAM;GACN;GACH,CAAC;GACJ;AAEF,QAAO;EAAE,MAAM,EAAE,MAAM;EAAE;EAAU;;AAGvC,SAAS,QAAQ,OAAuB;AACpC,QAAO,MACF,UAAU,OAAO,CACjB,QAAQ,eAAe,GAAG,CAC1B,kBAAkB,KAAK,CACvB,QAAQ,UAAU,GAAG,CACrB,QAAQ,8BAA8B,IAAI,CAC1C,QAAQ,aAAa,GAAG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { typstToHtml, typstToHtmlWithMetadata } from "./wasm.js";
|
|
2
|
+
import { load } from "cheerio";
|
|
3
|
+
|
|
4
|
+
//#region src/render.ts
|
|
5
|
+
function renderTypst(typst) {
|
|
6
|
+
return prepareHtml(typstToHtml(typst)).html;
|
|
7
|
+
}
|
|
8
|
+
function compileTypst(typst) {
|
|
9
|
+
const compiled = JSON.parse(typstToHtmlWithMetadata(typst));
|
|
10
|
+
if (!isRecord(compiled) || typeof compiled.html !== "string") throw new TypeError("Typst WASM returned an invalid compilation result");
|
|
11
|
+
if (!isRecord(compiled.metadata)) throw new TypeError("the Typst `astro` variable must be a dictionary");
|
|
12
|
+
const rendered = prepareHtml(compiled.html);
|
|
13
|
+
return {
|
|
14
|
+
html: rendered.html,
|
|
15
|
+
headings: rendered.headings,
|
|
16
|
+
metadata: compiled.metadata
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function isRecord(value) {
|
|
20
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
function prepareHtml(html) {
|
|
23
|
+
const $ = load(html, null, false);
|
|
24
|
+
const headings = [];
|
|
25
|
+
const usedIds = new Set($("[id]").map((_, element) => $(element).attr("id")).get().filter((id) => Boolean(id)));
|
|
26
|
+
$("h1, h2, h3, h4, h5, h6").each((_, heading) => {
|
|
27
|
+
const element = $(heading);
|
|
28
|
+
const text = element.text().trim();
|
|
29
|
+
let id = element.attr("id");
|
|
30
|
+
if (!id) {
|
|
31
|
+
const base = slugify(text) || "heading";
|
|
32
|
+
id = base;
|
|
33
|
+
let suffix = 2;
|
|
34
|
+
while (usedIds.has(id)) {
|
|
35
|
+
id = `${base}-${suffix}`;
|
|
36
|
+
suffix += 1;
|
|
37
|
+
}
|
|
38
|
+
element.attr("id", id);
|
|
39
|
+
usedIds.add(id);
|
|
40
|
+
}
|
|
41
|
+
headings.push({
|
|
42
|
+
depth: Number(heading.tagName.slice(1)),
|
|
43
|
+
slug: id,
|
|
44
|
+
text
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
html: $.html(),
|
|
49
|
+
headings
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function slugify(value) {
|
|
53
|
+
return value.normalize("NFKD").replace(/\p{Mark}+/gu, "").toLocaleLowerCase("en").replace(/[’']/gu, "").replace(/[^\p{Letter}\p{Number}]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
export { renderTypst as n, compileTypst as t };
|
|
58
|
+
//# sourceMappingURL=render-hwAGjpa0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-hwAGjpa0.js","names":[],"sources":["../src/render.ts"],"sourcesContent":["import { typstToHtml, typstToHtmlWithMetadata } from \"./wasm.js\";\nimport type { MarkdownHeading } from \"astro\";\nimport { load } from \"cheerio\";\n\nexport interface CompiledTypst {\n html: string;\n headings: MarkdownHeading[];\n metadata: Record<string, unknown>;\n}\n\nexport function renderTypst(typst: string): string {\n return prepareHtml(typstToHtml(typst)).html;\n}\n\nexport function compileTypst(typst: string): CompiledTypst {\n const compiled: unknown = JSON.parse(typstToHtmlWithMetadata(typst));\n\n if (!isRecord(compiled) || typeof compiled.html !== \"string\") {\n throw new TypeError(\n \"Typst WASM returned an invalid compilation result\",\n );\n }\n\n if (!isRecord(compiled.metadata)) {\n throw new TypeError(\"the Typst `astro` variable must be a dictionary\");\n }\n\n const rendered = prepareHtml(compiled.html);\n return {\n html: rendered.html,\n headings: rendered.headings,\n metadata: compiled.metadata,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction prepareHtml(html: string): {\n html: string;\n headings: MarkdownHeading[];\n} {\n const $ = load(html, null, false);\n const headings: MarkdownHeading[] = [];\n const usedIds = new Set(\n $(\"[id]\")\n .map((_, element) => $(element).attr(\"id\"))\n .get()\n .filter((id): id is string => Boolean(id)),\n );\n\n $(\"h1, h2, h3, h4, h5, h6\").each((_, heading) => {\n const element = $(heading);\n const text = element.text().trim();\n let id = element.attr(\"id\");\n\n if (!id) {\n const base = slugify(text) || \"heading\";\n id = base;\n let suffix = 2;\n while (usedIds.has(id)) {\n id = `${base}-${suffix}`;\n suffix += 1;\n }\n element.attr(\"id\", id);\n usedIds.add(id);\n }\n\n headings.push({\n depth: Number(heading.tagName.slice(1)),\n slug: id,\n text,\n });\n });\n\n return { html: $.html(), headings };\n}\n\nfunction slugify(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/\\p{Mark}+/gu, \"\")\n .toLocaleLowerCase(\"en\")\n .replace(/[’']/gu, \"\")\n .replace(/[^\\p{Letter}\\p{Number}]+/gu, \"-\")\n .replace(/^-+|-+$/gu, \"\");\n}\n"],"mappings":";;;;AAUA,SAAgB,YAAY,OAAuB;AAC/C,QAAO,YAAY,YAAY,MAAM,CAAC,CAAC;;AAG3C,SAAgB,aAAa,OAA8B;CACvD,MAAM,WAAoB,KAAK,MAAM,wBAAwB,MAAM,CAAC;AAEpE,KAAI,CAAC,SAAS,SAAS,IAAI,OAAO,SAAS,SAAS,SAChD,OAAM,IAAI,UACN,oDACH;AAGL,KAAI,CAAC,SAAS,SAAS,SAAS,CAC5B,OAAM,IAAI,UAAU,kDAAkD;CAG1E,MAAM,WAAW,YAAY,SAAS,KAAK;AAC3C,QAAO;EACH,MAAM,SAAS;EACf,UAAU,SAAS;EACnB,UAAU,SAAS;EACtB;;AAGL,SAAS,SAAS,OAAkD;AAChE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM;;AAG/E,SAAS,YAAY,MAGnB;CACE,MAAM,IAAI,KAAK,MAAM,MAAM,MAAM;CACjC,MAAM,WAA8B,EAAE;CACtC,MAAM,UAAU,IAAI,IAChB,EAAE,OAAO,CACJ,KAAK,GAAG,YAAY,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC,CAC1C,KAAK,CACL,QAAQ,OAAqB,QAAQ,GAAG,CAAC,CACjD;AAED,GAAE,yBAAyB,CAAC,MAAM,GAAG,YAAY;EAC7C,MAAM,UAAU,EAAE,QAAQ;EAC1B,MAAM,OAAO,QAAQ,MAAM,CAAC,MAAM;EAClC,IAAI,KAAK,QAAQ,KAAK,KAAK;AAE3B,MAAI,CAAC,IAAI;GACL,MAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,QAAK;GACL,IAAI,SAAS;AACb,UAAO,QAAQ,IAAI,GAAG,EAAE;AACpB,SAAK,GAAG,KAAK,GAAG;AAChB,cAAU;;AAEd,WAAQ,KAAK,MAAM,GAAG;AACtB,WAAQ,IAAI,GAAG;;AAGnB,WAAS,KAAK;GACV,OAAO,OAAO,QAAQ,QAAQ,MAAM,EAAE,CAAC;GACvC,MAAM;GACN;GACH,CAAC;GACJ;AAEF,QAAO;EAAE,MAAM,EAAE,MAAM;EAAE;EAAU;;AAGvC,SAAS,QAAQ,OAAuB;AACpC,QAAO,MACF,UAAU,OAAO,CACjB,QAAQ,eAAe,GAAG,CAC1B,kBAAkB,KAAK,CACvB,QAAQ,UAAU,GAAG,CACrB,QAAQ,8BAA8B,IAAI,CAC1C,QAAQ,aAAa,GAAG"}
|
package/dist/vite.cjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_render = require('./render-UEphEA_d.cjs');
|
|
3
|
+
|
|
4
|
+
//#region src/vite.ts
|
|
5
|
+
/** Transform imported `.typ` files into modules whose default export is HTML. */
|
|
6
|
+
function typst({ include = /\.typ$/ } = {}) {
|
|
7
|
+
return {
|
|
8
|
+
name: "astro-typst",
|
|
9
|
+
enforce: "pre",
|
|
10
|
+
transform: {
|
|
11
|
+
filter: { id: include },
|
|
12
|
+
handler(source) {
|
|
13
|
+
const html = require_render.renderTypst(source);
|
|
14
|
+
return {
|
|
15
|
+
code: `export default ${JSON.stringify(html)};`,
|
|
16
|
+
map: null
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.typst = typst;
|
|
25
|
+
//# sourceMappingURL=vite.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.cjs","names":["renderTypst"],"sources":["../src/vite.ts"],"sourcesContent":["import { renderTypst } from \"./render.js\";\nimport type { PluginOption } from \"vite\";\n\nexport interface TypstPluginOptions {\n include?: RegExp;\n}\n\n/** Transform imported `.typ` files into modules whose default export is HTML. */\nexport function typst({\n include = /\\.typ$/,\n}: TypstPluginOptions = {}): PluginOption {\n return {\n name: \"astro-typst\",\n enforce: \"pre\",\n transform: {\n filter: { id: include },\n handler(source) {\n const html = renderTypst(source);\n return {\n code: `export default ${JSON.stringify(html)};`,\n map: null,\n };\n },\n },\n };\n}\n"],"mappings":";;;;;AAQA,SAAgB,MAAM,EAClB,UAAU,aACU,EAAE,EAAgB;AACtC,QAAO;EACH,MAAM;EACN,SAAS;EACT,WAAW;GACP,QAAQ,EAAE,IAAI,SAAS;GACvB,QAAQ,QAAQ;IACZ,MAAM,OAAOA,2BAAY,OAAO;AAChC,WAAO;KACH,MAAM,kBAAkB,KAAK,UAAU,KAAK,CAAC;KAC7C,KAAK;KACR;;GAER;EACJ"}
|
package/dist/vite.d.cts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PluginOption } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/vite.d.ts
|
|
4
|
+
interface TypstPluginOptions {
|
|
5
|
+
include?: RegExp;
|
|
6
|
+
}
|
|
7
|
+
/** Transform imported `.typ` files into modules whose default export is HTML. */
|
|
8
|
+
declare function typst({
|
|
9
|
+
include
|
|
10
|
+
}?: TypstPluginOptions): PluginOption;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { TypstPluginOptions, typst };
|
|
13
|
+
//# sourceMappingURL=vite.d.cts.map
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PluginOption } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/vite.d.ts
|
|
4
|
+
interface TypstPluginOptions {
|
|
5
|
+
include?: RegExp;
|
|
6
|
+
}
|
|
7
|
+
/** Transform imported `.typ` files into modules whose default export is HTML. */
|
|
8
|
+
declare function typst({
|
|
9
|
+
include
|
|
10
|
+
}?: TypstPluginOptions): PluginOption;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { TypstPluginOptions, typst };
|
|
13
|
+
//# sourceMappingURL=vite.d.ts.map
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { n as renderTypst } from "./render-hwAGjpa0.js";
|
|
2
|
+
|
|
3
|
+
//#region src/vite.ts
|
|
4
|
+
/** Transform imported `.typ` files into modules whose default export is HTML. */
|
|
5
|
+
function typst({ include = /\.typ$/ } = {}) {
|
|
6
|
+
return {
|
|
7
|
+
name: "astro-typst",
|
|
8
|
+
enforce: "pre",
|
|
9
|
+
transform: {
|
|
10
|
+
filter: { id: include },
|
|
11
|
+
handler(source) {
|
|
12
|
+
const html = renderTypst(source);
|
|
13
|
+
return {
|
|
14
|
+
code: `export default ${JSON.stringify(html)};`,
|
|
15
|
+
map: null
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { typst };
|
|
24
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["import { renderTypst } from \"./render.js\";\nimport type { PluginOption } from \"vite\";\n\nexport interface TypstPluginOptions {\n include?: RegExp;\n}\n\n/** Transform imported `.typ` files into modules whose default export is HTML. */\nexport function typst({\n include = /\\.typ$/,\n}: TypstPluginOptions = {}): PluginOption {\n return {\n name: \"astro-typst\",\n enforce: \"pre\",\n transform: {\n filter: { id: include },\n handler(source) {\n const html = renderTypst(source);\n return {\n code: `export default ${JSON.stringify(html)};`,\n map: null,\n };\n },\n },\n };\n}\n"],"mappings":";;;;AAQA,SAAgB,MAAM,EAClB,UAAU,aACU,EAAE,EAAgB;AACtC,QAAO;EACH,MAAM;EACN,SAAS;EACT,WAAW;GACP,QAAQ,EAAE,IAAI,SAAS;GACvB,QAAQ,QAAQ;IACZ,MAAM,OAAO,YAAY,OAAO;AAChC,WAAO;KACH,MAAM,kBAAkB,KAAK,UAAU,KAAK,CAAC;KAC7C,KAAK;KACR;;GAER;EACJ"}
|
package/dist/wasm.cjs
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
let wasm_typst_wasm_js = require("../wasm/typst_wasm.js");
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, 'typstToHtml', {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
get: function () {
|
|
7
|
+
return wasm_typst_wasm_js.typstToHtml;
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
Object.defineProperty(exports, 'typstToHtmlWithMetadata', {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return wasm_typst_wasm_js.typstToHtmlWithMetadata;
|
|
14
|
+
}
|
|
15
|
+
});
|
package/dist/wasm.d.cts
ADDED
package/dist/wasm.d.ts
ADDED
package/dist/wasm.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@floffah/astro-typst",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "Typst rendering primitives, an Astro content loader, and a Vite plugin powered by WebAssembly.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"wasm"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"require": {
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"default": "./dist/index.cjs"
|
|
23
|
+
},
|
|
24
|
+
"default": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"./loader": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/loader.d.ts",
|
|
32
|
+
"default": "./dist/loader.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/loader.d.cts",
|
|
36
|
+
"default": "./dist/loader.cjs"
|
|
37
|
+
},
|
|
38
|
+
"default": {
|
|
39
|
+
"types": "./dist/loader.d.cts",
|
|
40
|
+
"default": "./dist/loader.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"./vite": {
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./dist/vite.d.ts",
|
|
46
|
+
"default": "./dist/vite.js"
|
|
47
|
+
},
|
|
48
|
+
"require": {
|
|
49
|
+
"types": "./dist/vite.d.cts",
|
|
50
|
+
"default": "./dist/vite.cjs"
|
|
51
|
+
},
|
|
52
|
+
"default": {
|
|
53
|
+
"types": "./dist/vite.d.cts",
|
|
54
|
+
"default": "./dist/vite.cjs"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"./wasm": {
|
|
58
|
+
"import": {
|
|
59
|
+
"types": "./dist/wasm.d.ts",
|
|
60
|
+
"default": "./dist/wasm.js"
|
|
61
|
+
},
|
|
62
|
+
"require": {
|
|
63
|
+
"types": "./dist/wasm.d.cts",
|
|
64
|
+
"default": "./dist/wasm.cjs"
|
|
65
|
+
},
|
|
66
|
+
"default": {
|
|
67
|
+
"types": "./dist/wasm.d.cts",
|
|
68
|
+
"default": "./dist/wasm.cjs"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"scripts": {
|
|
73
|
+
"build:wasm": "wasm-pack build crates/typst-wasm --release --target nodejs --out-dir ../../wasm --no-pack && bun run scripts/write-wasm-package.ts",
|
|
74
|
+
"build:ts": "tsdown",
|
|
75
|
+
"build": "bun run build:wasm && bun run build:ts",
|
|
76
|
+
"lint": "tsc -p tsconfig.json --noEmit && eslint . --ext .ts",
|
|
77
|
+
"format": "prettier --write .",
|
|
78
|
+
"format:check": "prettier --check .",
|
|
79
|
+
"check": "bun run lint && bun run format:check",
|
|
80
|
+
"test": "bun test && cargo test --manifest-path crates/typst-wasm/Cargo.toml",
|
|
81
|
+
"lint:package": "attw --pack . --profile node16 && publint --package . --pack npm",
|
|
82
|
+
"changeset": "changeset",
|
|
83
|
+
"version": "changeset version",
|
|
84
|
+
"release": "bun run build && changeset publish"
|
|
85
|
+
},
|
|
86
|
+
"engines": {
|
|
87
|
+
"node": ">=22.12.0"
|
|
88
|
+
},
|
|
89
|
+
"peerDependencies": {
|
|
90
|
+
"astro": ">=6.4"
|
|
91
|
+
},
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"cheerio": "^1.2.0"
|
|
94
|
+
},
|
|
95
|
+
"devDependencies": {
|
|
96
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
97
|
+
"@changesets/changelog-github": "^0.6.0",
|
|
98
|
+
"@changesets/cli": "^2.31.0",
|
|
99
|
+
"@eslint/compat": "^2.0.5",
|
|
100
|
+
"@eslint/js": "^10.0.1",
|
|
101
|
+
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
|
102
|
+
"@types/bun": "^1.3.14",
|
|
103
|
+
"astro": "^6.4.7",
|
|
104
|
+
"eslint": "^10.2.1",
|
|
105
|
+
"eslint-config-prettier": "^10.1.8",
|
|
106
|
+
"jiti": "^2.6.1",
|
|
107
|
+
"prettier": "^3.8.3",
|
|
108
|
+
"publint": "^0.3.18",
|
|
109
|
+
"tsdown": "^0.21.10",
|
|
110
|
+
"typescript": "^5.9.3",
|
|
111
|
+
"typescript-eslint": "^8.59.0",
|
|
112
|
+
"vite": "^7.3.1"
|
|
113
|
+
},
|
|
114
|
+
"keywords": [
|
|
115
|
+
"astro",
|
|
116
|
+
"typst",
|
|
117
|
+
"vite",
|
|
118
|
+
"webassembly"
|
|
119
|
+
],
|
|
120
|
+
"repository": {
|
|
121
|
+
"type": "git",
|
|
122
|
+
"url": "git+https://github.com/Floffah/astro-typst.git"
|
|
123
|
+
},
|
|
124
|
+
"author": "Floffah <therealfloffah@gmail.com>",
|
|
125
|
+
"publishConfig": {
|
|
126
|
+
"access": "public"
|
|
127
|
+
},
|
|
128
|
+
"packageManager": "bun@1.3.14",
|
|
129
|
+
"license": "MIT"
|
|
130
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/* @ts-self-types="./typst_wasm.d.ts" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} input
|
|
5
|
+
* @returns {string}
|
|
6
|
+
*/
|
|
7
|
+
function typstToHtml(input) {
|
|
8
|
+
let deferred3_0;
|
|
9
|
+
let deferred3_1;
|
|
10
|
+
try {
|
|
11
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
12
|
+
const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
13
|
+
const len0 = WASM_VECTOR_LEN;
|
|
14
|
+
wasm.typstToHtml(retptr, ptr0, len0);
|
|
15
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
16
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
17
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
18
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
19
|
+
var ptr2 = r0;
|
|
20
|
+
var len2 = r1;
|
|
21
|
+
if (r3) {
|
|
22
|
+
ptr2 = 0; len2 = 0;
|
|
23
|
+
throw takeObject(r2);
|
|
24
|
+
}
|
|
25
|
+
deferred3_0 = ptr2;
|
|
26
|
+
deferred3_1 = len2;
|
|
27
|
+
return getStringFromWasm0(ptr2, len2);
|
|
28
|
+
} finally {
|
|
29
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
30
|
+
wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.typstToHtml = typstToHtml;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compile a Typst content entry and return `{ html, metadata }` as JSON.
|
|
37
|
+
* @param {string} input
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
function typstToHtmlWithMetadata(input) {
|
|
41
|
+
let deferred3_0;
|
|
42
|
+
let deferred3_1;
|
|
43
|
+
try {
|
|
44
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
45
|
+
const ptr0 = passStringToWasm0(input, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
46
|
+
const len0 = WASM_VECTOR_LEN;
|
|
47
|
+
wasm.typstToHtmlWithMetadata(retptr, ptr0, len0);
|
|
48
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
49
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
50
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
51
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
52
|
+
var ptr2 = r0;
|
|
53
|
+
var len2 = r1;
|
|
54
|
+
if (r3) {
|
|
55
|
+
ptr2 = 0; len2 = 0;
|
|
56
|
+
throw takeObject(r2);
|
|
57
|
+
}
|
|
58
|
+
deferred3_0 = ptr2;
|
|
59
|
+
deferred3_1 = len2;
|
|
60
|
+
return getStringFromWasm0(ptr2, len2);
|
|
61
|
+
} finally {
|
|
62
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
63
|
+
wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.typstToHtmlWithMetadata = typstToHtmlWithMetadata;
|
|
67
|
+
function __wbg_get_imports() {
|
|
68
|
+
const import0 = {
|
|
69
|
+
__proto__: null,
|
|
70
|
+
__wbg_Error_fdd633d4bb5dd76a: function(arg0, arg1) {
|
|
71
|
+
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
72
|
+
return addHeapObject(ret);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
__proto__: null,
|
|
77
|
+
"./typst_wasm_bg.js": import0,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function addHeapObject(obj) {
|
|
82
|
+
if (heap_next === heap.length) heap.push(heap.length + 1);
|
|
83
|
+
const idx = heap_next;
|
|
84
|
+
heap_next = heap[idx];
|
|
85
|
+
|
|
86
|
+
heap[idx] = obj;
|
|
87
|
+
return idx;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function dropObject(idx) {
|
|
91
|
+
if (idx < 1028) return;
|
|
92
|
+
heap[idx] = heap_next;
|
|
93
|
+
heap_next = idx;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let cachedDataViewMemory0 = null;
|
|
97
|
+
function getDataViewMemory0() {
|
|
98
|
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
|
99
|
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
|
100
|
+
}
|
|
101
|
+
return cachedDataViewMemory0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getStringFromWasm0(ptr, len) {
|
|
105
|
+
return decodeText(ptr >>> 0, len);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let cachedUint8ArrayMemory0 = null;
|
|
109
|
+
function getUint8ArrayMemory0() {
|
|
110
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
111
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
112
|
+
}
|
|
113
|
+
return cachedUint8ArrayMemory0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getObject(idx) { return heap[idx]; }
|
|
117
|
+
|
|
118
|
+
let heap = new Array(1024).fill(undefined);
|
|
119
|
+
heap.push(undefined, null, true, false);
|
|
120
|
+
|
|
121
|
+
let heap_next = heap.length;
|
|
122
|
+
|
|
123
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
124
|
+
if (realloc === undefined) {
|
|
125
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
126
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
127
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
128
|
+
WASM_VECTOR_LEN = buf.length;
|
|
129
|
+
return ptr;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let len = arg.length;
|
|
133
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
134
|
+
|
|
135
|
+
const mem = getUint8ArrayMemory0();
|
|
136
|
+
|
|
137
|
+
let offset = 0;
|
|
138
|
+
|
|
139
|
+
for (; offset < len; offset++) {
|
|
140
|
+
const code = arg.charCodeAt(offset);
|
|
141
|
+
if (code > 0x7F) break;
|
|
142
|
+
mem[ptr + offset] = code;
|
|
143
|
+
}
|
|
144
|
+
if (offset !== len) {
|
|
145
|
+
if (offset !== 0) {
|
|
146
|
+
arg = arg.slice(offset);
|
|
147
|
+
}
|
|
148
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
149
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
150
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
151
|
+
|
|
152
|
+
offset += ret.written;
|
|
153
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
WASM_VECTOR_LEN = offset;
|
|
157
|
+
return ptr;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function takeObject(idx) {
|
|
161
|
+
const ret = getObject(idx);
|
|
162
|
+
dropObject(idx);
|
|
163
|
+
return ret;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
167
|
+
cachedTextDecoder.decode();
|
|
168
|
+
function decodeText(ptr, len) {
|
|
169
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const cachedTextEncoder = new TextEncoder();
|
|
173
|
+
|
|
174
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
175
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
176
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
177
|
+
view.set(buf);
|
|
178
|
+
return {
|
|
179
|
+
read: arg.length,
|
|
180
|
+
written: buf.length
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let WASM_VECTOR_LEN = 0;
|
|
186
|
+
|
|
187
|
+
const wasmPath = `${__dirname}/typst_wasm_bg.wasm`;
|
|
188
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
189
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
190
|
+
let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports());
|
|
191
|
+
let wasm = wasmInstance.exports;
|
|
Binary file
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
export const memory: WebAssembly.Memory;
|
|
4
|
+
export const typstToHtml: (a: number, b: number, c: number) => void;
|
|
5
|
+
export const typstToHtmlWithMetadata: (a: number, b: number, c: number) => void;
|
|
6
|
+
export const __wbindgen_add_to_stack_pointer: (a: number) => number;
|
|
7
|
+
export const __wbindgen_export: (a: number, b: number) => number;
|
|
8
|
+
export const __wbindgen_export2: (a: number, b: number, c: number, d: number) => number;
|
|
9
|
+
export const __wbindgen_export3: (a: number, b: number, c: number) => void;
|