Skip to content

Commit

Permalink
feat(pkg::core): add snippet library
Browse files Browse the repository at this point in the history
+ prepare for the all-in-one library.
  • Loading branch information
Myriad-Dreamin committed Oct 29, 2023
1 parent 2e59fe6 commit f298282
Show file tree
Hide file tree
Showing 17 changed files with 696 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gh_pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | bash
- name: Download & install typst-book
run: |
curl -L https://github.com/Myriad-Dreamin/typst-book/releases/download/v0.1.2-nightly1/typst-book-x86_64-unknown-linux-gnu.tar.gz | tar -xvz
curl -L https://github.com/Myriad-Dreamin/typst-book/releases/download/v0.1.2-nightly2/typst-book-x86_64-unknown-linux-gnu.tar.gz | tar -xvz
chmod +x typst-book-x86_64-unknown-linux-gnu/bin/typst-book
sudo cp typst-book-x86_64-unknown-linux-gnu/bin/typst-book /usr/bin/typst-book
- name: Run sccache-cache
Expand Down
3 changes: 2 additions & 1 deletion docs/cookery/book.typ
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@
#prefix-chapter("introduction.typ")[Introduction]
= Quickstart
- #chapter("get-started.typ", section: "1")[Get started]
- #chapter(none, section: "1.1")[All-in-one JavaScript Library]
- #chapter("guide/all-in-one.typ", section: "1.1")[All-in-one JavaScript Library]
- #chapter("guide/compilers.typ", section: "2")[Compilers]
- #chapter("guide/compiler/ts-cli.typ", section: "2.1")[Command Line Interface]
- #chapter("guide/compiler/service.typ", section: "2.2")[Compiler Service Library]
- #chapter("guide/compiler/serverless.typ", section: "2.3")[Serverless (In-browser) Compiler]
- #chapter("guide/compiler/node.typ", section: "2.4")[Compiler for Node.js]
- #chapter("guide/e2e-renderers.typ", section: "3")[End-to-end Renderers]
- #chapter(none, section: "3.1")[React Library]
- #chapter(none, section: "3.2")[Angular Library]
Expand Down
147 changes: 147 additions & 0 deletions docs/cookery/guide/all-in-one.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#import "/docs/cookery/book.typ": book-page

#show: book-page.with(title: "All-in-one (Simplified) JavaScript Library")

= All-in-one (Simplified) JavaScript Library

#let snippet-source = "https://github.com/Myriad-Dreamin/typst.ts/blob/main/packages/typst.ts/src/contrib/snippet.mts"
#let snippet-lib = link(snippet-source)[`snippet`]

The most simple examples always work with #snippet-lib utility library, an all-in-one JavaScript Library with simplified API interfaces:

```ts
import { $typst } from '@myriaddreamin/typst.ts/dist/esm/contrib/snippet.mjs';
console.log((await $typst.svg({
mainContent: 'Hello, typst!' })).length);
// :-> 7317
```

However, it is less flexible and stable than the underlying interfaces, the `TypstCompiler` and `TypstRenderer`. If you've become more familar with typst.ts, we recommend you rewrite your library with underlying interfaces according to example usage shown by the #snippet-lib library.

Note: If your script targets to *CommonJS*, you should import it in *CommonJS* path instead of In *ES Module* path:

```ts
const { createTypstCompiler } = require(
'@myriaddreamin/typst.ts/dist/cjs/compiler.cjs');
```

== Examples

Here are some examples for the #snippet-lib utility library.

=== Example: Use the _global shared_ compiler instance:

```typescript
import { $typst } from '@myriaddreamin/typst.ts/dist/esm/contrib/snippet.mjs';
```

Note: if you want to compile multiple documents, you should create a new instance for each compilation work or maintain the shared state on the utility instance `$typst` carefully,
because the compilation process may change the state of that.

=== Example: Create an instance of the utility class:

```typescript
// optional renderer instance
const renderer = enableRendering ?? (() => {
return createGlobalRenderer(createTypstRenderer,
undefined /* pdfJsLib */, initOptions);
});
const $typst = new TypstSnippet(() => {
return createGlobalCompiler(createTypstCompiler,
initOptions);
}, renderer);
```

=== Example: get output from input

get output with *single file*:

```ts
const mainContent = 'Hello, typst!';
// into vector format
await $typst.vector({ mainContent });
// into svg format
await $typst.svg({ mainContent });
// into pdf format
await $typst.pdf({ mainContent });
// into canvas operations
await $typst.canvas(div, { mainContent });
```

