@fraqjs/plugin-takumi 0.1.0 → 0.1.2

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/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Context, Disposable } from "@fraqjs/fraq";
2
2
  import { ConstructRendererOptions, Font, RenderOptions, Renderer } from "@takumi-rs/core";
3
3
  import { ReactElementLike } from "@takumi-rs/helpers";
4
+ import { EmojiType } from "@takumi-rs/helpers/emoji";
4
5
  import { ReactNode } from "react";
5
6
 
6
7
  //#region src/service.d.ts
@@ -15,15 +16,18 @@ interface PathBasedFontDetails {
15
16
  style?: 'normal' | 'italic' | 'oblique' | `oblique ${number}deg` | (string & {});
16
17
  }
17
18
  declare class TakumiService implements Disposable {
18
- private readonly ctx;
19
+ private readonly ctx?;
19
20
  readonly renderer: Renderer;
20
21
  private readonly abortController;
21
22
  private readonly registeredFontFamilies;
22
23
  private onFontRegisterConflict;
23
- constructor(ctx: Context, options: TakumiServiceOptions);
24
+ constructor(options?: TakumiServiceOptions, ctx?: Context | undefined);
24
25
  registerFontFamily(family: string, fonts: (string | PathBasedFontDetails | Font)[], signal?: AbortSignal): Promise<void>;
25
26
  renderJsx(jsx: ReactNode | ReactElementLike, renderOptions?: RenderOptions, signal?: AbortSignal): Promise<Buffer>;
27
+ renderJsxWithEmoji(jsx: ReactNode | ReactElementLike, renderOptions?: RenderOptions, signal?: AbortSignal, emojiType?: EmojiType): Promise<Buffer>;
26
28
  renderHtml(html: string, renderOptions?: RenderOptions, signal?: AbortSignal): Promise<Buffer>;
29
+ renderHtmlWithEmoji(html: string, renderOptions?: RenderOptions, signal?: AbortSignal, emojiType?: EmojiType): Promise<Buffer>;
30
+ private processEmoji;
27
31
  dispose(): void;
28
32
  }
29
33
  //#endregion
