@bendyline/squisq-formats 2.8.10 → 2.8.11

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.
@@ -1247,6 +1247,7 @@ function titleForFilename(path, mdDoc) {
1247
1247
  // src/html/pageTemplate.ts
1248
1248
  var HTML_PAGE_TEMPLATE_CONTENT_START = "<!-- squisq:content -->";
1249
1249
  var HTML_PAGE_TEMPLATE_CONTENT_END = "<!-- /squisq:content -->";
1250
+ var HTML_PAGE_TEMPLATE_IMAGE_SLOT = "og-image";
1250
1251
  var HtmlPageTemplateError = class extends Error {
1251
1252
  constructor(issues) {
1252
1253
  super(
@@ -1293,6 +1294,10 @@ function analyzeTemplate(html) {
1293
1294
  isTemplate: tags.some(isLightSlot),
1294
1295
  defaultSlotCount: defaultSlots.length,
1295
1296
  namedSlots,
1297
+ usesImage: namedSlots.includes(HTML_PAGE_TEMPLATE_IMAGE_SLOT) || tags.some((tag) => {
1298
+ if (tag.kind !== "start" || tag.name !== "meta" || tag.inert) return false;
1299
+ return IMAGE_URL_META.has(metaKey(tag) ?? "");
1300
+ }),
1296
1301
  errors: errors.sort((a, b) => a.line - b.line)
1297
1302
  }
1298
1303
  };
@@ -1304,19 +1309,24 @@ function applyHtmlPageTemplate(template, content, options = {}) {
1304
1309
  const { tags, slots, inspection } = analyzeTemplate(template);
1305
1310
  if (inspection.errors.length > 0) throw new HtmlPageTemplateError(inspection.errors);
1306
1311
  const values = content.values ?? {};
1307
- const valueFor = (name) => name === "title" ? content.title : values[name];
1312
+ const { image } = content;
1313
+ const valueFor = (name) => name === "title" ? content.title : name === HTML_PAGE_TEMPLATE_IMAGE_SLOT ? void 0 : values[name];
1314
+ const slotMarkup = (name) => {
1315
+ if (name === null) {
1316
+ return `${HTML_PAGE_TEMPLATE_CONTENT_START}
1317
+ ${content.bodyHtml}
1318
+ ${HTML_PAGE_TEMPLATE_CONTENT_END}`;
1319
+ }
1320
+ if (name === HTML_PAGE_TEMPLATE_IMAGE_SLOT) return image && imageElement(image);
1321
+ const value = valueFor(name);
1322
+ return value === void 0 ? void 0 : escapeHtml3(value);
1323
+ };
1308
1324
  const edits = [];
1309
1325
  const replacements = /* @__PURE__ */ new Set();
1310
1326
  for (const slot of slots) {
1311
- const value = slot.name === null ? void 0 : valueFor(slot.name);
1312
- if (slot.name === null || value !== void 0) {
1313
- const replacement = {
1314
- start: slot.start,
1315
- end: slot.end,
1316
- text: slot.name === null ? `${HTML_PAGE_TEMPLATE_CONTENT_START}
1317
- ${content.bodyHtml}
1318
- ${HTML_PAGE_TEMPLATE_CONTENT_END}` : escapeHtml3(value ?? "")
1319
- };
1327
+ const markup = slotMarkup(slot.name);
1328
+ if (markup !== void 0) {
1329
+ const replacement = { start: slot.start, end: slot.end, text: markup };
1320
1330
  edits.push(replacement);
1321
1331
  replacements.add(replacement);
1322
1332
  } else {
@@ -1337,8 +1347,8 @@ ${HTML_PAGE_TEMPLATE_CONTENT_END}` : escapeHtml3(value ?? "")
1337
1347
  }
1338
1348
  for (const tag of tags) {
1339
1349
  if (tag.kind !== "start" || tag.name !== "meta" || tag.inert) continue;
1340
- const key = (attributeValue(tag, "name") ?? attributeValue(tag, "property"))?.toLowerCase();
1341
- const value = key ? metaValue(key, content.title, values.description) : void 0;
1350
+ const key = metaKey(tag);
1351
+ const value = key ? metaValue(key, content.title, values.description, image) : void 0;
1342
1352
  if (value === void 0) continue;
1343
1353
  const contentAttribute = tag.attributes.find((attribute) => attribute.name === "content");
1344
1354
  const text = `content="${escapeAttr2(value)}"`;
@@ -1615,11 +1625,34 @@ function fillTitleText(text, valueFor) {
1615
1625
  }
1616
1626
  var TITLE_META = /* @__PURE__ */ new Set(["og:title", "twitter:title"]);
1617
1627
  var DESCRIPTION_META = /* @__PURE__ */ new Set(["description", "og:description", "twitter:description"]);
1618
- function metaValue(key, title, description) {
1628
+ var IMAGE_URL_META = /* @__PURE__ */ new Set([
1629
+ "og:image",
1630
+ "og:image:url",
1631
+ "og:image:secure_url",
1632
+ "twitter:image",
1633
+ "twitter:image:src"
1634
+ ]);
1635
+ var IMAGE_ALT_META = /* @__PURE__ */ new Set(["og:image:alt", "twitter:image:alt"]);
1636
+ function metaKey(tag) {
1637
+ return (attributeValue(tag, "name") ?? attributeValue(tag, "property"))?.toLowerCase();
1638
+ }
1639
+ function metaValue(key, title, description, image) {
1619
1640
  if (TITLE_META.has(key)) return title;
1620
1641
  if (DESCRIPTION_META.has(key)) return description;
1642
+ if (!image) return void 0;
1643
+ if (IMAGE_URL_META.has(key)) return image.url;
1644
+ if (IMAGE_ALT_META.has(key)) return image.alt;
1645
+ if (key === "og:image:width" || key === "og:image:height") {
1646
+ const size = key === "og:image:width" ? image.width : image.height;
1647
+ return size === void 0 ? void 0 : String(size);
1648
+ }
1649
+ if (key === "og:image:type") return image.type;
1621
1650
  return void 0;
1622
1651
  }
1652
+ function imageElement(image) {
1653
+ const size = image.width !== void 0 && image.height !== void 0 ? ` width="${String(image.width)}" height="${String(image.height)}"` : "";
1654
+ return `<img src="${escapeAttr2(image.src ?? image.url)}" alt="${escapeAttr2(image.alt ?? "")}"${size}>`;
1655
+ }
1623
1656
  function headInsertionPoint(tags) {
1624
1657
  for (const tag of tags) {
1625
1658
  if (tag.inert) continue;
@@ -2691,6 +2724,7 @@ export {
2691
2724
  collectLinkRefs,
2692
2725
  HTML_PAGE_TEMPLATE_CONTENT_START,
2693
2726
  HTML_PAGE_TEMPLATE_CONTENT_END,
2727
+ HTML_PAGE_TEMPLATE_IMAGE_SLOT,
2694
2728
  HtmlPageTemplateError,
2695
2729
  isHtmlPageTemplate,
2696
2730
  inspectHtmlPageTemplate,
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  convert,
8
8
  defaultRegistry
9
- } from "./chunk-MJTPWLVV.js";
9
+ } from "./chunk-JINVU6QA.js";
10
10
  import {
11
11
  ConversionError
12
12
  } from "./chunk-KXOZMWBS.js";
@@ -48,6 +48,8 @@ declare function extractFilename(path: string): string;
48
48
  * - `<slot name="title">` receives the document title, and
49
49
  * `<slot name="KEY">` the caller's value for `KEY` (typically a
50
50
  * frontmatter field), inserted as text;
51
+ * - `<slot name="og-image">` receives the page's social preview picture, when
52
+ * the caller supplies one, as an `<img>`;
51
53
  * - a slot with no value is replaced by its children — the standard fallback
52
54
  * content — so a template opened directly in a browser previews with its
53
55
  * placeholders.
@@ -57,7 +59,7 @@ declare function extractFilename(path: string): string;
57
59
  *
58
60
  * `<title>` cannot contain elements, so slots written inside it are matched
59
61
  * as text; a `<title>` without one is replaced by the document title.
60
- * Existing description and Open Graph / Twitter title and description
62
+ * Existing description and Open Graph / Twitter title, description, and image
61
63
  * `<meta>` tags receive the document's values. Relative URLs in the template
62
64
  * are rewritten so they still resolve from the generated page's location.
63
65
  *
@@ -69,6 +71,28 @@ declare function extractFilename(path: string): string;
69
71
  declare const HTML_PAGE_TEMPLATE_CONTENT_START = "<!-- squisq:content -->";
70
72
  /** Marks the end of the injected document body in a templated page. */
71
73
  declare const HTML_PAGE_TEMPLATE_CONTENT_END = "<!-- /squisq:content -->";
74
+ /** The named slot that receives the page's social preview picture. */
75
+ declare const HTML_PAGE_TEMPLATE_IMAGE_SLOT = "og-image";
76
+ /** A page's social preview picture: its Open Graph and Twitter card image. */
77
+ interface HtmlPageTemplateImage {
78
+ /**
79
+ * URL written to the image `<meta>` tags. Social sites expect an absolute
80
+ * URL; a relative one is written as given.
81
+ */
82
+ url: string;
83
+ /**
84
+ * URL the `og-image` slot's `<img>` loads, typically relative to the page
85
+ * so it also shows before the site is published. Default: {@link url}.
86
+ */
87
+ src?: string;
88
+ /** Size in pixels. */
89
+ width?: number;
90
+ height?: number;
91
+ /** Text alternative. */
92
+ alt?: string;
93
+ /** Media type, such as `image/png`. */
94
+ type?: string;
95
+ }
72
96
  interface HtmlPageTemplateContent {
73
97
  /** Rendered document body. Inserted as-is; the caller sanitizes it. */
74
98
  bodyHtml: string;
@@ -82,9 +106,16 @@ interface HtmlPageTemplateContent {
82
106
  title: string;
83
107
  /**
84
108
  * Text for named slots, keyed by slot name. `description` also fills
85
- * description `<meta>` tags. `title` comes from {@link title}.
109
+ * description `<meta>` tags. `title` comes from {@link title}, and
110
+ * `og-image` from {@link image}.
86
111
  */
87
112
  values?: Readonly<Record<string, string | undefined>>;
113
+ /**
114
+ * The page's social preview picture. Fills the template's `og-image` slot
115
+ * and the Open Graph / Twitter image `<meta>` tags it declares; without
116
+ * one, both keep what the template wrote.
117
+ */
118
+ image?: HtmlPageTemplateImage;
88
119
  }
89
120
  interface HtmlPageTemplateOptions {
90
121
  /**
@@ -111,6 +142,12 @@ interface HtmlPageTemplateInspection {
111
142
  defaultSlotCount: number;
112
143
  /** Distinct named slots, in document order. */
113
144
  namedSlots: string[];
145
+ /**
146
+ * True when a supplied {@link HtmlPageTemplateContent.image} would appear
147
+ * in the page: the template has an `og-image` slot or an Open Graph /
148
+ * Twitter image `<meta>` tag. A host can skip drawing a picture otherwise.
149
+ */
150
+ usesImage: boolean;
114
151
  /** Problems that make {@link applyHtmlPageTemplate} refuse the template. */
115
152
  errors: HtmlPageTemplateIssue[];
116
153
  }
@@ -350,4 +387,4 @@ declare function docToHtml(doc: Doc, options: HtmlExportOptions): string;
350
387
  */
351
388
  declare function docToHtmlZip(doc: Doc, options: HtmlZipExportOptions): Promise<Blob>;
352
389
 
353
- export { HTML_PAGE_TEMPLATE_CONTENT_END, HTML_PAGE_TEMPLATE_CONTENT_START, type HtmlBundleOptions, HtmlExportOptions, type HtmlPageTemplateContent, HtmlPageTemplateError, type HtmlPageTemplateInspection, type HtmlPageTemplateIssue, type HtmlPageTemplateOptions, type HtmlZipExportOptions, type PlainHtmlBundleOptions, applyHtmlPageTemplate, arrayBufferToBase64DataUrl, collectLinkRefs, docToHtml, docToHtmlZip, extractFilename, extractHtmlPageTemplateContent, inferMimeType, inspectHtmlPageTemplate, isHtmlPageTemplate, markdownDocsToHtmlBundle, markdownDocsToPlainHtmlBundle };
390
+ export { HTML_PAGE_TEMPLATE_CONTENT_END, HTML_PAGE_TEMPLATE_CONTENT_START, HTML_PAGE_TEMPLATE_IMAGE_SLOT, type HtmlBundleOptions, HtmlExportOptions, type HtmlPageTemplateContent, HtmlPageTemplateError, type HtmlPageTemplateImage, type HtmlPageTemplateInspection, type HtmlPageTemplateIssue, type HtmlPageTemplateOptions, type HtmlZipExportOptions, type PlainHtmlBundleOptions, applyHtmlPageTemplate, arrayBufferToBase64DataUrl, collectLinkRefs, docToHtml, docToHtmlZip, extractFilename, extractHtmlPageTemplateContent, inferMimeType, inspectHtmlPageTemplate, isHtmlPageTemplate, markdownDocsToHtmlBundle, markdownDocsToPlainHtmlBundle };
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  HTML_PAGE_TEMPLATE_CONTENT_END,
3
3
  HTML_PAGE_TEMPLATE_CONTENT_START,
4
+ HTML_PAGE_TEMPLATE_IMAGE_SLOT,
4
5
  HtmlPageTemplateError,
5
6
  applyHtmlPageTemplate,
6
7
  collectImagePaths,
@@ -18,7 +19,7 @@ import {
18
19
  markdownDocToPlainHtmlParts,
19
20
  markdownDocsToHtmlBundle,
20
21
  markdownDocsToPlainHtmlBundle
21
- } from "../chunk-QPJHAMOW.js";
22
+ } from "../chunk-4SPMLGOG.js";
22
23
  import {
23
24
  arrayBufferToBase64DataUrl,
24
25
  extractFilename,
@@ -30,6 +31,7 @@ import "../chunk-JZR32FON.js";
30
31
  export {
31
32
  HTML_PAGE_TEMPLATE_CONTENT_END,
32
33
  HTML_PAGE_TEMPLATE_CONTENT_START,
34
+ HTML_PAGE_TEMPLATE_IMAGE_SLOT,
33
35
  HtmlPageTemplateError,
34
36
  applyHtmlPageTemplate,
35
37
  arrayBufferToBase64DataUrl,
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  htmlToMarkdown,
6
6
  htmlToMarkdownDoc,
7
7
  htmlToMarkdownDocSync
8
- } from "./chunk-QPJHAMOW.js";
8
+ } from "./chunk-4SPMLGOG.js";
9
9
  import {
10
10
  docToEpub,
11
11
  markdownDocToEpub
@@ -29,7 +29,7 @@ import {
29
29
  renderOutsideInDocument,
30
30
  withOutsideInMarkdownEditing,
31
31
  withOutsideInMetadata
32
- } from "./chunk-NCDY3EOL.js";
32
+ } from "./chunk-D2JA3MLG.js";
33
33
  import {
34
34
  OUTSIDE_IN_FORMAT_IDS,
35
35
  OUTSIDE_IN_OUTPUT_TEMPLATE_KEY,
@@ -45,7 +45,7 @@ import {
45
45
  defaultRegistry,
46
46
  prepareConversion,
47
47
  resolveConversionLimits
48
- } from "./chunk-MJTPWLVV.js";
48
+ } from "./chunk-JINVU6QA.js";
49
49
  import {
50
50
  ConversionError
51
51
  } from "./chunk-KXOZMWBS.js";
@@ -7,7 +7,7 @@ import {
7
7
  renderOutsideInDocument,
8
8
  withOutsideInMarkdownEditing,
9
9
  withOutsideInMetadata
10
- } from "../chunk-NCDY3EOL.js";
10
+ } from "../chunk-D2JA3MLG.js";
11
11
  import {
12
12
  OUTSIDE_IN_FORMAT_IDS,
13
13
  OUTSIDE_IN_OUTPUT_TEMPLATE_KEY,
@@ -15,7 +15,7 @@ import {
15
15
  isOutsideInTargetPath,
16
16
  resolveOutsideInLayout
17
17
  } from "../chunk-WJTC5OD6.js";
18
- import "../chunk-MJTPWLVV.js";
18
+ import "../chunk-JINVU6QA.js";
19
19
  import "../chunk-KXOZMWBS.js";
20
20
  import "../chunk-7AWFHP5U.js";
21
21
  export {
@@ -9,7 +9,7 @@ import {
9
9
  defaultRegistry,
10
10
  prepareConversion,
11
11
  resolveConversionLimits
12
- } from "../chunk-MJTPWLVV.js";
12
+ } from "../chunk-JINVU6QA.js";
13
13
  import {
14
14
  ConversionError
15
15
  } from "../chunk-KXOZMWBS.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bendyline/squisq-formats",
3
- "version": "2.8.10",
3
+ "version": "2.8.11",
4
4
  "description": "Document format converters — DOCX, PDF, OOXML import/export",
5
5
  "license": "MIT",
6
6
  "author": "Bendyline",
@@ -135,7 +135,7 @@
135
135
  "typecheck": "tsc --noEmit"
136
136
  },
137
137
  "dependencies": {
138
- "@bendyline/squisq": "2.13.8",
138
+ "@bendyline/squisq": "2.13.9",
139
139
  "@pdf-lib/fontkit": "1.1.1",
140
140
  "@pdf-lib/upng": "1.0.1",
141
141
  "@xmldom/xmldom": "0.9.12",
File without changes