get output with *multiple files*:

```ts
// the default value of main path is '/main.typ'
await $typst.addSource('/main.typ', mainContent);
// set path to main file
const mainFilePath = '/tasks/1/main.typ';
await $typst.setMainFilePath(mainFilePath)
await $typst.addSource(mainFilePath, mainContent);
```

What is quite important is that, when you are running multiple tasks asynchronously or in parallel, the call pattern `await $typst.xxx({ mainContent });` is unsafe (introduces undefined behavior). Insteadly you should call compilation by specifying path to the main file:

```ts
const mainFilePath = '/tasks/1/main.typ';
await $typst.addSource(mainFilePath, mainContent);
// compile source of path
await $typst.svg({ mainFilePath });
```

get output with *binary files*:

```ts
const encoder = new TextEncoder();
// add a json file (utf8)
compiler.mapShadow('/assets/data.json', encoder.encode(jsonData));
// remove a json file
compiler.unmapShadow('/assets/data.json');
// add an image file
const pngData = await fetch(...).arrayBuffer();
compiler.mapShadow('/assets/tiger.png', new Uint8Array(pngData));
```

clean up shadow files for underlying access model:

```ts
compiler.resetShadow();
```

Note: this function will also clean all files added by `addSource`.

=== Example: reuse compilation result

The compilation result could be stored in an artifact in #link("https://github.com/Myriad-Dreamin/typst.ts/blob/main/docs/proposals/8-vector-representation-for-rendering.typ")[_Vector Format_], so that you could decouple compilation from rendering or make high-level cache compilation.

```ts
const vectorData = await $typst.vector({ mainContent });
// into svg format
await $typst.svg({ vectorData });
// into canvas operations
await $typst.canvas(div, { vectorData });
```

Note: the compilation is already cached by typst's `comemo` implicitly.

== Specify extra init options

The extra init options must be at the start of the main routine, or accurately before all invocations.

```ts
// Example: cache default fonts to file system
$typst.setCompilerInitOptions(await cachedFontInitOptoins());
// specify init options to renderer
$typst.setRendererInitOptions(rendererInitOptions);
// wire other `pdfJsLib` instance for renderer
$typst.setPdfjsModule(pdfJsLib);
// The compiler instance is initialized in this call.
await $typst.svg({ mainContent });
```

== Specify extra render options

See #link(snippet-source)[comments on source] for more details.
7 changes: 7 additions & 0 deletions docs/cookery/guide/compiler/node.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import "/docs/cookery/book.typ": book-page

#show: book-page.with(title: "Compiler for Node.js")

= Compiler for Node.js

We are actively work on documentation, but you could take reference from #link("https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/serverless.html")[Serverless Compiler]
71 changes: 70 additions & 1 deletion docs/cookery/guide/compiler/serverless.typ
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,73 @@

= Serverless Compiler

Sample page
The most simple examples always work with `snippet` utility library:

```ts
import { $typst } from '@myriaddreamin/typst.ts/dist/esm/contrib/snippet.mjs';
console.log((await $typst.svg({
mainContent: 'Hello, typst!' })).length);
// :-> 7317
```

Please

Quick example:

```ts
import { createTypstCompiler } from '@myriaddreamin/typst.ts';
const mainFile = '/main.typ';
const cc /* compiler */ = createTypstCompiler();
await cc.init();
cc.addSource(mainFile, 'Hello, typst!');
await cc.compile({ mainFilePath: mainFile });
```

Note: For #link("https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking")[_tree-shaking_], you should import it with longer path:

In *ES Module* path:

```ts
import { createTypstCompiler } from '@myriaddreamin/typst.ts/dist/esm/compiler.mjs';
```

Or in *CommonJS* path:

```ts
const { createTypstCompiler } = require('@myriaddreamin/typst.ts/dist/cjs/compiler.cjs');
```

== Add or remove source/binary files

You can also use the `{map,unmap,reset}Shadow` function to manipulate any text or binary file data for typst compiler. They will shadow the file access from provided access model directly in memory.

The `mapShadow(path: string, content: Uint8Array): void;` resembles `addSource(path: string, source: string): void;`, but retrieves some binary data without guessing the underlying encoding.

Example usage:

```ts
const encoder = new TextEncoder();
// add a json file (utf8)
compiler.mapShadow('/assets/data.json', encoder.encode(jsonData));
// remove a json file
compiler.unmapShadow('/assets/data.json');
// clean up all shadow files (Note: this function will also clean all files added by `addSource`)
compiler.resetShadow();
// add an image file
const pngData = await fetch(...).arrayBuffer();
compiler.mapShadow('/assets/tiger.png', new Uint8Array(pngData));
```

== Specify output format

Export document as #link("https://github.com/Myriad-Dreamin/typst.ts/blob/main/docs/proposals/8-vector-representation-for-rendering.typ")[_Vector Format_] which can then load to the renderer to render the document.

```ts
const artifactData = await compiler.compile({
mainFilePath: '/main.typ',
// the default value of format field:
// format: 'vector',
});
```
7 changes: 6 additions & 1 deletion docs/cookery/guide/compilers.typ
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@

= Compilers

Sample page
See:

+ #link("https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/ts-cli.html")[Command Line Interface]
+ #link("https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/service.html")[Compiler Service Library]
+ #link("https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/serverless.html")[Serverless (In-browser) Compiler]
+ #link("https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/node.html")[Compiler for Node.js]
8 changes: 7 additions & 1 deletion docs/cookery/templates/page.typ
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
"Source Han Serif TC",
)

#let main-font-size = if is-web-target {
16pt
} else {
12pt
}

#let code-font = (
"BlexMono Nerd Font Mono",
// typst-book's embedded font
Expand Down Expand Up @@ -98,7 +104,7 @@
) if is-web-target;

// set text style
set text(font: main-font, size: 16pt, fill: main-color, lang: "en")
set text(font: main-font, size: main-font-size, fill: main-color, lang: "en")

set line(stroke: main-color)

Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# typst-ts-web-compiler

See [Typst.ts](https://github.com/Myriad-Dreamin/typst.ts)
The compiler can run in both the browser and node.js. See documentation for details:

- [Serverless Compiler](https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/serverless.html)
- [Compiler for Node.js](https://myriad-dreamin.github.io/typst.ts/cookery/guide/compiler/node.html)
3 changes: 3 additions & 0 deletions packages/templates/node.js-compiler-next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@

```shell
yarn install
# use snippet below to run
npx tsc && node ./dist/esm/main.snippet.js
# full example
npx tsc && node ./dist/esm/main.js
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { preloadFontAssets } from '@myriaddreamin/typst.ts/dist/cjs/options.init.cjs';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import * as path from 'path';
import { HttpsProxyAgent } from 'https-proxy-agent';

export async function cachedFontInitOptoins() {
const fetcher = (await import('node-fetch')).default;
const dataDir =
process.env.APPDATA ||
(process.platform == 'darwin'
? process.env.HOME + '/Library/Preferences'
: process.env.HOME + '/.local/share');

const cacheDir = path.join(dataDir, 'typst/fonts');

return {
beforeBuild: [
preloadFontAssets({
assets: ['text', 'cjk', 'emoji'],
fetcher: async (url: URL | RequestInfo, init?: RequestInit | undefined) => {
const cachePath = path.join(cacheDir, url.toString().replace(/[^a-zA-Z0-9]/g, '_'));
if (existsSync(cachePath)) {
const font_res = {
arrayBuffer: async () => {
return readFileSync(cachePath).buffer;
},
};

return font_res as any;
}

console.log('loading remote font:', url);
const proxyOption = process.env.HTTPS_PROXY
? { agent: new HttpsProxyAgent(process.env.HTTPS_PROXY) }
: {};

const font_res = await fetcher(url as any, {
...proxyOption,
...((init as any) || {}),
});
const buffer = await font_res.arrayBuffer();
mkdirSync(path.dirname(cachePath), { recursive: true });
writeFileSync(cachePath, Buffer.from(buffer));
font_res.arrayBuffer = async () => {
return buffer;
};
return font_res as any;
},
}),
],
};
}
15 changes: 15 additions & 0 deletions packages/templates/node.js-compiler-next/src/main.snippet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { $typst } from '@myriaddreamin/typst.ts/dist/cjs/contrib/snippet.cjs';

import { cachedFontInitOptoins } from './cached-font-middleware';

async function main() {
$typst.setCompilerInitOptions(await cachedFontInitOptoins());

const svg = await $typst.svg({
mainContent: 'Hello, typst!',
});

console.log('Renderer works exactly! The rendered SVG file:', svg.length);
}

main();
Loading

0 comments on commit f298282

Please sign in to comment.