@@ -31,6 +35,7 @@ declare class TakumiService implements Disposable {
31
35
  interface TakumiPluginOptions extends TakumiServiceOptions {
32
36
  loadBuiltinFonts?: boolean;
33
37
  }
38
+ declare function loadBuiltinFontsForService(service: TakumiService): Promise<void>;
34
39
  declare const TakumiPlugin: import("@fraqjs/fraq").Plugin<[options?: TakumiPluginOptions | undefined], import("@fraqjs/fraq").Injection | undefined, import("@fraqjs/fraq").Injection | undefined>;
35
40
  //#endregion
36
- export { PathBasedFontDetails, TakumiPlugin, TakumiPlugin as default, TakumiPluginOptions, TakumiService, TakumiServiceOptions };
41
+ export { PathBasedFontDetails, TakumiPlugin, TakumiPlugin as default, TakumiPluginOptions, TakumiService, TakumiServiceOptions, loadBuiltinFontsForService };
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { definePlugin } from "@fraqjs/fraq";
3
- import { Renderer } from "@takumi-rs/core";
3
+ import { Renderer, extractResourceUrls } from "@takumi-rs/core";
4
+ import { fetchResources } from "@takumi-rs/helpers";
5
+ import { extractEmojis } from "@takumi-rs/helpers/emoji";
4
6
  import { fromHtml } from "@takumi-rs/helpers/html";
5
7
  import { fromJsx } from "@takumi-rs/helpers/jsx";
6
8
  import fs from "node:fs/promises";
@@ -21,19 +23,21 @@ var TakumiService = class {
21
23
  abortController = new AbortController();
22
24
  registeredFontFamilies = /* @__PURE__ */ new Set();
23
25
  onFontRegisterConflict;
24
- constructor(ctx, options) {
26
+ constructor(options, ctx) {
25
27
  this.ctx = ctx;
26
- this.renderer = new Renderer(options.renderer);
27
- this.onFontRegisterConflict = options.onFontRegisterConflict ?? "warn-and-ignore";
28
+ this.renderer = new Renderer(options?.renderer);
29
+ this.onFontRegisterConflict = options?.onFontRegisterConflict ?? "warn-and-ignore";
28
30
  }
29
31
  async registerFontFamily(family, fonts, signal) {
30
32
  if (this.registeredFontFamilies.has(family)) {
31
33
  const message = `Font family "${family}" has already been registered.`;
32
34
  if (this.onFontRegisterConflict === "error") throw new Error(message);
33
35
  else if (this.onFontRegisterConflict === "warn-and-ignore") {
34
- this.ctx.logger.warn(`${message} Ignoring new registration.`);
36
+ if (this.ctx) this.ctx.logger.warn(`${message} Ignoring new registration.`);
37
+ else console.warn(`${message} Ignoring new registration.`);
35
38
  return;
36
- } else if (this.onFontRegisterConflict === "warn-and-replace") this.ctx.logger.warn(`${message} Replacing previous registration.`);
39
+ } else if (this.onFontRegisterConflict === "warn-and-replace") if (this.ctx) this.ctx.logger.warn(`${message} Replacing previous registration.`);
40
+ else console.warn(`${message} Replacing previous registration.`);
37
41
  }
38
42
  await this.renderer.loadFonts(await Promise.all(fonts.map(async (font) => {
39
43
  if (typeof font === "string") return {
@@ -56,30 +60,56 @@ var TakumiService = class {
56
60
  const { node, stylesheets } = await fromJsx(jsx);
57
61
  return this.renderer.render(node, withMergedStylesheets(stylesheets, renderOptions), combineAbortSignals(signal, this.abortController.signal));
58
62
  }
63
+ async renderJsxWithEmoji(jsx, renderOptions, signal, emojiType = "noto") {
64
+ const { node, stylesheets } = await fromJsx(jsx);
65
+ const { node: processedNode, fetchedResources } = await this.processEmoji(node, emojiType);
66
+ return this.renderer.render(processedNode, withMergedStylesheets(stylesheets, {
67
+ ...renderOptions,
68
+ fetchedResources: [...fetchedResources, ...renderOptions?.fetchedResources ?? []]
69
+ }), combineAbortSignals(signal, this.abortController.signal));
70
+ }
59
71
  async renderHtml(html, renderOptions, signal) {
60
72
  const { node, stylesheets } = fromHtml(html);
61
73
  return this.renderer.render(node, withMergedStylesheets(stylesheets, renderOptions), combineAbortSignals(signal, this.abortController.signal));
62
74
  }
75
+ async renderHtmlWithEmoji(html, renderOptions, signal, emojiType = "noto") {
76
+ const { node, stylesheets } = fromHtml(html);
77
+ const { node: processedNode, fetchedResources } = await this.processEmoji(node, emojiType);
78
+ return this.renderer.render(processedNode, withMergedStylesheets(stylesheets, {
79
+ ...renderOptions,
80
+ fetchedResources: [...fetchedResources, ...renderOptions?.fetchedResources ?? []]
81
+ }), combineAbortSignals(signal, this.abortController.signal));
82
+ }
83
+ async processEmoji(node, emojiType = "twemoji") {
84
+ const processedNode = extractEmojis(node, emojiType);
85
+ return {
86
+ node: processedNode,
87
+ fetchedResources: await fetchResources(extractResourceUrls(processedNode))
88
+ };
89
+ }
63
90
  dispose() {
64
91
  this.abortController.abort();
65
92
  }
66
93
  };
67
94
  //#endregion
68
95
  //#region src/index.ts
96
+ async function loadBuiltinFontsForService(service) {
97
+ const require = createRequire(import.meta.url);
98
+ await service.registerFontFamily("Inter", [require.resolve("../fonts/Inter-VariableFont_opsz,wght.ttf"), require.resolve("../fonts/Inter-Italic-VariableFont_opsz,wght.ttf")]);
99
+ await service.registerFontFamily("Roboto Mono", [require.resolve("../fonts/RobotoMono-VariableFont_wght.ttf"), require.resolve("../fonts/RobotoMono-Italic-VariableFont_wght.ttf")]);
100
+ await service.registerFontFamily("Noto Sans SC", [require.resolve("../fonts/NotoSansSC-VariableFont_wght.ttf")]);
101
+ }
69
102
  const TakumiPlugin = definePlugin({
70
103
  name: "takumi",
71
104
  provides: [TakumiService],
72
105
  async apply(ctx, options) {
73
- const service = new TakumiService(ctx, options ?? {});
106
+ const service = new TakumiService(options, ctx);
74
107
  if (options?.loadBuiltinFonts ?? true) {
75
108
  ctx.logger.debug("Loading built-in fonts...");
76
- const require = createRequire(import.meta.url);
77
- await service.registerFontFamily("Inter", [require.resolve("../fonts/Inter-VariableFont_opsz,wght.ttf"), require.resolve("../fonts/Inter-Italic-VariableFont_opsz,wght.ttf")]);
78
- await service.registerFontFamily("Roboto Mono", [require.resolve("../fonts/RobotoMono-VariableFont_wght.ttf"), require.resolve("../fonts/RobotoMono-Italic-VariableFont_wght.ttf")]);
79
- await service.registerFontFamily("Noto Sans SC", [require.resolve("../fonts/NotoSansSC-VariableFont_wght.ttf")]);
109
+ await loadBuiltinFontsForService(service);
80
110
  }
81
111
  ctx.provide(TakumiService, service);
82
112
  }
83
113
  });
84
114
  //#endregion
85
- export { TakumiPlugin, TakumiPlugin as default, TakumiService };
115
+ export { TakumiPlugin, TakumiPlugin as default, TakumiService, loadBuiltinFontsForService };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fraqjs/plugin-takumi",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "description": "Takumi integration plugin for Fraq",
6
6
  "files": [
7
7
  "dist",